当前位置: 首页 > news >正文

Android学习之路(21) 进程间通信-AIDL与Servce基本使用

Service 与 Thread 和 进程 之间的关系

  • 进程:应用程序在内存中分配的空间。(正在运行中的程序)
  • 线程:负责程序执行的单元,也称为执行路径。(需要线程来执行代码)。一个进程至少包含一条线程,如果一个进程中出现了条线程,此程序就为多线程程序。
  • Service是一个组件:默认运行在Main线程(进程中)。和Thread没关系。如果Service在清单文件中用 android:process 属性另开进程运行此Service组件,就算Service被销毁进程也不会停止。

Service的生命周期图

!
|
【Android】跨进程通信——AIDL、之Service基本细节使用之:精通

Service 与 Thread 和 进程 之间的关系

  • 进程:应用程序在内存中分配的空间。(正在运行中的程序)
  • 线程:负责程序执行的单元,也称为执行路径。(需要线程来执行代码)。一个进程至少包含一条线程,如果一个进程中出现了条线程,此程序就为多线程程序。
  • Service是一个组件:默认运行在Main线程(进程中)。和Thread没关系。如果Service在清单文件中用 android:process 属性另开进程运行此Service组件,就算Service被销毁进程也不会停止。

Service的生命周期图

回调方法详解

  • onCreate(),创建Service时调用,整个生命周期中只执行一次。
  • onBind(),必须实现的方法,客户端调用bindService()时回调该方法,返回一个IBind对象,通过此对象与被绑定Service通信。此后如果再次使用bindService绑定Service,系统不会再调用onBind()方法,只会直接把IBinder对象传递给其他后来增加的客户端!
  • onStartCommand(),早期版本是onStart()。客户端通过startService()时调用。可多次调用startService(),但不会创建新的Service对象,继续复用已创建的Service,会继续回调onStartCommand()方法。
  • onUnbind(),当该Service上绑定的所有客户端都断开时会回调该方法!
  • onDestroy(),服务被关闭前调用此方法。如调用stopService(),或者调用unbindService()。

三种服务的启动方式

  1. startService()启动Service
  • 方法回调:onCreate() —> onStartCommand() —> 进入运行状态。
  • 联系绑定:与调用者无联系,就算调用者结束了生命,只要不调用stopService()方法,Service还会继续运行。
  • 结束方法:stopService()
  1. bindService()启动Service
  • 方法回调:onCreate() —> onBind() —> 进入运行状态。
  • 联系绑定:服务与调用者进行联系绑定。1. 如果调用者没有手动调用unbindService()关闭服务,当调用者销毁后,服务也会被销毁,并回调 onUnbind() —> onDestroy()。 2. 如果是多个客户端绑定同一个Service的话,当所有的客户端都和service解除绑定或销毁后,系统才会销毁service。
  • 结束方法:unbindService()
  • 注意:如果中途中通过startService() 方法启动服务,则服务不会和全部调用者进行联系绑定。
  1. startService()启动Service后,调用bindService()绑定Service
  • 方法回调:onCreate() —> onStartCommand() —> onBind() —> 进入运行状态。
  • 方法回调2:如果两条语句在一个方法内:onCreate() —> onBind() —> onStartCommand() —> 进入运行状态。
  • 联系绑定:与调用者无联系,因为服务是通过startService()启动的。
  • 结束方法:先unbindService()解绑,再调用stopService()关闭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)。
<service android:name=".myService"android:enabled="true"android:exported="true"android:icon="@drawable/ic_launcher"android:label="string"android:process=":string"android:permission="string">
</service>

Intent的显式与隐式启动

一、显示启动service

Intent intent = new Intent(this, MyService.class);
startService(intent);

二、隐式启动service

首先自定义意图过滤器:标签 intent-filter、Action

<service android:name=".MyService" android:enabled="true" ><intent-filter android:priority="1000" ><action android:name="com.bin.action.MyService" /></intent-filter>
</service>
<!-- android:priority="1000" 服务优先级 -->

三、5.0后隐式启动 service

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(Application Not Responding)

public class MyService extends Service {@Overridepublic void onCreate(){super.onCreate();// 调用startForegroundService()后5秒内没有调用startForeground(),会有ANRif (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());}}@Overridepublic 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,还需要添加权限:

 <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

跨进程通信

跨进程通信的场景有两种:

