Android HandlerThread全面解析

在之前的一篇博客Android 异步消息处理机制:Looper、Handler、Message中,我们讲解了Looper、Handler、Message三者的关系。实际开发中我们使用的Looper基本都是定义在UI线程中,在子线程中通过handler.post(new Runnable())可以在UI线程中更新界面,这其实是UI线程在默默的为我们服务。
其实我们完全可以借鉴UI线程Looper的思想,搞个子线程Looper,也通过Handler、Message通信,可以适用于很多场景。
一个最标准的异步消息处理线程的写法应该是这样:

class LooperThread extends Thread {  
      public Handler mHandler;

      public void run() {  
          Looper.prepare();   
          mHandler = new Handler() {  
              public void handleMessage(Message msg) {  
                  // process incoming messages here  
              }  
          };
          Looper.loop();  
      }  
  } 

但是Android为我们提供了一个更方便的实现类—HandlerThread。

HandlerThread实例演示

这里的实例是用一个TextView显示当前时间,每隔2秒更新一次。

public class MainActivity extends Activity {
    private TextView text;
    private Button button;

    private HandlerThread mThread;
    private Handler mHandler;
    private boolean isUpdateInfo;
    private static final int MSG_UPDATE_INFO = 0x110;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView) findViewById(R.id.text);
        button = (Button) findViewById(R.id.start_service);
        //初始化HandlerThread       
        initBackThread();
    }

    @Override
    protected void onResume() {
        super.onResume();
        //开始更新
        isUpdateInfo = true;
        mHandler.sendEmptyMessage(MSG_UPDATE_INFO);
    }

    @Override
    protected void onPause() {
        super.onPause();
        //停止更新
        isUpdateInfo = false;
        mHandler.removeMessages(MSG_UPDATE_INFO);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //释放资源
        mThread.quit();
    }

    private void initBackThread() {
        mThread = new HandlerThread("ActivityStartArguments");
        mThread.start();
        //注意这里Handler中传入的是HandlerThread的Looper
        mHandler = new Handler(mThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                //UI线程中更新界面
                runOnUiThread(new Runnable(){
                    @Override
                    public void run() {
                        text.setText(getCurrentTime());
                    }
                });
                //模拟耗时操作,等待2s
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    Thread.currentThread().interrupt();
                }
                //发送新的任务
                if (isUpdateInfo) {
                    mHandler.sendEmptyMessage(MSG_UPDATE_INFO);
                }
            }
        };
    } 

    public String getCurrentTime(){
        Date date = new Date();          
        DateFormat df = DateFormat.getTimeInstance(DateFormat.FULL);  
        String dateStr = df.format(date);  
        return dateStr;
    }

}

可以看到我们在onCreate中,去创建和启动了HandlerThread,并且关联了一个mThread。然后我们分别在onResume和onPause中去开启和暂停我们的任务,最后在onDestory中去释放资源。
这样就实现了每隔2s去更新我们的UI,当然我们这里通过Thread.sleep()模拟耗时,返回了当前时间,大家可以很轻易的换成真正的数据接口。
这里我们是在旧的Message处理完毕之后再发送新的Message给HandlerThread,其实这个过程可以是异步的,也就是说在旧的Message还没有处理完,新的Message就可以发送了,这时新的Message会存放在Looper的MessageQueue中,待旧的Message处理完毕后,然后Looper会不断从该MessageQueue中读取新的Message并处理。Looper当没有消息的时候会阻塞,有消息到来的时候就会唤醒。

运行看效果:
这里写图片描述

HandlerThread源码分析

首先看HandlerThread 启动的地方:

mThread = new HandlerThread("ActivityStartArguments");
mThread.start();

其实我们就是初始化和启动了一个线程,看HandlerThread内部构造:

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中取消息处理,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。

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

mHandler = new Handler(mThread.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;
    }

mThread.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()就是为了解决这两个线程的同步问题。

HandlerThread结合Service使用

我们都知道在Service里面不能直接进行耗时操作,一般都需要去开启子线程去做一些事情,自己去管理Service的生命周期以及子线程并非是个优雅的做法;其实我们可以结合HandlerThread完成对Service生命周期的控制,非常完美。

public class MyService extends Service {
    private HandlerThread mThread;
    private Handler mHandler;

    @Override  
    public void onCreate() {  
        super.onCreate();  
        initBackThread();
    }  

