zoukankan      html  css  js  c++  java
  • Android HandlerThread 完全解析

    转载请标明出处:
    http://blog.csdn.net/lmj623565791/article/details/47079737
    本文出自:【张鸿洋的博客】

    1、概述

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

    当然了,本文不是要教大家做这样一个app,只是举个场景。言归正传,回到我们的HandlerThread,大家一定听说过Looper、Handler、Message三者的关系(如果不了解,可以查看Android 异步消息处理机制 让你深入理解 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三者关系估计扫几眼就会了。

    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。

    还记得在Android Handler 异步消息处理机制的妙用 创建强大的图片加载类一文中有类似的说明:

    如果你比较细心,可能会发现里面还有一些信号量的操作的代码,如果你不了解什么是信号量,可以参考:Java 并发专题 : Semaphore 实现 互斥 与 连接池 。 简单说一下mSemaphore(信号数为1)的作用,由于mPoolThreadHander实在子线程初始化的,所以我在初始化前调用了mSemaphore.acquire去请求一个信号量,然后在初始化完成后释放了此信号量,我为什么这么做呢?因为在主线程可能会立即使用到mPoolThreadHander,但是mPoolThreadHander是在子线程初始化的,虽然速度很快,但是我也不能百分百的保证,主线程使用时已经初始化结束。

    哈,当时也有很多人问,为什么使用这个Semaphore,到这里我想大家应该清楚了。话说假设我当时真的HanderThread这个类,可能之前的代码能简化不少呢~

    对了,你可能会问与Timer相比有什么优势呢?

    直接参考这篇文章吧:Handler vs Timer

    ok~~

    欢迎关注我的微博http://weibo.com/u/3165018720

    群号:463081660,欢迎入群

    微信公众号:hongyangAndroid
    (欢迎关注,第一时间推送博文信息)

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    77、Android设置虚线、圆角、渐变
    c# 制作自定义控件并生成dll文件旷展到其他项目使用
    VB ListView罗列图片
    (壹)、java面向对象详解
    php基本语法
    1.单例模式
    Memcached详解
    MySql的优化
    页面静态化详解
    mysql常用知识点
  • 原文地址:https://www.cnblogs.com/dingxiaoyue/p/4924834.html
Copyright © 2011-2022 走看看