  • 本地service跨进程通信:是和当前程序内的service进行通信
  • 远程Service跨进程通信:是和其它程序内的service进行通信,已知方式:
  1. AIDL 接口描述文件方式
  2. 不用AIDL方式,使用 Binder 的 onTransact 方法
  3. 使用Messenger:Handler的方式

先来了解一下 :bindService()、ServiceConnection、Binder

1.1 bindService(Intent service, ServiceConnection conn, int flags)

  • service : 通过该intent指定要启动的Service
  • conn : ServiceConnection对象,用户监听访问者与Service间的连接情况,连接成功回调该对象中的onServiceConnected(ComponentName,IBinder)方法;如果Service所在的宿主由于异常终止或者其他原因终止,导致Service与访问者间断开,连接时调用onServiceDisconnected(CompanentName)方法,主动通过unbindService() 方法断开并不会调用上述方法!
  • flags : 指定绑定时是否自动创建Service(如果Service还未创建),参数可以是 0 (不自动创建),BIND_AUTO_CREATE(自动创建)

1.2 ServiceConnection

private IBinder myService;private ServiceConnection con = new ServiceConnection(){@Overridepublic void onServiceConnected(ComponentName package, IBinder service){myService = service;Toast.makeText(MyService.this, "连接成功", Toast.LENGTH_LONG).show();}@Overridepublic void onServiceDisconnected(ComponentName package){myService = null;Toast.makeText(MyService.this, "连接断开", Toast.LENGTH_LONG).show();}
};

1.3 返回的 Binder 对象

返回的对象不能为null,否则连接不成立。

2. 本地 Service 跨进程通信

服务端:

服务端需要写一个继承自 Binder 的类与服务通信。而这个类是你自定义的。

public class MyService extends Service
{private MyBinder binder;@Overridepublic 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 属性另开进程

<service android:name=".MyService" 
android:enabled="true"
android:process=":MyService" />

客户端:

public class MainActivity extends Activity 
{private IBinder myService;private ServiceConnection con = new ServiceConnection() {@Overridepublic 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){}}@Overridepublic void onServiceDisconnected(ComponentName package){myService = null;Toast.makeText(MainActivity.this, "连接断开", Toast.LENGTH_LONG).show();}};@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);bindService(new Intent(this, MyService.class), con, BIND_AUTO_CREATE);}}

3. 远程 Service 跨进程通信

1. AIDL跨进程通信

编写 AIDL 的注意事项:

  • 接口名词需要与aidl文件名相同。
  • 接口和方法前面不要加访问权限修饰符:public ,private,protected等,也不能用static final。
  • AIDL默认支持的类型包括Java基本类型,String,List,Map,CharSequence,除此之外的其他类型都 需要import声明,对于使用自定义类型作为参数或者返回值,自定义类型需要实现Parcelable接口。
  • 自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义 的包在同一个包中。
  • 常见坑:两个应用程序的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;@Overridepublic IBinder onBind(Intent intent) {if (binder == null) {binder = new AIDLBinder();}return binder;}private final class AIDLBinder extends Stub {@Overridepublic 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 要定义 意图过滤器

<service android:name=".AIDLService"><intent-filter><action android:name="android.intent.action.AIDLService" /><category android:name="android.intent.category.DEFAULT" /></intent-filter>
</service>

是 Intent默认添加的附加属性,所以加上。

服务端编写完毕

客户端:

首先把 服务端的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(){@Overridepublic 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){}}@Overridepublic void onServiceDisconnected(ComponentName package){Toast.makeText(MainActivity.this, "断开连接", Toast.LENGTH_LONG).show();iPerson = null;}};@Overrideprotected 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;@Overridepublic IBinder onBind(Intent intent) {if (binder == null) {binder = new IPCBinder();}return binder;}private final class IPCBinder extends Binder {@Overrideprotected 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(){@Overridepublic 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();}@Overridepublic void onServiceDisconnected(ComponentName package){Toast.makeText(MainActivity.this, "断开连接", Toast.LENGTH_LONG).show();iBinder = null;}};@Overrideprotected 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跨进程通信

  • 服务端实现一个Handler,由其接受来自客户端的每个调用的回调
  • 使用实现的Handler创建Messenger对象
  • 通过Messenger得到一个IBinder对象,并将其通过onBind()返回给客户端
  • 客户端使用 IBinder 将 Messenger(引用服务的 Handler)实例化,然后使用后者将 Message 对象发送给服务
  • 服务端在其 Handler 中(具体地讲,是在 handleMessage() 方法中)接收每个 Message

服务端:

public class MessengerService extends Service {private final Messenger mMessenger = new Messenger(new ServiceHandler());class ServiceHandler extends Handler {@Overridepublic 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@Overridepublic IBinder onBind(Intent intent) {//返回给客户端一个IBinder实例return mMessenger.getBinder();}
}

客户端:

public class MainActivity extends Activity 
{private Messenger mService;private ServiceConnection con = new ServiceConnection(){@Overridepublic void onServiceConnected(ComponentName package, IBinder service){Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_LONG).show();//接收onBind()传回来的IBinder,并用它构造MessengermService = new Messenger(service);//构造一个消息对象Message msg = Message.obtain(null, 2, 0, 0);try {//把这个信息发送给服务端mService.send(msg);} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName package){Toast.makeText(MainActivity.this, "断开连接", Toast.LENGTH_LONG).show();mService = null;}};@Overrideprotected 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);}}

双进程守护
原理:两个服务之间互相绑定,监听服务之间的 ServiceConnection 对象,重写 onServiceDisconnected() 方法 在对方异常断开连接时重启对方。

服务1:

public class MyService1 extends Service
{private MyBinder binder;private IBinder mService;private Intent intent;private ServiceConnection con = new ServiceConnection(){@Overridepublic void onServiceConnected(ComponentName package, IBinder service){Toast.makeText(MyService1.this, "连接成功", Toast.LENGTH_LONG).show();mService = service;}@Overridepublic void onServiceDisconnected(ComponentName package){Toast.makeText(MyService1.this, "连接断开", Toast.LENGTH_LONG).show();mService = null;startService(intent);bindService(intent, con, 0);}};@Overridepublic void onCreate(){intent = new Intent(this, MyService2.class);startService(intent);bindService(intent, con, 0);super.onCreate();}@Overridepublic 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(){@Overridepublic void onServiceConnected(ComponentName package, IBinder service){Toast.makeText(MyService2.this, "连接成功", Toast.LENGTH_LONG).show();mService = service;}@Overridepublic void onServiceDisconnected(ComponentName package){Toast.makeText(MyService2.this, "连接断开", Toast.LENGTH_LONG).show();mService = null;startService(intent);bindService(intent, con, 0);}};@Overridepublic void onCreate(){intent = new Intent(this, MyService1.class);startService(intent);bindService(intent, con, 0);super.onCreate();}@Overridepublic IBinder onBinder(Intent intent){if (binder == null) {binder = new MyBinder();}return binder;}private class MyBinder extends Binder{// TODO}}

启动服务的方式可以在 Activity 中,也可以用广播接收器,我这里就用活动来启动服务

public class MainActivity extends Activity 
{@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);Intent service = new Intent(this, MyService1.class);startService(service);}}

记得在清单文件声明Service组件

<service android:name=".MyService1" />
<service android:name=".MyService2"android:process=":MyService2"/>

注意:双进程守护只能防止第三方应用kill,并不能防止用户手动停止掉

| |
|–|–|
| | |

回调方法详解
onCreate(),创建Service时调用,整个生命周期中只执行一次。
onBind(),必须实现的方法,客户端调用bindService()时回调该方法,返回一个IBind对象,通过此对象与被绑定Service通信。此后如果再次使用bindService绑定Service,系统不会再调用onBind()方法,只会直接把IBinder对象传递给其他后来增加的客户端!
onStartCommand(),早期版本是onStart()。客户端通过startService()时调用。可多次调用startService(),但不会创建新的Service对象,继续复用已创建的Service,会继续回调onStartCommand()方法。
onUnbind(),当该Service上绑定的所有客户端都断开时会回调该方法!
onDestroy(),服务被关闭前调用此方法。如调用stopService(),或者调用unbindService()。
三种服务的启动方式

  1. startService()启动Service
    方法回调:onCreate() —> onStartCommand() —> 进入运行状态。
    联系绑定:与调用者无联系,就算调用者结束了生命,只要不调用stopService()方法,Service还会继续运行。
    结束方法:stopService()
  2. bindService()启动Service
    方法回调:onCreate() —> onBind() —> 进入运行状态。
    联系绑定:服务与调用者进行联系绑定。1. 如果调用者没有手动调用unbindService()关闭服务,当调用者销毁后,服务也会被销毁,并回调 onUnbind() —> onDestroy()。 2. 如果是多个客户端绑定同一个Service的话,当所有的客户端都和service解除绑定或销毁后,系统才会销毁service。
    结束方法:unbindService()
    注意:如果中途中通过startService() 方法启动服务,则服务不会和全部调用者进行联系绑定。
  3. startService()启动Service后,调用bindService()绑定Service
    方法回调:onCreate() —> onStartCommand() —> onBind() —> 进入运行状态。
    方法回调2:如果两条语句在一个方法内:onCreate() —> onBind() —> onStartCommand() —> 进入运行状态。
    联系绑定:与调用者无联系,因为服务是通过startService()启动的。
    结束方法:先unbindService()解绑,再调用stopService()关闭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的显式与隐式启动
一、显示启动service
Intent intent = new Intent(this, MyService.class);
startService(intent);
二、隐式启动service
首先自定义意图过滤器:标签 intent-filter、Action


    <action android:name="com.bin.action.MyService" /></intent-filter>

三、5.0后隐式启动 service
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(),会有ANRif (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,还需要添加权限:

跨进程通信 跨进程通信的场景有两种:

本地service跨进程通信:是和当前程序内的service进行通信
远程Service跨进程通信:是和其它程序内的service进行通信,已知方式:
1. AIDL 接口描述文件方式

             2. 不用AIDL方式,使用 Binder 的 onTransact 方法3. 使用Messenger:Handler的方式

先来了解一下 :bindService()、ServiceConnection、Binder

1.1 bindService(Intent service, ServiceConnection conn, int flags)
service : 通过该intent指定要启动的Service
conn : ServiceConnection对象,用户监听访问者与Service间的连接情况,连接成功回调该对象中的onServiceConnected(ComponentName,IBinder)方法;如果Service所在的宿主由于异常终止或者其他原因终止,导致Service与访问者间断开,连接时调用onServiceDisconnected(CompanentName)方法,主动通过unbindService() 方法断开并不会调用上述方法!
flags : 指定绑定时是否自动创建Service(如果Service还未创建),参数可以是 0 (不自动创建),BIND_AUTO_CREATE(自动创建)
1.2 ServiceConnection
private IBinder myService;

private ServiceConnection con = new ServiceConnection(){

	@Overridepublic void onServiceConnected(ComponentName package, IBinder service){myService = service;Toast.makeText(MyService.this, "连接成功", Toast.LENGTH_LONG).show();}@Overridepublic void onServiceDisconnected(ComponentName package){myService = null;Toast.makeText(MyService.this, "连接断开", Toast.LENGTH_LONG).show();}

};

1.3 返回的 Binder 对象注意
返回的对象不能为null,否则连接不成立。

  1. 本地 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() {@Overridepublic 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){}}@Overridepublic 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);
}

}

