ca88编程Android Looper原理深入分析

作者:ca88编程

我们都理解,系统是分歧目的在于子线程中访问UI的,但借使在主线程中实行耗费时间操作,又会急剧地妨碍客户体验。所以,大家得以在子线程中张开耗费时间操作,操作结束后布告主线程更新UI。为了便于在线程之间开展通讯,官方提供了一种很好的消除方法——Handler。有效地减轻了在子线程中不可能访谈UI的冲突。

音信机制概述

实在业务应用情形: 

       某业务场景须求将地点数据传递到服务端,服务端再回到传递成功依然战败的音讯。

           1、 失败时: 重传5次  

           2、设置客商端央求的一丝一毫时间间隔,这么些间隔内最多乞请1次

       具体逻辑如下:(这里呼吁的细微时间间距设置为:80s,幸免顾客端由于某种格外频仍的调用服务端,形成服务端卓殊)

ca88编程 1

实现:

handler 的实现:

public class BindHandler extends Handler {
    private final static String TAG = "BindHandler";
    private static final String DEFAULT_HANDLER_THREAD_NAME = "DeviceBindingHandlerThread";
    static final int MSG_DEVICE_BIND = 1;
    static final int MSG_DEVICE_BIND_RETRY = 2;
    private BindManager mManager;
    private HandlerThread mHandlerThread;

    BindHandler(BindManager manager){
        this(manager,createDefaultHandlerThread());
    }

    private BindHandler(BindManager manager,HandlerThread thread){
        super(thread.getLooper());
        mManager = manager;
        mHandlerThread = thread;
    }

    private static HandlerThread createDefaultHandlerThread() {
        HandlerThread handlerThread = new HandlerThread(DEFAULT_HANDLER_THREAD_NAME, Process.THREAD_PRIORITY_BACKGROUND);
        handlerThread.start();
        return handlerThread;
    }

    public void quitHandlerThread(){
        if(null != mHandlerThread){
            mHandlerThread.getLooper().quit();
        }
    }

    public void startHandlerThread(){
        if(null != mHandlerThread){
            mHandlerThread.getLooper().loop();
        }
    }

    public boolean isHandlerThreadAlive(){
        return mHandlerThread.isAlive();
    }
    @Override
    public void handleMessage(Message msg) {
        if(null == msg){
            return;
        }
        switch (msg.what){
            case MSG_DEVICE_BIND:
                mManager.handleBinding(true);
                break;
            case MSG_DEVICE_BIND_RETRY:
                mManager.handleBinding(false);
                break;
            default:
                break;
        }
    }
}

上述代码中 设置了三个HandlerThread 用于开启一个线程,乞求的发送运转在该线程中(获取该HandlerThread的Looper,用该Looper初阶化Handler,那样就足以将该Handler中handleMessage函数运转在开拓的线程中)。

style="background-color: #ffff00">通过Looper.quit函数截止该线程,否者该线程的Looper向来在循环等待消息的过来,占用财富(在HandlerThread已经失去价值的时候理应马上停掉它)。

 方法1: mHandlerThread.getLooper().quit();

//方法2: mHandlerThread.quit();

  方法3:mHandlerThread.getLooper().quitSafely();
//方法4:mHandlerThread.quitSafely();

1 和 2 甘休的方法等同,不管音讯队列中有未有音讯都甘休该线程。

3 和 4 中只要音信队列中有新闻,则不会立刻终止,待管理完音信队列中的新闻在得了该线程(管理进程中实现的音讯不再管理)

 

央浼管理类:

1、 央求管理类中定义自变量: mBindHandler,用于发送音讯

2、设置重传战略

3、设置幸免频仍调用的攻略,(检查新闻队列中是不是有消息,若是有则抛弃该音讯)

      mBindHandler.hasMessages(BindHandler.MSG_DEVICE_BIND_RETRY)

         具体代码不再详细深入分析

Android系统中的视图组件并不是线程安全的,固然要立异视图,必得在主线程中更新,不能在子线程中实施更新的操作。在子线程中通报主线程大家需求接收到handler对象。

应用Handler,经常要求持续Handler类,仁同一视写handleMessage()方法。举二个轻巧易行的用法例子:

