您的位置:首页 > 移动开发 > Android开发

Android IntentService的使用与源码解析

2015-12-12 15:48 621 查看
尊重原创,转载请注明出处:http://blog.csdn.net/a740169405/article/details/50274537

前言:

大家都知道,Service是Android中运行在后台的,不可见的服务。
但其和异步线程又不一样,因为Service是运行在主线程,也就是UI线程里。所以我们并不能在Service中执行耗时的操作。
为了解决这个问题,大家可能回在Service中开启一个线程,来执行耗时操作。
谷歌为我们实现了类似的功能,那就是IntentService。


IntentService介绍:

IntentService是继承了Service的抽象类,它是个服务,要使用它必须继承IntentService并实现抽象方法。

IntentService可以执行后台耗时任务,当任务都执行完成后,IntentService会自动结束,不需要手动调用stopSelf();

IntentService的优先级比线程高,因为是跑在后台的服务,所以不容易被系统杀死。

IntentService内部是通过封装HandlerIntent来执行后台任务的,因为是通过消息队列接受Intent,所以后台任务是串行执行。

IntentService使用:

新建一个Activity与一个IntentService,从Activity中发送三个Intent,也就是启动执行startService三次,每次intent里的数据都不一样。

IntentServiceActivity:

public class IntentServiceActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, MyIntentService.class);
intent.putExtra(MyIntentService.EXTRA_KEY, "my task one #");
// 发送任务1
startService(intent);
intent.putExtra(MyIntentService.EXTRA_KEY, "my task two #");
// 发送任务2
startService(intent);
intent.putExtra(MyIntentService.EXTRA_KEY, "my task three #");
// 发送任务3
startService(intent);
}
}


Activity中仅仅是向IntentService发送了三个消息

继承IntentService,并实现onHandleIntent方法。

MyIntentService:

public class MyIntentService extends IntentService {

public static final String TAG = MyIntentService.class.getSimpleName();

public static final String EXTRA_KEY = "task_name";

public MyIntentService() {
super(TAG);
}

@Override
protected void onHandleIntent(Intent intent) {
String value = intent.getStringExtra(EXTRA_KEY);
if (value.contains("two")) {
try {
// 如果是第二个任务,则sleep三秒。
// 发现第三个任务仍然在第二个任务之后执行,说明任务是串行执行的
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.e(TAG, "thread : " + Thread.currentThread().getName() + "  " + value);
}

@Override
public void onDestroy() {
super.onDestroy();
// 服务结束时,打印Log
Log.e(TAG, "onDestroy");
}
}


在MyIntentService的onHandleIntent方法里,打印出了intent的key为task_name对应的值。

当收到含有”two”字符(这里我为了方便这样做的),线程Sleep三秒。在每次onHandleIntent被调用的时候,

打印出当前线程的名字。

看看Log信息:

chen.com.incubationcenter E/MyIntentService: thread : IntentService[MyIntentService] my task one #

chen.com.incubationcenter E/MyIntentService: thread : IntentService[MyIntentService] my task two #

chen.com.incubationcenter E/MyIntentService: thread : IntentService[MyIntentService] my task three #

chen.com.incubationcenter E/MyIntentService: onDestroy

我们会发现,处理任务的线程并不是主线程,并且即使第二个任务sleep了三秒,但是第三个任务还是在第二个任务完成后才执行,说明任务的处理是串行的。

IntentService源码解析:

下面我们分析三个问题:

1. IntentService是怎么实现异步线程执行任务的。

2. 为什么任务是串行执行的。

3. 每次startService并没有重新启动服务,并且,在所以任务完成后,服务自动终止。

带着问题分析源码:

IntentService内部是通过HandlerThread实现异步执行任务的。

想了解HandlerThread的相关知识的同学,可以查阅博客:HandlerThread源码解析

HandlerThread是一个内部维护了一个消息队列的线程。任务就是通过它来执行的。

我们找到IntentService类的onCreate方法:

@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类并传入工作线程的名字
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
// 开启后台线程
thread.start();

// 获取后台线程的Looper
mServiceLooper = thread.getLooper();
// 创建一个ServiceHandler对象,用来处理消息。
mServiceHandler = new ServiceHandler(mServiceLooper);
}


在onCreate方法里,首先新建了一个HandlerThread对象,接着创建了一个叫ServiceHandler的类,

该类是IntentService的内部类,该类继承了Handler,构造函数传入了HandlerThread对象的Looper。

看看他的定义:

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

@Override
public void handleMessage(Message msg) {
// 收到消息的时候,回调了onHandleIntent方法,并把Intent对象传递过去
onHandleIntent((Intent)msg.obj);
// 完成任务后,调用stopSelf方法,并传入消息的索引
stopSelf(msg.arg1);
}
}


我们发现,onHandleIntent方法就是在ServiceHandler收到消息后回调的。

也就是说onHandleIntent方法是在HandlerThread线程中执行的,这也就是疑惑一中为什么任务在异步线程中执行。

此外,因为任务是通过ServiceHandler发送给异步线程的消息队列的,而消息队列里的消息是依次取出并执行的,这也就是疑惑二中为什么任务是串行执行的。

最后,我们发现,onHandleIntent执行完成以后,IntentService并没有马上执行onstopSerlf方法,而是执行了其另一个带参数的方法。

该方法是定义在Service类下的。

/**
* Old version of {@link #stopSelfResult} that doesn't return a result.
*
* @see #stopSelfResult
*/
public final void stopSelf(int startId) {
if (mActivityManager == null) {
return;
}
try {
mActivityManager.stopServiceToken(
new ComponentName(this, mClassName), mToken, startId);
} catch (RemoteException ex) {
}
}


该方法调用了ActivityManager的stopServiceToken方法来停止当前服务,关于ActivityManager这里不做介绍,有兴趣的朋友可以google相关资料。

既然调用了结束服务的方法,为什么服务没有马上结束,而是继续完成剩下的任务后才结束呢。

我们发现这里有个startId,没错就是因为他。startId是在Service类的onStart方法里被初始化的,

而onStart方法又是在onStartCommand方法里被调用的。

我们看看IntentService的源码:

onStartCommand:

/**
* 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方法
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}


onStart:

@Override
public void onStart(Intent intent, int startId) {
// 构建了一个消息
Message msg = mServiceHandler.obtainMessage();
// 将startId作为参数传递
msg.arg1 = startId;
// 需要处理的
msg.obj = intent;
// 调用ServiceHandler发送消息
mServiceHandler.sendMessage(msg);
}


我们知道,每次调用startServcie方法启动一个服务,都会调用一次onStartCommand,每次也都会带一个startId过去,那么执行多个任务就有多个startId(关于这里不做介绍,大家可以理解为服务启动的顺序ID)

那么在onHandleIntent执行后调用的stopSelf方法确实告诉系统需要停止服务,但是系统会根据startId来判断是否所有的任务都已完成,如果是,则停止服务,如果不是,则等待所有的任务都完成后停止服务。到此,疑惑三搞定。

**

总结:

**

1. IntentService内部通过HandlerThread线程来执行异步任务,我们不需要自己创建线程

2. IntentService默认实现了onStartCommand以及onStart方法来讲intent传递给HandlerThread的消息队列

3. IntentService默认实现了onBind方法,并返回了空。

4. 无需手动调用stopSelf方法,所有任务都执行完成后,IntentService会自动结束服务。

感谢:

Android Service总结05 之IntentService

Android基本功:IntentService的使用

《Android开发艺术探秘》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: