Android Sensor Input类型 (五) Sensor HAL NativeSensorManager

2024-04-18 10:58

本文主要是介绍Android Sensor Input类型 (五) Sensor HAL NativeSensorManager,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

NativeSensorManager

代码路径:

code/hardware/qcom/sensors/NativeSensorManager.cpp
code/hardware/qcom/sensors/NativeSensorManager.h

NativeSensorManager类定义

class NativeSensorManager : public Singleton<NativeSensorManager> {friend class Singleton<NativeSensorManager>;NativeSensorManager();~NativeSensorManager();struct sensor_t sensor_list[MAX_SENSORS]; //!< sensorlist数组 >! NoteBy: yujixuanstruct SensorContext context[MAX_SENSORS]; //!< context数组 >! NoteBy: yujixuanstruct SensorEventMap event_list[MAX_SENSORS]; //!< 用于上报的evnet 数组 >! NoteBy: yujixuanstatic const struct SysfsMap node_map[]; //sysfs 设备节点名static const struct sensor_t virtualSensorList[];static char virtualSensorName[][SYSFS_MAXLEN];  //虚拟sensor相关int mSensorCount;bool mScanned;int mEventCount;DefaultKeyedVector<int32_t, struct SensorContext*> type_map;DefaultKeyedVector<int32_t, struct SensorContext*> handle_map;DefaultKeyedVector<int, struct SensorContext*> fd_map;void compositeVirtualSensorName(const char *sensor_name, char *chip_name, int type);int getNode(char *buf, char *path, const struct SysfsMap *map);int getSensorListInner();int getDataInfo();int registerListener(struct SensorContext *hw, struct SensorContext *virt);int initCalibrate(const SensorContext *list);int initVirtualSensor(struct SensorContext *ctx, int handle, struct sensor_t info);int addDependency(struct SensorContext *ctx, int handle);int getEventPath(const char *sysfs_path, char *event_path);int getEventPathOld(const struct SensorContext *list, char *event_path);
public:int getSensorList(const sensor_t **list);  //!< 获取sensor列表 >! NoteBy: yujixuaninline SensorContext* getInfoByFd(int fd) { return fd_map.valueFor(fd); };inline SensorContext* getInfoByHandle(int handle) { return handle_map.valueFor(handle); };inline SensorContext* getInfoByType(int type) { return type_map.valueFor(type); };int getSensorCount() {return mSensorCount;}void dump();int hasPendingEvents(int handle);int activate(int handle, int enable);int setDelay(int handle, int64_t ns);int readEvents(int handle, sensors_event_t *data, int count); //!< 读取events >! NoteBy: yujixuanint calibrate(int handle, struct cal_cmd_t *para);int batch(int handle, int64_t sample_ns, int64_t latency_ns);int flush(int handle);
};

class NativeSensorManager 继承了Singleton,单例模式,只存在一个实例对象,即在SensorHal 中被多次访问使用的sm;

通过NativeSensorManager class的定义可知 主要有以下内容:

  • 定义维护了 在SensorHAL 中由 sensor_t 组成的 sensor_list 数组;
  • 定义由SensorContext 组成的 Context数组;
  • 定义了SensorEventMap 组成的 event_list 数组;
  • 定义了用于记录sensor 在sys中设备节点路径的 SysfsMap

SysfsMap node_map

struct SysfsMap {int offset;const char *node;int type;int required;
};
const struct SysfsMap NativeSensorManager::node_map[] = {{offsetof(struct sensor_t, name), SYSFS_NAME, TYPE_STRING, 1},{offsetof(struct sensor_t, vendor), SYSFS_VENDOR, TYPE_STRING, 1},{offsetof(struct sensor_t, version), SYSFS_VERSION, TYPE_INTEGER, 1},{offsetof(struct sensor_t, type), SYSFS_TYPE, TYPE_INTEGER, 1},{offsetof(struct sensor_t, maxRange), SYSFS_MAXRANGE, TYPE_FLOAT, 1},{offsetof(struct sensor_t, resolution), SYSFS_RESOLUTION, TYPE_FLOAT, 1},{offsetof(struct sensor_t, power), SYSFS_POWER, TYPE_FLOAT, 1},
......
};

