深入理解Service(三)——前台服务和IntentService

在深入理解Service(一)——服务生命周期中解释了Service的生命周期,在深入理解Service(二)——绑定服务中解释了如何绑定服务,IPC机制,在本篇博客中将谈论服务的另两个常见用法:前台服务和IntentService。

在前台运行服务

我们之前定义的服务都是运行在后台的,这样的服务当系统内存不足时将会被杀死,而用户却毫不知情。如果想要一个服务的状态可以被用户一眼看到,那么可以使用前台服务。比如说我们之前的一个播放音乐例子,常见的应用是会在通知栏有一个控制的通知栏,可以暂停、下一首、上一首等等,那就是前台服务的表现形式,一旦结束了服务,那么可以将通知移除,这样用户就能知道播放服务结束了。
下面依然以播放音乐为例,将一个服务设置为前台服务,只需要在绑定时调用startForeground将自己设置为前台服务即可,其中第二个参数是Notification对象,表示在通知栏中显示的通知。下面的代码是在PlayService中:

 @Override
    public IBinder onBind(Intent intent) {

        //设置为前台服务
        Notification.Builder builder = new Notification.Builder(this);
        builder.setContentTitle("播放音乐中..").setContentText("Diamonds")
                .setWhen(System.currentTimeMillis()).setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
        Notification notifation = builder.getNotification();
        startForeground(2, notifation);

        return mBinder;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mediaPlayer != null)
            mediaPlayer.release();
        stopForeground(true);
    }

上面的代码在onBind中调用startForeground,在onDestory调用stopForegroun()方法,其中参数表示移除通知。

IntentService

服务和Activity一样,都是Android的组件,生命周期中的各个方法也都运行在主线程中。所以虽然服务名为“服务”,但是如果想要其中执行耗时任务,还是需要将任务放入到线程中。IntentService就是这样一种服务,Android已经为我们封装了创建线程的过程,只需要我们直接实现耗时任务的逻辑即可。IntentService的使用有如下限制:
- 不可以直接和用户交互。如果需要将结果与UI交互,需要将结果返回给Activity
- 工作请求遵循线性,FIFO
- IntentService中的操作不能被打断

使用IntentService

使用IntentService,只需要继承自IntentService类,需要实现onHandleIntent方法,在这个方法中就可以编写耗时任务。下面是一个IntentService的例子:

public class MyIntentService extends IntentService {

    public static final String TAG = "MyIntentService";

    public MyIntentService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        //模拟耗时任务
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.i(TAG, "Num:" + intent.getIntExtra("num", 0));

    }
}

上面的代码实现了onHandleIntent,让线程睡了10秒模拟耗时任务,然后打印出intent中的值,为了验证请求的线性。
Activity上的界面就是一个启动按钮,开启10个服务并传入不同的值,方法如下:

 public void startIntentService(View view) {

        Intent intent = new Intent(this, MyIntentService.class);
        for (int i = 0; i < 10; i++) {
            intent.putExtra("num", i);
            startService(intent);
        }

    }

当点击按钮后,Log日志信息如下:
深入理解Service(三)——前台服务和IntentService_第1张图片
从上面的图片可以看到:数字从0-9打印出来,并且时间均都相隔10s,可以验证处理任务时按照FIFO原则。下面就将结合IntentService的源码解释这一点。

IntentService源码分析

IntentService继承自Service,并且在内部实现了一个Handler用来串行处理消息,代码比较少,下面就全部贴出来了,如下:

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() {


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

HandlerTrhread是一个带有Handler的线程,这也就意味着HandlerThread的Handler、Looper处于非主线程的消息,ServiceHandler使用HandlerThread的Looper创建,那么ServiceHandler的handleMessage方法就是在该线程中,也就意味着onHandleIntent方法是在非UI线程中调用的,自然可以进行耗时任务。另外由于Handler的Looper使用一个消息队列控制消息的执行,所以onHandleIntent也就是按照请求的顺序执行的。
如果关于Handler不熟悉的朋友,可以先阅读深入理解Handler先了解一下Handler的机制。

总结

至此,关于服务的常见用法就到此结束了。从生命周期、启动服务、绑定服务到服务的两个特殊用法:前台服务和IntentService,知识点还是很多的,不过还都是很基础的,所以我们更要好好掌握。
本文的代码可以查看我的Github地址

你可能感兴趣的:(Android基础)