美文网首页
3.1异步消息处理机制-handler

3.1异步消息处理机制-handler

作者: 205蚁 | 来源:发表于2018-11-09 09:43 被阅读0次

异步消息处理机制-handler详解

    1. 什么是handler
    1. handler的使用方法
      postRunnable,sendMessage
    1. handler机制的原理
      handler,looper,messagequeue,ThreadLooper四者的关系
    1. handler引起的内存泄露以及解决办法
      非静态内部类持有外部类的引用造成的

1.什么是handler

现象

1.异常1:android.view.ViewRootImpl$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.

    1. 根本原因是,android的View不是线程安全的,程序主线程负责UI的展示,UI事件消息的分发,主线程也称作UI线程
    1. Handler机制,可以通过handler在子线程发送消息给主线程来更新UI,子线程可以做耗时操作,用handler将结果切换的主线程处理
      1. 简单讲:handler是更新UI的机制
      1. 复杂点:在一些场景中,文件的读取,网络数据的获取,这些耗时操作完成后需要在UI线程做出改变,耗时操作在子线程做的,又由于android是不安全的(??怎样的不安全),不能再子线程中更新UI.
    1. 总结:handler通过发送和处理Message和Runnable对象来关联相对应MessageQueue
      1. 可以让对应的Message和Runnable在未来的某个时间点进行相应的处理
      1. 让自己想要处理的耗时操作放在子线程,让更新UI的操作放在主线程。

2.handler的使用方法

    1. 方式1:handler.post(Runnable),2,handler.sendMessage(Message)


      图1

      步骤:

        1. 创建:Handler mHandler = new Handler();创建的handler会绑定到当前所在的线程,在Activity中也就是主线程
      • 2 发送消息 mHandler.post(Runnable)
    1. 方式2 :sendMessage


      图2
        1. 创建Handler,复写handleMessage方法
        1. Message msg = Messager.obtain();
          msg.what = 1;
          msg.arg1 = xx;
          msg.arg2 = xx;
        1. handler.sendMessage(msg);

3.handler机制的原理

    1. handler,looper,messagequeue,ThreadLooper四者的关系

    逻辑图:


    UML图:


    图3

ThreadLocal 图见1

    #sThreadLocal:ThreadLocal<Looper>
    -mMainLooper:static Looper
    #mQueue:MessageQueue
    #mThread:Thread
    ----------------
    +prepare()
    +looper()
    +myLooper()
    +quit()

Looper

图4 图5
图6
    其中+Looper有一个mQueue消息队列
    MessageQueue
    #mMessages:Message
    -------------------
    +isIdle():boolaen
    #next():Message
    +enqueueMessage(msg:Message,when long)
    +removeMessage()

MessageQueue

图5
    其中MessageQueue有一个mMessages消息
    Message
    +what int
    +arg1 int
    +arg2 int
    +obj  Object
    #when long
    #target Handler
    #next Message
    ---------------------
    +Obtain() Message
    +recycle()

Message

    其中+Message有一个Handler对象
    Handler
    #mQueue:MessageQueue
    #mLooper:Looper
    -----------------------
    +dispatchMessage(msg:Message)
    +handleMessage(msg:Message)
    +obtainMessage():Message
    +sendMessage(msg:Message)
    +removeMessage()
    1. Looper是每一个线程所独有,Looper通过looper方法,读取MessageQueue的消息,读到消息后,把消息发送给handler来处理。
  • 2.MessageQueue是一个消息队里,只是一个先进先出的方式来管理message。
    • 1.在创建Looper时会同时创建MessageQueue,二者会关联在一起

    • 2.Message是消息对象

    • 3.Handler:两个作用:1.发送消息 2.处理消息

        1. handler只能将消息发送到自己相关的线程,也就是他相关线程的MessageQueue当中
        1. 而MessageQueue又是跟Looper相关联的,所以说handler要发送消息必须要有一个looper。所以这三者关系到了一起

现象

抛异常:Can‘t craeate handler inside thread that has not called Looper.prepare();

