理解 IntentService 原理

栏目: Android · 发布时间: 5年前

内容简介:本人只是 Android小菜一个,写技术文档只是为了总结自己在最近学习到的知识,从来不敢为人师,如果里面有些不正确的地方请大家尽情指出,谢谢!其实

本人只是 Android小菜一个,写技术文档只是为了总结自己在最近学习到的知识,从来不敢为人师,如果里面有些不正确的地方请大家尽情指出,谢谢!

1.概述

service 的作用相信大家都是非常熟悉的,主要用来在后台进行任务处理,例如后台播放音乐、下载文件、上传文件等等。由于 service 是运行在主线程中的,也有一定的时间限制,如果在主线程中对一个任务的处理时间超过了限制,进程就会出现“应用不响应”,即 ANR, Application Not Responding 。为了避免这样情况,都会在 service 里用新的 thread 处理一些可能需要更多处理时间的任务。

其实 Android 早就替我们设计了一种更方便的 service + thread 模式,就是本文要讲的 IntentService ,通过它可以很方便地实现在 service 中使用 thread 进行耗时任务的处理。

本文将首先给大家演示下它的基本使用方式,再讲解下 IntentService 的内部原理。

2. IntentService 的使用

在知道如何使用前,先看看 IntentService 到底是什么东西,它的声明如下:

/**
 * 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.
 */
public abstract class IntentService extends Service { ... }
复制代码

相信大家都能很容易看懂这段声明的意思,小菜在这里简单为大家总结下,这么一大段文字主要是说明了两个问题:

  1. IntentService 是什么:用来进行处理异步请求的服务,其内部有一个工作线程,所有发送给服务的请求都会在这个工作线程中按序执行,在处理完所有请求后服务会自动停止。
  2. IntentService 如何使用:拓展 IntentService 并在其拓展类或者叫子类中实现 onHandleIntent(Intent) 接口,在这个接口中进行实际的请求处理,这些请求通过 Context.startService(Intent) 来进行发送。

Android SDK 真的可以作为所有 SDK 的典范,它会清楚地告诉你“是什么”和“怎么用”的问题,针对相对复杂的情况,还会直接在声明里给出范例。

既然我们已经知道要如何使用 IntentService 了,就让我们用一个小例子来演示一下:

public class TestIntentService extends IntentService {
    private static final String TAG = "TestIntentService";
    
    private static final String DEFAULT_NAME = "default_name";
    
    // 为了区分不同的请求和方便调用端使用,直接定义了不同的 ACTION.
    public static final String DOWNLOAD_ACTION = "com.test.intent.action.DOWNLOAD";
    public static final String UPLOAD_ACTION = "com.test.intent.action.UOLOAD";

    // 要在 AndroidManifest.xml 里声明 servcie,必须提供一个无参构造函数.
    public TestIntentService() {
        // IntentService 的构造函数需要提供一个工作线程的名字信息.
        super(DEFAULT_NAME);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "onCreate");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy");
    }

    @Override
    public void onHandleIntent(Intent intent) {
        String action = intent.getAction();
        // 根据不同的请求类型进行不同的处理,这里只是休眠一段时间,并没有进行实际的处理。
        if (DOWNLOAD_ACTION.equals(action)) {
            try {
                Log.i(TAG, "onHandleIntent, start to download");
                Thread.sleep(30 * 1000);
            } catch (InterruptedException ie) {
                ie.printStackTrace();
            }
        } else if (UPLOAD_ACTION.equals(action)) {
            try {
                Log.i(TAG, "onHandleIntent, start to upload");
                Thread.sleep(40 * 1000);
            } catch (InterruptedException ie) {
                ie.printStackTrace();
            }
        }
    }
}
复制代码

在这段代码里,请求处理函数 onHandleIntent(Intent) 会根据接收到的请求进行不同的处理,如果收到的是“下载”请求就休眠30秒模拟下载过程,如果收到的是“上传”请求就休眠40秒模拟上传过程。在写好了 service 逻辑后一定不要忘记在 AndroidManifest.xml 对其进行注册,否则是无法使用的,注册代码如下:

<service android:name=".TestIntentService" />
复制代码

注意: 这里只是简单地对其进行注册,并没有设置其他相关属性,例如 intent-filter ,因为这些和本文所讲内容并无直接关系。

请求的接收和处理代码都已完成,接下来就是发送请求的代码逻辑,如下:

// 发送“下载”请求
Intent downloadIntent = new Intent(this, TestIntentService.class);
downloadIntent.setAction(TestIntentService.DOWNLOAD_ACTION);
startService(downloadIntent);

// 发送“上传”请求
Intent upIntent = new Intent(this, TestIntentService.class);
upIntent.setAction(TestIntentService.UPLOAD_ACTION);
startService(upIntent);
复制代码