    private void initBackThread() {
        mThread = new HandlerThread("ServiceStartArguments");
        mThread.start();
        mHandler = new Handler(mThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                //线程操作
                MainActivity.showlog("processing...msg.arg1="+msg.arg1);
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    Thread.currentThread().interrupt();
                }
                //和当前startId一致时,才销毁service
                stopSelf(msg.arg1);
            }
        };
    }

    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {          
        //执行新的任务,传入startId
        Message msg = mHandler.obtainMessage();
        msg.arg1 = startId;
        mHandler.sendMessage(msg);      
        return START_STICKY;
    }  

    @Override  
    public void onDestroy() {  
        super.onDestroy();
        MainActivity.showlog("onDestroy");
        mThread.quit();
    }  

    @Override  
    public IBinder onBind(Intent intent) {
        return null;
    }  

}  

启动Service:

    @Override  
    public void onClick(View v) {  
        switch (v.getId()) {  
        case R.id.start_service:
            showlog("click Start Service button");
            Intent it1 = new Intent(this, MyService.class);  
            startService(it1);  
            break;
        default:  
            break;  
        }  
    }  

当我们多次点击Start Service按钮,多次启动Service,onStartCommand执行多次,会发送多个Message到HandlerThread,然后Looper会不断从该MessageQueue中读取新的Message并处理。
关键点来了,执行完任务后有这么一句话,stopSelf(msg.arg1);,注意这个msg.arg1是个int值,相当于一个请求的唯一标识,其实它就是启动Service的startId,我们每次启动Service会把这个startId传到onStartCommand的第三个参数,并将它置为当前的启动ID。当调用stopSelf(msg.arg1)方法时,若该标识与当前的启动ID一致,则销毁service,如果在调用stopSelf(msg.arg1)之前,Service收到一个新的Start请求,ID将无法匹配,Service并不会停止。

我们看一下效果,启动程序,连续点击三次Start Service:
这里写图片描述

可以看到,处理了三次任务,每次间隔时间2s,最后一次处理完毕后,Service自动销毁。这样就自动完成了Service生命周期的控制,Service完全不需要我们自己手动去销毁。

其实所有这些过程,Android已经帮我们封装好了,Android已经给我们提供了一个现成类,叫做IntentService,下面看具体用法。

IntentService使用

使用IntentService的要点如下:

  • 默认在子线程中处理回传到onStartCommand()方法中的Intent;
  • 在重写的onHandleIntent()方法中处理按时间排序的Intent队列,所以不用担心多线程(multi-threading)带来的问题。
  • 当所有请求处理完成后,自动停止service,无需手动调用stopSelf()方法; 默认实现了onBind()方法,并返回null;
  • 默认实现了onStartCommand()方法,并将回传的Intent以序列的形式发送给onHandleIntent(),您只需重写该方法并处理Intent即可。

综上所述,您只需重写onHandleIntent()方法即可,当然,还需要创建一个构造方法,示例如下:

public class MyIntentService extends IntentService {        

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override  
    public void onHandleIntent(Intent intent) {
        //线程操作
        MainActivity.showlog("processing...");
        try {
            Thread.sleep(2000);
        } catch (Exception e) {
            Thread.currentThread().interrupt();
        } 
    }

    @Override  
    public void onDestroy() {  
        super.onDestroy();
        MainActivity.showlog("onDestroy");
    }  

}  

是不是简单多了呢?其实效果和上面是一样的。如果你还希望在IntentService的继承类中重写其他生命周期方法,如onCreate()、onStartCommand() 或 onDestroy(),那么请先调用各自的父类方法以保证子线程能够正常启动。 比如,这里我重写了onDestroy()方法,添加了Log信息。
注:除onHandleIntent()外,onBind()方法也无需调用其父类方法。

连续点击三次Start Intent Service按钮,Log如下:
这里写图片描述

下面我们再稍微看下IntentService的源码:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }


    public IntentService(String name) {
        super();
        mName = name;
    }


    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }


    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    protected abstract void onHandleIntent(Intent intent);
}

可以看到它在onCreate里面初始化了一个HandlerThread,然后每次调用onStart的时候,通过mServiceHandler发送一个消息,消息中包含我们的intent。然后在该mServiceHandler的handleMessage中去回调onHandleIntent(intent);就可以了。果然是这样,onStartCommand中回调了onStart,onStart中通过mServiceHandler发送消息到该handler的handleMessage中去。最后handleMessage中回调onHandleIntent(intent)。回调完成后回调用 stopSelf(msg.arg1),当任务完成销毁Service回调onDestory,可以看到在onDestroy中释放了我们的Looper:mServiceLooper.quit()。

Demo下载地址

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值