SensorEventMap

struct SensorEventMap {char data_name[80];char data_path[PATH_MAX];
};

继承Singleton 单例模式

前面可知,NativeSensorManager : public Singleton,集成了Sigleton, 是单例模式;

Singleton模板类,指定了 template的类型,T为 NativeSensorManager; (note:C++模板)

在NativeSensorManager.cpp的最开始,创建了实例;

ANDROID_SINGLETON_STATIC_INSTANCE(NativeSensorManager);

代码路径:system/core/include/utils/Singleton.h

template <typename TYPE>
class ANDROID_API Singleton
{
public:static TYPE& getInstance() {Mutex::Autolock _l(sLock);TYPE* instance = sInstance;if (instance == 0) {instance = new TYPE();sInstance = instance;}//如果实例对象不存在,实例化一个对象, 已存在直接返回;//TYPE == NativeSensorManagerreturn *instance;}static bool hasInstance() {Mutex::Autolock _l(sLock);return sInstance != 0;}
protected:~Singleton() { };Singleton() { };
private:Singleton(const Singleton&);Singleton& operator = (const Singleton&);static Mutex sLock;static TYPE* sInstance;
};
#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE)                 \template<> ::android::Mutex  \(::android::Singleton< TYPE >::sLock)(::android::Mutex::PRIVATE);  \template<> TYPE* ::android::Singleton< TYPE >::sInstance(0);  \template class ::android::Singleton< TYPE >;

由上分析可知,NativeSensorManager未实例化在第一次调用时有 instance = new TYPE(); 即 new NativeSensorManager();

以下是 NativeSensorManager类构造器的实现:

NativeSensorManager::NativeSensorManager():mSensorCount(0), mScanned(false), mEventCount(0), type_map(NULL), handle_map(NULL), fd_map(NULL)
{int i;memset(sensor_list, 0, sizeof(sensor_list));memset(context, 0, sizeof(context));type_map.setCapacity(MAX_SENSORS);handle_map.setCapacity(MAX_SENSORS);fd_map.setCapacity(MAX_SENSORS);for (i = 0; i < MAX_SENSORS; i++) {context[i].sensor = &sensor_list[i];sensor_list[i].name = context[i].name;sensor_list[i].vendor = context[i].vendor;list_init(&context[i].listener);list_init(&context[i].dep_list);}if(getDataInfo()) {ALOGE("Get data info failed\n");}dump();
}

通过getDataInfo 初始化sensor list数组,sensor context数组,对NativeSensorManager 做实际的数据填充;

dump 显示当前NativeSensorManager的主要内容;

getDataInfo 填充数据

NativeSensorManager是管理sensor HAL 处理的核心,getDataInfo是对填充构建内部细节的实际实现,一下是其主要内容:

int NativeSensorManager::getDataInfo() {int i, j;struct SensorContext *list;int has_acc = 0;int has_compass = 0;int has_gyro = 0;int has_light = 0;int has_proximity = 0;struct sensor_t sensor_mag;struct sensor_t sensor_acc;struct sensor_t sensor_light;struct sensor_t sensor_proximity;struct sensor_t sensor_gyro;mSensorCount = getSensorListInner();//1:完成SensorContext list 获取和填充 包含sensor_tfor (i = 0; i < mSensorCount; i++) {struct SensorRefMap *item;list = &context[i];list->is_virtual = false;item = new struct SensorRefMap;item->ctx = list;/* hardware sensor depend on itself */list_add_tail(&list->dep_list, &item->list);if (strlen(list->data_path) != 0)list->data_fd = open(list->data_path, O_RDONLY | O_CLOEXEC | O_NONBLOCK);//open event 路径,获取文件描述符if (list->data_fd > 0) {fd_map.add(list->data_fd, list);// DefaultKeyedVector<int, struct SensorContext*> fd_map;// 添加到sm的fd_map中;}type_map.add(list->sensor->type, list);//  DefaultKeyedVector<int32_t, struct SensorContext*> type_map;// 添加到sm的type_map中;handle_map.add(list->sensor->handle, list);//  DefaultKeyedVector<int32_t, struct SensorContext*> handle_map;// 添加到sm的handle_map中;//根据不同的sensor type,创建不同的SensorBase子类实例;switch (list->sensor->type) {case SENSOR_TYPE_ACCELEROMETER:has_acc = 1;list->driver = new AccelSensor(list);//假设,type是acc,new一个 AccelSensor; 一个SensorBase子类实例sensor_acc = *(list->sensor);break;case SENSOR_TYPE_MAGNETIC_FIELD:has_compass = 1;list->driver = new CompassSensor(list);sensor_mag = *(list->sensor);break;case SENSOR_TYPE_PROXIMITY:has_proximity = 1;//......list->driver = new LightSensor(list);sensor_light = *(list->sensor);break;//......default:list->driver = NULL;ALOGE("No handle %d for this type sensor!", i);break;}initCalibrate(list);//校准相关}//virtual sensors //......
}

校准相关,virtual sensor相关暂不分析; 通过以上代码,可知在getDataInfo 中主要实现了:

