使用IntentService的正确姿势
在开发安卓应用的过程中,我们经常需要这样的场景:希望APP在不影响当前用户的操作的前提下,在后台默默的做一些任务。比如:静默的下载文件或者上传数据。我们一般会考虑到新启一个线程去做异步的操作。不错,新启线程确实可以做到。那为啥还需要IntentService呢?
. 1.优点
- 本质上IntentService也是开了一个线程,但是IntentService是继承自Service的,所以根据Android系统Kill Application的机制,使用IntentService的应用的优先级更高一点。通俗点说如果使用IntentService做后台任务时,当您的程序退到后台时,被杀死的几率会更低一点。
- 既然IntentService是在Service里开启线程去做任务处理,那我直接在Service里启动线程去做不就好了吗?当然可以,但是IntentService已经帮您封装好了,为什么还要自己再去实现IntentService的一套逻辑呢?
- IntentService会在任务执行完成后自行结束自己,而不需要外部去调用stopService了。
. 2.正确使用的姿势
优点我们已经知道了,那该如何正确的使用呢?我们知道Service可以通过startService和bindService这两种方式启动。当然喽,IntentService是继承自Service的,自然也是可以通过上面两种方式启动。但是呢,是不建议使用 bindService 去启动的。为什么呢?我们看下IntentService的源码:
1 // 首先呢在Service的onCreate生命周期中创建了一个子线程的Handler
2 @Override
3 public void onCreate() {
4 // TODO: It would be nice to have an option to hold a partial wakelock
5 // during processing, and to have a static startService(Context, Intent)
6 // method that would launch the service & hand off a wakelock.
7
8 super.onCreate();
9 HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
10 thread.start();
11
12 mServiceLooper = thread.getLooper();
13 mServiceHandler = new ServiceHandler(mServiceLooper);
14 }
15
16 private final class ServiceHandler extends Handler {
17 public ServiceHandler(Looper looper) {
18 super(looper);
19 }
20
21 @Override
22 public void handleMessage(Message msg) {
23 // 这个是我们子线程Handler调用 onHandleIntent()方法处理异步事物
24 onHandleIntent((Intent)msg.obj);
25 stopSelf(msg.arg1);
26 }
27 }
如果你通过bindService的方式启动,如果IntentService没有启动过,确实会走onCreate方法,但是onCreate中没有去调用handler的处理方法handleMessage。因为只有handleMessage方法调用后才会调用onHandleIntent方法去处理任务。那么IntentService在哪里去调用Handler的handleMessage方法的呢?我们看下面的源码部分:
1 @Override
2 public void onStart(@Nullable Intent intent, int startId) {
3 Message msg = mServiceHandler.obtainMessage();
4 msg.arg1 = startId;
5 msg.obj = intent;
6 // 此处调用
7 mServiceHandler.sendMessage(msg);
8 }
是onStart的生命周期里调用的。我们都知道bindService启动的生命周期是不会回调onStart的生命周期的,所以onHandleIntent的异步处理方法也不会回调。也就是说,如果您使用bindService方法启动IntentService,其实不会享受到IntentService的一点优点。onHandleIntent根本不会调用,和启动一个普通的Service没两样。
综上,我们应该使用startService的方式启动IntentService。并且通过源码我们知道,处理异步任务是在onIntentService中的。所以我们应该讲任务逻辑放在onIntentService中处理。
. 3.应用
学以致用。我们来看一个商业应用的实例,比如我们考虑这样一个简单的场景,我们如何从后台静默上传数据?简单捋一下,首先静默上传是指不会影响用户正常的交互的。所以我们考虑使用IntentService去异步处理,另外我们要考虑到如果上传失败,是否有重传的机制呢?所以我这里在上传失败的时候会缓存在本地,在下一次监听到网络变化时重新上传。
所以,第一步我会写一个BroadcastReceiver去监听网络的变化:
1public class NetworkReceiver extends BroadcastReceiver {
2
3 private static final String TAG = NetworkReceiver.class.getSimpleName();
4
5 @Override
6 public void onReceive(Context context, Intent intent) {
7 SLog.d(NetworkReceiver.class.getSimpleName(), "onReceive");
8 if (null == intent) {
9 return;
10 }
11 SLog.d(TAG, "action=" + intent.getAction());
12 ConnectivityManager connectivityManager = (ConnectivityManager) context.getApplicationContext()
13 .getSystemService(CONNECTIVITY_SERVICE);
14
15 NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
16
17 if (null == networkInfo || !networkInfo.isConnected()) {
18 return;
19 }
20 long currentTimeMillis = System.currentTimeMillis();
21 if (currentTimeMillis - GlobalCacheManager.getInstance().lastNetworkConnectTimeMillis < 1000) {
22 SLog.d(TAG, "may be receive twice in the same time");
23 return;
24 }
25
26 // upload data by start intentservice
27 Intent serviceIntent = new Intent(context, YourUploadService.class);
28 context.startService(serviceIntent);
29
30 }
31}
第二步,写一个IntentService的实现类YourUploadService.java:
1public class YourUploadService extends IntentService {
2 public YourUploadService() {
3 super("YourUploadService");
4 }
5
6 @Override
7 protected void onHandleIntent(Intent intent) {
8 ConnectivityManager manager = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
9 NetworkInfo info = manager.getActiveNetworkInfo();
10 if (info != null) {
11 // TODO do your upload logic }
12 }
13 }
14}
至此,我们应该学会了怎么正确的使用IntentService了。
完整源码可关注以下公众号,私信邮箱地址获取哦
上一篇: nginx详解