欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用IntentService的正确姿势

程序员文章站 2024-01-26 22:54:28
...

使用IntentService的正确姿势

在开发安卓应用的过程中,我们经常需要这样的场景:希望APP在不影响当前用户的操作的前提下,在后台默默的做一些任务。比如:静默的下载文件或者上传数据。我们一般会考虑到新启一个线程去做异步的操作。不错,新启线程确实可以做到。那为啥还需要IntentService呢?

. 1.优点

  • 本质上IntentService也是开了一个线程,但是IntentService是继承自Service的,所以根据Android系统Kill Application的机制,使用IntentService的应用的优先级更高一点。通俗点说如果使用IntentService做后台任务时,当您的程序退到后台时,被杀死的几率会更低一点。
  • 既然IntentService是在Service里开启线程去做任务处理,那我直接在Service里启动线程去做不就好了吗?当然可以,但是IntentService已经帮您封装好了,为什么还要自己再去实现IntentService的一套逻辑呢?
  • IntentService会在任务执行完成后自行结束自己,而不需要外部去调用stopService了。

. 2.正确使用的姿势

优点我们已经知道了,那该如何正确的使用呢?我们知道Service可以通过startServicebindService这两种方式启动。当然喽,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了。


完整源码可关注以下公众号,私信邮箱地址获取哦