  1. 远程 Service 跨进程通信
  2. AIDL跨进程通信
    编写 AIDL 的注意事项:

接口名词需要与aidl文件名相同。
接口和方法前面不要加访问权限修饰符:public ,private,protected等,也不能用static final。
AIDL默认支持的类型包括Java基本类型,String,List,Map,CharSequence,除此之外的其他类型都 需要import声明,对于使用自定义类型作为参数或者返回值,自定义类型需要实现Parcelable接口。
自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义 的包在同一个包中。
常见坑:两个应用程序的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 {@Overridepublic 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 要定义 意图过滤器

是 Intent默认添加的附加属性,所以加上。

服务端编写完毕

客户端:

首先把 服务端的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(){

	@Overridepublic 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){}}@Overridepublic 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);
}

}

  1. 不用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 {@Overrideprotected 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(){

	@Overridepublic 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();}@Overridepublic 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);
}

}

  1. 使用Messenger跨进程通信
    服务端实现一个Handler,由其接受来自客户端的每个调用的回调
    使用实现的Handler创建Messenger对象
    通过Messenger得到一个IBinder对象,并将其通过onBind()返回给客户端
    客户端使用 IBinder 将 Messenger(引用服务的 Handler)实例化,然后使用后者将 Message 对象发送给服务
    服务端在其 Handler 中(具体地讲,是在 handleMessage() 方法中)接收每个 Message
    服务端:

public class MessengerService extends Service {

private final Messenger mMessenger = new Messenger(new ServiceHandler());class ServiceHandler extends Handler {@Overridepublic 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(){

	@Overridepublic void onServiceConnected(ComponentName package, IBinder service){Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_LONG).show();//接收onBind()传回来的IBinder,并用它构造MessengermService = new Messenger(service);//构造一个消息对象Message msg = Message.obtain(null, 2, 0, 0);try {//把这个信息发送给服务端mService.send(msg);} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic 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);
}

}

双进程守护
原理:两个服务之间互相绑定,监听服务之间的 ServiceConnection 对象,重写 onServiceDisconnected() 方法 在对方异常断开连接时重启对方。

服务1:

public class MyService1 extends Service
{
private MyBinder binder;
private IBinder mService;
private Intent intent;

private ServiceConnection con = new ServiceConnection(){@Overridepublic void onServiceConnected(ComponentName package, IBinder service){Toast.makeText(MyService1.this, "连接成功", Toast.LENGTH_LONG).show();mService = service;}@Overridepublic 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(){@Overridepublic void onServiceConnected(ComponentName package, IBinder service){Toast.makeText(MyService2.this, "连接成功", Toast.LENGTH_LONG).show();mService = service;}@Overridepublic 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,并不能防止用户手动停止掉

相关文章:

Android学习之路(21) 进程间通信-AIDL与Servce基本使用

Service 与 Thread 和 进程 之间的关系 进程&#xff1a;应用程序在内存中分配的空间。&#xff08;正在运行中的程序&#xff09;线程&#xff1a;负责程序执行的单元&#xff0c;也称为执行路径。&#xff08;需要线程来执行代码&#xff09;。一个进程至少包含一条线程&…...

【MATLAB源码-第54期】基于白鲸优化算法(WOA)和遗传算法(GA)的栅格地图路径规划最短路径和适应度曲线对比。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 ​1.白鲸优化算法&#xff08;WOA&#xff09;&#xff1a; 白鲸优化算法是一种受白鲸捕食行为启发的优化算法。该算法模拟了白鲸群体捕食的策略和行为&#xff0c;用以寻找问题的最优解。其基本思想主要包括以下几点&#x…...

关于计算机找不到vcomp140.dll无法继续执行怎么修复

在计算机使用过程中&#xff0c;我们可能会遇到各种问题&#xff0c;其中之一就是vcomp140.dll文件丢失。vcomp140.dll是一个动态链接库文件&#xff0c;它通常用于支持软件运行和系统功能。当这个文件丢失时&#xff0c;可能会导致程序无法正常运行&#xff0c;甚至系统出现错…...

qt-C++笔记之信号与槽

qt-C笔记之信号与槽 code review! 本文抄自公众号&#xff1a;嵌入式小生 文章目录 qt-C笔记之信号与槽一.信号2.1.信号的发出2.2.信号的处理 二.槽函数2.1.带有默认参数的信号和槽函数2.2.使用QObject::connect()将信号连接到槽函数的三种方法2.2.1.第一种方法&#xff1a;使…...

linux安装visual studio code

下载 https://code.visualstudio.com/ 下载.deb文件 安装 假如文件被下载到了 /opt目录下 进入Opt目录&#xff0c;右键从当前目录打开终端。 输入下面的安装命令。 sudo apt-get install ./code_1.83.1-1696982868_amd64.deb 安装成功。 配置 打开 visual studio cod…...

VM虚拟机创建centos7 64位系统提示此主机不支持64位客户机操作系统,此系统无法运行

VM虚拟机创建centos7 64位系统提示此主机不支持64位客户机操作系统,此系统无法运行 背景解决方案 背景 本身系统是window10 64位专业版系统&#xff0c;理论上不应该不支持64位的。 解决方案 最近安装docker开启了虚拟化hyper-v&#xff0c;关闭即可。 打开cmd&#xff08;…...

跟着NatureMetabolism学作图:R语言ggplot2转录组差异表达火山图

论文 Independent phenotypic plasticity axes define distinct obesity sub-types https://www.nature.com/articles/s42255-022-00629-2#Sec15 s42255-022-00629-2.pdf 论文中没有公开代码&#xff0c;但是所有作图数据都公开了&#xff0c;我们可以试着用论文中提供的数据…...

Linux进程与线程的内核实现

进程描述符task_struct 进程描述符&#xff08;struct task_struct&#xff09;pid与tgid进程id编号分配规则内存管理mm_struct进程与文件,文件系统 进程,线程创建的本质 clone函数原型线程创建的实现进程创建的实现 总结 进程描述符task_struct 进程描述符&#xff08;st…...

Flink学习之旅:(四)Flink转换算子(Transformation)

1.基本转换算子 基本转换算子说明映射&#xff08;map&#xff09;将数据流中的数据进行转换&#xff0c;形成新的数据流过滤&#xff08;filter&#xff09;将数据流中的数据根据条件过滤扁平映射&#xff08;flatMap&#xff09;将数据流中的整体&#xff08;如&#xff1a;集…...

CesiumJS 中绘制大多边形

本文翻译自Cesium官方&#xff0c;有改动。 本文中提及到的“大多边形”就如下图所示。 在Cesium的早期版本和一些引擎中&#xff0c;我们绘制这种跨度比较大的多边形&#xff0c;经常会看到一些奇怪的冲突问题&#xff0c;如下图所示。 要渲染任何几何体&#xff0c;我们必…...

FreeRTOS移植以及任务

FreeRTOS移植 1.在sys.h中需要把SYSTEM_SUPPORT_OS 改为 1&#xff0c;支持我们使用 FreeRTOS //0,不支持 os //1,支持 os #define SYSTEM_SUPPORT_OS 1 //定义系统文件夹是否支持 OS2.出现报错 …\SYSTEM\usart\usart.c(6): error: #5: cannot open source input file “incl…...

笙默考试管理系统-MyExamTest----codemirror(41)

笙默考试管理系统-MyExamTest----codemirror&#xff08;40&#xff09; 目录 一、 笙默考试管理系统-MyExamTest 二、 笙默考试管理系统-MyExamTest 三、 笙默考试管理系统-MyExamTest 四、 笙默考试管理系统-MyExamTest 五、 笙默考试管理系统-MyExamTest 笙默考试…...

C#数据结构--数组和ArrayList

目录 本章目录&#xff1a; 2.1 数组基本概念 2.1.1 数组的声明和初始化 2.1.2 数组元素的设置和存取访问 2.1.4 多维数组 2.1.5 参数数组 2.2ArrayList 类 2.2.1ArrayList 类的成员 2.2.2 应用 ArrayList 类 数组和ArrayList之间的区别以及使用的场景 数组&#xf…...

Stable Diffusion WebUI扩展adetailer安装及功能介绍

ADetailer是Stable Diffusion WebUI的一个扩展,类似于检测细节器。 目录 安装地址 如何安装 1. Windows系统 (1)手动安装 (2)一体机...

Linux安装MINIO

MINIO简介MINIO目录 mkdir -p /opt/minio/data && cd /opt/minio MINIO下载 wget https://dl.minio.org.cn/server/minio/release/linux-amd64/minio MINIO授权 chmod x minio MINIO端口 firewall-cmd --zonepublic --add-port7171/tcp --permanent && firewal…...

Java架构师分布式搜索架构

目录 1 导学1.1 初识Elasticsearch1.1.1 Elasticsearch的作用1.1.2 ELK技术栈1.1.3 Elasticsearch和lucene1.1.4.为什么不是其他搜索技术?1.1.5.总结2 Elasticsearch快速建立知识体系3 Elasticsearch和MySQL实体建立联系3.1.mapping映射属性3.2 数据分组聚合3.2.1.聚合的种类3…...

简单宿舍管理系统(springboot+vue)

简单宿舍管理系统&#xff08;springbootvue&#xff09; 1.创建项目1.前端2.数据库3.后端 2.登陆1.前端1.准备工作2.登陆组件3.配置 2.后端1.链接数据库2.创建用户实体类3.数据操作持久层1.配置2.内容3.测试 4.中间业务层1.异常2.业务实现3.测试 5.响应前端控制层 3.前后对接4…...

Socks5代理、IP代理的关键作用

Socks5代理与SK5代理&#xff1a;网络安全的卫士 Socks5代理作为一项先进的代理协议&#xff0c;其多协议支持、身份验证功能以及UDP支持使其成为网络安全的重要支持者。 IP代理&#xff1a;隐私保护与无限访问的利器 IP代理技术通过隐藏真实IP地址&#xff0c;保护用户隐私…...

前端 CSS 经典:box-shadow

1. 基础属性 /* box-shadow: h-shadow v-shadow blur spread color inset; */ box-shadow: 10px 10px 2px 2px red inset; h-shadow: 必填&#xff0c;水平阴影的位置&#xff0c;允许负值 v-shadow: 必填&#xff0c;垂直阴影的位置&#xff0c;允许负值 blur: 可选&#xff…...

使用数组方法打印出 1 - 10000 之间的所有对称数。例如:121、1331等

&#xff08;我从别的人那复制的&#xff0c;原文章请点击此处&#xff09; 源代码&#xff1a; function getNum (start, end) {var arr [];for(var i start; i < end; i) {if (i.toString() i.toString().split().reverse().join() && i.toString().length &…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...