Android多线程-IntentService的使用以及原理浅析

上一章我们学习了HandlerThread,这一章我们来认识一下Android多线程通信常用的最后一个类IntentService。顾名思义,IntentService是一个Service的子类,其本质还是一个服务,不过它与它的父类还是有一些区别的,下面我们就来详细讲一下。

IntentService与Service的区别

  • IntentService是Service的子类,同样需要在AndroidManifest文件中注册
  • IntentService可以在其onHandleIntent方法中执行异步操作,Service若要执行异步操作需要开辟子线程
  • IntentService不使用bindService的方式启动,因为使用bindService后其异步方法不会执行,且内部HandlerThread不会初始化
  • IntentService不需要执行stop或者stopSelf方法来关闭,在异步任务执行完毕后,其内部会自动调用stopSelf
  • IntentService是串行模式,放入IntentService中执行的任务会依次执行,全部执行完毕后销毁自身

IntentService的简单使用

IntentService的使用十分简单,分为下面几个步骤:

  1. 新建子类继承IntentService,在AndroidManifest注册该Service, 复写onHandleIntent方法并在onHandleintent方法中进行异步操作
  2. 一般在Activity中调用IntentService,与使用普通Service类似,不过不要使用绑定方式,而要使用startService启动,并在Intent中放入相应的数据
  3. 使用Handler或者本地广播等手段将onHandleIntent异步执行结果传给主线程
  4. 主线程获取到异步操作结果并对其进行处理和展示等等

代码示例

新建IntentService子类,并复写onHandleIntent方法:

/**
 * 工作线程执行,其原理还是Handler,详细细节见源码
 * @param intent
 */
@WorkerThread
@Override
protected void onHandleIntent(@Nullable Intent intent) {
    Bundle bundle = intent.getExtras();
    String fileName = bundle.getString("file_name");
    Log.e(TAG,"当前下载"+fileName+"当前线程id"+Thread.currentThread().getId());
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Intent resultIntent = new Intent(DOWNLOAD_FINISH);
    resultIntent.putExtras(bundle);
    LocalBroadcastManager.getInstance(this).sendBroadcast(resultIntent);
}

Activity启动service

//启动两次,但是顺序执行,第一个任务执行完毕才会执行第二个任务的操作
public void onClick(View view) {
    Intent intent = new Intent(this,DownLoadIntentService.class);
    Bundle bundle = new Bundle();
    bundle.putString("file_name","巴啦啦小魔仙.mp4");
    intent.putExtras(bundle);
    startService(intent);

    Intent intent2 = new Intent(this,DownLoadIntentService.class);
    Bundle bundle2 = new Bundle();
    bundle2.putString("file_name","金刚葫芦娃.mp4");
    intent2.putExtras(bundle2);
    startService(intent2);
}

使用本地广播传递给主线程

Intent resultIntent = new Intent(DOWNLOAD_FINISH);
resultIntent.putExtras(bundle);
LocalBroadcastManager.getInstance(this).sendBroadcast(resultIntent);

Activity获取结果并进行相应处理

intentFilter = new IntentFilter(DownLoadIntentService.DOWNLOAD_FINISH);
receiver = new DownLoadBroadcastReceiver();
LocalBroadcastManager.getInstance(this).registerReceiver(receiver,intentFilter);

private class DownLoadBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if(TextUtils.equals(intent.getAction(),DownLoadIntentService.DOWNLOAD_FINISH)) {
            Bundle bundle = intent.getExtras();
            String fileName = bundle.getString("file_name");
            String result = tvResult.getText().toString();
            result += "名为"+fileName+"的文件下载完成!\r";
            tvResult.setText(result);
            Toast.makeText(IntentServiceDemoActivity.this,"下载完成,当前下载文件名"+fileName,Toast.LENGTH_SHORT).show();
        }
    }
}

这里我使用的本地广播进行消息的传递。

程序执行结果

源码分析

我们打开IntentService 的源码来分析其实现原理。

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;//子线程绑定的Looper
    private volatile ServiceHandler mServiceHandler;//HandlerThread实例
    private String mName;//传入的参数,默认缺省
    private boolean mRedelivery;

    /** 绑定工作线程Handler,handleMessage运行于子线程中 **/
    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() {
        super.onCreate();
        //生成一个HandlerThread实例并启动这个线程
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        //将HandlerThread中的Looper与该Service 绑定,并新建子线程Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    //调用startService后调用onStartCommand并转到该方法中
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
        //发出消息,arg1带上自身ID,使其完成时关闭自身
    }

    //如果系统在服务完成之前关闭Service,START_NOT_STICKY型服务会直接被关闭,而START_REDELIVER_INTENT 型服务会在可用资源不再吃紧的时候尝试再次启动服务。
    @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() {
        //Looper退出循环
        mServiceLooper.quit();
    }


    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        //默认返回null
        return null;
    }

    /** 子线程中运行,在Handler的handleMessage中调用此方法 **/
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

可以看到将注释删除后也就只有短短100行。具体的分析可以体现为:

调用startService() -> onCreate()被调用,绑定一个HandlerThread到当前Service,并生成一个子线程Handler -> onStartCommand()被调用 -> 最终调用onStart(@Nullable Intent intent, int startId) 方法并发送消息给消息循环 -> 其余就是Handler的处理消息过程,最终调用onHandleIntent并在执行完毕后调用stopSelf关闭自身。

这章的内容就是这样,如果文章内容出现错误或者偏差,希望您能联系我,我会尽快进行更改。

demo地址

我的个人博客,欢迎来访~

感谢阅读~

你可能感兴趣的:(Android知识体系)