  • 通过getSensorListInner 完成SensorContext list 获取和填充 包含sensor_t
  • 通过for循环,对每一个sensor做处理,打开其event patch,记录得到的文件描述符fd到map中;
  • 判断sensor type 根据返回的结果 创建基于SensorBase的sensor driver实例;

getSensorListInner获取

通过前面的blog可知,sensor kernel driver 注册了sensor class类,提供了若干api 用于往sensor class注册;

sensor driver的具体实现中注册了 input device;通过adb, 可以直观的相关的device node如下:

msm8909:/sys/class/sensors # ls
MPU6050-accel MPU6050-gyro mmc3416x-mag stk3x1x-light stk3x1x-proximitymsm8909:/sys/class/sensors/MPU6050-accel # ls
calibrate enable_wakeup             flags  max_delay   min_delay  power      sensor_power uevent
device    fifo_max_event_count      flush  max_latency name       resolution subsystem    vendor
enable    fifo_reserved_event_count handle max_range   poll_delay self_test  type         versionmsm8909:/sys/class/sensors/MPU6050-accel/device # ls
MPU6050-accel capabilities enable id       name poll_delay properties subsystem uniq
addr          device       event4 modalias phys power      reg        uevent    writemsm8909:/sys/class/sensors/MPU6050-accel/device # cat uevent
PRODUCT=18/0/0/0
NAME="MPU6050-accel"
PROP=0
EV=9
ABS=100 7
MODALIAS=input:b0018v0000p0000e0000-e0,3,kra0,1,2,28,mlsfw

通过adb 可以直接与这些device node做交互,控制sensor,读取event数据等, 在sensor hal的代码中如何完成这些操作?

前面的内容提到,getDataInfo 填充NativeSensorManager,其中关键是getSensorListInner 完成SensorContext list 获取和填充,下面是它的具体实现:

int NativeSensorManager::getSensorListInner()
{int number = 0;int err = -1;const char *dirname = SYSFS_CLASS;//1: SYSFS_CLASS == "/sys/class/sensors/"char devname[PATH_MAX];char *filename;char *nodename;DIR *dir;struct dirent *de;struct SensorContext *list;unsigned int i;dir = opendir(dirname);strlcpy(devname, dirname, PATH_MAX);filename = devname + strlen(dirname);//2:filename指针指在SYSFS_CLASS之后,即/sys/class/sensors/ 后while ((de = readdir(dir))) {if(de->d_name[0] == '.' &&(de->d_name[1] == '\0' ||(de->d_name[1] == '.' && de->d_name[2] == '\0')))continue; //3:去掉. .. 等无关目录list = &context[number];//4:发现一个目标目录,实现一个list 元素;strlcpy(filename, de->d_name, PATH_MAX - strlen(SYSFS_CLASS));//5:把读到目录名赋值到file name指针的位置;即填充了 devname[PATH_MAX]中 /sys/class/sensors/ 后的内容nodename = filename + strlen(de->d_name);//6:同样的,nodename 指针指向 filename后的位置*nodename++ = '/';for (i = 0; i < ARRAY_SIZE(node_map); i++) {strlcpy(nodename, node_map[i].node, PATH_MAX - strlen(SYSFS_CLASS) - strlen(de->d_name));//7:把node_map中的 预设的name 传入err = getNode((char*)(list->sensor), devname, &node_map[i]);if (err) {ALOGE("Get node for %s failed.\n", devname);break;}//8;过程与上相同,获取每个node的具体路径}list->sensor->handle = SENSORS_HANDLE(number);//#define SENSORS_HANDLE(x) (SENSORS_HANDLE_BASE + x + 1)strlcpy(nodename, "", SYSFS_MAXLEN);strlcpy(list->enable_path, devname, PATH_MAX);/* initialize data path */strlcpy(nodename, "device", SYSFS_MAXLEN);//路径指向:  /sys/class/sensors/MPU6050-accel/deviceif (getEventPath(devname, list->data_path) == -ENODEV) {getEventPathOld(list, list->data_path);}//9:获取event 节点路径,用来读取数据, 比如 MPU6050-accel, 注册的input event路径是:///dev/input/event4number++;}closedir(dir);return number;
}

在/code/hardware/qcom/sensors/sensors.h 中已经预设了 若干信息,例如:

#define SYSFS_CLASS         "/sys/class/sensors/"
#define SYSFS_NAME          "name"
#define SYSFS_VENDOR        "vendor"                                                                                              #define SYSFS_VERSION       "version"

getSensorListInner 完成对sys中sensor class下每一个sensor的处理;

通过getNode把每一个sensor下的节点数据获取到,并填充保存到相应的数据结构中;

通过getEventPatch 获取到sensor driver驱动中注册的 input device在 input 下的路径;

比如通过adb 可以看到/sys/class/sensors/MPU6050-accel/device

下event的编号是event4, 则记录input device的device node 是/dev/input/event4, 后续读取sensor data 则是通过这个路径。

以下是getNode的实现:

getNode
int NativeSensorManager::getNode(char *buf, char *path, const struct SysfsMap *map) {char * fret;ssize_t len = 0;int fd;char tmp[SYSFS_MAXLEN];//......memset(tmp, 0, sizeof(tmp));fd = open(path, O_RDONLY);//......len = read(fd, tmp, sizeof(tmp) - 1);//......tmp[len - 1] = '\0';if (tmp[strlen(tmp) - 1] == '\n')tmp[strlen(tmp) - 1] = '\0';//把p 指向sensor_t map->offset 的位置,以name为例,指向指向sensor_t 的nameif (map->type == TYPE_INTEGER) {int *p = (int *)(buf + map->offset);*p = atoi(tmp);} else if (map->type == TYPE_STRING) {char **p = (char **)(buf + map->offset);strlcpy(*p, tmp, SYSFS_MAXLEN);} else if (map->type == TYPE_FLOAT) {float *p = (float*)(buf + map->offset);*p = atof(tmp);} else if (map->type == TYPE_INTEGER64) {int64_t *p = (int64_t *)(buf + map->offset);*p = atoll(tmp);}//read node中的内容,并根据node map中预设的类型 做好转换,把每个sesnor_t 的内容填充上close(fd);return 0;
}

getNode把每一个sensor下的节点数据获取到,根据map->type 做好指的转换并记录下;

node_map 前面提过,它是SysfsMap元素组成的 预设的数组

struct SysfsMap {int offset;const char *node;int type;int required;
};
const struct SysfsMap NativeSensorManager::node_map[] = {{offsetof(struct sensor_t, name), SYSFS_NAME, TYPE_STRING, 1},{offsetof(struct sensor_t, vendor), SYSFS_VENDOR, TYPE_STRING, 1},{offsetof(struct sensor_t, version), SYSFS_VERSION, TYPE_INTEGER, 1},{offsetof(struct sensor_t, type), SYSFS_TYPE, TYPE_INTEGER, 1},{offsetof(struct sensor_t, maxRange), SYSFS_MAXRANGE, TYPE_FLOAT, 1},{offsetof(struct sensor_t, resolution), SYSFS_RESOLUTION, TYPE_FLOAT, 1},{offsetof(struct sensor_t, power), SYSFS_POWER, TYPE_FLOAT, 1},{offsetof(struct sensor_t, minDelay), SYSFS_MINDELAY, TYPE_INTEGER, 1},{offsetof(struct sensor_t, fifoReservedEventCount), SYSFS_FIFORESVCNT, TYPE_INTEGER, 0},{offsetof(struct sensor_t, fifoMaxEventCount), SYSFS_FIFOMAXCNT, TYPE_INTEGER, 0},
#if defined(SENSORS_DEVICE_API_VERSION_1_3)
#if defined(__LP64__){offsetof(struct sensor_t, maxDelay), SYSFS_MAXDELAY, TYPE_INTEGER64, 0},{offsetof(struct sensor_t, flags), SYSFS_FLAGS, TYPE_INTEGER64, 0},
#else{offsetof(struct sensor_t, maxDelay), SYSFS_MAXDELAY, TYPE_INTEGER, 0},{offsetof(struct sensor_t, flags), SYSFS_FLAGS, TYPE_INTEGER, 0},
#endif
#endif
};

SensorBase

在前面的getDataInfo处理过程中,其中判断sensor type 根据返回的结果 创建基于SensorBase的sensor driver实例是很关键的一步;

HAL中完成框架性的处理,实际操作需要根据每个sensor的特性对不同的sensor种类做不同的定义处理,以下是以acc为例:

在getDataInfo中,new了一个AccelSensor:

        switch (list->sensor->type) {case SENSOR_TYPE_ACCELEROMETER:has_acc = 1;list->driver = new AccelSensor(list);

AccelSensor类的定义如下:

class AccelSensor : public SensorBase {InputEventCircularReader mInputReader;sensors_event_t mPendingEvent;bool mHasPendingEvent;int64_t mEnabledTime;int setInitialState();
public:AccelSensor();AccelSensor(char *name);AccelSensor(struct SensorContext *context);virtual ~AccelSensor();virtual int readEvents(sensors_event_t* data, int count);virtual bool hasPendingEvents() const;virtual int setDelay(int32_t handle, int64_t ns);virtual int enable(int32_t handle, int enabled);virtual int calibrate(int32_t handle, struct cal_cmd_t *para,struct cal_result_t *cal_result);virtual int initCalibrate(int32_t handle, struct cal_result_t *cal_result);
};

它继承自SensorBase类, SensorBase也是各类sensor类的父类,以下是SensorBase的类的定义:

class SensorBase {
protected:const char* dev_name;const char* data_name;const sensor_cal_algo_t*    algo;char        input_name[PATH_MAX];int     dev_fd;int     data_fd;int64_t report_time;bool mUseAbsTimeStamp;sensors_meta_data_event_t   meta_data;char input_sysfs_path[PATH_MAX];int input_sysfs_path_len;int mEnabled;int mHasPendingMetadata;int64_t sysclk_sync_offset;int openInput(const char* inputName);static int64_t getTimestamp();static int64_t getClkOffset();static int64_t timevalToNano(timeval const& t) {return t.tv_sec*1000000000LL + t.tv_usec*1000;}int open_device();int close_device();
public:SensorBase(const char* dev_name, const char* data_name,const struct SensorContext* context = NULL);virtual ~SensorBase();virtual int readEvents(sensors_event_t* data, int count) = 0;virtual int injectEvents(sensors_event_t* data, int count);virtual bool hasPendingEvents() const;virtual int getFd() const;virtual int setDelay(int32_t handle, int64_t ns);virtual int enable(int32_t handle, int enabled) = 0;virtual int calibrate(int32_t handle, struct cal_cmd_t *para, struct cal_result_t *cal_result);virtual int initCalibrate(int32_t handle, struct cal_result_t *cal_result);virtual int setLatency(int32_t handle, int64_t ns);virtual int flush(int32_t handle);
};

SensorBase类中定义了如,readEvents,enable,flush等操作函数,AccelSensor继承了这些虚函数,并且完成了重写;

前文提到,读取poll_poll是调用了NativeSensorManager::readEvents,

list->driver->readEvents(data, count); 即是取出SensorBase指针指向的对象,调用它的readEvents函数;(note:多态的应用,通过指向父类类型对象,调用虚函数,实际调用到子类重写的具体的readEvents函数)

InputEventCircularReader

AccelSensor类定义的首个元素是:mInputReader,它是InputEventCircularReader 类对象;如其名称,输入事件循环读取,它主要用于对sensor event data的获取;

InputEventCircularReader 类定义如下:

class InputEventCircularReader
{struct input_event* const mBuffer;struct input_event* const mBufferEnd;struct input_event* mHead;struct input_event* mCurr;ssize_t mFreeSpace;
public:InputEventCircularReader(size_t numEvents);~InputEventCircularReader();ssize_t fill(int fd);ssize_t readEvent(input_event const** events);void next();
};
ssize_t InputEventCircularReader::fill(int fd)
{size_t numEventsRead = 0;if (mFreeSpace) {const ssize_t nread = read(fd, mHead, mFreeSpace * sizeof(input_event));if (nread<0 || nread % sizeof(input_event)) {// we got a partial event!!return nread<0 ? -errno : -EINVAL;}numEventsRead = nread / sizeof(input_event);if (numEventsRead) {mHead += numEventsRead;mFreeSpace -= numEventsRead;if (mHead > mBufferEnd) {size_t s = mHead - mBufferEnd;memcpy(mBuffer, mBufferEnd, s * sizeof(input_event));mHead = mBuffer + s;}}}return numEventsRead;
}
ssize_t InputEventCircularReader::readEvent(input_event const** events)
{*events = mCurr;ssize_t available = (mBufferEnd - mBuffer) - mFreeSpace;return available ? 1 : 0;
}
void InputEventCircularReader::next()
{mCurr++;mFreeSpace++;if (mCurr >= mBufferEnd) {mCurr = mBuffer;}
}

InputEventCircularReader 提供了fill,和 readEvent函数;

AccelSensor中的readEvents函数是使用它们来完成;以下是 readEvents的实现,这也是 读取HAL读取event data的最终实现的部分;

int AccelSensor::readEvents(sensors_event_t* data, int count)
{if (count < 1)return -EINVAL;if (mHasPendingEvent) {mHasPendingEvent = false;mPendingEvent.timestamp = getTimestamp();*data = mPendingEvent;return mEnabled ? 1 : 0;}if (mHasPendingMetadata) {mHasPendingMetadata--;meta_data.timestamp = getTimestamp();*data = meta_data;return mEnabled ? 1 : 0;}ssize_t n = mInputReader.fill(data_fd);if (n < 0)return n;int numEventReceived = 0;input_event const* event;
#if FETCH_FULL_EVENT_BEFORE_RETURN
again:
#endifwhile (count && mInputReader.readEvent(&event)) {int type = event->type;if (type == EV_ABS) {float value = event->value;if (event->code == EVENT_TYPE_ACCEL_X) {mPendingEvent.data[0] = value * CONVERT_ACCEL_X;} else if (event->code == EVENT_TYPE_ACCEL_Y) {mPendingEvent.data[1] = value * CONVERT_ACCEL_Y;} else if (event->code == EVENT_TYPE_ACCEL_Z) {mPendingEvent.data[2] = value * CONVERT_ACCEL_Z;}} else if (type == EV_SYN) {switch (event->code){case SYN_TIME_SEC:{mUseAbsTimeStamp = true;report_time = event->value*1000000000LL;}break;case SYN_TIME_NSEC:{mUseAbsTimeStamp = true;mPendingEvent.timestamp = report_time+event->value;}break;case SYN_REPORT:{if(mUseAbsTimeStamp != true) {mPendingEvent.timestamp = timevalToNano(event->time);}mPendingEvent.timestamp -= sysclk_sync_offset;if (mEnabled) {*data++ = mPendingEvent;numEventReceived++;count--;}}break;}} else {ALOGE("AccelSensor: unknown event (type=%d, code=%d)",type, event->code);}mInputReader.next();}
#if FETCH_FULL_EVENT_BEFORE_RETURN/* if we didn't read a complete event, see if we can fill andtry again instead of returning with nothing and redoing poll. */if (numEventReceived == 0 && mEnabled == 1) {n = mInputReader.fill(data_fd);if (n)goto again;}
#endifreturn numEventReceived;
}

这篇关于Android Sensor Input类型 (五) Sensor HAL NativeSensorManager的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/914589

相关文章

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk