一、前言

  在学习Service的时候,我们一定会知道IntentService:官方文档不止一次强调,Service本身是运行在主线程中的(详见:【Android】Service),而主线程中是不适合进行耗时任务的,因而官方文档叮嘱我们一定要在Service中另开线程进行耗时任务处理。IntentService正是为这个目的而诞生的一个优雅设计,让程序员不用再管理线程的开启和允许。

  至于介绍HandlerThread,一方面是因为IntentService的实现中使用到了HandlerThread,另一方面是因为IntentService和HandlerThread以及很多Android中的类一样,其实都是为了方便某个目的,对最基本的类进行的一定的扩充,并且结构精巧,便于使用,很适合阅读研究。

二、HandlerThread源码

先来一段结结实实的完整源码:

 /*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package android.os; /**
* Handy class for starting a new thread that has a looper. The looper can then be
* used to create handler classes. Note that start() must still be called.
*/
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper; public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
} /**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
} /**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
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;
} /**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
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;
} /**
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
} /**
* Quits the handler thread's looper safely.
* <p>
* Causes the handler thread's looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
} /**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}

  总共就149行代码。下面捡重点分析。

  首先,类注释(20-21行)明确指出,该类实现了一个带looper的Thread。23行明确看出HandlerThread是继承于Thread的。那么为什么需要一个Thread带上looper呢?如果想要了解,可以阅读:Android Handler机制,想要深入了解,则可以阅读【Android】Handler、Looper源码分析。简而言之,一个类具有的Looper,就可以接受并且处理消息了。当我们不用HandlerThread而直接使用Thread去实现这样一个功能的时候,需要如下代码:

 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();
}
}

  至于其中的Looper.prepare()和Looper.loop()方法起什么作用,读完【Android】Handler、Looper源码分析应该一目了然。OK,很明显,这样创建一个戴Handler的Thread有很多重复的地方,那么怎么复用这些代码,让程序员可以直接而简单的创建呢?我们来看HandlerThread的实现。

  该类注释很明确的说,使用之前必须调用start()方法,Thread类的start()方法调用后会去执行run()方法体,而源码中51-63行则覆盖了Thread的run()方法,注意到这里实现了两行最关键的代码:54和61行。因而一个Thread就具备了Looper的特性。当然,里面为了让用户有足够的可操控性,还设置了一个回调方法。

  另外,读者可能对55-58行感到奇怪:为什么需要synchronized关键字修饰两行代码呢?答案其实在另外一个方法里面:71-86行的getLooper()方法中,getLooper()的时候,线程可能已经启动,但是还没有准备好Looper,而从run()看,其实很快就会准备好,因而这个方法回去wait(),55-58行代码的目的是:一旦准备好Looper,就立马通知被阻塞的线程,防止有别的线程因为调用自身的getLooper()方法而阻塞。

  OK,HandlerThread就介绍到这里。至于怎么使用它,IntentService提供了完美的例子。

三、IntentService

同样来一段结实的完整源码:

 /*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package android.app; import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message; /**
* IntentService is a base class for {@link Service}s that handle asynchronous
* requests (expressed as {@link Intent}s) on demand. Clients send requests
* through {@link android.content.Context#startService(Intent)} calls; the
* service is started as needed, handles each Intent in turn using a worker
* thread, and stops itself when it runs out of work.
*
* <p>This "work queue processor" pattern is commonly used to offload tasks
* from an application's main thread. The IntentService class exists to
* simplify this pattern and take care of the mechanics. To use it, extend
* IntentService and implement {@link #onHandleIntent(Intent)}. IntentService
* will receive the Intents, launch a worker thread, and stop the service as
* appropriate.
*
* <p>All requests are handled on a single worker thread -- they may take as
* long as necessary (and will not block the application's main loop), but
* only one request will be processed at a time.
*
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For a detailed discussion about how to create services, read the
* <a href="{@docRoot}guide/topics/fundamentals/services.html">Services</a> developer guide.</p>
* </div>
*
* @see android.os.AsyncTask
*/
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);
}
} /**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public IntentService(String name) {
super();
mName = name;
} /**
* Sets intent redelivery preferences. Usually called from the constructor
* with your preferred semantics.
*
* <p>If enabled is true,
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_REDELIVER_INTENT}, so if this process dies before
* {@link #onHandleIntent(Intent)} returns, the process will be restarted
* and the intent redelivered. If multiple Intents have been sent, only
* the most recent one is guaranteed to be redelivered.
*
* <p>If enabled is false (the default),
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
* dies along with it.
*/
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
} @Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock. 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);
} /**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@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();
} /**
* Unless you provide binding for your service, you don't need to implement this
* method, because the default implementation returns null.
* @see android.app.Service#onBind
*/
@Override
public IBinder onBind(Intent intent) {
return null;
} /**
* This method is invoked on the worker thread with a request to process.
* Only one Intent is processed at a time, but the processing happens on a
* worker thread that runs independently from other application logic.
* So, if this code takes a long time, it will hold up other requests to
* the same IntentService, but it will not hold up anything else.
* When all requests have been handled, the IntentService stops itself,
* so you should not call {@link #stopSelf}.
*
* @param intent The value passed to {@link
* android.content.Context#startService(Intent)}.
*/
protected abstract void onHandleIntent(Intent intent);
}

  同样很短,只有162行。IntentService的目的前面已经叙述了,这边类的注释中也有描述。IntentService实现了"work queue processor",可以将任务剥离主线程(即不会阻塞主线程)并按次序完成任务,当任务完成之后,则会自动关闭自身~听起来非常神奇,非常方便,那如何实现呢?

  我们知道一个Service的声明周期如下:

  

  这两列分别表示两种启动Service的方法:startService和bindService。源码144-147行的实现否决了右边调用bindService启动Service的方法(不是不可用,程序员可以重载IntentService进行进一步的定制,只不过直接使用IntentService的bindService是不能没有意义的~)。既然如此,我们按照左边的生命周期查看源码。

  首先看100-112行的OnCreate()方法,看到没有?HandlerThread!源码在这里创建了一个HandlerThread,HandlerThread的使用方法就在107-111行处~首先是创建实例,然后必须start()(想想run()方法里面干了什么?),接着通过getLooper方法取出Looper用于第111行的ServiceHandler创建,这是一个内部类,继承了Handler而已,重载handleMessage方法,执行两个动作:回调onHandleIntent方法,终止Service。

  128-132行重载了onStartCommand方法,这个方法每次在startService()方法调用的时候都会被执行,它可以根据一个boolean值决定返回值(这里可以去查看一下Service该方法返回值的含义,它决定了Service被杀死之后如何复苏),另外也调用了onStart()方法,即执行114-120行的代码。onStart方法则将传入的intent以及startId包装秤一个msg,交给mServiceHandler发送给HandlerThread的实例去处理。

  OKOK,到这里一口气吃的有点多,停下来整理一下~IntentService到底干了什么?IntentService在内部启动了一个带Looper的Thread,当然,这个Thread也就具备类消息处理的能力,外部每一次调用startService,都会传进一个Intent,而该Intent稍后就会被封装成消息交给HandlerThread处理,处理完毕之后,HandlerThread会主动调用stopSelf()停止服务。

  不知道有没有读者感到奇怪,这里都已经停止服务了,怎么还能继续处理消息呢?其实Service在这里只是主线程和工作线程之间的一个桥梁,Service的Intance唯一性保证了不管调用几次startService,只会有一个工作线程被实例化,从而接受工作,HandlerThread其实是一个一直在等待消息的工作线程,而Service只是负责将任务封装成消息交给它,而每次要传递任务,都必须调用IntentService的startService()方法启动Service,因而在任务执行完毕后关闭Service是没有问题,不会影响后续的任务传递,但是如果任务正在传递中,比如新的任务传递执行到117行,前一个任务刚好执行完毕,这个时候调用stopSelf,即66行,会发生什么呢?官方文档有解释:

However, if your service handles multiple requests to onStartCommand() concurrently, then you shouldn't stop the service when you're done processing a start request, because you might have since received a new start request (stopping at the end of the first request would terminate the second one). To avoid this problem, you can use stopSelf(int) to ensure that your request to stop the service is always based on the most recent start request. That is, when you call stopSelf(int), you pass the ID of the start request (the startId delivered to onStartCommand()) to which your stop request corresponds. Then if the service received a new start request before you were able to call stopSelf(int), then the ID will not match and the service will not stop.

  OK,到这里,关于IntentService的实现也解释完毕了。

四、总结

  Android中有很多这样短小精悍的代码,以精妙的设计方式简化了开发过程,非常值得学习,IntentService的实现展现了内部类的作用,内部类和外部类的交互显得自然而又紧密,非常NICE,很值得学习研究~~

【Android】IntentService & HandlerThread源码解析的更多相关文章

  1. Android 开源项目源码解析(第二期)

    Android 开源项目源码解析(第二期) 阅读目录 android-Ultra-Pull-To-Refresh 源码解析 DynamicLoadApk 源码解析 NineOldAnimations ...

  2. Android MIFARE NFCA源码解析

    Android MIFARE NFCA源码解析TagTechnology定义了所有标签的共有接口类BasicTagTechnology 实现了TagTechnology的一些接口 再有具体的标签协议继 ...

  3. Android HandlerThread源码解析

    在上一章Handler源码解析文章中,我们知道App的主线程通过Handler机制完成了一个线程的消息循环.那么我们自己也可以新建一个线程,在线程里面创建一个Looper,完成消息循环,可以做一些定时 ...

  4. 【Android】EventBus 源码解析

    EventBus 源码解析 本文为 Android 开源项目实现原理解析 中 EventBus 部分项目地址:EventBus,分析的版本:ccc2771,Demo 地址:EventBus Demo分 ...

  5. [原创]android开源项目源码解析(一)----CircleImageView的源码解析

    CircleImageView的代码很简洁,因此先将此工程作为源码解析系列的第一篇文章. 解析说明都在代码里了. /* * Copyright 2014 - 2015 Henning Dodenhof ...

  6. Android 数据库 ObjectBox 源码解析

    一.ObjectBox 是什么? greenrobot 团队(现有 EventBus.greenDAO 等开源产品)推出的又一数据库开源产品,主打移动设备.支持跨平台,最大的优点是速度快.操作简洁,目 ...

  7. 史上最详细的Android消息机制源码解析

    本人只是Android菜鸡一个,写技术文章只是为了总结自己最近学习到的知识,从来不敢为人师,如果里面有不正确的地方请大家尽情指出,谢谢! 606页Android最新面试题含答案,有兴趣可以点击获取. ...

  8. Android LayoutInflater&LayoutInflaterCompat源码解析

    本文分析版本: Android API 23,v4基于 23.2.1 1 简介 实例化布局的XML文件成相应的View对象.它不能被直接使用,应该使用getLayoutInflater()或getSy ...

  9. Android短彩信源码解析-短信发送流程(二)

    转载请注明出处:http://blog.csdn.net/droyon/article/details/11699935 2,短彩信发送framework逻辑 短信在SmsSingleRecipien ...

随机推荐

  1. Backbone入门——开发第一个Backbone页面

    1. 功能描述在新建的html页面中,通过导入的backbone文件搭建一个简单的mvc结构.当用户进入该页时,id号为“divTip”的<div>元素中将显示“hello,backbon ...

  2. 重装Windows系统后,Linux系统启动引导失败

    说明:我的电脑是在装了Windows系统后,装的Linux系统,所以启动引导是Linux干的活.可是,今天重装了Windows系统,启动引导就换成了Windows的,Windows的启动引导把Linu ...

  3. C#与数据库访问技术总结(十三)之DataReader对象

    DataReader对象与数据获取 DataReader对象以“基于连接”的方式来访问数据库. 也就是说,在访问数据库.执行SQL操作时,DataReader要求一直连在数据库上. 这将会给数据库的连 ...

  4. 地理围栏算法解析(Geo-fencing)

    地理围栏算法解析 http://www.cnblogs.com/LBSer/p/4471742.html 地理围栏(Geo-fencing)是LBS的一种应用,就是用一个虚拟的栅栏围出一个虚拟地理边界 ...

  5. Atitit. js mvc 总结(2)----angular 跟 Knockout o99 最佳实践

    Atitit. js mvc 总结(2)----angular  跟 Knockout o99 最佳实践 1. 框架 angular 跟Knockout 1 2. 2. 简单的列表绑定:Knockou ...

  6. R 报错:package ‘***’ is not available (for R version ****) 的解决方案

    R 安装sparklyr,ggplot2等包出现如下warning package '****' is not available (for R version 3.0.2) 系统环境 ubuntu1 ...

  7. 每天一个linux命令(6):rmdir 命令

    今天学习一下linux中命令: rmdir命令.rmdir是常用的命令,该命令的功能是删除空目录,一个目录被删除之前必须是空的.(注意,rm - r dir命令可代替rmdir,但是有很大危险性.)删 ...

  8. INNO setup安装卸载钱判断进程中是否在运行总结

    1.安装前判断进程中是否有程序在运行. [files] ; 安装前判断进程,dll文件放在inno的安装目录中Source: compiler:psvince.dll; Flags: dontcopy ...

  9. 用GO语言开发editplus编辑器插件(附源码)

    我要开发的插件功能极为简单,就是对用户选中的内容进行base64编码或解密工作. 其中所涉及的技术部分主要是GO语言程序开发和editplus插件配置的部分,首先我们来看一下GO语言代码的写法,如下: ...

  10. .net下BerkeleyDB操作封装C#版(附单元测试)

        using System; using System.Collections.Generic; using System.IO; using System.Linq; using System ...