Android 多线程之 IntentService 源码分析

Android 多线程之 IntentService 源码分析

一、前言

这主要是一个讲解 Android 中多线程的系列,全部文章如下:

IntentService 从名字可以看出是一个 Service,我们知道 Service 是没有前台页面的,主要用于长时间的后台任务,比如播放音乐等场景。

正常情况下,我们使用 Service 已经足够了,为什么官方又提供了一个 IntentService呢?

这是因为如果我们只使用 Service 的话,Service 默认是运行在主线程的,如果我们需要在 Service 中处理一些耗时任务,那么我们还需要去手动的创建线程或者使用线程池去处理耗时任务,然后在处理完以后手动关闭Service,而 IntentService 已经帮我们做好了这些工作,我们只需要在 onHandleIntent 中写上耗时任务的代码,就可以在子线程中去执行,因为 onHandleIntent 是运行在子线程中的,并且在任务执行完以后,IntentService 会自己执行stopSelf(startId)方法,自行关闭。

二、初识IntentService

2.1 API 文档定义

老规矩,先看下官方文档关于IntentService的介绍

意思是说:

IntentService 继承于 Service,并且在需要的时候处理一些异步请求。客户端根据需要通过调用Context.startService(Intent)发送请求来启动,使用工作线程(一般指的是子线程)来依次处理任务,并且在出错的时候自行关闭
这种“工作队列处理器”模式一般用于从主线程拆解任务,IntentService的存在简化了这种模式和处理机制,如果要使用它,只需要继承与 IntentService 并且实现onHandleIntent(Intent)方法,就可以接收到 Intent 、开启一个工作线程并且在合适的时机关闭。
所有的请求都运行在同一个工作线程,可能需要更多的时间,但是不会阻塞组线程,一次只处理一个线程

上面是官方 APIIntentService 的描述,我的理解:

IntentService 本质上是一个 Service,自身封装了异步任务的方法,不需要手动开启子线程去处理异步任务,所有异步任务运行在一个线程中,按照启动顺序串行有序处理,处理完所有任务或者出错后会自动关闭。

2.1 使用时需要关注的方法 onHandleIntent(Intent intent)

onHandleIntent 是运行在子线程的,IntentService自身已经帮助我们封装好了,所以我们使用的时候只需要新建类继承于 IntentService ,并且在onHandleIntent方法里面写上异步任务代码以及处理完任务的操作就可以了。

三、基本使用

下面通过模拟加载本地图片的方式来进行演示

首先新建 UploadImageService

public class UploadImageService extends IntentService {

    public static String IMG_PATH_KEY = "imgPath";

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     */
    public UploadImageService() {
        super("UploadImageService");
    }

    public static void startUpload(Context context, String imgPath) {
        Intent intent = new Intent(context, UploadImageService.class);
        intent.putExtra(IMG_PATH_KEY, imgPath);
        context.startService(intent);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        if (intent != null && intent.hasExtra(IMG_PATH_KEY)) {
            String path = intent.getStringExtra(IMG_PATH_KEY);
            upload(path);
        }
    }

