Anroid HandlerThread 完全解析

来源:互联网 发布:mac必备软件 知乎 编辑:程序博客网 时间:2024/05/24 03:55

1、概述

话说最近股市变动不变,也成了热火朝天的话题。不知道大家有没有考虑做个实时更新股市数据的app呢?假设我们要做一个股市数据实时更新的app,我们可以在网上找个第三方的股市数据接口,然后在我们的app中每隔1分钟(合适的时间)去更新数据,然后更新我们的UI即可。

当然了,本文不是要教大家做这样一个app,只是举个场景。言归正传,回到我们的HandlerThread,大家一定听说过Looper、Handler、Message三者的关系,在我们的UI线程默默的为我们服务。其实我们可以借鉴UI线程Looper的思想,搞个子线程,也通过Handler、Message通信,可以适用于很多场景。

对了,我之前写过一篇博文Android Handler 异步消息处理机制的妙用 创建强大的图片加载类,这篇博文中就在子线程中创建了Looper,Handler,原理和HandlerThread一模一样,可惜当时我并不知道这个类,不过大家也可以当做HandlerThread应用场景进行学习。

2、HandlerThread实例

下面看我们模拟大盘走势的代码,其实非常简单,就一个Activity

package com.zhy.blogcodes.intentservice;import android.os.Bundle;import android.os.Handler;import android.os.HandlerThread;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.text.Html;import android.widget.TextView;import com.zhy.blogcodes.R;public class HandlerThreadActivity extends AppCompatActivity{    private TextView mTvServiceInfo;    private HandlerThread mCheckMsgThread;    private Handler mCheckMsgHandler;    private boolean isUpdateInfo;    private static final int MSG_UPDATE_INFO = 0x110;    //与UI线程管理的handler    private Handler mHandler = new Handler();    @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_thread_handler);        //创建后台线程        initBackThread();        mTvServiceInfo = (TextView) findViewById(R.id.id_textview);    }    @Override    protected void onResume()    {        super.onResume();        //开始查询        isUpdateInfo = true;        mCheckMsgHandler.sendEmptyMessage(MSG_UPDATE_INFO);    }    @Override    protected void onPause()    {        super.onPause();        //停止查询        isUpdateInfo = false;        mCheckMsgHandler.removeMessages(MSG_UPDATE_INFO);    }    private void initBackThread()    {        mCheckMsgThread = new HandlerThread("check-message-coming");        mCheckMsgThread.start();        mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper())        {            @Override            public void handleMessage(Message msg)            {                checkForUpdate();                if (isUpdateInfo)                {                    mCheckMsgHandler.sendEmptyMessageDelayed(MSG_UPDATE_INFO, 1000);                }            }        };    }    /**     * 模拟从服务器解析数据     */    private void checkForUpdate()    {        try        {            //模拟耗时            Thread.sleep(1000);            mHandler.post(new Runnable()            {                @Override                public void run()                {                    String result = "实时更新中,当前大盘指数:<font color='red'>%d</font>";                    result = String.format(result, (int) (Math.random() * 3000 + 1000));                    mTvServiceInfo.setText(Html.fromHtml(result));                }            });        } catch (InterruptedException e)        {            e.printStackTrace();        }    }    @Override    protected void onDestroy()    {        super.onDestroy();        //释放资源        mCheckMsgThread.quit();    }}

可以看到我们在onCreate中,去创建和启动了HandlerThread,并且关联了一个mCheckMsgHandler。然后我们分别在onResume和onPause中去开启和暂停我们的查询,最后在onDestory中去释放资源。

这样就实现了我们每隔5s去服务端查询最新的数据,然后更新我们的UI,当然我们这里通过Thread.sleep()模拟耗时,返回了一个随机数,大家可以很轻易的换成真正的数据接口。

布局文库

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"                xmlns:tools="http://schemas.android.com/tools"                android:layout_width="match_parent"                android:layout_height="match_parent"                android:paddingLeft="@dimen/activity_horizontal_margin"                android:paddingRight="@dimen/activity_horizontal_margin"                android:paddingTop="@dimen/activity_vertical_margin"                android:paddingBottom="@dimen/activity_vertical_margin">    <TextView        android:id="@+id/id_textview"        android:text="正在加载大盘指数..."        android:layout_width="wrap_content"        android:layout_height="wrap_content"/></RelativeLayout>

运行效果图
这里写图片描述
别问我为什么要用红色!!!

ok,当然了,我们的效果很单调,但是你完全可以去扩展,比如ListView显示用户关注的股票数据。或者是其他的需要一直检测更新的数据。

看到这,你是否好奇呢,HandlerThread的内部是如何做的呢?其实非常的简单,如果你看过Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系估计扫几眼就会了。

3、HandlerThread 源码分析

对于所有Looper,Handler相关细节统一参考上面提到的文章。

我们轻轻的掀开HandlerThread的源码,还记得我们是通过

mCheckMsgThread = new HandlerThread("check-message-coming");mCheckMsgThread.start(); 

创建和启动的对象,那么随便扫一眼:

package android.os;public class HandlerThread extends Thread {    int mPriority;    int mTid = -1;    Looper mLooper;    public HandlerThread(String name) {        super(name);        mPriority = Process.THREAD_PRIORITY_DEFAULT;    }    protected void onLooperPrepared() {    }    @Override    public void run() {        mTid = Process.myTid();        Looper.prepare();        synchronized (this) {            mLooper = Looper.myLooper();            notifyAll();        }        Process.setThreadPriority(mPriority);        onLooperPrepared();        Looper.loop();        mTid = -1;    }

看到了什么,其实我们就是初始化和启动了一个线程;然后我们看run()方法,可以看到该方法中调用了Looper.prepare(),Loop.loop();

prepare()呢,中创建了一个Looper对象,并且把该对象放到了该线程范围内的变量中(sThreadLocal),在Looper对象的构造过程中,初始化了一个MessageQueue,作为该Looper对象成员变量。

loop()就开启了,不断的循环从MessageQueue中取消息处理了,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。如果你不明白我说的,参考上面推荐的文章。

接下来,我们创建了一个mCheckMsgHandler,是这么创建的:

mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper())

对应源码

public Looper getLooper() {        if (!isAlive()) {            return null;        }        // If the thread has been started, wait until the looper has been created.        synchronized (this) {            while (isAlive() && mLooper == null) {                try {                    wait();                } catch (InterruptedException e) {                }            }        }        return mLooper;    }

mCheckMsgThread.getLooper()返回的就是我们在run方法中创建的mLooper。

那么Handler的构造呢,其实就是在Handler中持有一个指向该Looper.mQueue对象,当handler调用sendMessage方法时,其实就是往该mQueue中去插入一个message,然后Looper.loop()就会取出执行。

好了,到这我们就分析完了,其实就几行代码;不过有一点我想提一下:

如果你够细心你会发现,run方法里面当mLooper创建完成后有个notifyAll(),getLooper()中有个wait(),这是为什么呢?因为的mLooper在一个线程中执行,而我们的handler是在UI线程初始化的,也就是说,我们必须等到mLooper创建完成,才能正确的返回getLooper();wait(),notify()就是为了解决这两个线程的同步问题。

不过对于这样的线程间的同步问题,我非常喜欢使用Semaphore。

0 0
原创粉丝点击