构造方法中:

    public Handler(Callback callback,boolean async){
        //xx判断
        mLooper = Looper.myLooper();
        //判断mLooper是否为空,为空 抛异常:Can‘t craeate handler inside thread that has not called         Looper.prepare();
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
    其他线程使用
        Looper.prepare();
        Hander handler = new Handler()
        Looper.loop();
        
    其中Looper类中mLooper.myLooper(){
        return sThreadLocal.get();
    }
    

sThreadLocal 是什么呢

  • 就是不同的线程访问同一个ThreadLocal,不管是set还是get方法,他们对ThreadLocal的所有的读写操作仅限于各自的线程内部,这就是为什么handler里面要通过ThreadLocal来保存looper对象,这样他就可以使每一个线程有一个唯一的looper

疑问:这里用的是ThreadLocal的get方法,那set方法什么时候调用呢

其中Looper类中
public static void prepareMainLooper(){
    prepare(false);
    synchronized(Looper.class){
        if(mMainLooper !=null){
            throw new IllegalStateException("The main looper has already bean prepared.")
        }
        mMainLooper = myLooper();
    }
}
其中Looper类中
public static void prepare(boolean quiallowed){
    if(sThreadLocal.get()!=null){
        throw new RuntimeException("Only one Looper may be created per thread")
    }
    sThreadLocal.set(new Looper(quitAllowed));
}
    1. 这个set方法,将Looper设置到ThreadLocal中,保证了每个线程looper对象的唯一性
    1. 这时候整个MessageQueue通过Looper和线程关联上了,这样我们可以在不同的线程访问不同的消息队列
    1. 回到Handler的构造方法:由于在handler构造方法中创建了looper,接着又根据mQueue = mLooper.mQueue.这样hanlder和MessageQueue关联到了一起,而MessageQueue通过Looper来管理,这是三者最重要的机制。
    1. 前面说,只能在主线程(UI线程)更新UI,就是因为每一个Handler要与主线程的消息队列关联上,所以一定要在主线程创建Handler(构造方法会将所在线程的mQueue进行绑定),而不能在内部类创建Handler的原因,这样就能将处理Message在主线程执行,就不会抛出最开始的异常

Looper 相关
post(Runnable),sendMessage方法都需要先开启Looper.looper()开启轮询才能走起来,

Looper.loop(){
    final Looper me = myLooper();
    if(me ==null) //throw new RuntimeException("No Looper;Looper.prepare() wasn't called on the thread")
        final MessageQueue queue = me.mQueue;
        for(;;){
            Message msg = queue.next();
            if(msg == null){
            return;
            }
            final Printer logging = me.mLogging;
            if()xxx
            try{
                msg.target.dispatchMessage(msg);
            }finally{}
        }
}

创建了一个死循环,从消息队列中逐个的获取消息

总结Looper

    1. Looper通过prepare创建Looper并保存在ThreadLocal中,然后通过loop来进行消息的分发msg.target.dispatchMessage(msg);
    1. 这个target就是Handler,handler发送消息给消息队列,消息队列执行时交给handler处理,一个是发送消息,一个是处理消息

其中Handler类中

public void dispatchMessage(Message msg){
        if(msg.callback !=null){
            handleCallback(msg);
        }else{
            if(mCallback !=null){
                if(mCallback.handleMessage(msg)){
                    return;
                }
            }
            handleMessage(msg);
        }
}
  • 这个方法是一个中转器(分发器),先判断msg.callback是否为空,
    不为空则:
private static void handleCallback(Message message){
        message.callback.run();就是调用的线程的Runn方法
}

4.handler引起的内存泄露以及解决办法

    1. 非静态内部类持有外部类的引用造成的
    • 在Activity中private Handler mHandler = new Handler();
    • 由于不是静态内部类,所以会引用外部即、Activity的引用,
    1. 当Activity要回收时,Handler可能在做一些耗时操作,没有释放引用,导致Activity不能及时销毁
      1. 解决方式,改用private static Handler = new Handler()
      1. 处理完使用removeCallBack
    1. 若在内部类需要引用外部对象,采用弱引用

相关文章

网友评论

      本文标题:3.1异步消息处理机制-handler

      本文链接:https://www.haomeiwen.com/subject/nbknxqtx.html