码迷,mamicode.com
首页 > 移动开发 > 详细

Android HandlerThread 完全解析

时间:2015-09-25 13:26:22      阅读:188      评论:0      收藏:0      [点我收藏+]

标签:

想想已经理解的Handler异步处理流程,你就很容易理解HandlerThread.

在你看原文之前,我给你捋一捋思路,方便你理解鸿洋前辈的文章。

A线程要发消息给B线程,那么B线程必须持有A线程的Handler,之后在B线程中发消息发消息给A线程,B线程中处理消息(收发分别在俩线程中进行,加上MessageQueue正好构成异步通信),队列在A线程中(Looper对象也在A线程中,什么looper.prepare()以及looper.loop()当然也在A线程中)

到了HandlerThread这,就超级简单了,因为A线程已经确定了,就是这个HandlerThread线程,就是说,你创建一个HandlerThread对象的时候(我是说它的start()被调用的时候),Looper.prepare() 和Looper.loop()已经ok了,你要做的是在你的线程中用HandlerThread中的Looper对象去初始化你线程中的Handler就好了(这样就算拿到了HandlerThread线程的Handlder了,好了,给HandlerThread这个线程发消息吧),就是下文中的new Handler(mCheckMsgThread.getLooper()),当然handMessage()还是要你去重载的.

说到底就是对了Looper机制和Thread机制做了一个整合,你也可以的,不信你试试。

废话完毕,看原文:(下文转载自   http://blog.csdn.net/lmj623565791/article/details/47079737


HandlerThread实例

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>

运行效果图

技术分享

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。



注: 对于要保证 looper.prepare()在looper.looper()之前执行,这里用同步进行的加锁阻塞,Semaphore也是一种机制。我觉得java的同步锁就已经够了。

其他补充:

"那么Handler的构造呢,其实就是在Handler中持有一个指向该Looper.mQueue对象"

这句说的并不准确,看代码:

public Handler(Callback callback, boolean async) {  
        if (FIND_POTENTIAL_LEAKS) {  
            final Class<? extends Handler> klass = getClass();  
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
                    (klass.getModifiers() & Modifier.STATIC) == 0) {  
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
                    klass.getCanonicalName());  
            }  
        }  
   
        mLooper = Looper.myLooper();  
        if (mLooper == null) {  
            throw new RuntimeException(  
                "Can‘t create handler inside thread that has not called Looper.prepare()");  
        }  
        mQueue = mLooper.mQueue;  
        mCallback = callback;  
        mAsynchronous = async;  
    }

也就是:

Handler  ---->Looper----->MessageQueue  (代码见上)

而  Looper ------->Handler  是在 Looper.loop()中  msg.target.dispatchMessage()时候,在取出的Message的target属性中关联的

Android HandlerThread 完全解析

标签:

原文地址:http://my.oschina.net/wizardmerlin/blog/511065

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!