在深入理解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()方法,其中参数表示移除通知。
服务和Activity一样,都是Android的组件,生命周期中的各个方法也都运行在主线程中。所以虽然服务名为“服务”,但是如果想要其中执行耗时任务,还是需要将任务放入到线程中。IntentService就是这样一种服务,Android已经为我们封装了创建线程的过程,只需要我们直接实现耗时任务的逻辑即可。IntentService的使用有如下限制:
- 不可以直接和用户交互。如果需要将结果与UI交互,需要将结果返回给Activity
- 工作请求遵循线性,FIFO
- 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日志信息如下:
从上面的图片可以看到:数字从0-9打印出来,并且时间均都相隔10s,可以验证处理任务时按照FIFO原则。下面就将结合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地址