目录
Service 与 Thread 和 进程 之间的关系
Service的生命周期图
回调方法详解
三种服务的启动方式
1. startService()启动Service
2. bindService()启动Service
3. startService()启动Service后,调用bindService()绑定Service
清单文件声明 Service 组件及属性
Intent的显式与隐式启动
一、显示启动service
二、隐式启动service
三、5.0后隐式启动 service
前台Service
系统8.0、8.1的后台 Service 必须切换成前台服务
跨进程通信
1.1 bindService(Intent service, ServiceConnection conn, int flags)
1.2 ServiceConnection
1.3 返回的 Binder 对象注意
2. 本地 Service 跨进程通信
3. 远程 Service 跨进程通信
1. AIDL跨进程通信
2. 不用AIDL方式,使用 Binder 的 onTransact 方法
3. 使用Messenger跨进程通信
4. AIDL、onTransact、Messenger 对比 (个人理解,不考虑性能)
双进程守护
Service的生命周期图
三种服务的启动方式
不管哪种方法启动服务,都可以调用 stopService() 关闭服务。上面是按照规范关闭服务,当然你有需求的话。
清单文件声明 Service 组件及属性
android:exported | 表示是否允许除了当前程序之外的其他程序访问这个服务 |
android:enabled | 表示是否启用这个服务 |
android:permission | 是权限声明 |
android:process | 是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote。 |
android:isolatedProcess | 设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(bind and start)。 |
Intent的显式与隐式启动
Intent intent = new Intent(this, MyService.class);
startService(intent);
首先自定义意图过滤器:标签 intent-filter、Action
5.0 之前的隐式启动service
Intent intent = new Intent("com.bin.action.MyService");
startService(intent);
5.0 之后的隐式启动service
Intent intent = new Intent("com.bin.action.MyService");
intent.setPackage(getPackageName()); //如果是另一个程序的service则指定它的包名
startService(intent);
前台Service
原理:在服务里通过 startForeground() 方法启动一个通知 Notification,也可以使用兼容包里的 通知对象。
public void onCreate()
{
super.onCreate();
Notification.Builder localBuilder = new Notification.Builder(this);
localBuilder.setSmallIcon(R.drawable.ic_launcher);
localBuilder.setContentTitle("Service标题");
localBuilder.setContentText("正在运行...");
startForeground(1, localBuilder.getNotification());
}
系统8.0、8.1的后台 Service 必须切换成前台服务
注意:调用startForegroundService()后5秒内没有调用startForeground(),会有ANR
public class MyService extends Service {
@Override
public void onCreate()
{
super.onCreate();
// 调用startForegroundService()后5秒内没有调用startForeground(),会有ANR
if (Build.VERSION.SDK_INT >= Build.VERSION_CODE.O) {
Notification.Builder localBuilder = new Notification.Builder(this);
localBuilder.setSmallIcon(R.drawable.ic_launcher);
localBuilder.setContentTitle("Service标题");
localBuilder.setContentText("正在运行...");
startForeground(1, localBuilder.getNotification());
}
}
@Override
public void onDestroy() {
stopForeground(true);
super.onDestroy();
}
}
版本兼容判断 启动Service(8.0、8.1启动Service方法用startForegroundService())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODE.O) {
startForegroundService(intent);
} else {
startService(intent);
}
使用 startForegroundService()
方法启动service,还需要添加权限:
跨进程通信
跨进程通信的场景有两种:
1. AIDL 接口描述文件方式
2. 不用AIDL方式,使用 Binder 的 onTransact 方法
3. 使用Messenger:Handler的方式
先来了解一下 :bindService()、ServiceConnection、Binder
private IBinder myService;
private ServiceConnection con = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName package, IBinder service)
{
myService = service;
Toast.makeText(MyService.this, "连接成功", Toast.LENGTH_LONG).show();
}
@Override
public void onServiceDisconnected(ComponentName package)
{
myService = null;
Toast.makeText(MyService.this, "连接断开", Toast.LENGTH_LONG).show();
}
};
返回的对象不能为null,否则连接不成立。
2. 本地 Service 跨进程通信
服务端:
服务端需要写一个继承自 Binder 的类与服务通信。而这个类是你自定义的。
public class MyService extends Service
{
private MyBinder binder;
@Override
public IBinder onBinder(Intent intent)
{
if (binder == null) {
binder = new MyBinder();
}
return binder;
}
private class MyBinder extends Binder
{
// ...code
// 返回此对象与Service进行通信
public void toast(Context context, String str)
{
Toast.mackText(context, str, Toast.LENGTH_LONG).show();
}
}
}
然后在清单文件声明服务组件,并用 android:process 属性另开进程
客户端:
public class MainActivity extends Activity
{
private IBinder myService;
private ServiceConnection con = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName package, IBinder service)
{
myService = service;
Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_LONG).show();
try
{
if (myService != null) {
Toast.makeText(MainActivity.this, myService.toast(this, "myService 弹出我吧!"), Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "服务端未启动,或异常关闭", Toast.LENGTH_LONG).show();
}
}
catch (RemoteException e)
{}
}
@Override
public void onServiceDisconnected(ComponentName package)
{
myService = null;
Toast.makeText(MainActivity.this, "连接断开", Toast.LENGTH_LONG).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
bindService(new Intent(this, MyService.class), con, BIND_AUTO_CREATE);
}
}
3. 远程 Service 跨进程通信
编写 AIDL 的注意事项:
服务端:创建 AIDL 文件接口 :IPerson.aidl
package com.bin.aidl;
interface IPerson {
String getContent(int postion);
}
保存后会在 gen目录的包路径下 自动生成 IPerson.java (图片来源网络)
自定义Service类:
注意:这时候我们返回的 Binder 对象是继承自 AIDL接口的 内部类Stub,IPerson.java下的 Stub类
public static abstract class Stub extends android.os.Binder implements com.bin.aidl.IPerson
public class AIDLService extends Service {
private IBinder binder;
@Override
public IBinder onBind(Intent intent) {
if (binder == null) {
binder = new AIDLBinder();
}
return binder;
}
private final class AIDLBinder extends Stub {
@Override
public String getContent(int postion) throws RemoteException {
String result = null;
switch (postion) {
case 0:
result = "我是0";
break;
case 1:
result = "我是1";
break;
case 2:
result = "我是2";
break;
default:
result = "我是默认";
break;
}
return result;
}
}
}
在AndroidManifest.xml文件中注册Service
注意:从一个进程启动另一个进程的 Service 要定义 意图过滤器
服务端编写完毕
客户端:
首先把 服务端的AIDL文件复制过来,不能修改AIDL文件里的任何东西,包括 所在 package ...
注意:AIDL存放的包路径必须与服务端一致
与服务端连接成功后,返回的Binder对象要强转为 AIDL 接口对象才能使用自定义方法:
// public static com.bin.aidl.IPerson asInterface(android.os.IBinder obj)
IPerson.Stub.asInterface(service);
public class MainActivity extends Activity
{
private IPerson iPerson;
private ServiceConnection con = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName package, IBinder service)
{
Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_LONG).show();
iPerson = IPC.Stub.asInterface(service);
try
{
Toast.makeText(MainActivity.this, iPerson.getContent(0), Toast.LENGTH_LONG).show();
}
catch (RemoteException e)
{}
}
@Override
public void onServiceDisconnected(ComponentName package)
{
Toast.makeText(MainActivity.this, "断开连接", Toast.LENGTH_LONG).show();
iPerson = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent service = new Intent("android.intent.action.AIDLService");
service.setPackage("com.bin.aidl");
bindService(service, con, BIND_AUTO_CREATE);
}
}
2. 不用AIDL方式,使用 Binder 的 onTransact 方法
服务端:
public class IPCService extends Service {
private static final String DESCRIPTOR = "IPCService";
private IBinder binder;
@Override
public IBinder onBind(Intent intent) {
if (binder == null) {
binder = new IPCBinder();
}
return binder;
}
private final class IPCBinder extends Binder {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code){
case 0x001: {
//读取发送来的消息
data.enforceInterface(DESCRIPTOR);
int postion = data.readInt();
//处理
String result = null;
switch (postion) {
case 0:
result = "我是0";
break;
case 1:
result = "我是1";
break;
case 2:
result = "我是2";
break;
default:
result = "我是默认";
break;
}
//返回数据
reply.writeNoException();
reply.writeString(result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
}
客户端:
public class MainActivity extends Activity
{
private IBinder iBinder;
private ServiceConnection con = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName package, IBinder service)
{
Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_LONG).show();
iBinder = service;
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
String result = null;
int postion = 1;
try {
//写入信息
data.writeInterfaceToken("IPCService");
data.writeInt(postion);
//发送消息
mIBinder.transact(0x001, data, reply, 0);
//读取返回信息
reply.readException();
result = reply.readString();
}catch (RemoteException e) {
e.printStackTrace();
} finally {
//释放资源
reply.recycle();
data.recycle();
}
Toast.makeText(MainActivity.this, result, Toast.LENGTH_LONG).show();
}
@Override
public void onServiceDisconnected(ComponentName package)
{
Toast.makeText(MainActivity.this, "断开连接", Toast.LENGTH_LONG).show();
iBinder = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent service = new Intent("android.intent.action.IPCService");
service.setPackage("com.bin.ipcservice");
bindService(service, con, BIND_AUTO_CREATE);
}
}
3. 使用Messenger跨进程通信
服务端:
public class MessengerService extends Service {
private final Messenger mMessenger = new Messenger(new ServiceHandler());
class ServiceHandler extends Handler {
@Override
public void handleMessage(Message msg) {
String result = null;
switch (msg.what) {
case 0:
result = "我是0";
break;
case 1:
result = "我是1";
break;
case 2:
result = "我是2";
break;
default:
result = "我是默认";
break;
}
Toast.makeText(MessengerService.this, result, Toast.LENGTH_LONG).show();
super.handleMessage(msg);
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//返回给客户端一个IBinder实例
return mMessenger.getBinder();
}
}
客户端:
public class MainActivity extends Activity
{
private Messenger mService;
private ServiceConnection con = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName package, IBinder service)
{
Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_LONG).show();
//接收onBind()传回来的IBinder,并用它构造Messenger
mService = new Messenger(service);
//构造一个消息对象
Message msg = Message.obtain(null, 2, 0, 0);
try {
//把这个信息发送给服务端
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName package)
{
Toast.makeText(MainActivity.this, "断开连接", Toast.LENGTH_LONG).show();
mService = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent service = new Intent("android.intent.action.MessengerService");
service.setPackage("com.bin.messengerservice");
bindService(service, con, BIND_AUTO_CREATE);
}
}
4. AIDL、onTransact、Messenger 对比 (个人理解,不考虑性能)
双进程守护
原理:两个服务之间互相绑定,监听服务之间的 ServiceConnection 对象,重写 onServiceDisconnected() 方法 在对方异常断开连接时重启对方。
服务1:
public class MyService1 extends Service
{
private MyBinder binder;
private IBinder mService;
private Intent intent;
private ServiceConnection con = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName package, IBinder service)
{
Toast.makeText(MyService1.this, "连接成功", Toast.LENGTH_LONG).show();
mService = service;
}
@Override
public void onServiceDisconnected(ComponentName package)
{
Toast.makeText(MyService1.this, "连接断开", Toast.LENGTH_LONG).show();
mService = null;
startService(intent);
bindService(intent, con, 0);
}
};
@Override
public void onCreate()
{
intent = new Intent(this, MyService2.class);
startService(intent);
bindService(intent, con, 0);
super.onCreate();
}
@Override
public IBinder onBinder(Intent intent)
{
if (binder == null) {
binder = new MyBinder();
}
return binder;
}
private class MyBinder extends Binder
{
// TODO
}
}
服务2:
public class MyService2 extends Service
{
private MyBinder binder;
private IBinder mService;
private Intent intent;
private ServiceConnection con = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName package, IBinder service)
{
Toast.makeText(MyService2.this, "连接成功", Toast.LENGTH_LONG).show();
mService = service;
}
@Override
public void onServiceDisconnected(ComponentName package)
{
Toast.makeText(MyService2.this, "连接断开", Toast.LENGTH_LONG).show();
mService = null;
startService(intent);
bindService(intent, con, 0);
}
};
@Override
public void onCreate()
{
intent = new Intent(this, MyService1.class);
startService(intent);
bindService(intent, con, 0);
super.onCreate();
}
@Override
public IBinder onBinder(Intent intent)
{
if (binder == null) {
binder = new MyBinder();
}
return binder;
}
private class MyBinder extends Binder
{
// TODO
}
}
启动服务的方式可以在 Activity 中,也可以用广播接收器,我这里就用活动来启动服务
public class MainActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent service = new Intent(this, MyService1.class);
startService(service);
}
}
记得在清单文件声明Service组件
注意:双进程守护只能防止第三方应用kill,并不能防止用户手动停止掉