Android Handler机制深一步理解

Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃。解决这一问题,即创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现崩溃了。这种处理方式被称为异步消息处理线程。

一.Handler的基本使用 在主线程定义Handler

public class MainActivity extends AppCompatActivity {

    public static final String TAG = MainActivity.class.getSimpleName();
    private TextView texttitle = null;

    /**
     * 在主线程中定义Handler,并实现对应的handleMessage方法
     */
    public static Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //接收消息
            if (msg.what == 101) {
                texttitle.setText("接收到handler消息...");
              
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        texttitle = (TextView) findViewById(R.id.texttitle);
        texttitle.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread() {
                    @Override
                    public void run() {
                        // 在子线程中发送异步消息 通知主线程更新UI
                        mHandler.sendEmptyMessage(101);
                    }
                }.start();
            }
        });
    }
}

二.在子线程中定义Handler

主要用于进程之间通讯

2.1 直接在子线程第一handler会报错误

 new Thread(new Runnable() {  

            @Override  

            public void run() {  

            Looper.prepare();//每一个线程只能调用一次

            mHandler = new Handler() {

           public void handleMessage(Message msg) {

               Log.i(TAG, "在子线程中定义Handler,并接收到消息。。。");

        super.handleMessage(msg);

        System.out.println("这个消息是从-->>" + msg.obj+"过来的,在第二个子线程当中" );

        }

       }}

Looper.loop();//开始轮循

              }).start();  

在子线程中创建的Handler是会导致程序崩溃的,提示的错误信息为 Can't create handler inside thread that has not called Looper.prepare() 。说是不能在没有调用Looper.prepare() 的线程中创建Handler,那么可以理解当我们在new Handler的时候还需要有个Looper,

2.2查看Handler代码

  1. public Handler() {  
  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.     mLooper = Looper.myLooper();  //从Looper.myLooper方法中取个looper对象
  11.     if (mLooper == null) {  
  12.         throw new RuntimeException(  
  13.             "Can't create handler inside thread that has not called Looper.prepare()");  //如果为空则报异常
  14.     }  
  15.     mQueue = mLooper.mQueue;  
  16.     mCallback = null;  
  17. }  

查看Looper.myLooper();  代码

  1. public static final Looper myLooper() {  
  2.     return (Looper)sThreadLocal.get();  
  3. }  
  4. //从sThreadLocal中得到looper并返回与下边Looper.perpare中sThreadLocal.set方法想吻合

查看perpare方法代码可看到,自动创建一个Looper

public static final void prepare() {  

    if (sThreadLocal.get() != null) {  

        throw new RuntimeException("Only one Looper may be created per thread");  

    }  

    sThreadLocal.set(new Looper());  

}  

public static final void prepare() {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper());
}

可以看到,首先判断sThreadLocal中是否已经存在Looper了,如果还没有则创建一个新的Looper设置进去。这样也就完全解释了为什么我们要先调用Looper.prepare()方法,才能创建Handler对象。同时也可以看出每个线程中最多只会有一个Looper对象。


 

再创建第二个子线程

new Thread(new Runnable() {

                      @Override
                      public void run() {
                         Looper loop = Looper.myLooper();
               Message msg = childHandler.obtainMessage();
                         msg.obj = "第二个线程信息";
                         mHandler.sendMessage(msg);
                     }
                 }).start();

 

这样一个线程发送消息,一个线程接收消息 完成线程之间的通讯,但是在主线程中定义Handler会默认在程序启动的时候,系统已经帮我们自动调用了Looper.prepare()方法;

只有在子线程定义Handler需要prepare

 三.其他异步更新UI方法

1.Activity中的runOnUiThread()方法,代码如下所示:

     public final void runOnUiThread(Runnable action) {  
  1.     if (Thread.currentThread() != mUiThread) {  
  2.         mHandler.post(action); //不为主线程时候
  3.     } else {  
  4.         action.run();  
  5.     }  
  6. }  

2.

然后再来看一下View中的post()方法,代码如下所示:

public boolean post(Runnable action) {  
  1.     Handler handler;  
  2.     if (mAttachInfo != null) {  
  3.         handler = mAttachInfo.mHandler;  
  4.     } else {  
  5.         ViewRoot.getRunQueue().post(action);  
  6.         return true;  
  7.     }  
  8.     return handler.post(action);  //同样会调用handler.post();
  9. }  

查看Handler中的post()方法代码:

  1. public final boolean post(Runnable r)  
{  
  1.    return  sendMessageDelayed(getPostMessage(r), 0);  
  2. }  
public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

这里调用了sendMessageDelayed()方法去发送一条消息,并且还使用了getPostMessage()方法将Runnable对象转换成了一条消息,看下这个方法的源码:

private final Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

在这个方法中将消息的callback字段的值指定为传入的Runnable对象。callback在Handler的dispatchMessage()方法中原来有做一个检查,

通过追踪可以知道就是我们定义的Handler对象,然后我们查看一下Handler类的dispatchMessage方法:

/**
     * 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);
        }
    }

 

如果Message的callback等于null才会去调用handleMessage()方法,否则就调用handleCallback()方法。那我们快来看下handleCallback()方法中的代码吧:

private final void handleCallback(Message message) {  

    message.callback.run();  //调用我们run方法

}  

上边说了getPostMessage(Runnable r)方法中将我们自己的Runnble给了m.callback = r;message.callback相当于我们自己的Runnble 此时调用到了run方法。

 

自己的一些简单理解,诸位见笑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值