android 开发Handler源码剖析详解手机开发

Android的消息机制主要是Handler的运行机制,而讲Handler的机制,又需要和MessageQueue和Looper结合。MessageQueue中文意思是消息队列,虽说叫队列,但是其内部结构并不是队列组成的,而是采用了单链表的形式来存储消息。MessageQueue只是负责存储消息,并不处理消息(这里指消息的轮训),Looper刚好弥补了这个空缺。我在知道,Handler创建的时候,会默认为我们创建一个Looper对象,那么如何获取当前的Looper呢,这里就使用到了一个TheadLocal的概念,TheadLocal可以轻松的获取当前使用的Looper。

Handler的使用:

Handler 主要有两种用法:( 1 )调度消息和 runnable 对象,并在某一时间点执行。( 2 )依次存放分属不同线程的行为。

线程在默认的时候是没有Looper与之相关联。在线程中,我们可以通过调用prepare 方法来启动一个消息loop,调用loop方法来通知Looper来处理消息,直至结束。Looper提供了很多的静态方法来与线程、消息队列进行交互。一个线程最多允许创建一个Looper

以下是SDK文档中介绍的在线程中使用handler的一种方法:

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


注意这里,官方的例子是和Looper关联了,那么如果我们不关联Looper的话会怎么样呢

 mRunnable = new  Runnable(){            @Override            public void run() {               // TODO Auto-generated method stub                   String s = mHandler.getLooper().getThread().getName();               Log.e("handler", s);               mHandler.postDelayed(mRunnable, 1000);            }}; Thread t  = new Thread(mRunnable,"thread");         t.start();

运行结果:
ERROR/handler(1630): main,可以发现我们创建了一个名为“thread”的线程,病通过handler发送消息,我们创建的handler自动和UI线程的Looper关联上了。

那么,可不可以在主线程中定义handler,在代码中动态改变handler使之与其他线程的looper相关联呢?是可以的。

mRunnable = new  Runnable(){ 
              @Override 
              public void run() { 
                  // TODO Auto-generated method stub 
                  Looper.prepare(); 
                  mHandler = new Handler(Looper.myLooper()); 
                  String s = mHandler.getLooper().getThread().getName(); 
                  Log.e("handler", s); 
                  mHandler = new Handler(Looper.getMainLooper()); 
                   s = mHandler.getLooper().getThread().getName(); 
                  Log.e("handler", s);         
              }}; 
    Thread t  = new Thread(mRunnable,"subthread"); 
            t.start();

运行结果:

ERROR/handler(4049):subthread

ERROR/handler(4049): main

不过我们Android为我们提供了一个包含Looper的Thead类,HandlerThread  HandlerThread自带了一个Lopper,

 handlerThread=new HandlerThread("handlerThead"); 
        handlerThread.start(); 
        handler=new Handler(handlerThread.getLooper()); 
        handler.post(new Runnable() { 
            @Override 
            public void run() { 
                String s=handler.getLooper().getThread().getName(); 
                Log.d("test",s); 
            } 
        });

注:android认为在非线程中操作UI界面是不安全的,因此禁止在其它线程中修改UI界面。解决的方法有两种,一是通过在主线程中定义的handler更新界面,二是直接调用被修改的viewpostInvalidate方法刷新单个view

其实上面的描述是不准确的,实际上在activity第一次执行的时候,是可以在子线程直接更新主线程数据的,条件是不能执行Thread.sleep(),

原因是什么呢?

1.当刚启动Activity即onCreate里面此时onResume方法还没有执行的时候可以,因为在线程中更新UI时会调用ViewParent.invalidateChild()方法检查当前的Thread是否是UIThread,若为UIThread则可以更新(ViewParent是一个接口类,其实现是ViewRootImpl;invalidateChild()方法调用checkThread()方法来检查线程是否为主线程)。ViewRootImp是在onResume方法中初始化的,所以只要在ViewRootImpl创建之前更新UI(在onCreate方法中创建线程并执行,此时还没有初始化ViewRootImp),就可以逃避掉checkThread()的检查进而更新UI。
2.–>刚启动的时候,立即在非UI线程更新->不报错(onResume还没有执行)
—>休眠2s钟以后,更新——————>报错

最后贴上一张图

android 开发Handler源码剖析详解手机开发

分析完了Android消息机制的流程,那么我们接下来分别理解一些重要的概念。主要从以下几个方面增强理解,Handler,MessageQueue,Looper和Threadloacal.






原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/5889.html

(0)
上一篇 2021年7月17日
下一篇 2021年7月17日

相关推荐

发表回复

登录后才能评论