现在看当发送这两个“下载”和“上传”请求后, IntentService 是如何响应的:

02-27 12:58:23.100 24190 24190 I TestIntentService: onCreate
02-27 12:58:23.102 24190 24240 I TestIntentService: onHandleIntent, start to download
02-27 12:58:53.107 24190 24240 I TestIntentService: onHandleIntent, start to upload
02-27 12:59:33.115 24190 24190 I TestIntentService: onDestroy
复制代码

可以看到:在发送第一个“下载”请求的时候, service 首先被创建,然后开始处理这个“下载请求”,仅接着第二个“上传”请求也被接收并在处理完第一个请求后开始处理,在处理完所有请求后 service 被自动销毁。

3. IntentService 的原理

前面已经讲了如何通过 IntentService 实现在工作线程中处理较耗时任务,那么 IntentService 内部又是如何实现的呢?本节我们通过分析它的源码来一探究竟。

3.1 创建工作线程

既然 IntentService 的功能是在工作线程中处理任务,首先来看看这个工作线程是如何创建出来的。

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);
}
复制代码

IntentService 第一次启动的时候会调用其 onCreate 来完成一些初始化操作:

  1. 首先创建了一个 HandlerThread 对象,这就是前面一直提到的“工作线程”。大家对 HandlerThread 都很了解,那这个 HandlerThread 是什么呢?简单来说,它就是内部有一个消息循环队列的线程,我们知道默认的线程内部是没有消息循环队列的,这就导致我们无法直接在其内部使用 HandlerAndroid 为了方便使用,直接提供了一个含有消息循环队列的 HandlerThread
  2. 利用已创建的 HandlerThread 内部的消息循环创建一个 ServiceHandler 对象,这样它的消息处理函数 handleMessage 就会在对应的线程中执行了。

3.2 接收和处理请求

既然工作线程已经创建完成,这时就要考虑如何接收客户端发送过来的请求了,已经了解到客户端是通过 startService 来发送请求的,结合 service 的生命周期,紧接着会执行 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(@Nullable Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

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

从这段代码看到, onStartCommand 会直接调用 onStart ,在这里对发送过来的请求接收并通过 mServiceHandler 进行处理。

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);
    }
}
复制代码

handleMessage 中对接收到的请求用 onHandleIntent 进行实际的处理,而 onHandleIntent 就是我们在使用过程中必须实现的处理逻辑。

3.3 销毁工作线程

前面提到:当所有请求都被处理完成后, service 就会被销毁,这是如何实现的呢?在上面看到 handleMessage 方法里在处理完当前请求时会调用 stopSelf(msg.arg1) 来尝试停止当前服务,之所以说“尝试”,是因为它不一定能真正停止服务。还是来看下 stopSelf(int) 的实现代码:

/**
 * 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) {
    }
}

/**
 * Stop the service if the most recent time it was started was 
 * <var>startId</var>.  This is the same as calling {@link 
 * android.content.Context#stopService} for this particular service but allows you to 
 * safely avoid stopping if there is a start request from a client that you 
 * haven't yet seen in {@link #onStart}. 
 */
public final boolean stopSelfResult(int startId) {
    if (mActivityManager == null) {
        return false;
    }
    try {
        return mActivityManager.stopServiceToken(
                new ComponentName(this, mClassName), mToken, startId);
    } catch (RemoteException ex) {
    }
    return false;
}
复制代码

stopSelf(int) 的声明里提到它是 stopSelfResult(int) 的老版本,唯一的区别就是没有返回值。那我们直接看 stopSelfResult(int) 的声明,其中提到只有在当前的 service 的最近一次启动是 startId 发起的才会被停止。把这句话放在 IntentService 的场景里去理解,如果说当前接收到3个请求,在处理第一个请求后打算去停止服务,但是调用 stopSelf() 的时候发现最后一次启动是第三个请求发生的,并不会停止服务;处理完第二个请求后是类似的,只有在处理完第三个请求后,去尝试停止服务,这时发现最近一次启动就是它发起的,可以去停止服务了。停止服务时,其 onDestroy 会得到调用:

@Override
public void onDestroy() {
    mServiceLooper.quit();
}
复制代码

在这里会停止工作线程的消息循环,等待线程退出。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Mastering Regular Expressions, Second Edition

Mastering Regular Expressions, Second Edition

Jeffrey E F Friedl / O'Reilly Media / 2002-07-15 / USD 39.95

Regular expressions are an extremely powerful tool for manipulating text and data. They have spread like wildfire in recent years, now offered as standard features in Perl, Java, VB.NET and C# (and an......一起来看看 《Mastering Regular Expressions, Second Edition》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

html转js在线工具
html转js在线工具

html转js在线工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换