·前言

长久以来,我使用Hanlder仅限于它的基本使用:post 和 sendMessage

而对于Hanlder的原理和消息处理机制并不清楚。找了两篇比较深入的博客:

http://www.cnblogs.com/angeldevil/p/3340644.html  AngelDevil的博客

http://blog.csdn.net/amazing7/article/details/51424038  amazing7的博客

学习了一下,又对照了源码,小有收获,俗话说“好记性不如烂笔头”,所以做一下笔记,总结一下,方便以后回顾。

·Android是消息驱动的

AngelDevil的博客的博客中指出Android是消息驱动的,这一点,看一下类ActivityThread就一目了然了。


public final class ActivityThread {        //……        final ApplicationThread mAppThread = new ApplicationThread();        //……        final H mH = new H();        //……        final Handler getHandler() {        return mH;    }        //……        private class ApplicationThread extends ApplicationThreadNative {//……}    //……        private class H extends Handler {//……}        //……         public static void main(String[] args) {                //……                   Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        if (false) {            Looper.myLooper().setMessageLogging(new                LogPrinter(Log.DEBUG, "ActivityThread"));        }        // End of event ActivityThreadMain.        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }}

源码中找到了入口方法main(),从Looper.prepareMainLooper();这一行看起,到Looper.looper(); 我们都知道,main()方法是程序的入口,方法里面就是主线程,从Looper.prepareMainLooper()到Looper.looper(),表明开启了轮询器,其间不断的进行消息的发送和处理,所以说Android确实是“消息驱动的”。 ActivityThread thread = new ActivityThread(): 表明在入口方法main()中创建了一个ActivityThread自己的对象,并通过代码
if (sMainThreadHandler == null) {     sMainThreadHandler = thread.getHandler(); }
获得ActivityThread内部的Handler,而ActivityThread源码中有这这几行代码:    
 final ApplicationThread mAppThread = new ApplicationThread();    final H mH = new H();    final Handler getHandler() {        return mH;    }    private class ApplicationThread extends ApplicationThreadNative {//……}    private class H extends Handler {//……}

getHanlder就是H,H继承于Handler,而H(extends Handler)的handleMessage(){}中接受的都是一些系统消息。

也就是说,在主线程中,有一个Looper轮询器,在Looper.prepareMainLooper()和Looper.loop()之间创建了ActivityThread实例,并获得了ActivityThread内部的Handler实例,相当于在Looper的两个方法中间创建了Hanlder。Android正是通过这个Handler进行的消息传递。

·Looper轮询器

下面看一下Android源码中提供的Looper典型使用

/**  * ……  * This is a typical example of the implementation of a Looper thread,  * using the separation of {@link #prepare} and {@link #loop} to create an  * initial Handler to communicate with the Looper.  *//这是一个Looper线程实现的典型案例,使用Looper.prepare()和Looper.loop()中间被隔开的部分创建  *//和初始化Handler,以便与Looper进行通信。  *   *  class LooperThread extends Thread {  *      public Handler mHandler;  *  *      public void run() {  *          Looper.prepare();  *  *          mHandler = new Handler() {  *              public void handleMessage(Message msg) {  *                  // process incoming messages here  *              }  *          };  *  *          Looper.loop();  *      }  *  }  */public final class Looper {//……}

正好与上面ActivityThead类中的用法相对应,只不过一个在主线程中(main()中),一个在子线程中。其本质用法是一样的。 下面是Looper的源码:
/**  * 这个类用于为一个线程运行一个消息循环。默认的线程并没有一个消息循环与它们相关联;  * 在线程中调用此类的prepare()方法来创建一个消息循环并且运行这个循环,然后调用Loop()  * 来使消息循环处理消息直到这个循环被终止。  *  * 大多数与消息循环的交互是通过Handler类来实现的。  *  * 这是一个Looper线程实现的典型案例,通过使用Looper.prepare()和Looper.loop()中间被隔开的部分创建  * 和初始化Handler,以便与Looper进行通信。  * This is a typical example of the implementation of a Looper thread,  * using the separation of {@link #prepare} and {@link #loop} to create an  * initial Handler to communicate with the Looper.  *  * 
  *  class LooperThread extends Thread {  *      public Handler mHandler;  *  *      public void run() {  *          Looper.prepare();  *  *          mHandler = new Handler() {  *              public void handleMessage(Message msg) {  *                  // process incoming messages here  *              }  *          };  *  *          Looper.loop();  *      }  *  }
*/public final class Looper { private static final String TAG = "Looper"; // sThreadLocal.get() 将返回null,除非你已经调用了prepare(). static final ThreadLocal sThreadLocal = new ThreadLocal(); private static Looper sMainLooper; // guarded by Looper.class final MessageQueue mQueue; final Thread mThread; private Printer mLogging; /** 将当前线程初始化为一个轮询器(looper)。(也就是在当前线程中维持一个轮询器) * 这样你就可以创建handler,handler之后会在开始轮询之前引用这个轮询器。要确保在调用 * 这个方法后调用{@link #loop()}方法,并且调用{@link #quit()}方法结束它。 */ public static void prepare() { prepare(true); }/** * 这个方法中创建了Looper对象,并与线程关联 * */ private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } /** * 将当前线程初始化为一个轮询器(looper), 并标记为一个应用的主轮询器。 * 应用的朱轮询器由Android环境创建(在ActivityThread#main()中被调用), * 所以你不需要自己调用这个方法。可以看:{@link #prepare()}。 */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } /** * 返回应用的主轮询器(looper),它存在于应用的主线程。 */ public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } } /** * 在当前线程中运行(轮询)消息队列。确保调用{@link #quit()}方法结束循环。 */ public static void loop() {//获取当前线程的Looper final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); }//获取Looper中的消息队列 final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();//循环取出消息队列中的消息 for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); }//###通过message的target(Handler)分发处理消息 msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } } /** * 返回与当前线程关联的Looper对象,如果当前线程未关联Looper,返回null。 */ public static @Nullable Looper myLooper() { return sThreadLocal.get(); } /** * 返回与当前线程关联的 {@link MessageQueue} 对象。这个方法必须在运行着Looper的线程中 * 调用,否则会抛出空指针异常。 */ public static @NonNull MessageQueue myQueue() { return myLooper().mQueue; }/** * 私有构造方法。 * 其中创建了消息队列(MessageQueue)。获取了当前线程。 * 在prepare()方法中调用创建实例。可以通过myLooper()方法获取looper实例。 */ private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } /** * 停止当前轮询器。 *

* 使{@link #loop} 停止,不再处理消息队列中的任何消息。 *

* 轮询器停止(轮询)后,任何尝试发送消息到队列都将失败。 * 例如,{@link Handler#sendMessage(Message)} 方法将会返回false。 *

* 使用这个方法可能不安全,因为一些消息可能在轮询器结束之前没有被分发处理。 * 考虑使用{@link #quitSafely} 代替,以确保所有待完成的工作能被井井有条地完成。 *

*/ public void quit() { mQueue.quit(false); } /** * 安全停止当前轮询器。 *

* 一旦消息队列中所有余下的到期的消息被分发处理,就使循环{@link #loop} 停止。 * 然而,还有一定时间到期的延迟消息不会在循环终止之前分发处理。 *

* 轮询器停止(轮询)后,任何尝试发送消息到队列都将失败。 * 例如,{@link Handler#sendMessage(Message)} 方法将会返回false。 *

*/ public void quitSafely() { mQueue.quit(true); } /** * 获得与这个轮询器关联的线程。 * * @return 轮询器的线程 */ public @NonNull Thread getThread() { return mThread; } /** * 获得轮询器的消息队列。 * * @return 轮询器的消息队列 */ public @NonNull MessageQueue getQueue() { return mQueue; }//......}
从Looper源码中可以得知: ① Looper的构造方法被私有化, 只能Looper能创建自己对象,而且在构造方法中,创建MessageQueue实例,获得当前线程。 在Looper的prepare()方法中创建了一个Looper对象,并且设置给sThreadLocal(ThreadLocal,sThreadLocal.set(new Looper(quitAllowed));
③ Looper.prepare()在 一个线程中只能实例化一次,否则将抛出异常 ④ Looper的loop()方法中,要从当前Looper(Looper me = myLooper())中获得它的MessageQueue,并循环取出Message进行处理。     *myLooper()方法获得设置给ThreadLocal中的Looper,sThreadLocal.get()。 Looper的prepare()方法中调用了ThreadLocal的set()方法,myLooper()中调用了ThreadLocal的get()方法,下面看ThreadLocal的get()和set()方法:
public T get() {    Thread t = Thread.currentThread();    ThreadLocalMap map = getMap(t);    if (map != null) {        ThreadLocalMap.Entry e = map.getEntry(this);        if (e != null)            return (T)e.value;    }    return setInitialValue();}public void set(T value) {    Thread t = Thread.currentThread();    ThreadLocalMap map = getMap(t);    if (map != null)        map.set(this, value);    else        createMap(t, value);}//获取线程t的ThreadLocalMapThreadLocalMap getMap(Thread t) {    return t.threadLocals;}//为线程t的(ThreadLocalMap)threadLocals赋值void createMap(Thread t, T firstValue) {    t.threadLocals = new ThreadLocalMap(this, firstValue);}
set()方法将looper设置给当前线程,get()方法从当前线程中获得looper。 小结一下: ①Looper通过prepare()方法,创建Looper自己的对象,同时创建了一个消息队列(MessageQueue),并通过ThreadLocal的set()方法设置给了当前线程(其实是当前线程的ThreadLocalMap的Entry的value); ②Looper通过myLooper(),其实是通过sThreadLocal(ThreadLocal)的get()方法从当前线程中获得Looper。 ③Looper的loop()方法中,获得当前线程的Looper,并获得Looper的MessageQueue,然后循环取出消息,交给Message的target(发送Message的Hanlder)分发处理。     *我们都知道Activity属于应用的主线程,在Activity中创建的Hanlder(不指定Looper),其实是在上面main()中的Looper.prepareMainLooper()和Looper.loop()之间,与Looper的典型使用,一模一样。

Hanlder

看一下Hanlder的构造方法: Android消息处理机制、Hanlder机制(Handler、Looper、MessageQueue和Message)_第1张图片
下面是两个主要构造方法的源码:
 public Handler(Callback callback, boolean async) {        if (FIND_POTENTIAL_LEAKS) {            final Class<? extends Handler> klass = getClass();            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&                    (klass.getModifiers() & Modifier.STATIC) == 0) {                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +                    klass.getCanonicalName());            }        }        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }        public Handler(Looper looper, Callback callback, boolean async) {        mLooper = looper;        mQueue = looper.mQueue;        mCallback = callback;        mAsynchronous = async;    }
从Handler(Callback callback, boolean async)的mLooper = Looper.myLooper()处往下看,这是要获得当前线程的Looper,如果mLooper == null,就抛出运行时异常 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException(//不能在没有调用Looper.prepare()的线程里创建handler. "Can’t create handler inside thread that has no called Looper.prepare()"); } 这两个构造方法中,一个是从当前线程取出与之关联的Looper,另一个要传入一个Looper,所以说要在线程中创建Handler实例,需要一个Looper。

Handler发送消息

下面是Handler发送消息内部调用流程 Android消息处理机制、Hanlder机制(Handler、Looper、MessageQueue和Message)_第2张图片
发现Handler发送消息方法中最终调用的是sendMessageAtTime()方法,下面是消息发送的源码:
public final boolean sendMessage(Message msg){    return sendMessageDelayed(msg, 0);}public final boolean sendEmptyMessage(int what){    return sendEmptyMessageDelayed(what, 0);}public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {    Message msg = Message.obtain();    msg.what = what;    return sendMessageDelayed(msg, delayMillis);}public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {    Message msg = Message.obtain();    msg.what = what;    return sendMessageAtTime(msg, uptimeMillis);}public final boolean sendMessageDelayed(Message msg, long delayMillis){    if (delayMillis < 0) {        delayMillis = 0;    }    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}public boolean sendMessageAtTime(Message msg, long uptimeMillis) {    MessageQueue queue = mQueue;    if (queue == null) {        RuntimeException e = new RuntimeException(                this + " sendMessageAtTime() called with no mQueue");        Log.w("Looper", e.getMessage(), e);        return false;    }    return enqueueMessage(queue, msg, uptimeMillis);}public final boolean post(Runnable r){   return  sendMessageDelayed(getPostMessage(r), 0);}public final boolean postAtTime(Runnable r, long uptimeMillis){    return sendMessageAtTime(getPostMessage(r), uptimeMillis);}public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);}public final boolean postDelayed(Runnable r, long delayMillis){    return sendMessageDelayed(getPostMessage(r), delayMillis);}private static Message getPostMessage(Runnable r) {    Message m = Message.obtain();    m.callback = r;    return m;}private static Message getPostMessage(Runnable r, Object token) {    Message m = Message.obtain();    m.obj = token;    m.callback = r;    return m;}private static void handleCallback(Message message) {    message.callback.run();}
我们可以看出, ① 其他发送消息的方法最后都调用了sendMessageAtTime()方法,然后返回了enqueueMessage()方法 ② 发送空消息(sendEmptyMessage)时,其实创建了消息实体,只是没有消息内容(数据) ③ post系列方法看似发送的是Runnable,但最终调用sendMessageAtTime(Message msg, long uptimeMillis),而这个方法第一个参数是一个Message,其实发送的还是Message,看一下getPostMessage()会发现,里面创建了消息,并将Runnable赋值给message的callback。而在handler的dispatchMessage()中调用了(if (msg.callback != null))handleCallback(msg),而handleCallback()中运行了Runnable(message.callback.run())

下面看一下此方法源码:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {    msg.target = this;    if (mAsynchronous) {        msg.setAsynchronous(true);    }    return queue.enqueueMessage(msg, uptimeMillis);}
在这个方法中需要注意     1.msg.target = this         这是将当前handler赋值给了Message的target,而在之后Looper的loop()方法中,从MessageQueue中循环取出Message(msg)后,     调用msg.target.dispatchmessage(msg)来处理这个消息。     2.queue.enqueueMessage(msg, uptimeMillis)         queue由sendMessageAtTime方法传传参而来,最终来自handler构造方法的myLooper()方法,也就是当前handler所在线程的Looper管理的MessageQueue。     也就是说,handler发送消息到当前线程的Looper的消息队列里。

Message

下面看一下Message部分的源码:
public final class Message implements Parcelable {        public int what;     public int arg1;     public int arg2;    public Object obj;//......        /*package*/ Handler target;        /*package*/ Runnable callback;        // sometimes we store linked lists of these things    /*package*/ Message next;    private static final Object sPoolSync = new Object();    private static Message sPool;    private static int sPoolSize = 0;    private static final int MAX_POOL_SIZE = 50;    private static boolean gCheckRecycle = true;    /**     * Return a new Message instance from the global pool. Allows us to     * avoid allocating new objects in many cases.从全局池中返回一个新的消息实例。 * 使我们在很多情况下避免分配新的对象。     */    public static Message obtain() {        synchronized (sPoolSync) {            if (sPool != null) {                Message m = sPool;                sPool = m.next;                m.next = null;                m.flags = 0; // clear in-use flag                sPoolSize--;                return m;            }        }        return new Message();    }//......    /**     * Return a Message instance to the global pool.回收一个消息放到全局池。     * 

* You MUST NOT touch the Message after calling this function because it has * effectively been freed. It is an error to recycle a message that is currently * enqueued or that is in the process of being delivered to a Handler. * 调用这个方法后你就不能再用Message,因为它已经被有效释放。回收一个正在列队或正在被 * 分配给Hanlder的过程中的Message是错误的。 *

*/ public void recycle() { if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked(); } /** * Recycles a Message that may be in-use. * Used internally by the MessageQueue and Looper when disposing of queued Messages. */ void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }//...... public void setTarget(Handler target) { this.target = target; } public Handler getTarget() { return target; }//...... /** * Sends this Message to the Handler specified by {@link #getTarget}. * Throws a null pointer exception if this field has not been set. */ public void sendToTarget() { target.sendMessage(this); } /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}). * 构造器 (但是获得一个Message的首选方式是调用Message.obtain()) */ public Message() { }//......}
从Message部分源码中可以看出:     1. 创建Message实例,首选Message.obtain()     2. Message的target确实是一个Handler,而Message的callback是一个Runnable     3.Message内部保存了一个全局缓存的消息池,我们可以用obtain从缓存池获得一个消息,Message使用完后系统会调用recycle回收,如果自己new很多Message,每次使用完后不能及时回收,会占用很多内存的。

Handler处理消息

看一下Looper的loop()方法的源码:
public static void loop() {    final Looper me = myLooper();//获得当前线程的Looper    if (me == null) {//如果为null,抛出运行时异常("没有Looper;线程中没有调用Looper.prepare()        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."    }    final MessageQueue queue = me.mQueue; //从Looper中获得消息队列    // ……//开始无限循环    for (;;) {//不断从消息队列中取出消息(Message)        Message msg = queue.next(); // might block 可能阻断        if (msg == null) {            // No message indicates that the message queue is quitting. 如果没有消息标识(没有            return;        }        //……        try { //msg.target其实是一个Hanlder,Hanlder发送消息时,会把当前这个handler作为target            msg.target.dispatchMessage(msg);//Hanlder分发消息        } finally {            //……        }        //……        msg.recycleUnchecked();    }}
代码,msg.target.dispatchMessage(msg),表明Message的target处理了消息,也就是发送消息的handler处理了消息(谁发送,谁处理),也是这个Looper所在线程的Handler处理了消息。 所以说,Looper.prepare()中创建了消息队列(MessageQueue),并与当前线程关联;Looper.loop()获得当前线程的Looper并循环遍历其管理的MessageQueue(),用Message自己的target处理消息。这个target就是当前线程Looper两个方法之间创建的handler对象。 我们就知道了,Looper的loop()方法是取出消息并处理消息的场所,而真正处理消息的对象,还是发送消息的handler。 Looper的loop()方法中使用了msg.target.dispatchMessage(msg),也就是Handler的dispatchMessage()方法,下面看一下handler的消息分发处理的源码:
/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. * 实例化一个handler时,为避免必须实现你自己的Hanlder的子类,你可以使用回调接口。 */public interface Callback {    public boolean handleMessage(Message msg);}/** * Subclasses must implement this to receive messages.//子类必须实现这个方法来接收消息。 */public void handleMessage(Message msg) {}/** * Handle system messages here.这里处理系统的消息。 */public void dispatchMessage(Message msg) {    if (msg.callback != null) {        handleCallback(msg);    } else {        if (mCallback != null) {            if (mCallback.handleMessage(msg)) {                return;            }        }        handleMessage(msg);    }}private static void handleCallback(Message message) {    message.callback.run();}
从源码可以看出,消息分发处理有三层: ① 当msg.callback !=null,也就是使用了handler的post系列方法发送了消息(将Runnable赋值给了message的callback),消息由handleCallback()方法处理(message.callback.run()) ② 当mCallback != null,也就是handler设置了Callback,消息就由handler的Callback的handleMessage()处理 ③ 当mCallback为空或Callback的handleMessage()处理完消息表明还有消息要处理,返回false,消息由handler子类的实现方法handleMessage()处理。     *handler的Callback的源码注释中指出,如果不想实现Handler的子类(必须实现handleMessage()方法处理消息),可以设置CallBack回调接口。

·总结

1. 一个线程中要想创建Handler发送处理消息,要先调用Looper.prepare()和Looper.loop()来创建一个消息队列(MessageQueue)开启一个轮询器与线程关联。

2. 在Looper的两个方法中间创建Handler,handler创建时要拿到与当前线程关联的轮询器(Looper)和轮询器管理的消息队列(MessageQueue)

3. Handler发送消息(Message)其实是发送到了2中的消息队列(MessageQueue),发送消息同时,Handler将自己设置给Message的target

4. 消息是在Looper的loop()方法中循环取出处理,最终由Message的target也就是发送自己的handler处理。


Handler:

创建:在Looper.prepare()和Looper.loop()之间创建

作用:发送消息(Message)到所在线程关联的轮询器(Looper)的消息队列(MessageQueue),并将自己作为target设置给Message

    处理消息,由dispatchMessage()分发给①Callback的handleMessage()、②Handler的handleMessage()或③Handler的handleCallback()处理

Looper 轮询器

创建:由自己的prepare()方法创建并关联当前线程。

作用:Looper.perpare(),创建自己的实例同时创建一个MessageQueue,并将自己设置给当前线程

    Looper.loop()从当前线程拿到Looper及其MessageQueue,循环遍历MessageQueue,取出消息,交给Message的target(发送消息的handler)处理

*Looper.myLooper():获得当前线程所关联的Looper。

MessageQueue 消息队列

创建:Looper的构造方法中创建,最终由Looper的prepare()方法创建

作用:接收handler发送消息时加入进来的消息,将其放入队列中,先入先出

Message 消息

创建:优先用Message.obtain()获取实例,Handler发送消息时创建,最终由MessageQueue管理

作用:携带消息内容传递数据,或作为一个信号(what)

注意:Message在被发送时会将发送它的Handler作为target,在使用Handler的post系列方法时,传入的Runnable对象由Message的callback得到。







更多相关文章

  1. android在一个app程序中,打开另一个app的方法
  2. 从另一种方式理解Android消息处理机制
  3. 小米5手机Android运行程序闪退出错解决方法
  4. Android 查看SHA1值的方法
  5. Android 更新UI 只能在主线程?
  6. 如何正确实现Android启动屏画面的方法(避免白屏)

随机推荐

  1. Android调用系统分享功能总结
  2. webrtc服务器janus android客户端编译学
  3. Android(安卓)UI设计:Notification
  4. 使用Kotlin Android(安卓)Extensions
  5. 【Android】ListView RecyclerView
  6. Android开发技术周报 Issue#68
  7. android sdcard read-only file system
  8. Android(安卓)游戏框架 libgdx 学习笔记
  9. davlik虚拟机内存管理之一——内存分配
  10. Android(安卓)开源项目第三篇——优秀项