Android IntentService解析

一、IntentService是什么

正常情况下,Service和Activity一样,都是处于主线程中,不能做耗时操作,但是IntentService却可以,IntentService是继承Service的抽象类,用于处理异步请求任务,并在任务结束后,自动销毁。正是由于IntentService是Service,优先级较高,不易被回收,因此可以用来在后台执行耗时和重要的任务。

二、IntentService使用实例

通过IntentService实现文件下载效果,效果如图所示:

例子代码如下:

1. IntentService的实现

继承IntentService,并实现onHandleIntent()方法,由于该方法是在工作线程中调用,因此可以在该方法中执行耗时操作;代码实现,如下:

public class MyIntentService extends IntentService {
    private static final String TAG = "MyIntentService";
    public static final String ACTION = "test.intent.service.action";
    public static final String PARAM_PROGRESS = "local.broadcast.progress";
    private LocalBroadcastManager mLocalBroadcastManager;
    private int progress;

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
        Log.i(TAG, "onCreate");
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand,startId:" + startId);
        return super.onStartCommand(intent, flags, startId);
    }

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

    @Override
    protected void onHandleIntent(Intent intent) {
        progress = 0;
        while (progress < 100) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            progress++;
            Intent intentSend = new Intent();
            intentSend.setAction(ACTION);
            intentSend.putExtra(PARAM_PROGRESS, progress);
            mLocalBroadcastManager.sendBroadcast(intentSend);
            Log.i(TAG, "当前进度progress:" + progress);
            Log.i(TAG, "当前线程onHandleIntent:" + Thread.currentThread().getName());
        }
    }
}

复制代码

2. 启动IntentService

Intent intent = new Intent(this, MyIntentService.class);
startService(intent);
复制代码

3. 接收回传进度,更新进度条

class LocalBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        int progress = intent.getIntExtra(MyIntentService.PARAM_PROGRESS, 0);
        progressbar.setProgress(progress);
        Log.i(TAG, "progress:" + progress);
        Log.i("TAG, "当前线程onReceive:" + Thread.currentThread().getName());
    }
}
复制代码

打印结果:

三、IntentService实现原理

可以把IntentService理解为Service和HandlerThread,在IntentService的onCreate()方法中,创建了一个HandlerThread,然后获取到HandlerThread工作线程Looper对象,创建了一个该工作线程的Handler对象,代码如下:

 @Override
 public void onCreate() {
     super.onCreate();
     HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
     thread.start();

     mServiceLooper = thread.getLooper();
     mServiceHandler = new ServiceHandler(mServiceLooper);
 }
复制代码

通过start方式启动的Service生命周期方法是先调用onCreate()方法,然后是onStartCommand()方法,当调用stopService()或stopSelf()方法,会调Service的onDestroy()方法;

在onStartCommand()方法中调用了onStart()方法,在onStart方法中,通过Handler发送给HandlerThread的工作线程一个消息,当handleMessage()接收到Message后,调用了onHandleIntent()来处理消息,然后执行stopSelf(startId)来结束IntentService;代码如下:

@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);
}

@Override
public void handleMessage(Message msg) {
    onHandleIntent((Intent)msg.obj);
    stopSelf(msg.arg1);
}

复制代码

四、IntentService的思考

1. IntentSercice的启动模式

Service有两种启动模式,start模式和bind模式,IntentService是Service的子类,也是有这两种启动模式的;在Service中,start模式用于没有数据交互的启动模式,有数据交互的,最好使用bind模式,IntentService是不是也是这样的呢?其实InentService不是这样的,InentService是可以执行耗时操作的Service,并在任务执行结束后,自动结束;InentService结束服务的方式是stopSelf(startId),如果使用bind方式启动,则在任务执行结束后,Service就不能停止了,这样是违背InentService设计的初衷的,因此InentService只能通过start方式启动;

2. 如何进行数据回传

由于InentService只能通过start方式启动,因此不能用bindService这种方式进行数据的传递,我们可以使用广播,EventBus,回调等方式来完成数据的交互;

3. 多次启动生命周期流程

当多次调用startService,可以看到打印结果如下: onCreat方法执行了一次,每调用一次startService就会调用一次onStartCommand,最后执行onDestroy方法,则整个Service生命周期结束。每次调用startService传递数据时,都会调用onStartCommand中接收数据,onStartCommand把intent放到Message的obj参数中,则在handleMessage中可以收到startService传递过来的数据。

需要注意的是:由于Handler是个消息队列,所以每次调用startService的时候,只有在上一个消息执行完成之后才再次会回调onHandleIntent方法。

由于个人能力和水平有限,如有不足之处,希望大家能够给与批评和指正,希望能和大家一起学习,一起进步。

分类:
Android