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()。
三种服务的启动方式
- startService()启动Service
- 方法回调:onCreate() —> onStartCommand() —> 进入运行状态。
- 联系绑定:与调用者无联系,就算调用者结束了生命,只要不调用stopService()方法,Service还会继续运行。
- 结束方法:stopService()
- bindService()启动Service
- 方法回调:onCreate() —> onBind() —> 进入运行状态。
- 联系绑定:服务与调用者进行联系绑定。1. 如果调用者没有手动调用unbindService()关闭服务,当调用者销毁后,服务也会被销毁,并回调 onUnbind() —> onDestroy()。 2. 如果是多个客户端绑定同一个Service的话,当所有的客户端都和service解除绑定或销毁后,系统才会销毁service。
- 结束方法:unbindService()
- 注意:如果中途中通过startService() 方法启动服务,则服务不会和全部调用者进行联系绑定。
- 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进行通信,已知方式:
- AIDL 接口描述文件方式
- 不用AIDL方式,使用 Binder 的 onTransact 方法
- 使用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()。
三种服务的启动方式
- startService()启动Service
方法回调:onCreate() —> onStartCommand() —> 进入运行状态。
联系绑定:与调用者无联系,就算调用者结束了生命,只要不调用stopService()方法,Service还会继续运行。
结束方法:stopService() - bindService()启动Service
方法回调:onCreate() —> onBind() —> 进入运行状态。
联系绑定:服务与调用者进行联系绑定。1. 如果调用者没有手动调用unbindService()关闭服务,当调用者销毁后,服务也会被销毁,并回调 onUnbind() —> onDestroy()。 2. 如果是多个客户端绑定同一个Service的话,当所有的客户端都和service解除绑定或销毁后,系统才会销毁service。
结束方法:unbindService()
注意:如果中途中通过startService() 方法启动服务,则服务不会和全部调用者进行联系绑定。 - 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,否则连接不成立。
- 本地 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);
}
}
- 远程 Service 跨进程通信
- 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);
}
}
- 不用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);
}
}
- 使用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组件
相关文章:
Android学习之路(21) 进程间通信-AIDL与Servce基本使用
Service 与 Thread 和 进程 之间的关系 进程:应用程序在内存中分配的空间。(正在运行中的程序)线程:负责程序执行的单元,也称为执行路径。(需要线程来执行代码)。一个进程至少包含一条线程&…...
【MATLAB源码-第54期】基于白鲸优化算法(WOA)和遗传算法(GA)的栅格地图路径规划最短路径和适应度曲线对比。
操作环境: MATLAB 2022a 1、算法描述 1.白鲸优化算法(WOA): 白鲸优化算法是一种受白鲸捕食行为启发的优化算法。该算法模拟了白鲸群体捕食的策略和行为,用以寻找问题的最优解。其基本思想主要包括以下几点&#x…...
关于计算机找不到vcomp140.dll无法继续执行怎么修复
在计算机使用过程中,我们可能会遇到各种问题,其中之一就是vcomp140.dll文件丢失。vcomp140.dll是一个动态链接库文件,它通常用于支持软件运行和系统功能。当这个文件丢失时,可能会导致程序无法正常运行,甚至系统出现错…...
qt-C++笔记之信号与槽
qt-C笔记之信号与槽 code review! 本文抄自公众号:嵌入式小生 文章目录 qt-C笔记之信号与槽一.信号2.1.信号的发出2.2.信号的处理 二.槽函数2.1.带有默认参数的信号和槽函数2.2.使用QObject::connect()将信号连接到槽函数的三种方法2.2.1.第一种方法:使…...
linux安装visual studio code
下载 https://code.visualstudio.com/ 下载.deb文件 安装 假如文件被下载到了 /opt目录下 进入Opt目录,右键从当前目录打开终端。 输入下面的安装命令。 sudo apt-get install ./code_1.83.1-1696982868_amd64.deb 安装成功。 配置 打开 visual studio cod…...
VM虚拟机创建centos7 64位系统提示此主机不支持64位客户机操作系统,此系统无法运行
VM虚拟机创建centos7 64位系统提示此主机不支持64位客户机操作系统,此系统无法运行 背景解决方案 背景 本身系统是window10 64位专业版系统,理论上不应该不支持64位的。 解决方案 最近安装docker开启了虚拟化hyper-v,关闭即可。 打开cmd(…...
跟着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 论文中没有公开代码,但是所有作图数据都公开了,我们可以试着用论文中提供的数据…...
Linux进程与线程的内核实现
进程描述符task_struct 进程描述符(struct task_struct)pid与tgid进程id编号分配规则内存管理mm_struct进程与文件,文件系统 进程,线程创建的本质 clone函数原型线程创建的实现进程创建的实现 总结 进程描述符task_struct 进程描述符(st…...
Flink学习之旅:(四)Flink转换算子(Transformation)
1.基本转换算子 基本转换算子说明映射(map)将数据流中的数据进行转换,形成新的数据流过滤(filter)将数据流中的数据根据条件过滤扁平映射(flatMap)将数据流中的整体(如:集…...
CesiumJS 中绘制大多边形
本文翻译自Cesium官方,有改动。 本文中提及到的“大多边形”就如下图所示。 在Cesium的早期版本和一些引擎中,我们绘制这种跨度比较大的多边形,经常会看到一些奇怪的冲突问题,如下图所示。 要渲染任何几何体,我们必…...
FreeRTOS移植以及任务
FreeRTOS移植 1.在sys.h中需要把SYSTEM_SUPPORT_OS 改为 1,支持我们使用 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(40) 目录 一、 笙默考试管理系统-MyExamTest 二、 笙默考试管理系统-MyExamTest 三、 笙默考试管理系统-MyExamTest 四、 笙默考试管理系统-MyExamTest 五、 笙默考试管理系统-MyExamTest 笙默考试…...
C#数据结构--数组和ArrayList
目录 本章目录: 2.1 数组基本概念 2.1.1 数组的声明和初始化 2.1.2 数组元素的设置和存取访问 2.1.4 多维数组 2.1.5 参数数组 2.2ArrayList 类 2.2.1ArrayList 类的成员 2.2.2 应用 ArrayList 类 数组和ArrayList之间的区别以及使用的场景 数组…...
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)
简单宿舍管理系统(springbootvue) 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代理:网络安全的卫士 Socks5代理作为一项先进的代理协议,其多协议支持、身份验证功能以及UDP支持使其成为网络安全的重要支持者。 IP代理:隐私保护与无限访问的利器 IP代理技术通过隐藏真实IP地址,保护用户隐私…...
前端 CSS 经典:box-shadow
1. 基础属性 /* box-shadow: h-shadow v-shadow blur spread color inset; */ box-shadow: 10px 10px 2px 2px red inset; h-shadow: 必填,水平阴影的位置,允许负值 v-shadow: 必填,垂直阴影的位置,允许负值 blur: 可选ÿ…...
使用数组方法打印出 1 - 10000 之间的所有对称数。例如:121、1331等
(我从别的人那复制的,原文章请点击此处) 源代码: function getNum (start, end) {var arr [];for(var i start; i < end; i) {if (i.toString() i.toString().split().reverse().join() && i.toString().length &…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
沙箱虚拟化技术虚拟机容器之间的关系详解
问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...
