一、service主要分为两种状态
服务可以长时间运行在后台,没有图形界面的应用组件;可以后台播放音乐、下载文件等
- 启动状态:启动之后就会在后台无限运行,除非手动关闭,并且不会有返回值
- 绑定状态:通过bindService()绑定服务,可以进行交互,可以多个组件同时绑定到该服务,全部解绑之后服务将被注销。
二、启动状态
1.声明与文件清单
<service android:name="MyService"
android:enabled="true"
android:exported="true"
android:process=":remote">
</service>
exported:能否被其他应用隐式调用,enabled是否可以创建实例,要注意设置运行的进程process,还有权限android:permission、android:label、android:isolatedProcess等。
2.MainActivity.class开启、关闭服务
package com.mrdouya.myservicetest;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity {
private Button btStrService;
private Button btCloService;
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btStrService = (Button) findViewById(R.id.bt_start_service);
btCloService = (Button) findViewById(R.id.bt_close_service);
intent = new Intent(MainActivity.this,MyService.class);
btStrService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//启动服务
startService(intent);
}
});
btCloService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//关闭服务
stopService(intent);
}
});
}
}
3.创建服务创建
继承Service、一个构造方法、几个必要个方法覆盖(理解注释Override的意思,被标记未这样有什么作用,将会被怎样调用?)
package com.mrdouya.myservicetest;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
Log.d("MyService","onCreate!");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d("MyService","onBind!");
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("MyService","onStartCommand!");
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onUnbind(Intent intent) {
Log.d("MyService","onUnbind!");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyService","onDestroy!");
}
}
4.生命周期
生命周期按以下代码顺序执行,必然实现onCreate、onStartCommand两个方法;除此之外还有很多方法stopSelf()、onRebind()、onConfigurationChanged()、onLowMemory()等等
2019-01-03 08:26:06.676 1068-1089/system_process I/ActivityManager: Start proc 8815:com.mrdouya.myservicetest:remote/u0a93 for service com.mrdouya.myservicetest/.MyService
2019-01-03 08:26:07.226 8815-8815/? D/MyService: onCreate!
2019-01-03 08:26:07.228 8815-8815/? D/MyService: onStartCommand!
2019-01-03 08:26:20.451 8815-8815/com.mrdouya.myservicetest:remote D/MyService: onDestroy!
注意:
1)onCreate()、onStartCommand(Intent intent, int flags, int startId)onCreate()只会在第一次启动Service时调用,之后启动都不会调用该方法onStartCommand(Intent intent, int flags, int startId)每次启动都会调用它,主要在这个方法中执行一些逻辑
2)onStartCommand(Intent intent, int flags, int startId)的参数以及返回值intent是启动服务时传递过来的,activity可以封装信息传递给服务startId表示当前服务的唯一ID,配合方法stopSelfResult (int startId)使用flags表示启动请求时是否有额外的数据,它与onStartCommand的返回值相关联,关系如下:
- 0:表示没有返回值
- START_FLAG_REDELIVERY:表示返回值为START_REDELIVER_INTENT,内存不足会被回收,在回收之前会调用stopSelf()停止服务,之后会重建服务,并传递最后一个intent
- START_FLAG_RETRY:表示onStartCommand如果一直没有返回值,则会尝试重新调用该方法onStartCommand
返回值,3种:
- START_STICKY:因内存不足时被杀死后系统空闲时会重新创建Service,并且会回调onStartCommand,但其中的intent为空,除非有挂起的Intent
- START_NOT_STICKY:服务被杀死之后不会重新创建服务,除非重新调用startService方法。
- START_REDELIVER_INTENT:当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个Intent 调用 onStartCommand(),任何挂起 Intent均依次传递
三、绑定状态
服务处于绑定状态时,形成客户端-服务器,Service相当于其中的服务器。Activity与Service绑定时,Acticity可以调用Service的方法、传递请求数据、实现进程通信等等。绑定服务的生命周期会随着服务解绑毁灭。服务的绑定服务需要我们提供一个 IBinder接口的实现类,该类用以提供客户端用来与服务进行交互的编程接口,该接口可以通过三种方法定义接口:
1.扩展接口Binder
适用于应用自身使用的服务,可以防止其他应用程序的调用;实现过程:在Service中定义MyBinder类继承Binder,MyBinder类即可以给服务类提供binder对象,又可以给客户端提供服务的实列,实现客户端和服务端的交互!客户端通过ServiceConnection的onServiceConnect方法获取到服务的对象和对应的binder对象;然后通过binderService绑定服务,服务对象调用服务的公共方法;unbinderServie解绑服务。
服务类:
package com.mrdouya.myservicetest;
import android.app.Service;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
public class MyBinderService extends Service {
private final String TAG = "MrDouYa";
private boolean quit = true;
@Override
public void unbindService(ServiceConnection conn) {
Log.d(TAG,"unbindService!");
super.unbindService(conn);
}
/**
*创建内部类继承Binder
*给服务端MyBinderService提供Binder对像
*给客户端提供MyBinderService的对象
*/
public class MyBinder extends Binder{
//获取MyBinderService的对象
MyBinderService getService(){
Log.d(TAG,"MyBinder return MyBinderService");
return MyBinderService.this;
}
}
//创建MyBinder的对象并提供给MyBinderService绑定
private MyBinder myBinder = new MyBinder();
private int iCount = 0;
private Thread MyThread = null;
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG,"onBind return myBinder");
//绑定binder
return myBinder;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"onCreate!");
MyThread = new Thread(){
@Override
public void run() {
while(quit){
try {
iCount++;
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
MyThread.start();
}
@Override
public void onDestroy() {
Log.d(TAG,"onDestroy!");
if(MyThread != null){
MyThread.interrupt();
MyThread = null;
}
quit = false;
super.onDestroy();
}
/**
*设置一个方法供客户端调用
*/
public int MyBinderServiceCount(){
Log.d(TAG,"MyBinderServiceCount' method");
return iCount;
}
}
客户端:
package com.mrdouya.myservicetest;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;
import android.util.Log;
public class MainActivity extends Activity implements View.OnClickListener {
private Button btBinder;
private Button btUnbinder;
private Button btGetData;
private ServiceConnection myServiceConnection;
private final String TAG = "MrDouYa";
private MyBinderService myBinderService;
private Intent myBinderIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btBinder = (Button) findViewById(R.id.bt_binder_service);
btUnbinder = (Button) findViewById(R.id.bt_unbinder_service);
btGetData = (Button)findViewById(R.id.bt_service_get_data);
btBinder.setOnClickListener(this);
btUnbinder.setOnClickListener(this);
btGetData.setOnClickListener(this);
myBinderIntent = new Intent(this,MyBinderService.class);
/**
*ServiceConnection实际是服务中Unbinder方法传入的参数,主要用于服务绑定、解绑定时调用
*onServiceConnected绑定时调用,可以带到服务端的binder对象、服务的对象
* onServiceDisconnected只有在服务解绑定时调用
*/
myServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.d(TAG,"ServiceConnection onServiceConnected");
//获取到MyBinder的对象
MyBinderService.MyBinder myBinder = (MyBinderService.MyBinder) iBinder;
//通过MyBinder对象获取MyBinderService的对象
myBinderService = myBinder.getService();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.d(TAG,"ServiceConnection onServiceConnected");
myBinderService = null;
}
};
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.bt_binder_service :
Log.d(TAG,"onClick binder service!");
bindService(myBinderIntent,myServiceConnection,Service.BIND_AUTO_CREATE);
break;
case R.id.bt_unbinder_service :
Log.d(TAG,"onClick unbinder service!");
if(myBinderService != null) {
myBinderService = null;
unbindService(myServiceConnection);
}
break;
case R.id.bt_service_get_data :
if(myBinderService != null) {
Log.d(TAG,"onClick use service method ,iCount = "+myBinderService.MyBinderServiceCount());
}else {
Log.d(TAG,"onClick can not use service,you don't binder it");
}
break;
}
}
}
执行log
2019-01-03 14:49:20.227 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onClick binder service!
2019-01-03 14:49:20.254 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onCreate!
2019-01-03 14:49:20.258 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onBind return myBinder
2019-01-03 14:49:20.269 23513-23513/com.mrdouya.myservicetest D/MrDouYa: ServiceConnection onServiceConnected
2019-01-03 14:49:20.269 23513-23513/com.mrdouya.myservicetest D/MrDouYa: MyBinder return MyBinderService
2019-01-03 14:49:23.148 23513-23513/com.mrdouya.myservicetest D/MrDouYa: MyBinderServiceCount' method
2019-01-03 14:49:23.148 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onClick use service method ,iCount = 3
2019-01-03 14:49:34.519 23513-23513/com.mrdouya.myservicetest D/MrDouYa: MyBinderServiceCount' method
2019-01-03 14:49:34.519 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onClick use service method ,iCount = 15
2019-01-03 14:49:40.810 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onClick unbinder service!
2019-01-03 14:49:40.823 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onDestroy!
2019-01-03 14:49:45.217 23513-23513/com.mrdouya.myservicetest D/MrDouYa: onClick can not use service,you don't binder it
生命周期:onCreate、onBind、unbinder、onDestroy相比未绑定服务,没有执行onStartCommand方法需要注意,服务端怎么获取到Binder对象,怎么给客户端提供服务端的实例;客户端是如何绑定服务,以及获取到服务端的Binder对象,服务端的实例
2.使用Message
适用于自身应用中启动单独的进程的服务以及不同进程间的应用,可实现应用之间的交互。服务端首先定义服务的handler,将handler对象封装在Messenger对象中,在binder的过程返回用Messenger对象getBinder()方法获取的binder对象。在客户端用binderService()、ServiceConnection对象绑定服务,将handler对象封装在Messenger对象中,Messenger对象在ServiceConnection的onServiceConnected方法中绑定服务端的binder,并通过Message进行客户-服务端的交互。
MainActivity类代码:
package com.mrdouya.myservicetest;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.util.Log;
public class MainActivity extends Activity implements View.OnClickListener {
private Button btMessageBinder;
private Button btMessageUnBinder;
private Button btMessageSend;
private ServiceConnection myMessageServiceConnection;
private Messenger messenger;
private boolean mServiceFlg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btMessageBinder = findViewById(R.id.bt_Message_service_binder);
btMessageUnBinder = findViewById(R.id.bt_Message_service_unbinder);
btMessageSend = findViewById(R.id.bt_message_send_service);
btMessageBinder.setOnClickListener(this);
btMessageUnBinder.setOnClickListener(this);
btMessageSend.setOnClickListener(this);
myMessageServiceConnection =new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.d(TAG,"ServiceConnection onServiceConnected!");
messenger = new Messenger(iBinder);
mServiceFlg = true;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.d(TAG,"ServiceConnection onServiceDisconnected!");
messenger = null;
mServiceFlg = false;
}
};
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.bt_Message_service_binder :
Log.d(TAG,"onClick Message binder service!");
bindService(new Intent(MainActivity.this,MyMessageService.class),myMessageServiceConnection,Service.BIND_AUTO_CREATE);
break;
case R.id.bt_Message_service_unbinder :
Log.d(TAG,"onClick Message unbinder service!");
if(mServiceFlg) {
unbindService(myMessageServiceConnection);
mServiceFlg = false;
}
break;
case R.id.bt_message_send_service :
Log.d(TAG,"onClick message send!");
sendToService();
break;
}
}
private void sendToService() {
if(!mServiceFlg){
Log.d(TAG,"this is MainActivity,message is not send,mServiceFlg is "+mServiceFlg);
return;
}
Message msg = Message.obtain(null,MyMessageService.MSG_FROM_CLIENT,0,0);
//把接收服务器端的回复的Messenger通过Message的replyTo参数传递给服务端
msg.replyTo = mReceivedReplyMessage;
try {
messenger.send(msg);
Log.d(TAG,"this is MainActivity,message is send!");
} catch (RemoteException e) {
e.printStackTrace();
}
}
private Messenger mReceivedReplyMessage = new Messenger(new MyReceiverReplyMsgHandler());
private class MyReceiverReplyMsgHandler extends Handler{
@Override
public void handleMessage( Message msg) {
switch (msg.what){
case MyMessageService.MSG_FROM_CLIENT:
Log.d(TAG,"this is MainActivity,i have received message from Service!");
break;
default:
super.handleMessage(msg);
}
}
}
}
MyMessageService类代码:
package com.mrdouya.myservicetest;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
public class MyMessageService extends Service {
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"MyMessageService, onCreate");
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG,"MyMessageService, onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG,"MyMessageService, onDestroy");
}
static final int MSG_FROM_CLIENT = 1;
private final String TAG = "MrDouYa";
/**
* 内部类MyHander继承Handler
* 用于接收客户端传来的消息
* 给mMessenger传入Handler的实例对象
*/
class MyHander extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MSG_FROM_CLIENT :
Log.d(TAG,"this is MyMessageService, received message from client!");
//先将回复的消息封装到Bundle中
Bundle mBundle = new Bundle();
mBundle.putString("reply","this is MyMessageService,i have reply you!");
//获取一个Message对象,将mBundle放入message中
Message replyMessage = Message.obtain(null,MyMessageService.MSG_FROM_CLIENT);
replyMessage.setData(mBundle);
//获取一个Messenger对象,将message中信息回复出去
Messenger replyClient = msg.replyTo;
try {
replyClient.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
//创建Messenger实例接收Handler对象,并在onBind类里获取一个与客户端绑定的Binder
final Messenger mMessenger = new Messenger(new MyHander());
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG,"MyMessageService, onBind");
return mMessenger.getBinder();
}
}
log结果:
2019-08-24 16:49:29.967 9277-9277/com.mrdouya.myservicetest D/MrDouYa: onClick Message binder service!
2019-08-24 16:49:30.561 9354-9354/com.mrdouya.myservicetest:remote D/MrDouYa: MyMessageService, onCreate
2019-08-24 16:49:30.563 9354-9354/com.mrdouya.myservicetest:remote D/MrDouYa: MyMessageService, onBind
2019-08-24 16:49:30.566 9277-9277/com.mrdouya.myservicetest D/MrDouYa: ServiceConnection onServiceConnected!
2019-08-24 16:49:35.645 9277-9277/com.mrdouya.myservicetest D/MrDouYa: onClick message send!
2019-08-24 16:49:35.647 9277-9277/com.mrdouya.myservicetest D/MrDouYa: this is MainActivity,message is send!
2019-08-24 16:49:35.648 9354-9354/com.mrdouya.myservicetest:remote D/MrDouYa: this is MyMessageService, received message from client!
2019-08-24 16:49:35.663 9277-9277/com.mrdouya.myservicetest D/MrDouYa: this is MainActivity,i have received message from Service!
2019-08-24 16:49:58.917 9277-9277/com.mrdouya.myservicetest D/MrDouYa: onClick Message unbinder service!
2019-08-24 16:49:58.923 9354-9354/com.mrdouya.myservicetest:remote D/MrDouYa: MyMessageService, onUnbind
2019-08-24 16:49:58.923 9354-9354/com.mrdouya.myservicetest:remote D/MrDouYa: MyMessageService, onDestroy
注意:
1)app中单独开进程服务,在manifest文件中添加属性android:process=":remote",在1中使用该属性会在onServiceConnected中出现异:java.lang.ClassCastException:android.os.BinderProxy cannot be cast to …;
2)在sendToService()方法中调用的
//把接收服务器端的回复的Messenger通过Message的replyTo参数传递给服务端
msg.replyTo = mReceivedReplyMessage;
四、绑定服务注意点
1.多个客户端绑定同一个服务时,只有在第一个客户端绑定时,系统才会调用服务的 onBind() 方法来检索 IBinder,便可将同一 IBinder 传递至任何其他绑定的客户端。当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非 startService() 也启动了该服务)。
2.在客户端生命周期设置绑定和取消绑定操作,以便控制绑定状态下的Service,一般有以下两种情况:
- 如果只需要在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定。
- 如果希望Activity 在后台停止运行状态下仍可接收响应,则可在 onCreate() 期间绑定,在 onDestroy()期间取消绑定。需要注意的是,这意味着 Activity在其整个运行过程中(甚至包括后台运行期间)都需要使用服务,因此如果服务位于其他进程内,那么当提高该进程的权重时,系统很可能会终止该进程。
3.通常情况下(注意),切勿在 Activity 的 onResume() 和 onPause() 期间绑定和取消绑定,因为每一次生命周期转换都会发生这些回调,这样反复绑定与解绑是不合理的。此外,如果应用内的多个 Activity 绑定到同一服务,并且其中两个 Activity 之间发生了转换,则如果当前 Activity 在下一次绑定(恢复期间)之前取消绑定(暂停期间),系统可能会销毁服务并重建服务,因此服务的绑定不应该发生在 Activity 的 onResume() 和 onPause()中。
4.我们应该始终捕获 DeadObjectException异常,该异常是在连接中断时引发的,表示调用的对象已死亡,也就是Service对象已销毁,这是远程方法引发的唯一异常,DeadObjectException继承自RemoteException,因此我们也可以捕获RemoteException异常。
5.应用组件(客户端)可通过调用 bindService() 绑定到服务,Android 系统随后调用服务的 onBind() 方法,该方法返回用于与服务交互的 IBinder,而该绑定是异步执行的。
五、启动服务以及绑定服务转换共存
Android系统只会为Service创建一次实例,所以不管是启动服务还是绑定服务,都是对服务的唯一实例对象进行操作。启动服务、绑定服务的先后顺序会有两种情况:
- 先绑定后启动:以启动服务状态运行时,绑定服务会转为启动服务,之前绑定的宿主被销毁,服务会运行下去,直到停止服务或者被系统kill
- 先启动后绑定:绑定后,启动服务不会转化为绑定服务,服务还是与宿主处于绑定状态,宿主被销毁后,服务会运行下去,直到停止服务或者被系统kill
六、前\后台服务与通知以及通知信道
Android 8.0以上系统常见的JE报错,系统不允许在用户不知道的情况下后台启动服务;后台服务必须在通知栏显示通知,这相当于将后台服务转化为前台服务,服务不停止,通知无法取消。要后台启动提供startForegroundService()方法、并且需要在服务端设置通知、调用startForeground()方法
MainActivity类:
package com.mrdouya.myservicetest;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.util.Log;
public class MainActivity extends Activity implements View.OnClickListener {
private final String TAG = "MrDouYa";
private Button btForegroundService;
private Button bt_stop_Foreground_Service;
private Intent myForegroundServiceIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btForegroundService = findViewById(R.id.bt_start_Foreground_Service);
bt_stop_Foreground_Service = findViewById(R.id.bt_stop_Foreground_Service);
myForegroundServiceIntent = new Intent(MainActivity.this,MyForegroundService.class);
btForegroundService.setOnClickListener(this);
bt_stop_Foreground_Service.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.bt_stop_Foreground_Service :
Log.d(TAG,"onClick stop Foreground Service !");
stopService(myForegroundServiceIntent);
break;
case R.id.bt_start_Foreground_Service :
Log.d(TAG,"onClick Foreground Service!");
this.startForegroundService(myForegroundServiceIntent);
break;
}
}
}
MyForegroundService后台服务类:
package com.mrdouya.myservicetest;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.util.Log;
public class MyForegroundService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d("MrDouYa","MyForegroundService onBind");
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d("MrDouYa","MyForegroundService onCreate");
startForegroundChannel();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("MrDouYa","MyForegroundService onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
public void startForegroundChannel() {
Log.d("MrDouYa","MyForegroundService startForegroundChannel");
String channelId = "startForeground";
String name = "Foreground chanel";
NotificationChannel notificationChannel = new NotificationChannel(channelId,name,NotificationManager.IMPORTANCE_LOW);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(notificationChannel);
Notification notification = new Notification.Builder(MyForegroundService.this,channelId)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("Foreground Service")
.setContentText("this is a Foreground Service")
.setWhen(System.currentTimeMillis())
.build();
startForeground(1, notification);
}
}
七、服务与线程的区别与关系
1.服务和线程是两个不同的东西,也没有关系!硬要扯点联系:
- 服务是四大组件运行在UI线程中。
- 服务与子线程经常搭配起来,后台做耗时任务。
2.线程是CPU的最小执行单元,常说的主线程就是UI线程。一些注意点就是子线程不能更新UI、主线程不能阻塞、子线程可以后台执行一些耗时操作
3.服务就是无法与用户交互的系统组件。注意后台启动问题、同样不能阻塞进行耗时操作,用于配合子线程后台处理耗时任务
八、生命周期与显(隐)启动
1.生命周期跟Acticity相似,主要是两种状态有不一样的生命周期
2.一般是使用显式启动,隐式启动用于不同应用之间的启动,但是从5.0之后隐式启动被禁止了。
显示启动:
Intent mIntent = new Intent(MainActivity.this,MyService.class);
startService(mIntent);
隐式启动:
Intent mIntent = new Intent();
mIntent.setAction("com.mrdouya.myservicetest.MyForegroundService");
startService(mIntent);
5.0之后会抛出异常,可以查看ContextImpl源码中的validateServiceIntent(Intent service)方法,内容如下:
- 5.0之前,为打印的log提示:
1572 private void validateServiceIntent(Intent service) {
1573 if (service.getComponent() == null && service.getPackage() == null) {
1574 if (true || getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.KITKAT) {
1575 Log.w(TAG, "Implicit intents with startService are not safe: " + service
1576 + " " + Debug.getCallers(2, 3));
1577 //IllegalArgumentException ex = new IllegalArgumentException(
1578 // "Service Intent must be explicit: " + service);
1579 //Log.e(TAG, "This will become an error", ex);
1580 //throw ex;
1581 }
1582 }
1583 }
- 8.0根据SDK版本,打印log或者抛出异常:
1462 private void validateServiceIntent(Intent service) {
1463 if (service.getComponent() == null && service.getPackage() == null) {
1464 if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
1465 IllegalArgumentException ex = new IllegalArgumentException(
1466 "Service Intent must be explicit: " + service);
1467 throw ex;
1468 } else {
1469 Log.w(TAG, "Implicit intents with startService are not safe: " + service
1470 + " " + Debug.getCallers(2, 3));
1471 }
1472 }
1473 }
解决方案:
1.设置需要启动服务的包名
Intent mIntent = new Intent();
mIntent.setAction("com.mrdouya.myservicetest.MyForegroundService");
mIntent.setPackage(getPackageName());//使不符合validateServiceIntent中的判断条件
startService(mIntent);
2.转化为显示调用
public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
Intent mIntent = new Intent();
mIntent.setAction("com.mrdouya.myservicetest.MyForegroundService");
final Intent serviceIntent=new Intent(getExplicitIntent(this,mIntent));//其实也是给Intent添加包名、类名
startService(serviceIntent);
九、保证服务的生存
1.将onStartCommand() 方法的返回值设为 START_STICKY或START_REDELIVER_INTENT
2.用户通过 settings -> Apps -> Running -> Stop 方式杀死Service 。这个过程会执行Service的生命周期,也就是onDestory方法会被调用,这时便可以在 onDestory() 中发送广播重新启动。也可以使用两个服务相互检测,当其中一个停止,就从另外一个服务启动,循环保证两个服务不死。
3.用户通过 settings -> Apps -> Running -> Force Stop 方式杀死Service 。无解!
参考
文章内容基本出自博客:https://blog.csdn.net/hdhhd/article/details/80612726