Android JNI知识简介

简介:
转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/17361775

前言

上周对Android中的事件派发机制进行了分析,这次博主要对消息队列和Looper的源码进行简单的分析。大家耐心看下去,其实消息队列的逻辑比事件派发机制简单多了,所以大家肯定会很容易看懂的。

概念

1. 什么是消息队列

消息队列在android中对应MessageQueue这个类,顾名思义,消息队列中存放了大量的消息(Message)

2.什么是消息

消息(Message)代表一个行为(what)或者一串动作(Runnable),有两处会用到Message:Handler和Messenger

3.什么是Handler和Messenger

Handler大家都知道,主要用来在线程中发消息通知ui线程更新ui。Messenger可以翻译为信使,可以实现进程间通信(IPC),Messenger采用一个单线程来处理所有的消息,而且进程间的通信都是通过发消息来完成的,感觉不能像AIDL那样直接调用对方的接口方法(具体有待考证),这是其和AIDL的主要区别,也就是说Messenger无法处理多线程,所有的调用都是在一个线程中串行执行的。Messenger的典型代码是这样的:new Messenger(service).send(msg),它的本质还是调用了Handler的sendMessage方法

4.什么是Looper

Looper是循环的意思,它负责从消息队列中循环的取出消息然后把消息交给目标处理

5.线程有没有Looper有什么区别?

线程如果没有Looper,就没有消息队列,就无法处理消息,线程内部就无法使用Handler。这就是为什么在子线程内部创建Handler会报错:"Can't create handler inside thread that has not called Looper.prepare()",具体原因下面再分析。

6.如何让线程有Looper从而正常使用Handler?

在线程的run方法中加入如下两句:

Looper.prepare();

Looper.loop();

这一切不用我们来做,有现成的,HandlerThread就是带有Looper的线程。

想用线程的Looper来创建Handler,很简单,Handler handler = new Handler(thread.getLooper()),有了上面这几步,你就可以在子线程中创建Handler了,好吧,其实android早就为我们想到这一点了,也不用自己写,IntentService把我们该做的都做了,我们只要用就好了,具体怎么用后面再说。

消息队列和Looper的工作机制

一个Handler会有一个Looper,一个Looper会有一个消息队列,Looper的作用就是循环的遍历消息队列,如果有新消息,就把新消息交给它的目标处理。每当我们用Handler来发送消息,消息就会被放入消息队列中,然后Looper就会取出消息发送给它的目标target。一般情况,一个消息的target是发送这个消息的Handler,这么一来,Looper就会把消息交给Handler处理,这个时候Handler的dispatchMessage方法就会被调用,一般情况最终会调用Handler的handleMessage来处理消息,用handleMessage来处理消息是我们常用的方式。

源码分析

1. Handler发送消息的过程

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.    public final boolean sendMessage(Message msg)  
  2.    {  
  3.        return sendMessageDelayed(msg, 0);  
  4.    }  
  5.   
  6. public final boolean sendMessageDelayed(Message msg, long delayMillis)  
  7.    {  
  8.        if (delayMillis < 0) {  
  9.            delayMillis = 0;  
  10.        }  
  11.        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
  12.    }  
  13.   
  14. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
  15.     MessageQueue queue = mQueue;  
  16.     if (queue == null) {  
  17.         RuntimeException e = new RuntimeException(  
  18.                 this + " sendMessageAtTime() called with no mQueue");  
  19.         Log.w("Looper", e.getMessage(), e);  
  20.         return false;  
  21.     }  
  22.     return enqueueMessage(queue, msg, uptimeMillis);  
  23. }  
  24.   
  25. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
  26.        msg.target = this;  
  27.        if (mAsynchronous) {  
  28.            msg.setAsynchronous(true);  
  29.        }  
  30.        //这里msg被加入消息队列queue  
  31.        return queue.enqueueMessage(msg, uptimeMillis);  
  32.    }  

