Android HandlerThread 完全解析[通俗易懂]

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/47079737;本文出自:【张鸿洋的博客】1、概述话说最近股市变动不变,也成了热火朝天的话题。不知道大家有没有考虑做个实时更新股市数据的app呢?假设我们要做一个股市数据实时更新的app,我们可以在网上找个第三方的股市数据接口,然后在我们的app中每隔1分钟(合适的

大家好,又见面了,我是你们的朋友全栈君。

转载请标明出处:
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>

运行效果图

Android HandlerThread 完全解析[通俗易懂]

别问我为什么要用红色!!!

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
(欢迎关注,第一时间推送博文信息)
Android HandlerThread 完全解析[通俗易懂]

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/124981.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • java类的加载机制及加载过程_深入理解计算机pdf

    java类的加载机制及加载过程_深入理解计算机pdf每个开发人员对java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这个异常背后涉及到的是Java技术体系中的类加载机制。本文简述了JVM三种预定义类加载器,即启动类加载器、扩展类加载器和系统类加载器,并介绍和分析它们之间的关系和类加载所采用的双亲委派机制,给出并分析了与Java类加载原理相关的若干问题。

  • IntelliJ IDEA 报错:找不到包或者找不到符号

    IntelliJ IDEA 报错:找不到包或者找不到符号最近在使用IDEA的时候,突然出现过找不到包或者找不到符号的情况,在确定了自己引用存在的情况下,可以尝试以下几种方式来解决,以下是在开发过程中碰过问题同样解决过的几种办法,在此记录下也分享给大家,希望对各位有帮助。1.利用Maven-Reimport2.InvalidateandRestart3.编码统一4.重新编译点开ProjectStructu…

  • 基于spark的数据采集平台

    基于spark的数据采集平台数据采集平台管理端https://github.com/zhaoyachao/zdh_web数据采集平台服务https://github.com/zhaoyachao/zdh_server平台介绍数据采集,处理,监控,调度,管理一体化平台具体介绍请看github连接中的readme文档

  • IDEA热部署设置

    IDEA热部署设置转载位置:https://blog.csdn.net/nihao12323432/article/details/82664601

  • 第六周作业

    第六周作业

  • app放真机测试_什么软件可以验机安卓手机

    app放真机测试_什么软件可以验机安卓手机本方式测试环境为XCode3.2.3IOS4.0本方法只能把APP放到真机中测试,不能联机调试。 APP写好了之后,关闭XCode,记的要完全关闭哦。找到/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.0.sdk/里的SDKSettings.plist.先修改一下权限。右键GetInfo,不多…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号