    private void upload(String path) {
        try {
            // 模拟上传 3 秒结束
            Thread.sleep(3000);
            sendBroadcastToCaller(path);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void sendBroadcastToCaller(String path) {
        Intent intent = new Intent(UPLOAD_RESULT);
        intent.putExtra(IMG_PATH_KEY, path);
        sendBroadcast(intent);
    }
}

manifests.xml 中注册

<service android:name=".ui.a.activity.intentservice.UploadImageService" />

Activity代码

public class IntentServiceActivity extends BaseActivity {

    public static final String UPLOAD_RESULT = "com.sean.demo.ui.a.activity.UPLOAD_RESULT";
    private int i = 0;
    private LinearLayout mLyTaskContainer;
    private BroadcastReceiver uploadImgReceiver;
    private List<String> mUploadPaths;
    private AlertDialog mDialog;

    private void handleResult(String path) {
        TextView tv = mLyTaskContainer.findViewWithTag(path);
        mUploadPaths.remove(path);
        if (mUploadPaths.size() < 1) {
            mDialog.dismiss();
        }
        tv.setText(path + " upload success ~~~ ");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentLayout(R.layout.activity_intent_service);
        setBackArrow();
        uploadImgReceiver = new UploadBroadcastReceiver();
        mDialog = new AlertDialog.Builder(this).setView(new ProgressBar(this)).create();
        mLyTaskContainer = findViewById(R.id.id_ll_taskcontainer);
        registerReceiver();
        findViewById(R.id.add_task).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addTask();
            }
        });
    }

    private void registerReceiver() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(UPLOAD_RESULT);
        registerReceiver(uploadImgReceiver, filter);
    }

    public void addTask() {
        //模拟路径
        String path = "/sdcard/imgs/" + (++i) + ".png";
        if (null == mUploadPaths) {
            mUploadPaths = new ArrayList<>();
        }
        mUploadPaths.add(path);
        UploadImageService.startUpload(this, path);
        mDialog.show();
        TextView tv = new TextView(this);
        mLyTaskContainer.addView(tv);
        tv.setText(path + " is uploading ...");
        tv.setTag(path);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(uploadImgReceiver);
    }

    class UploadBroadcastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null && intent.getAction() != null && intent.getAction().equals(UPLOAD_RESULT)) {
                String successPath = intent.getStringExtra(IMG_PATH_KEY);
                handleResult(successPath);
            }
        }
    }

}

xml代码

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_intent_service"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context="com.sean.demo.ui.a.activity.intentservice.IntentServiceActivity">


    <Button
        android:id="@+id/add_task"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="addTask" />

    <LinearLayout
        android:id="@+id/id_ll_taskcontainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/add_task"
        android:orientation="vertical" />
</RelativeLayout>

效果图:

下面来看下 IntentService的源码:

三、源码分析

其实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() {
        // 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(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    @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 onDestroy() {
        mServiceLooper.quit();
    }

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

    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

3.1 成员变量

先看下成员变量,主要有:

  • Looper 当前IntentService 工作线程的 Looper
  • ServiceHandler 封装的一个继承于 Handler的子类
  • mName 名称(不重要)
  • mRedelivery 用来设置进程被杀掉以后会执行什么样的操作(控制异常关闭后是否重新启动服务的方式)

3.2流程解析

先看onCreate()(只有在第一次创建服务的时候调用)方法看起:

HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);

首先,会先创建一个HandlerThread对象,前面的文章已经介绍过了,HandlerThread 主要是为我们提供一个带有Looper的子线程,更加方便的让我们使用。

创建好以后执行 thread.start();去开启线程。
结合 HandlerThreadrun 方法:

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

我们可以知道,这个时候,已经创建好了Looper,并且开启了循环。

然后通过 HandlerThreadgetLooper() 拿到创建好的 Looper对象,用这个Looper来创建ServiceHandler对象。

总的来说:onCreate里面开启了一个带有Looper对象的子线程,并且通过这个Looper对象创建一个 ServiceHandler对象与之绑定。

接着看onStartCommand(在每次启动服务的时候调用)方法:

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

onStartCommand接收了三个参数:

  1. intent

启动 Service 时传递进来的 Intent对象

  1. flags

flags有三个值,分别是0,START_FLAG_REDELIVERY,START_FLAG_RETRY。 分别标志Service在什么时候被启动。
当调用startService来启动服务的时候,这里的值是 0,如果Service发生重启行为,flags 就不会是 0 了。(比如你的onStartCommand返回值是START_STICKYSTART_REDELIVER_INTENT,则Service终止后会重新启动)。他们的区别在于,如果Service重新启动了,当ServiceonStartCommand返回之前就被终止了,那么下次重启时flags的值为START_FLAG_RETRY,当ServiceonStartCommand返回后并且在stopSelf(int)还没执行前被终止,那么下次启动时flags的值为START_FLAG_REDELIVERY

  1. startId

