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

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


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

查看所有标签

猜你喜欢:

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

程序开发心理学

程序开发心理学

(美)杰拉尔德·温伯格 / 邓俊辉 / 清华大学出版社 / 2004-1-1 / 39.00元

本书开创"以人为本"研究方法的先驱,在长达25年的岁月中一直保持活力,至今仍在继续。1997年,本书作者温伯格因其在软件领域的杰出贡献,被美国计算机博物馆的计算机名人堂选为首批5位成员之一。 在计算机界,还没有任何一本计算机方面的书,在初次出版之后,能够在长达25年的岁月中一直保持活力--而且这种活力到今天仍在继续。《程序开发心理学》是开创"以人为本"研究方法的先驱,它以其对程序员们在智力、......一起来看看 《程序开发心理学》 这本书的介绍吧!

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

UNIX 时间戳转换

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具