2.Looper的工作过程

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public static void loop() {  
  2.      final Looper me = myLooper();  
  3.      if (me == null) {  
  4.          throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  5.      }  
  6.      //从Looper中取出消息队列  
  7.      final MessageQueue queue = me.mQueue;  
  8.   
  9.      // Make sure the identity of this thread is that of the local process,  
  10.      // and keep track of what that identity token actually is.  
  11.      Binder.clearCallingIdentity();  
  12.      final long ident = Binder.clearCallingIdentity();  
  13.   
  14.      //死循环,循环的取消息,没有新消息就会阻塞  
  15.      for (;;) {  
  16.          Message msg = queue.next(); // might block 这里会被阻塞,如果没有新消息  
  17.          if (msg == null) {  
  18.              // No message indicates that the message queue is quitting.  
  19.              return;  
  20.          }  
  21.   
  22.          // This must be in a local variable, in case a UI event sets the logger  
  23.          Printer logging = me.mLogging;  
  24.          if (logging != null) {  
  25.              logging.println(">>>>> Dispatching to " + msg.target + " " +  
  26.                      msg.callback + ": " + msg.what);  
  27.          }  
  28.   
  29.          //将消息交给target处理,这个target就是Handler类型  
  30.          msg.target.dispatchMessage(msg);  
  31.   
  32.          if (logging != null) {  
  33.              logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
  34.          }  
  35.   
  36.          // Make sure that during the course of dispatching the  
  37.          // identity of the thread wasn't corrupted.  
  38.          final long newIdent = Binder.clearCallingIdentity();  
  39.          if (ident != newIdent) {  
  40.              Log.wtf(TAG, "Thread identity changed from 0x"  
  41.                      + Long.toHexString(ident) + " to 0x"  
  42.                      + Long.toHexString(newIdent) + " while dispatching to "  
  43.                      + msg.target.getClass().getName() + " "  
  44.                      + msg.callback + " what=" + msg.what);  
  45.          }  
  46.   
  47.          msg.recycle();  
  48.      }  
  49.  }  

3.Handler如何处理消息

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Subclasses must implement this to receive messages. 
  3.  */  
  4. public void handleMessage(Message msg) {  
  5. }  
  6.   
  7. /** 
  8.  * Handle system messages here. 
  9.  */  
  10. public void dispatchMessage(Message msg) {  
  11.     if (msg.callback != null) {  
  12.         //这个方法很简单,直接调用msg.callback.run();  
  13.         handleCallback(msg);  
  14.     } else {  
  15.         //如果我们设置了callback会由callback来处理消息  
  16.         if (mCallback != null) {  
  17.             if (mCallback.handleMessage(msg)) {  
  18.                 return;  
  19.             }  
  20.         }  
  21.         //否则消息就由这里来处理,这是我们最常用的处理方式  
  22.         handleMessage(msg);  
  23.     }  
  24. }  
我们再看看msg.callback和mCallback是啥东西

/*package*/ Runnable callback;   

现在已经很明确了,msg.callback是个Runnable,什么时候会设置这个callback:handler.post(runnable),相信大家都常用这个方法吧

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.  /** 
  2.  * Callback interface you can use when instantiating a Handler to avoid 
  3.  * having to implement your own subclass of Handler. 
  4.  * 
  5.  * @param msg A {@link android.os.Message Message} object 
  6.  * @return True if no further handling is desired 
  7.  */  
  8. public interface Callback {  
  9.     public boolean handleMessage(Message msg);  
  10. }  
  11.       
  12. final Callback mCallback;  

而mCallback是个接口,可以这样来设置 Handler handler = new Handler(callback),这个callback的意义是什么呢,代码里面的注释已经说了,可以让你不用创建Handler的子类但是还能照样处理消息,恐怕大家常用的方式都是新new一个Handler然后override其handleMessage方法来处理消息吧,从现在开始,我们知道,不创建Handler的子类也可以处理消息。多说一句,为什么创建Handler的子类不好?这是因为,类也是占空间的,一个应用class太多,其占用空间会变大,也就是应用会更耗内存。

HandlerThread简介

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public void run() {  
  3.     mTid = Process.myTid();  
  4.     Looper.prepare();  
  5.     synchronized (this) {  
  6.         mLooper = Looper.myLooper();  
  7.         notifyAll();  
  8.     }  
  9.     Process.setThreadPriority(mPriority);  
  10.     onLooperPrepared();  
  11.     Looper.loop();  
  12.     mTid = -1;  
  13. }  
HandlerThread继承自Thread,其在run方法内部为自己创建了一个Looper,使用上HandlerThread和普通的Thread不一样,无法执行常见的后台操作,只能用来处理新消息,这是因为Looper.loop()是死循环,你的code根本执行不了,不过貌似你可以把你的code放在super.run()之前执行,但是这好像不是主流玩法,所以不建议这么做。

IntentService简介

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void onCreate() {  
  2.     // TODO: It would be nice to have an option to hold a partial wakelock  
  3.     // during processing, and to have a static startService(Context, Intent)  
  4.     // method that would launch the service & hand off a wakelock.  
  5.   
  6.     super.onCreate();  
  7.     HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");  
  8.     thread.start();  
  9.   
  10.     mServiceLooper = thread.getLooper();  
  11.     mServiceHandler = new ServiceHandler(mServiceLooper);  
  12. }  
