本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Service
Service是Context的子类
UI控件是线程不安全的,所有的UI操作必须在主线程中
Service并不会自动开启线程,所有代码默认运行在主线程,要手动开启子线程
在服务里开的子线程(IntentService)比在活动里开的子线程(AsyncTask)重要性更高更不容易被杀死
Service的使用
右击New→Service→Service
Export属性表示是否允许其它程序访问这个Service
Enabled属性表示是否启用这个Service
在任何一个位置都可以调用stopSelf()方法使Service停止
普通使用
启动Service
Intent intent=new Intent(this,MyService.class);
startService(intent);
结束Service
Intent intent=new Intent(this,MyService.class);
stopService(intent);
与Activity绑定
public class MyService extends Service {
class MyBinder extends Binder{
public void startSomething(){
...//可以在这里开IntentService
}
}
public MyService() { }
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();//把连接器暴露给外部,活动就可以使用连接器里的方法,在Service中执行
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
在活动中使用ServiceConnection获得Service返回回来的连接器
实现了指挥服务去干什么服务就去干什么的功能
private ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyService.MyBinder myBinder=(MyService.MyBinder) service;//获得连接器
myBinder.startSomething();//执行连接器中的方法
}
@Override
public void onServiceDisconnected(ComponentName name) { }
};
启动绑定
Intent intent=new Intent(this,MyService.class);
bindService(intent,connection,BIND_AUTO_CREATE);//通过ServiceConnection与后台某项特定服务绑定
结束绑定
unbindService(connection);
Service的生命周期
若进程里的Service正在执行生命周期回调,那么这个进程属于前台进程
若进程里的Service与目前处于前台的活动绑定,那么这个进程属于前台进程
使用startService()方式启动
只有第1次启动执行onCreate()->每次启动都执行onStartCommand()->stopService()/自己stopSelf()则执行onDestroy()
-
适用于长期执行进行某项任务
-
不管是否有Activity使用bindService()/unbindService()到该Service,该Service一直在后台运行,直到被调用stopService()/自己stopSelf()
-
不管startService()多少次,stopService()1次就会停止服务
-
Service启动之后就和Activity没什么关联了,不要求在Activity退出时手动退出Service
使用bindService()方式启动
只有第1次启动执行onCreate()->只有第1次启动执行onBind()->unbindService()则执行onUnBind()->onDestroy()
- 适用于与正在运行的Service取得联系/类似懒汉式的思路,开始先不创建,在需要用到的时候去bind一下,节约资源
- 该Service一直在后台运行,直到被调用unbindService()解绑/绑定了该Service的Context不存在了
- Service生命周期依附于启动它的Context,因此当前台调用bindService()的Context销毁
后,Service也会自动调用onUnbind()和onDestory()
使用混合型方式启动
Service被startService()的同时又被bindService(),该Service将会一直在后台运行,并且不管调用几次,onCreate()方法始终只会调用一次,onStartCommand()的调用次数与startService()调用的次数一致(使用bindService()方法不会调用onStartCommand())。同时,调用unBindService()将不会停止Service,必须调用stopService()或Service自身的stopSelf()来停止服务
前台Service
8.0+Service运行在后台时随时会被系统回收,只有应用前台可见时(以类似通知的形式存在),Service才能稳定运行
渠道一旦建立,修改渠道属性将不再生效,除非卸载重装/更改渠道id
9.0+需要添加一条普通权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
在Service的onCreate()方法
@Override
public void onCreate() {
super.onCreate();
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("foreground", "前台Service", NotificationManager.IMPORTANCE_DEFAULT);
manager.createNotificationChannel(channel);
}
Intent intent = new Intent(this, MainActivity.class);
//这里也可以使用taskStackBuilder的方式获得pendingIntent
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
//使用compat可以兼容所有安卓版本,参数为(上下文,渠道id)
Notification notification= new NotificationCompat.Builder(this,"foreground")
.setContentTitle("通知标题")
.setContentText("通知内容")
.setSmallIcon(R.drawable.xxx)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.xxx))//将图片解析为Bitmap
.setShowWhen(false)//是否显示时间
.setNotificationSilent()//静默通知
.setVisibility(NotificationCompat.VISIBILITY_SECRET)//锁屏不可见
.setContentIntent(pending)//点击通知后跳转到的页面
.build();//创建通知对象
//注意启动通知的方式不是通过管理器发送通知,而是启动前台
startForeground(1, notification);
}
取消
- 将前台服务降为后台服务,可以让它从任务栏消失
//此时服务并没有停止,直到调用stopService()
stopForeground(true);
- 直接stopService(),无论Service在前/后台都会直接结束啦
IntentService
是Service的子类(Android8.0+弃用)
Service本身是在主线程中运行,如果执行耗时操作需要自己手动开启子线程并且在运行结束后调用stopSelf或者stopService来停止,这样可行,但是自己去管理Service的生命周期和子线程并不是一个好的选择,因此android提供了IntentService来实现自动开启子线程并关闭
内部通过HandlerThread创建了一个工作线程,并将其与ServiceHandler绑定(把工作线程的Looper传入ServiceHandler构造器)
优先级高
新建一个继承了IntentService的类
public class MyIntentService extends IntentService {
public MyIntentService() {
//参数是工作线程的名字
super("MyIntentService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//这个方法已经是在子线程中运行的了,会依次处理消息队列中的Intent,全部执行完毕自动结束
//我们在这里可以根据intent的不同来做不同操作
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
//IntentService内部实现是将请求的Intent添加到消息队列里
return super.onStartCommand(intent, flags, startId);
}
}
使用方法
//一个IntentService只会开启一个工作线程,这两个Intent以队列的方式在onHandleIntent()中按顺序执行
Intent intent1 = new Intent(this,MyIntentService.class);
startService(intent1);
Intent intent = new Intent(this,MyIntentService.class);
startService(intent2);
既然是Service就要在Manifest文件里注册
<service
android:name=".MyIntentService"
android:enabled="true"
android:exported="true" />