首先,进程通常指一个执行单元,在移动设备上就是一个程序或应用,咱们在Android中所说的多进程(IPC)通常指一个应用包含多个进程。之因此要使用多进程有两方面缘由:某些模块因为特殊的需求要运行在单独的进程;增长应用可用的内存空间。java
Android中开启多线程只有一种方法,就是在AndroidManifest.xml中注册Service、Activity、Receiver、ContentProvider时指定android:process
属性,例如:android
<service android:name=".MyService" android:process=":remote">
</service>
<activity android:name=".MyActivity" android:process="com.shh.ipctest.remote2">
</activity>
复制代码
咱们为MyService
和MyActivity
指定的android:process
属性值有所不一样,它们的区别以下:git
:remote
:以:
开头是一种简写,系统会在当前进程名前附件当前包名,完整的进程名为:com.shh.ipctest:remote
,同时以:
开头的进程属于当前应用的私有进程,其它应用的组件不能和它跑在同一进程。com.shh.ipctest.remote2
:这是完整的命名方式,不会附加包名,其它应用若是和该进程的ShareUID
、签名
相同,则能够和它跑在同一个进程,实现数据共享。开启多进程虽简单,但会引起以下问题,必须引发注意。github
对于前两个问题,能够这么理解,在Android中,系统会为每一个应用或进程分配独立的虚拟机,不一样的虚拟机天然占有不一样的内存地址空间,因此同一个类的对象会产生不一样的副本,致使共享数据失败,必然也不能实现线程的同步。安全
因为SharedPreferences
底层采用读写XML的文件的方式实现,多进程并发的的读写极可能致使数据异常。bash
Application
被屡次建立和前两个问题相似,系统在分配多个虚拟机时至关于把同一个应用从新启动屡次,必然会致使 Application 屡次被建立,为了防止在 Application 中出现无用的重复初始化,可以使用进程名来作过滤,只让指定进程的才进行全局初始:网络
public class MyApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
String processName = "com.shh.ipctest";
if (getPackageName().equals(processName)){
// do some init
}
}
}
复制代码
Android中支持的多进程通讯方式主要有如下几种,它们之间各有优缺点,可根据使用场景选择选择:多线程
这里咱们主要讨论四大组件中Service在多进程通讯中的使用,这就涉及到了 AIDL、Messenger这两种多进程通讯方式,接下来重点看这两种 IPC 方式。并发
AIDL 的意思是 Android 接口定义语言,使用AIDL进行进程间通讯须要定义服务端和客户端,其中客户端和服务端能够在同一应用也能够在不一样应用。这里咱们服务端能够看作是图书馆,为客户端提供近期新书查询、图书捐赠、新书通知的服务。app
先建立一个 AIDL 文件,声明服务端要暴露给客户端的接口,而后建立一个 Service 监听客户端的链接请求,并在其中实现 AIDL 文件中的接口。
注意,为了方便开发,咱们通常把 AIDL 相关的文件放在同一包中,这样当客户端是另外一个应用时可方便的把整个包复制到客户端工程中。最终的AIDL文件包以下:
首先了解下 AIDL 文件支持的几种数据类型:
Book
是实现了Parcelable
的图书类,只定义了图书名name
字段,按照规定若是 AIDL 文件用到了自定义Parcelable
对象,同时须要提供一个Book.aidl
文件:
package com.shh.ipctest;
parcelable Book;
复制代码
ILibraryManager.aidl
定义了服务端要暴露给客户端的接口:
package com.shh.ipctest;
import com.shh.ipctest.Book;
interface ILibraryManager{
// 近期新书查询
List<Book> getNewBookList();
// 图书捐赠
void donateBook(in Book book);
}
复制代码
注意,尽管ILibraryManager.aidl
和Book
在同一包中,仍是须要显示的导入Book
类。除了基本类型数据外,其它类型的参数须要标注方向,可选的方向标识有:
in
:输入类型参数out
:输出类型参数inout
:输入输出类型参数接下来就是LibraryManagerService
这个服务类了,在编写服务类前要先编译项目,这样在服务类里使用 AIDL 生成的java类。
public class LibraryManagerService extends Service {
private static final String TAG = "LibraryManagerService";
// CopyOnWriteArrayList 支持并发读写
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
private Binder mBinder = new ILibraryManager.Stub() {
@Override
public List<Book> getNewBookList() throws RemoteException {
return mBookList;
}
@Override
public void donateBook(Book book) throws RemoteException {
mBookList.add(book);
}
};
public LibraryManagerService() {
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book("book0"));
mBookList.add(new Book("book1"));
}
}
复制代码
首先经过ILibraryManager.Stub()
建立一个mBinder
对象,并实现了ILibraryManager.aidl
中定义的接口方法,在onBind()
方法中返回建立的mBinder
,并在服务onCreate()
时添加两本书。 最后在 AndroidManifest.xml 注册服务:
<service android:name=".LibraryManagerService" android:process=":aidl_remote">
</service>
复制代码
到这里服务端的基本功能就完成了。
这里先把客户端和服务端放在同一个应用,客户端的实现相对简单些,首先在项目 app 的build.gradle
指定 AIDL
文件路径:
android {
......
// 指定 aidl 路径
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}
}
复制代码
以后就是绑定服务了:
public class AIDLActivity extends AppCompatActivity {
private static final String TAG = "AIDLActivity";
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ILibraryManager libraryManager = ILibraryManager.Stub.asInterface(service);
try {
// 近期新书查询
List<Book> books = libraryManager.getNewBookList();
Log.e(TAG, "books:" + books.toString());
// 捐赠一本书
libraryManager.donateBook(new Book("book" + books.size()));
List<Book> books2 = libraryManager.getNewBookList();
Log.e(TAG, "books:" + books2.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
bindNewService();
}
private void bindNewService() {
Intent intent = new Intent(AIDLActivity.this, LibraryManagerService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mServiceConnection);
super.onDestroy();
}
}
复制代码
先实现ServiceConnection
接口,在onServiceConnected()
方法中将IBinder
对象转换成ILibraryManager
对象,经过该对象就能调用ILibraryManager.aidl
中声明的方法了。 接下来绑定服务:
Intent intent = new Intent(AIDLActivity.this, LibraryManagerService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
复制代码
运行项目后观察log:
若是客户端和服务端在不一样的应用怎么实现呢?首先将服务端的 AIDL 包复制到客户端项目的main
目录下,在客户端项目 app 的build.gradle
指定 AIDL
文件路径,最后就是绑定服务了,因为客户端须要隐式绑定服务,因此要先修改服务端LibraryManagerService
的注册方式为:
<service android:name=".LibraryManagerService" android:process=":aidl_remote">
<intent-filter>
<action android:name="android.intent.action.LibraryManagerService" />
</intent-filter>
</service>
复制代码
而后就是在客户端隐式启动服务:
Intent intent = new Intent();
intent.setAction("android.intent.action.LibraryManagerService");
intent.setPackage("com.shh.ipctest");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
复制代码
若是要添加一个新书提醒功能,即图书馆每采购一本新书后须要通知订阅了新书提醒功能的用户,要怎么修改服务端和客户端呢?
首先建立一个服务端通知客户端的 IOnNewBookArrivedListener.aidl
接口:
package com.shh.ipctest;
import com.shh.ipctest.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book book);
}
复制代码
咱们约定服务端要先注册后才能收到通知,同时也能够取消注册,因此要给以前的ILibraryManager.aidl
添加连个方法了:
package com.shh.ipctest;
import com.shh.ipctest.Book;
import com.shh.ipctest.IOnNewBookArrivedListener;
interface ILibraryManager{
......
// 注册通知
void register(IOnNewBookArrivedListener listener);
// 取消注册
void unregister(IOnNewBookArrivedListener listener);
}
复制代码
接下来就是修改服务端的LibraryManagerService
:
// 只保留了相关核心代码
public class LibraryManagerService extends Service {
......
// 系统提供的专门用于保存、删除跨进程 listener 的类
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
// AtomicBoolean 支持并发读写
private AtomicBoolean mIsServiceDestroy = new AtomicBoolean(false);
private Binder mBinder = new ILibraryManager.Stub() {
......
@Override
public void register(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.register(listener);
Log.e(TAG, "register success");
}
@Override
public void unregister(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.unregister(listener);
Log.e(TAG, "unregister success");
}
};
.......
@Override
public void onCreate() {
super.onCreate();
......
// 在子线程中每隔3秒建立一本新书,并通知全部已注册的客户端
new Thread(new Runnable() {
@Override
public void run() {
// 若是服务还没终止
while (!mIsServiceDestroy.get()) {
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Book book = new Book("book" + mBookList.size());
mBookList.add(book);
bookArrivedNotify(book);
}
}
}).start();
}
private void bookArrivedNotify(Book book) {
int n = mListenerList.beginBroadcast();
for (int i = 0; i < n; i++) {
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if (listener != null) {
try {
listener.onNewBookArrived(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
mListenerList.finishBroadcast();
}
@Override
public void onDestroy() {
super.onDestroy();
mIsServiceDestroy.set(true);
}
}
复制代码
注意这里用到了RemoteCallbackList
类,它是系统提供的专门用于删除跨进程 listener 的类,用普通的集合难以保证客户端注册的 listener 和服务端存储的 listener 是同一个,会取消注册失败。在的register()
、unregister()
中实现了通知接口的绑定和解绑操做。在onCreate()
周期性的通知客户端有新书了。
在客户端中须要完成通知接口的注册和取消注册:
// 只保留了相关核心代码
public class AIDLActivity extends AppCompatActivity {
......
private ILibraryManager mLibraryManager;
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_NEW_BOOK_ARRIVED:
Log.e(TAG, "new book:" + msg.obj);
break;
}
return true;
}
});
private IOnNewBookArrivedListener listener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book book) throws RemoteException {
// 因为 onNewBookArrived 方法在子线程被调用,因此经过Handler切换到UI线程,方便UI操做
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
}
};
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ILibraryManager libraryManager = ILibraryManager.Stub.asInterface(service);
mLibraryManager = libraryManager;
try {
......
// 注册通知
libraryManager.register(listener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
......
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
bindNewService();
}
@Override
protected void onDestroy() {
unbindService(mServiceConnection);
if (mLibraryManager != null && mLibraryManager.asBinder().isBinderAlive()) {
try {
// 取消注册
mLibraryManager.unregister(listener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
}
复制代码
首先是建立IOnNewBookArrivedListener
接口的对象,为了方便 UI 操做能够在回调中用Handler
将服务端通知的结果切换到 UI 线程,并打印新书名,在onServiceConnected()
中注册接口,在onDestroy()
中取消注册。运行项目后看下客户端、服务端的log:
目前咱们的服务端任何客户端均可以链接,项目开发中为了安全起见,咱们须要在服务端作权限校验,只有客户端配置了指定的权限,才能调用服务端的方法。
服务端可使用permission
+包名
的方式来验证,首先在服务端的AndroidManifest.xml声明一个permission
:
<permission android:name="com.shh.ipctest.permission.ACCESS_LIBRARY_SERVICE" android:protectionLevel="normal" />
复制代码
接下来就是在LibraryManagerService
中校验要链接的客户端是配置了该permission
,以及包名是否符合指定规则,能够作校验的地方有两个,第一个是在onBind()
中校验:
@Override
public IBinder onBind(Intent intent) {
if (!passBindCheck()) {
Log.e(TAG, "bind denied");
return null;
}
return mBinder;
}
private boolean permissionCheck() {
// 客户端是否已申请了指定权限
int check = checkCallingOrSelfPermission("com.shh.ipctest.permission.ACCESS_LIBRARY_SERVICE");
if (check == PackageManager.PERMISSION_DENIED) {
return false;
}
// 检验客户端包名会否以com.shh开头
String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0 && !packages[0].startsWith("com.shh")) {
return false;
}
return true;
}
复制代码
注意,onBind()
是在客户端链接服务端时调用,若是客户端不能在此处经过校验则无发链接到服务。若是客户端和服务端是两个应用,则没法在onBind()
实现校验的功能!
第二个地方是在ILibraryManager.Stub
类的onTransact()
方法中,在该方法里校验解决了客户端和服务端是两个应用时没法在onBind()
实现校验的问题,代码以下:
private Binder mBinder = new ILibraryManager.Stub() {
......
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
if (!permissionCheck()) {
Log.e(TAG, "bind denied");
return false;
}
return super.onTransact(code, data, reply, flags);
}
};
复制代码
注意,该方法在onBind()
以后执行,意味着客户端和服务端已经链接,当客户端调用服务端的方法时会走onTransact()
方法。
最后须要在客户端的AndroidManifest.xml配置permission
权限:
<uses-permission android:name="com.shh.ipctest.permission.ACCESS_LIBRARY_SERVICE" />
复制代码
服务端进程可能会因为内存不足等缘由意外终止而致使服务被杀死,因此有必要在这种状况下从新链接到服务。链接的方式有两种:
第一种相对简单,是在ServiceConnection
接口的onServiceDisconnected()
方法中从新链接服务,注意该方法在UI线程执行。
第二种是给客户端获得的Binder
对象注册一个DeathRecipient
监听,首先来建立该监听:
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mLibraryManager != null) {
mLibraryManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mLibraryManager = null;
// 从新链接服务
bindNewService();
}
}
}
复制代码
当服务被杀死时binderDied()
方法会被调用,接下来就是在服务链接成功后设置死亡监听:
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ILibraryManager libraryManager = ILibraryManager.Stub.asInterface(service);
mLibraryManager = libraryManager;
try {
// 将mLibraryManager转成Binder对象而后注册死亡监听
mLibraryManager.asBinder().linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
......
}
复制代码
在 AIDL 的例子中,客户端之因此能和服务端通讯,主要依靠系统提供的Binder
类实现,它实现了IBinder
接口,咱们在前边的例子已经用到了Binder
类,例如在服务端的 Service 中建立了一个Binder
对象并在onBind()
中返回:
Binder mBinder = new ILibraryManager.Stub(){
......
}
复制代码
当在客户端绑定服务成功后,会获得服务端返回的Binder
对象,并将其转换成可调用服务端暴露的方法的ILibraryManager
对象:
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ILibraryManager libraryManager = ILibraryManager.Stub.asInterface(service);
.......
}
......
};
复制代码
能够发现不论是客户端仍是服务端都用到了ILibraryManager.java
类,回想一下以前咱们建立过一个ILibraryManager.aidl
文件,因此ILibraryManager.java
应该是ILibraryManager.aidl
在编译后生成的,果真在以下目录有这个类:
ILibraryManager.java
的代码以下:
public interface ILibraryManager extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.shh.ipctest.ILibraryManager {
// Binder 的惟一标识
private static final java.lang.String DESCRIPTOR = "com.shh.ipctest.ILibraryManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
// 将服务端的 Binder 对象转换成客户端须要的接口对象
// 若是客户端和服务端在同一进程则返回 Stub 对象自己,不然返回Stub.Proxy这个代理对象
public static com.shh.ipctest.ILibraryManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.shh.ipctest.ILibraryManager))) {
return ((com.shh.ipctest.ILibraryManager) iin);
}
return new com.shh.ipctest.ILibraryManager.Stub.Proxy(obj);
}
// 返回当前 Binder 对象
@Override
public android.os.IBinder asBinder() {
return this;
}
/** * 运行在服务端的 Binder 线程池,客户端发起的跨进程方法调用最终会交给该方法处理 * @param code 肯定客户端调用的是哪一个方法 * @param data 保存要调用的方法须要的参数 * @param reply 保存要调用的方法最后的返回值 * @param flags * @return 返回false调用 * @throws android.os.RemoteException */
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getNewBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.shh.ipctest.Book> _result = this.getNewBookList();
// 封装返回的数据
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_donateBook: {
data.enforceInterface(DESCRIPTOR);
com.shh.ipctest.Book _arg0;
if ((0 != data.readInt())) {
// 反序列化出具体的请求参数
_arg0 = com.shh.ipctest.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.donateBook(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_register: {
data.enforceInterface(DESCRIPTOR);
com.shh.ipctest.IOnNewBookArrivedListener _arg0;
_arg0 = com.shh.ipctest.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
this.register(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_unregister: {
data.enforceInterface(DESCRIPTOR);
com.shh.ipctest.IOnNewBookArrivedListener _arg0;
_arg0 = com.shh.ipctest.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
this.unregister(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
// 客户端跨进程调用服务端方法时,会先在客户端执行该类里的对应方法,
// 调用mRemote.transact()发起远程过程调用(RPC)请求,同时客户端对应的线程会挂起,
// 进而执行服务端的onTransact()方法,直到 RPC 请求结束,客户端线程继续执行,
// 这多是一个耗时的过程,因此要避免客户端出现 ANR 的问题。
private static class Proxy implements com.shh.ipctest.ILibraryManager {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.util.List<com.shh.ipctest.Book> getNewBookList() throws android.os.RemoteException {
// 保存请求参数的 Parcel 对象
android.os.Parcel _data = android.os.Parcel.obtain();
// 保存返回结果的 Parcel 对象
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.shh.ipctest.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
// 发起 RPC 请求
mRemote.transact(Stub.TRANSACTION_getNewBookList, _data, _reply, 0);
_reply.readException();
// 从 RPC 请求的结果中取出最终要返回的数据
_result = _reply.createTypedArrayList(com.shh.ipctest.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void donateBook(com.shh.ipctest.Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
// 将参数序列化保存
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_donateBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void register(com.shh.ipctest.IOnNewBookArrivedListener listener) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null)));
mRemote.transact(Stub.TRANSACTION_register, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void unregister(com.shh.ipctest.IOnNewBookArrivedListener listener) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null)));
mRemote.transact(Stub.TRANSACTION_unregister, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
// 客户端可调用的方法的对应 code
static final int TRANSACTION_getNewBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_donateBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_register = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_unregister = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
}
public java.util.List<com.shh.ipctest.Book> getNewBookList() throws android.os.RemoteException;
public void donateBook(com.shh.ipctest.Book book) throws android.os.RemoteException;
public void register(com.shh.ipctest.IOnNewBookArrivedListener listener) throws android.os.RemoteException;
public void unregister(com.shh.ipctest.IOnNewBookArrivedListener listener) throws android.os.RemoteException;
}
复制代码
ILibraryManager
类继承android.os.IInterface
接口,其实 aidl类型文件中定义的接口在编译后都会生成一个继承IInterface
的java类。这个类看起来有点复杂,但结构仍是很清晰的,主要有两部分,首先是继承了Binder
的Stub
类,还有ILibraryManager.aidl
中接口声明的方法。Stub
类中关键部分都作了注释,其实它的核心就是将客户端进程中发起的跨进程方法调用转换到服务端进程去执行,最终获得执行结果!因此直观的看,在 Service 中Binder
是做为跨进程通讯的桥梁存在的,在此基础上,咱们才能更好的理解使用 AIDL 背后的原理!
Messenger
是一种轻量级的多进程通讯方式,它是在 AIDL 的基础上封装而成的,能够看作是 AIDL 的简化版,支持一对多的串行实时通讯,一次只处理一个请求,不存在并发的问题。和 AIDL 的使用相似,但要简单的多,一样须要实现服务端和客户端。
首先来看服务端,功能就是接收客户端发送的消息,同时回复一条消息:
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
// 将Messenger和Handler关联起来
private Messenger mServiceMessenger = new Messenger(new MessengerHandler());
public MessengerService() {
}
@Override
public IBinder onBind(Intent intent) {
return mServiceMessenger.getBinder();
}
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MessengerActivity.MESSAGE_FROM_CLIENT:
// 打印接收到的客户端消息
Log.e(TAG, "receive message from client:" + msg.getData().getString("msg"));
// 给客户端回复一条消息
Messenger clientMessenger = msg.replyTo;
Message message = Message.obtain();
message.what = MessengerActivity.MESSAGE_FROM_SERVICE;
Bundle bundle = new Bundle();
bundle.putString("msg", "I am fine,thank you!");
message.setData(bundle);
try {
clientMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
}
复制代码
首先建立一个Handler
对象,并在其handleMessage()
中进行消息的接收和回复,注意回复消息是经过客户端传递过来的Messenger
对象发送一个Message
对象,有了Handler
对象后,须要把它和服务端建立的Messenger
关联起来:
Messenger mServiceMessenger = new Messenger(new MessengerHandler());
复制代码
并在onBind()
中返回服务端Messenger
包含的Binder
对象。
下来看客户端的实现,和服务端链接成功后,给服务端发送一条消息,并接收服务端回复的消息:
public class MessengerActivity extends AppCompatActivity {
private static final String TAG = "MessengerActivity";
public static final int MESSAGE_FROM_CLIENT = 1;
public static final int MESSAGE_FROM_SERVICE = 2;
private Messenger mServiceMessenger;
private Messenger mClientMessenger = new Messenger(new MessengerHandler());
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mServiceMessenger = new Messenger(service);
Message message = Message.obtain();
message.what = MESSAGE_FROM_CLIENT;
Bundle bundle = new Bundle();
bundle.putString("msg", "how are you?");
message.setData(bundle);
// 传递服务端回复客户端时须要使用的Messenger
message.replyTo = mClientMessenger;
try {
mServiceMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
Intent intent = new Intent(MessengerActivity.this, MessengerService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mServiceConnection);
super.onDestroy();
}
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MessengerActivity.MESSAGE_FROM_SERVICE:
Log.e(TAG, "receive message from service:" + msg.getData().getString("msg"));
break;
}
}
}
}
复制代码
在onServiceConnected()
中,将服务端的Binder
转换成服务端的Messenger
对象,而后发送消息,因为服务端还须要给客服端回复消息,因此须要在客户端建立一个Messenger
对象附加在消息上发送给服务端使用。
在Messenger
中也能够进行权限校验、服务端终止从新链接的操做,实现了 AIDL 的相似。
最后看下效果:
文中内容参考了《Android 开发艺术探索》,但愿对你我有所帮助吧! 源码地址:github.com/Othershe/IP…