IntentService继承自Service,它是一个抽象类,其被创建的时候就new了一个HandlerThread和ServiceHandler,有了它,就可以利用IntentService做一些优先级较高的task,IntentService不会被系统轻易杀掉。使用IntentService也是很简单,首先startService(intent),然后IntentService会把你的intent封装成Message然后通过ServiceHandler进行发送,接着ServiceHandler会调用onHandleIntent(Intent intent)来处理这个Message,onHandleIntent(Intent intent)中的intent就是你startService(intent)中的intent,ok,现在你需要做的是从IntentService派生一个子类并重写onHandleIntent方法,然后你只要针对不同的intent做不同的事情即可,事情完成后IntentService会自动停止。所以,IntentService是除了Thread和AsyncTask外又一执行耗时操作的方式,而且其不容易被系统干掉,建议关键操作采用IntentService。

在子线程创建Handler为什么会报错?

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public Handler(Callback callback, boolean async) {  
  2.     if (FIND_POTENTIAL_LEAKS) {  
  3.         final Class<? extends Handler> klass = getClass();  
  4.         if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  5.                 (klass.getModifiers() & Modifier.STATIC) == 0) {  
  6.             Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  7.                 klass.getCanonicalName());  
  8.         }  
  9.     }  
  10.     //获取当前线程的Looper  
  11.     mLooper = Looper.myLooper();  
  12.     //报错的根本原因是:当前线程没有Looper  
  13.     if (mLooper == null) {  
  14.         throw new RuntimeException(  
  15.             "Can't create handler inside thread that has not called Looper.prepare()");  
  16.     }  
  17.     mQueue = mLooper.mQueue;  
  18.     mCallback = callback;  
  19.     mAsynchronous = async;  
  20. }  
如何避免这种错误:在ui线程使用Handler或者给子线程加上Looper。
相关文章
|
7月前
|
Android开发
Android JNI与CAN通信遇到的问题总结
Android JNI与CAN通信遇到的问题总结
295 1
|
7月前
|
Android开发
Android JNI 报错(signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr )
Android JNI 报错(signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr )
1114 1
|
4月前
|
Java Android开发 C++
Android Studio JNI 使用模板:c/cpp源文件的集成编译,快速上手
本文提供了一个Android Studio中JNI使用的模板,包括创建C/C++源文件、编辑CMakeLists.txt、编写JNI接口代码、配置build.gradle以及编译生成.so库的详细步骤,以帮助开发者快速上手Android平台的JNI开发和编译过程。
340 1
|
7月前
|
缓存 算法 Java
Linux内核新特性年终大盘点-安卓杀后台现象减少的背后功臣MGLRU算法简介
MGLRU是一种新型内存管理算法,它的出现是为了弥补传统LRU(Least Recently Used)和LFU(Least Frequently Used)算法在缓存替换选择上的不足,LRU和LFU的共同缺点就是在做内存页面替换时,只考虑内存页面在最近一段时间内被访问的次数和最后一次的访问时间,但是一个页面的最近访问次数少或者最近一次的访问时间较早,可能仅仅是因为这个内存页面新近才被创建,属于刚刚完成初始化的年代代页面,它的频繁访问往往会出现在初始化之后的一段时间里,那么这时候就把这种年轻代的页面迁移出去
|
7月前
|
Java 开发工具 Android开发
OpenCV(一):Android studio jni配置OpenCV(亲测有效,保姆级)
OpenCV(一):Android studio jni配置OpenCV(亲测有效,保姆级)
891 0
|
7月前
|
Java Android开发
Android JNI 调用
Android JNI 调用
51 1
|
7月前
|
存储 编解码 API
Android Media Framework(一)OpenMAX 框架简介
OpenMAX IL是Khronos Group为嵌入式和移动设备设计的低层级接口,用于统一调用音频、视频和图像编解码器,确保跨平台兼容性。它包括Core API(管理组件加载和方法调用)和Component API(组件实现,如源、接收器、编解码器等)。组件通过端口进行数据交互,客户端使用Core API加载和控制组件。Android引入OMX IL以支持不同芯片上的编解码器。组件状态包括Loaded、Idle、Executing和Invalid。组件架构涉及参数配置、命令处理和缓冲区管理,数据交换通过回调函数完成,端口持有预分配或组件自分配的缓冲区。
112 0
|
7月前
|
传感器 Java 开发工具
[NDK/JNI系列03] Android Studio集成NDK开发环境
[NDK/JNI系列03] Android Studio集成NDK开发环境
77 0
|
7月前
|
Android开发
[Android jni] Bitmap与Mat对象的相互转换
[Android jni] Bitmap与Mat对象的相互转换
236 0
|
7月前
|
Java 开发工具 Android开发
[Android]JNI的基础知识
[Android]JNI的基础知识
122 0
[Android]JNI的基础知识