每次调用startServiceService传递任务请求时都会生成一个startId,用来代表这一次请求,这个值是唯一的,不管startService调用多少次,每一次的startId的值都是不一样的,这个值和stopSelfResult(int)或者stopSelf(int)一起使用,主要是为了确保Service中的所有任务都已经完成。

方法里面首先调用 onStart方法,

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

onStart 方法中,会创建一个 Message对象,然后通过mServiceHandler发送出去,然后在 mServiceHandlerhandleMessage中接收:

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);
    }
}
protected abstract void onHandleIntent(@Nullable Intent intent);

在这里就看到我们要实现的抽象方法:onHandleIntent,由于创建 ServiceHandler 时候使用的 Looper是在 HandlerThread中创建的,HandlerThread是一个子线程,所以HandlerThreadLooper也是子线程的。

那么与之绑定的ServiceHandler也是子线程绑定的Handler对象,它的 handleMessage 也是运行在子线程的,所以这就解释了为什么onHandleIntent是运行在子线程中了。

再来看下如何自动关闭的。

代码中可以看到,当我们执行完onHandleIntent((Intent)msg.obj);以后,会立即调用 stopSelf(msg.arg1);这里的 msg.arg1 就是在 onStart方法里面组装的startId

msg.arg1 = startId;

这个 startId 前面在讲onStartCommand参数的时候讲了:

我们都知道,当我们多次调用startService来启动同一个service时,只有第一次创建服务会执行onCreate,然后会多次调用onStartCommand,从而生成不同的startId,一般是从 1 开始,依次递增,这个值是唯一的,不管startService调用多少次,每一次的startId的值都是不一样的,这个值和stopSelfResult(int)或者stopSelf(int)一起使用,当我们调用stopSelf(int startId)时,系统会检测当前服务是否还有其它的startId存在,有的话就不销毁当前service,没有的话则销毁当前服务,这个机制主要是为了确保Service中的所有任务都已经完成才去销毁服务。

stopSelf()则是立即销毁该 Service

这里为什么不去掉用 stopSelf(),而要调用 stopSelf(int startId)呢?

因为 stopSelf(msg.arg1); 这行代码是在 onHandleIntent 后面的,那如果我们开启了两个服务来执行上传文件的操作,第一个服务运行结束,如果是使用 stopSelf()那么就直接结束掉Service了,第二个服务就得不到运行,那么如果使用stopSelf(startId)就可以保证在第所有服务运行结束后再关闭Service,能够保证我们的代码能够正确的运行。

最后在 onDestroy()方法里面mServiceLooper.quit();Looper进行了释放。

3.3 为什么多次启动IntentService 会在一个线程串行执行,执行完才会关闭

3.3.1运行在一个线程
@Override
public void onCreate() {
    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
}

其实这个前面已经讲了,IntentServiceonCreate() 的时候创建了 一个HandlerThread,并且使用HandlerThread对象的 Looper创建了一个 ServiceHandler对象,所以所有的Service实例都是在HandlerThread这个子线程中执行任务的。

3.3.2 按照顺序有序执行

所以当多次启动服务的时候,所有的启动服务传递进来的Intent和启动时生成startId会被封装成 Message对象,由IntentService的成员变量 mServiceHandler发送到mServiceLooperMessageQueue中,然后由mServiceLooperloop()方法中不断取出 Message交给 mServiceHandler 去处理,这样就保证了任务能够按照启动Service的顺序去执行。

3.3.3 所有任务执行完毕以后再关闭服务

再结合前面分析的 stopSelf(startId),系统会在等待所有Service执行完毕以后才会最终结束服务,在 onDestory()方法中执行mServiceLooper.quit();去释放掉Looper对象。

四、最后

到这里IntentService就算是分析完了,虽然代码不是很多,但是牵扯的内容还是不少的。

在日常开发中,还是要不断的发散思维,在合适的地方用不同的解决方案去解决问题,寻求解决问的最优解。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值