Android的新闻机制器重是Handler的音信机制,Handler的周转必要底层的MessageQueue和Looper的支撑。

HandlerThread 使用详细明白

/**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.
 */
public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;

    public 

style="background-color: #ffff00">HandlerThread(String name)

 {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public 

HandlerThread(String name, int priority)

 {
        super(name);
        mPriority = priority;
    }
    
    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

    @Override
    

public void run() {

        mTid = Process.myTid();
        

Looper.prepare();

        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll(); //

唤醒等待线程

        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        

Looper.loop();

        mTid = -1;
    }
    
    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason isAlive() returns false, this method will return null. If this thread
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper 

getLooper()

 {   //如果该线程没有start 则处于等待状态,当调用start后,notify
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();    //线程没有开启则处于等待状态
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    /**
     * @return a shared {@link Handler} associated with this thread
     * @hide
     */
    @NonNull
    public 

Handler getThreadHandler()

 {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }

    /**
     * Quits the handler thread's looper.
     * <p>
     * Causes the handler thread's looper to terminate without processing any
     * more messages in the message queue.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p class="note">
     * Using this method may be unsafe because some messages may not be delivered
     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
     * that all pending work is completed in an orderly manner.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     *
     * @see #quitSafely
     */
    public boolean 

quit()

 {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    /**
     * Quits the handler thread's looper safely.
     * <p>
     * Causes the handler thread's looper to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * Pending delayed messages with due times in the future will not be delivered.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p>
     * If the thread has not been started or has finished (that is if
     * {@link #getLooper} returns null), then false is returned.
     * Otherwise the looper is asked to quit and true is returned.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     */
    public boolean

quitSafely()

 {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}

 从源码能够观望HandlerThread继续自Thread,构造函数的传递参数有五个,叁个是name指的是线程的称呼,贰个是priority指的是线程优先级,大家根据供给调用就可以。当中成员变量mLooper正是HandlerThread本人有所的Looper对象。onLooperPrepared(卡塔尔国该办法是叁个空完成,是留住我们须要时方可去重写的,可是注意重写机遇是在Looper循环运行前,再看看THread的run方法.

       前边大家在HandlerThread的正规使用中深入分析过,在开创HandlerThread对象后必需调用其start(State of Qatar方法才具展开其余操作,而调用start(卡塔尔方法后相当于运转了线程,也等于run方法将会被调用,而作者辈从run源码中得以观望其实行了Looper.prepare()代码,这个时候Looper对象将被创立,当Looper对象被创设后将绑定在当下线程(也正是时下异步线程),那样大家才得以把Looper对象赋值给Handler对象,进而确定保证Handler对象中的handleMessage方法是在异步线程推行的。

synchronized (this) {

        mLooper = Looper.myLooper();

         notifyAll(卡塔尔国; //唤醒等待线程

}

       这里在Looper对象创立后将其赋值给HandlerThread的里边变量mLooper,并透过notifyAll(State of Qatar方法去提示等待线程,最终实施Looper.loop();代码,开启looper循环语句。那这里怎么要提醒等待线程呢?我们来拜谒,getLooper方法

  • HandlerThread本质上是多少个线程类,它一连了Thread;
  • HandlerThread有温馨的里边Looper对象,能够展开looper循环;
  • 经过获得HandlerThread的looper对象传递给Handler对象,能够在handleMessage方法中实践异步职责。
  • 始建HandlerThread后必需先调用HandlerThread.start(卡塔尔方法,Thread会先调用run方法,创设Looper对象。

参考: 

 

//更新视图

private Handler handler =new Handler() {

    @Override

    public void handleMessage(Message msg) {

        if(msg.what == COMPLETED) {

            stateText.setText("completed");

        }

    }

};

// 在主线程中 MyHandler mHandler = new MyHandler(); private void getDataFromNet() { new Thread(new Runnable() { @Override public void run() { //耗时操作,完成之后发送消息给Handler,完成UI更新; mHandler.sendEmptyMessage; // 如果想发送带参消息,则用sendMessage.start(); } class MyHandler extends Handler{ @Override public void handleMessage(Message msg) { super.handleMessage; // 这个方法需要自己写内容 switch  { case 0: //完成主界面更新,拿到数据 imageView.setImageResource(R.mipmap.ic_launcher); break; default: break; } } }
Handler:担负将供给更新分界面包车型大巴子线程切换成主线程。

Lopper.java 源码深入分析

Looper的中坚代码:

public static void loop() {

    Looper me = myLooper();

    if (me == null) {

        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");

    }

    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();

while (true) {

        Message msg = queue.next(); // might block

        if (msg != null) {

            if (msg.target == null) { // target为null,甘休新闻,甘休循环

                // No target is a magic identifier for the quit message.

                return;

        }

    }

}

private Looper() {

mQueue = new MessageQueue();

    mRun = true;

mThread = Thread.currentThread();

}

public void quit() {

    Message msg = Message.obtain();

    // NOTE: By enqueueing directly into the message queue, the message is left with a null target. This is how we know it is a quit message

ca88编程,// 发送一个target为null的音讯,作为完毕

    mQueue.enqueueMessage(msg, 0);

}

本人看来里边是二个无比循环,退出循环的尺度是:msg.target == null;

也正是说,假若大家向此looper的MessageQueue发送一个target为null的message,就足以告一段落这些线程的远征。(查看Looper.quit代码能够作证那或多或少)。

使用Looper.prepare(State of Qatar、Looper.loop(卡塔尔为Thread增添音讯队列后,该线程便张开了。

倘诺在Thread中管理百废待举的天职,然后经过handler对象告知主线程,就可以缓和线程安全难点。这些进程中,音讯机制起着关键的作用。Android通过Looper、Handler来完毕音讯循环机制。Android的新闻循环是指向性线程的,各个线程都能够有和煦的新闻队列和音信循环。

在上述的例子中,大家在主线程创设了一个MyHandler类型的靶子mHandler,在子线程完毕耗费时间操作(如互联网央浼获取图片)之后,用sendEmptyMessage(卡塔尔国/sendMessage(卡塔尔(قطر‎方法发送消息,随后在主线程中会调用mhandler的handleMessage(卡塔尔(قطر‎方法来管理新闻。怎么着,是还是不是很简单?

MessageQueue:消息队列,以队列的格局对外提供插入和删除的专门的学业,其内部构造是以链表的款式积攒音讯的。

应用计算

1. Looper类用来为七个线程开启一个新闻循环。
    暗中同意处境下android中新名落孙山的线程是未曾开启消息循环的。(主线程除此而外,主线程系统会活动为其创建Looper对象,开启音讯循环。)
    Looper对象通过MessageQueue来寄存新闻和事件。二个线程只好有一个Looper,对应三个MessageQueue。(假使对八个曾经quit的Looper重新start会现身非凡)
2. 日常性是透过Handler对象来与Looper举行相互的。Handler可看做是Looper的三个接口,用来向钦命的Looper发送新闻及定义处理方法。
    暗中同意意况下Handler会与其被定义时所在线程的Looper绑定,比方,Handler在主线程中定义,那么它是与主线程的Looper绑定。
mainHandler = new Handler() 等价于new Handler(Looper.myLooper()).
Looper.myLooper(卡塔尔(قطر‎:获取当前经过的looper对象,近似的 Looper.getMainLooper(卡塔尔(قطر‎ 用于获取主线程的Looper对象。

  1. 在非主线程中央司法机关接new Handler(卡塔尔会报如下的不当:
    E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception
    E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
    缘由是非主线程中暗中同意未有开创Looper对象,须求先调用Looper.prepare(卡塔尔(قطر‎启用Looper。
  2. Looper.loop(卡塔尔国; 让Looper开头工作,从音信队列里取音讯,管理音讯。
        注意:写在Looper.loop(State of Qatar之后的代码不会被推行,那些函数内部应该是三个周而复始,当调用mHandler.getLooper(卡塔尔(قطر‎.quit(卡塔尔后,loop才会一知半解,其后的代码手艺得以运营。
  3. 基于以上文化,可达成主线程给子线程(非主线程)发送音讯。

ca88编程 2

本文由ca88发布,转载请注明来源

关键词: ca88网址 Andr Android... 机制 消息