Android消息处理机制--Handler 超详细通俗的解读

Handler是什么

每个Handler在创建的时候,都会与一个Looper绑定,UI线程有一个默认的Looper,所以在Activity类的字段中中直接创建Handler,也就是在UI线程中创建Handler就会默认绑定UI线程的Looper,绑定了哪个Looper,就只能给哪个looper发送消息,如果没有绑定Looper那么调用Handler的sendmessage等类似的发送消息的方法就会报错,当然,Handler绑定的哪个looper发送消息的回调也就在那个looper所在线程的handleMessage方法,这一点很重要。

处理机制

通俗的讲Handler的处理机制,你在开会的时候需要上厕所了,然后就报告你的上司,当然自己就相当于Handler,而你的上司就相当于Looper,你的上司考虑了一下,说去吧,然后你就在handleMessage方法中接收到老板的消息,并且起身去了厕所。这样讲会不会比较形象。然后Message只不过是一个封装了信息的容器而已。

总结
    >Handler负责发送消息,Looper负责接收Handler发送的消息,并且传回给Handler自己,MessageQueue就是一个存储消息的容器

目录


HandlerThread的作用

其实主要作用是用来在主线程中给子线程发送消息,让子线程处理比较耗时的任务,一般情况下Handler都是在子线程给主线程发送消息,这样来更新UI的,当我们要让Handler在一个子线程中创建处理一些事情的时候,就需要把Handler与一个子线程想关联,这里有两种办法,其中一种不用HandlerThread(其实这并不是一种正确的写法,因为效率太低),是这样写的:

首先定义一个子线程


package com.liran.handlertest;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;


/**
 * Created by Lenovo on 2015-09-22.
 */
public class MyThrad extends Thread {

    private String TAG="handlerTest";
    public Handler handler;
    public Looper looper;
    @Override
    public void run() {
        super.run();
        Looper.prepare();
        looper=Looper.myLooper();

      handler=new Handler(){
          @Override
          public void handleMessage(Message msg) {
              super.handleMessage(msg);
              Log.d(TAG, "handleMessage 当前线程是:"+Thread.currentThread());
          }
      };

        Looper.loop();
    }
}

然后再主线程中这样使用


MyThrad myThrad=new MyThrad();
myThrad.start();
myThrad.handler.sendEmptyMessage(1);

然后就闪退了,报的这个错误:
Attempt to invoke virtual method ‘boolean android.os.Handler.sendEmptyMessage(int)’ on a null object reference
具体原因我一会再说,先稍微改一下。

//在start之后线程休眠500毫秒
  MyThrad myThrad=new MyThrad();
        myThrad.start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myThrad.handler.sendEmptyMessage(1);

这一次就非常正常的运行并且输出了结果
handleMessage 当前线程是:Thread[Thread-187,5,main]
这是一个子线程。具体是为什么呢?
因为myThrad.handler.sendEmptyMessage(1); 这一句是在UI线程中执行的,上面的start之后两条线程同时执行,当执行到了sendEmptyMessage的时候,此时的myThrad可能还没有初始化,所以就会出现空指针异常了,当然,如果此时的Handler没有关联一个Looper也会出现异常,直接就是闪退级别的。这个时候HandlerThread就派上用场了。再看完全正确的写法:

//为了清楚的看到区别,我在Oncreate中输出了主线程的信息

//声明变量
 private HandlerThread handlerThread;
 private Handler handlerTest;

 handlerThread = new HandlerThread("handlerThread");
        handlerThread.start();
        //handlerThread的Looper绑定
        handlerTest = new Handler(handlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.d(TAG, "handleMessage 当前线程是:"+Thread.currentThread());
            }
        };
        handlerTest.sendEmptyMessage(1);

运行的结果是:
oncreat 当前线程是:Thread[main,5,main]
handleMessage 当前线程是:Thread[handlerThread,5,main]
完全正确的结果。
也就是说完全可以用HandlerThread实现一个异步任务。

四种更新UI的方法

private void updateUI2() {
        textView.post(new Runnable() {
            @Override
            public void run() {
                textView.setText("textView.post");
            }
        });
    }

    private void updateUI1() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                textView.setText("runOnUiThread");
            }
        });
    }

    private void Handler2() {
        handler.sendEmptyMessage(1);
    }

    private void Handler1() {
        handler.post(new Runnable() {
            @Override
            public void run() {
                textView.setText("handler.post");
            }
        });
    }

下面是这个Activity的完整代码:

package com.liran.handlertest;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

/**
 * 四种在子线程中更新UI的方法
 */
public class MainActivity extends Activity {

    private String TAG = "handlerTest";

    private TextView textView;

    private HandlerThread handlerThread;
    private Handler handlerTest;

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            textView.setText("handleMessage");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textview1);

        Log.d(TAG + "main", "oncreat 当前线程是:" + Thread.currentThread());

        /*MyThrad myThrad=new MyThrad();
        myThrad.start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myThrad.handler.sendEmptyMessage(1);*/

/*
        handlerThread = new HandlerThread("handlerThread");
        handlerThread.start();
        //handlerThread的Looper绑定
        handlerTest = new Handler(handlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.d(TAG, "handleMessage 当前线程是:"+Thread.currentThread());
            }
        };
        handlerTest.sendEmptyMessage(1);*/


        new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                /**如果前面休眠两秒钟的话,执行下面直接settext就是报异常,如果不休眠直接修改就没有这个异常
                 * 因为安卓底层对非UI线程更新UI的判断放在onResume中进行,OnCreat在这个方法之前,所以如果、
                 * 不进行睡眠,就一定可以更新成功,这算是凑巧在非ui线程中更新了UI。*/
//                textView.setText("试试看看");


                   Handler1();
//                Handler2();
//                updateUI1();
//                updateUI2();

            }
        }).start();

    }

    private void updateUI2() {
        textView.post(new Runnable() {
            @Override
            public void run() {
                textView.setText("textView.post");
            }
        });
    }

    private void updateUI1() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                textView.setText("runOnUiThread");
            }
        });
    }

    private void Handler2() {
        handler.sendEmptyMessage(1);
    }

    private void Handler1() {
        handler.post(new Runnable() {
            @Override
            public void run() {
                textView.setText("handler.post");
            }
        });
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

结束语

总之,所有的东西都在我的github上,每天更新新的东西。地址:我的github地址


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值