Android多线程消息处理机制 HandlerThread案例分析

来源:互联网 发布:开车是什么意思网络语 编辑:程序博客网 时间:2024/06/06 03:11
前面在《Android多线程消息处理机制》讲过looper、thread的原理。
Main线程已经有了looper且已启动,意味着包含了消息队列。
所有没有指定looper对象而创建的handler,使用的都是main的looper。
这样的handler发送的message都放到了main线程的looper管理的消息队列中了。


发现有HandlerThread这个类,以前没用过,总以为是在Thread中内置了一个Handler。
在面试中也总是被问到,很心烦,于是索性来研究一下,正好前面也整体分析了looper,handler等技术。

先来看下源码吧:

/** * 创建一个新的线程类,同时包含有一个Looper. 然后可以使用这个Looper创建一个Handler. * Note:包含有一个Looper意味着什么? * 启动线程时启动looper消息循环,looper内置有messageQueue。这个线程专用来处理消息的了 * 看文章:Android多线程消息处理机制,http://blog.csdn.net/fesdgasdgasdg/article/details/52081773 */public class HandlerThread extends Thread {    int mPriority;//线程优先级    int mTid = -1;//进程id    Looper mLooper;//核心对象    public HandlerThread(String name) {        super(name);//获取线程默认的优先级,0        mPriority = Process.THREAD_PRIORITY_DEFAULT;    }        /**     * 构造函数     * @param name 线程名称     * @param priority 线程优先级. 必须从android.os.Process获取,而不能从java.lang.Thread获取.     */    public HandlerThread(String name, int priority) {        super(name);        mPriority = priority;    }        /**     * 如果需要在looper循环前执行一些操作时,可以显式的重写此方法     */    protected void onLooperPrepared() {    }/** * 线程的run方法,在线程start()后,就开始执行run方法, * 在run里面Looper.prepare()创建looper... * 此内容参考:Android多线程消息处理机制,http://blog.csdn.net/fesdgasdgasdg/article/details/52081773 */    @Override    public void run() {        mTid = Process.myTid();        Looper.prepare();        synchronized (this) {            mLooper = Looper.myLooper();            notifyAll();        }        Process.setThreadPriority(mPriority);        onLooperPrepared();//循环前的回调        Looper.loop();        mTid = -1;    }        /**     * 此方法返回当前线程关联的looper,     * 如果当前线程没有启动或者已停止等原因,此方法返回null     * 如果此方法已经启动,此方法将阻塞知道looper被初始化      * @return The looper.     */    public Looper getLooper() {    //如果当前线程不处在运行状态,则返回null        if (!isAlive()) {            return null;        }                // 如果此方法已经启动,此方法将阻塞知道looper被初始化        synchronized (this) {            while (isAlive() && mLooper == null) {                try {                    wait();                } catch (InterruptedException e) {                }            }        }        return mLooper;    }    /**     * 退出线程的looper循环.     * 使looper线程终止消息循环,不再处理消息队列中的任何消息 * 当looper调用quit之后,任何尝试post message到messageQueue中的操作都会失败     * 比喻:Handler的sendMessage(Message)方法会返回false     * 使用这个方法可能不安全,因为在looper停止之前可能有message没有被执行。     * 考虑使用quitSafely()方法代替quit()方法,以确保所有待完成的工作有序完成。     * @return 如果looper调用了quit,则返回true。如果线程还没有开始执行则返回false     *     * @see #安全退出     */    public boolean quit() {        Looper looper = getLooper();//获取当前线程的looper        if (looper != null) {            looper.quit();            return true;//成功的调用了quit,返回true        }        return false;    }    /**     * 安全的退出线程looper循环。     * 当messageQueue中所有已到期的message都处理完后,终止looper线程的消息循环。     * 但是由于延时的消息需要等待,故这些消息将不会被处理。     * Pending delayed messages with due times in the future will not be delivered.     * 当looper调用quit之后,任何尝试post message到messageQueue中的操作都会失败     * 比喻:Handler的sendMessage(Message)方法会返回false * 如果线程没有被启动,或者已经结束则返回false。否则在调用quitSafely()之后返回true。     */    public boolean quitSafely() {        Looper looper = getLooper();        if (looper != null) {            looper.quitSafely();            return true;        }        return false;    }    /**     * 返回此线程的标识符. 参考Process.myTid().     */    public int getThreadId() {        return mTid;    }}


这么一分析,是否都清楚了?无非就是个Thread,在里面内置了一个Looper,looper会自带一个MessageQueue。

在Thread的run方法中使用looper.prepqre()创建了looper。然后赋给当前线程的looper成员变量,供外面的handler使用。

接着调用looper.loop()方法启动消息循环。

前面也讲过可以自己创建LooperThread线程,HandlerThread就是google提供的经典实例,只不过HandlerThread里面提供了线程安全访问,退出消息循环等方法。

具体的用法简单的1C, 下面就用自己创建的LooperThread为例,如果你不喜欢的话,直接把LooperThread替换成现有的HandlerThread类即可。


public class MainActivity extends AppCompatActivity implements View.OnClickListener {    private Handler mHandler;    private LooperThread thread;    private TextView show;    private Button start;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        show = (TextView) findViewById(R.id.show);        start = (Button) findViewById(R.id.start);        start.setOnClickListener(this);        init();    }    private void init() {        thread = new LooperThread();        thread.start();        while (thread.getLooper() == null) {            try {                Thread.sleep(10);            } catch (InterruptedException e) {                e.printStackTrace();            }        }        mHandler = new Handler(thread.getLooper()) {            @Override            public void handleMessage(Message msg) {                //处理消息                Log.v("looper的handler", Thread.currentThread().getName() + " - " + msg.what);                //修改textview会报错的。                //show.setText("");            }        };    }    @Override    public void onClick(View v) {        mHandler.sendEmptyMessage(200);    }    /**     * Looper线程     */    class LooperThread extends Thread {        private Looper looper;        public Looper getLooper() {            return looper;        }        @Override        public void run() {            super.run();            Looper.prepare();            looper = Looper.myLooper();            Looper.loop();        }    }}


初始化的时候先创建LooperThread线程,且启动。

然后里面就会有looper了。

在创建handler的时候传入上面的looper。那么此时的handler已和LooperThread的消息队列绑定了。这个handler所发送和处理的消息只经过LooperThread的消息循环,

跟UI线程已有的消息循环没关系了。


最后有一点需要注意:

public void handleMessage(Message msg) {                //处理消息                Log.v("looper的handler", Thread.currentThread().getName() + " - " + msg.what);                //修改textview会报错的。                //show.setText("");            }


这个处理消息的方法,不能再处理UI线程创建的UI控件了。如果你真心想修改某一个ui,那的保证这个ui必须在LooperThread里面创建和添加到window中去。


不多说了,该去关心下宝宝。



2 1