android UEventObserver的用法
16lz
2021-01-24
在android的framework中想要监听底层的uevent事件是一件很简单的事情,只要以下几个步骤即可,拿UsbDeviceManager.java为例子。
首先,创建一个UEventObserver类:
private final UEventObserver mUEventObserver = new UEventObserver() { @Override public void onUEvent(UEventObserver.UEvent event) { if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString()); String state = event.get("USB_STATE"); String accessory = event.get("ACCESSORY"); if (state != null) { mHandler.updateState(state); } else if ("START".equals(accessory)) { if (DEBUG) Slog.d(TAG, "got accessory start"); startAccessoryMode(); } } };在这个类中要重写onUevent方法,在该方法中处理你得到的事件。
接着,调用startObserving方法即可:
mUEventObserver.startObserving(USB_STATE_MATCH); mUEventObserver.startObserving(ACCESSORY_START_MATCH);
这里:
private static final String USB_STATE_MATCH = "DEVPATH=/devices/virtual/android_usb/android0"; private static final String ACCESSORY_START_MATCH = "DEVPATH=/devices/virtual/misc/usb_accessory";
这样就可以监听上述路径下的uevent事件了。是不是很简单!
而UEventObserver类的实现以及JNI层和HAL层的实现,代码量也很少,看起来很简洁。
先看startObserving方法:
public final synchronized void startObserving(String match) { ensureThreadStarted(); sThread.addObserver(match, this); }
首先确保线程已经启动起来了,如果是第一次进来,肯定要启动线程了:
private static final synchronized void ensureThreadStarted() { if (sThreadStarted == false) { sThread = new UEventThread(); sThread.start(); sThreadStarted = true; } }
这个线程是单例模式,一个进程只启动一个就行了。
得到线程实例后,调用addObserver:
public void addObserver(String match, UEventObserver observer) { synchronized(mObservers) { mObservers.add(match); mObservers.add(observer); } }
把路径和UEventObserver实例保存到ArrayList中,因为一个进程可能有多个UEventObserver实例的。
这时候uevent线程已经运行起来了:
public void run() { native_setup(); byte[] buffer = new byte[1024]; int len; while (true) { len = next_event(buffer); if (len > 0) { String bufferStr = new String(buffer, 0, len); // easier to search a String synchronized (mObservers) { for (int i = 0; i < mObservers.size(); i += 2) { if (bufferStr.indexOf((String)mObservers.get(i)) != -1) { ((UEventObserver)mObservers.get(i+1)) .onUEvent(new UEvent(bufferStr)); } } } } } }
native_setup和next_event是JNI方法,他们分别调用HAL层的uevent_init和uevent_next_event方法:
int uevent_init(){ struct sockaddr_nl addr; int sz = 64*1024; int s; memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_pid = getpid(); addr.nl_groups = 0xffffffff; s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); if(s < 0) return 0; setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)); if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(s); return 0; } fd = s; return (fd > 0);}
int uevent_next_event(char* buffer, int buffer_length){ while (1) { struct pollfd fds; int nr; fds.fd = fd; fds.events = POLLIN; fds.revents = 0; nr = poll(&fds, 1, -1); if(nr > 0 && fds.revents == POLLIN) { int count = recv(fd, buffer, buffer_length, 0); if (count > 0) { struct uevent_handler *h; pthread_mutex_lock(&uevent_handler_list_lock); LIST_FOREACH(h, &uevent_handler_list, list) h->handler(h->handler_data, buffer, buffer_length); pthread_mutex_unlock(&uevent_handler_list_lock); return count; } } } // won't get here return 0;}监听到内核有uevent消息后,调用recv,把数据放到buffer中,至于uevent_handler_list队列,是为空的,因为没有调用到uevent_add_native_handler函数。内核的uevent数据格式是怎样的呢,比如插入usb连接电脑时候收到的log:
ACTION=change DEVPATH=/devices/virtual/android_usb/android0 SUBSYSTEM=android_usb USB_STATE=CONNECTED SEQNUM=1249 [email protected]/devices/virtual/android_usb/android0 ACTION=change DEVPATH=/devices/virtual/android_usb/android0 SUBSYSTEM=android_usb USB_STATE=CONNECTED SEQNUM=1249 [email protected]/devices/virtual/android_usb/android0 ACTION=change DEVPATH=/devices/virtual/android_usb/android0 SUBSYSTEM=android_usb USB_STATE=CONFIGURED SEQNUM=1250 [email protected]/devices/virtual/android_usb/android0 ACTION=change DEVPATH=/devices/virtual/android_usb/android0 SUBSYSTEM=android_usb USB_STATE=CONFIGURED SEQNUM=1250
framework的线程得到buffer数据后,用bufferStr.indexOf判断数据是否包含自己想要监控的路径,如果有,则回调onUEvent方法,并new一个自己的UEvent对象,把数据解析好,以便onUEvent更好更快的得到数据:
static public class UEvent { // collection of key=value pairs parsed from the uevent message public HashMap<String,String> mMap = new HashMap<String,String>(); public UEvent(String message) { int offset = 0; int length = message.length(); while (offset < length) { int equals = message.indexOf('=', offset); int at = message.indexOf(0, offset); if (at < 0) break; if (equals > offset && equals < at) { // key is before the equals sign, and value is after mMap.put(message.substring(offset, equals), message.substring(equals + 1, at)); } offset = at + 1; } } public String get(String key) { return mMap.get(key); } public String get(String key, String defaultValue) { String result = mMap.get(key); return (result == null ? defaultValue : result); } public String toString() { return mMap.toString(); } }
放到哈希表中的数据格式为:
ACTION changeDEVPATH /devices/virtual/android_usb/android0SUBSYSTEM android_usbUSB_STATE CONNECTEDSEQNUM 1249ACTION changeDEVPATH /devices/virtual/android_usb/android0SUBSYSTEM android_usbUSB_STATE CONFIGURED
这样根据索引很容易取出想要的数据。比如前面的onUEvent中:
event.get("USB_STATE") 得到CONNECT
event.get("ACCESSORY") 为空
结束~
更多相关文章
- 谈谈 View 绘制流程
- android很的意思的事情,关于Input…
- Android(安卓)面试整理
- Android音视频处理之MediaExtractor
- Ubuntu 编译Android若干错误及解决方法(转)
- Android(安卓)MediaPlayer基本使用方式
- [Android] IntentInjector
- Android中结合OrmLite for android组件对SQLite的CRUD(增删改查)
- Android(安卓)读取内存文件返回byte数组