安卓艺术开发学习笔记之IPC机制
2017-12-24 23:03
603 查看
IPC简介:
IPC是Inter-Process-Communication的缩写,含义为进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。什么是进程,什么是线程呢?
按照操作系统说的,线程是CPU调度的最小单元,同时线程是一种有限的系统资源。
进程一般指一个执行单元,在PC和移动设备上是一个程序或者一个应用。一个进程可以包含多个线程,一个进程至少包含一个线性,即主线程,在android中即UI线程
很多时候,当我们需要处理很多复杂的操作的时候,如果在主线程中执行这种大量耗时的任务,就会造成界面无法响应,在android中叫ANR(Application Not Responding)
android中的多进程模式
如何开启多进程模式呢,android中只有一种方式,就是给四大组件(Activity,Service,Recevier,ContentProvider)在AndroidManifest中指定android:process属性(还有就是通过JNI在native层去fork一个新进程)<activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".SecondActivity" android:configChanges="screenLayout" android:label="@string/app_name" android:process=":remote" > </activity> <activity android:name=".ThirdActivity" android:configChanges="screenLayout" android:label="@string/app_name" android:process="com.example.asus1.androidprmoteflighting.remote" > </activity>
依次再声明两个Activity,然后指定它的android:process属性,然后依次启动这三个Activity,当SecondActivity启动的时候,系统会为它创建一个进程,名字为
com.example.asus1.androidprmoteflighting:remote,当ThirdActivity启动的时候,系统也会为它创建一个单独的进程,名字为
com.example.asus1.androidprmoteflighting.remote,MainActivity是程序的入口,它运行在默认的进程中,默认进程为包名
图中每个进程都后面都是它的id,且都不相同
可以看到SecondActivity和ThirdActivity都的process的属性值不同,
对于:remote,“:”的含义是指在当前的进程名前面附上当前的包名,这是一种简写的方式,且以这种方式开头的进程属于当前应用的私有进程,其他应用组件不可以和它跑在同一个进程中。ThridActivity中process的属性值,是一种完整的命名方式,不会附加包名信息,属于全局进程,其他应用通过ShareUID方式可以和它跑在一个同一个进程中
Android系统会为每一个应用分配一个唯一deUID,具有相同的UID的应用才能共享数据。
为什么需要IPC:
Android为每一个应用分配一个独立的虚拟机,或者说每个进程都分配了一个独立的虚拟机,不同虚拟机在内存分配上有不同的地址空间,这就导致在不同的虚拟机中访问同一个类的对象会产生多份副本。例如,在上面那个项目中,我另外声明一个类,类中有一个静态变量,值为1,在MAinActivity中改为2,然后启动SecondActivity,打印这个静态变量的值,还是为1。所以运行在不同进程中的四大组件,只要它们之间需要通过内存来共享数据,都会共享失败。
使用多进程会造成的问题:
静态成员和单例模式完成失效
线程同步机制完全失效
SharedPreference的可靠性下降
Application会多次创建
Binder
Binder是Android中的一个类,它实现lBinder接口,从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在Linux中没有,从Android Framewok角度来说,Binder是ServiceManager连接各种Manger(ActivityManager,WindowManager等)和香油ManagerService的桥梁,从Android医用层来说,Binder是客户端和福区段进行通信的媒介,当bindService的时候,服务端会犯规一个包含了服务端业务调用的Binder对象。通过这个Binder对象,客户端就可以和服务端进行交互Android开发中,Binder主要用于Service中,包括AIDL和Messenger,其中普通Service中的Binder不涉及进程间通信,而Mseenger的底层其实就是AIDL,用AIDL分析Bider的工作机制:
先新建一个java包,在这个java包中新建三个文件:Book.java,Book.aidl,IBokkManager.aidl
//Book.java public class Book implements Parcelable { public int bookId; public String bookName; public Book(int bookId, String bookName) { this.bookId = bookId; this.bookName = bookName; } private Book(Parcel in){ bookId = in.readInt(); bookName = in.readString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeInt(bookId); parcel.writeString(bookName); } public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>(){ @Override public Book createFromParcel(Parcel parcel) { return new Book(parcel); } @Override public Book[] newArray(int i) { return new Book[i]; } }; } // Book.aidl package com.example.asus1.androidprmoteflighting.AIDLTest; // Declare any non-default types here with import statements parcelable Book; // IBookManager.aidl package com.example.asus1.androidprmoteflighting.AIDLTest; // Declare any non-default types here with import statements import com.example.asus1.androidprmoteflighting.AIDLTest.Book; interface IBookManager { List<Book> getBookList(); void addBook(in Book book); }
Book.java是一个表示图书信息的类,它实现了Parcelable接口。Book.aidl是Book类在AIDL中的声明。IBookManager.aidl是我们定义的一个接口,里面有两个方法,getBookList和addBook。其中getBookList用于从远程服务器获取图书列表,addBook用于往图书列表中添加一本书。然后重新build一下,在generated目录下的com.example.asus1.androidprmoteflighting.AIDLTest包下系统会生成一个IBookManager.java类
package com.example.asus1.androidprmoteflighting.AIDLTest; public interface IBookManager extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.asus1.androidprmoteflighting.AIDLTest.IBookManager { private static final java.lang.String DESCRIPTOR = "com.example.asus1.androidprmoteflighting.AIDLTest.IBookManager"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.asus1.androidprmoteflighting.AIDLTest.IBookManager interface, * generating a proxy if needed. */ public static com.example.asus1.androidprmoteflighting.AIDLTest.IBookManager asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.example.asus1.androidprmoteflighting.AIDLTest.IBookManager))) { return ((com.example.asus1.androidprmoteflighting.AIDLTest.IBookManager)iin); } return new com.example.asus1.androidprmoteflighting.AIDLTest.IBookManager.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @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_getBookList: { data.enforceInterface(DESCRIPTOR); java.util.List<com.example.asus1.androidprmoteflighting.AIDLTest.Book> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBook: { data.enforceInterface(DESCRIPTOR); com.example.asus1.androidprmoteflighting.AIDLTest.Book _arg0; if ((0!=data.readInt())) { _arg0 = com.example.asus1.androidprmoteflighting.AIDLTest.Book.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addBook(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.example.asus1.androidprmoteflighting.AIDLTest.IBookManager { 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.example.asus1.androidprmoteflighting.AIDLTest.Book> getBookList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.example.asus1.androidprmoteflighting.AIDLTest.Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.example.asus1.androidprmoteflighting.AIDLTest.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addBook(com.example.asus1.androidprmoteflighting.AIDLTest.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_addBook, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public java.util.List<com.example.asus1.androidprmoteflighting.AIDLTest.Book> getBookList() throws android.os.RemoteException; public void addBook(com.example.asus1.androidprmoteflighting.AIDLTest.Book book) throws android.os.RemoteException; }
上面代码是系统自动生成的,这个是根据IBookManager.aidl系统自动生成的IBookManager.java这个类,它继承了IInterface这个接口,同时它自己也是个接口,所有可以在Binder中传输的接口都需要继承IIenterface接口。
它声明了在IBookManager.aidl中声明的方法,然后还声明了整形id分别用于标识这些方法,这些id用于标识在transact过程中客户端所请求的到底是哪个方法。接着,它声明了一个内部类Stub,这个Stub就是一个Binder类,当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的transact国展,当两者位于不同进程时,方法调用需要走transact过程,这个逻辑由Sub的内部代理类Proxy来完成。
DESCRIPTOR:Binder的唯一标识,一般用当前Binder的类名表示,本例中是com.example.asus1.androidprmoteflighting.AIDLTest.IBookManager
asInterface(android.os.IBinder.obj):用于将服务器的Binder对象转换成客户端所需要的AIDL接口类型的对象,这种转换过程是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务器的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象
asBinder:此方法用于返回当前Binder对象
onTransact:这个方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装交由此方法来处理。服务端通过code可以确定客户端所请求的目标方法是什么,接着从data中取出目标方法所需的参数(如果目标方法有参数的话),然后执行目标方法。当目标方法执行完毕后,就向replay中写入返回值(如果目标方法有返回值的话)。如果此方法返回false的话,那么客户端的请求会失败
Proxy#getBookList:这个方法运行在客户端,当客户端远程调用此方法时,它的内部实现是这样的:首先创建该方法所需要的输入类型Parcel对象_data,输出类型Parcel对象_replay和返回值对象List;然后把该方法的参数信息写入_data中(如果有参数的话),接着调用transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起,然后服务器的onTransact方法会被调用,直到RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果,最后返回_reply中的数据
Proxy#addBook:这个方法和上面是差不多的,只是它没有返回值,所以不需要从_reply中取出返回值
当客户端发起远程请求的时候,当前线程会被挂起直到服务器进程返回数据
Binder中提供了两个配对的方法linkToDeath和unlinkToDeath,通过linkToDeath可以给Binder设置一个死亡代理DeathRecipient对象,它是一个接口,只有一个内部方法binderDied,当Binder死亡的时候,系统就会回调binderDied方法,然后就可以在这个方法里面移除之前绑定的binder代理并重新绑定服务
Android中的IPC方式
Bundle四大组件中的三大组件(Activity,Service,Recevier)都是支持在Intent中传递的Bundle数据的,由于Bundle实现了Parcelabe接口,所以它可以方便地在不同的进程间传输
使用文件共享
两个进程通过读写同一个文件来交换数据,但是要注意并发读写的问题。
SharedPreferences是一个特例,SharedPreference是Android中提供的轻量级存储方案,它通过键值对的方式来存储数据,在底层实现上它采用XML文件来存储键值对,每个应用的SharedPreferences文件都可以在当前包所在的data目录下查看到,。从本质上来说,SharedPreferences也属于文件的一种,但是由于系统对它的读写有一定的缓存策略,即在内存汇总会有一份SharedPreferences文件的缓存,因此在多进程模式下,系统对它的读写变得不可靠,当面对高并发的读写访问,SharedPreferences有很大几率丢失数据,因此不建议在进程间通信中使用SharedPreferences
使用Messenger
Messenger可以翻译为信使,通过它可以在不同进程中传递Message对象,在Message中放入我们需要传递的数据,就可以轻松实现数据的进程间传递了。Messenger是一种轻量级的IPC方案,它的底层实现是AIDL。它一次处理一个请求
//它的构造方法:表明它是基于AIDL的 public Messenger(Handler target) { mTarget = target.getIMessenger(); } public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); }
实现Messenger:
1. 服务端进程:
在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler并通过它来创建一个Messenger对象,然后在Service的obBind中返回这个Messenger对象底层的Binder即可
2. 客户端进程:
客户端进程中,首先绑定服务端的Service,绑定成功后用服务器返回的IBinder对象和藏家一个Messnger,通过这个Messenger就可以向福区段发送消息了,发消息类型为Message对象。如果服务端需要回复客户端,也需要创建一个Handle对象
客户端代码:
public class MessengerService extends Service { private static final String TAG = "MessengerService"; private static class MessengerHandler extends Handler{ @Override public void handleMessage(Message msg) { switch (msg.what){ case 1: Log.d(TAG,"receive msg from Cilent:"+msg.getData().getString("msg")); Messenger client = msg.replyTo; Message replyMessage = Message.obtain(null,2); Bundle bundle = new Bundle(); bundle.putString("reply","accept it"); replyMessage.setData(bundle); try { client.send(replyMessage); }catch (RemoteException e){ e.printStackTrace(); } break; default: super.handleMessage(msg); } } } private final Messenger mMessenger = new Messenger(new MessengerHandler()); public MessengerService() { Log.d(TAG,"MessengerServiceCreated"); } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. return mMessenger.getBinder(); } }
在服务端通过Message的replyTo得到客户端的Messenger
MessengerHandler用来处理可客户端发送的消息,并从消息中取出客户端发来的文本信息。Messenger的作用是将客户端发送的消息传递MessengerHandler处理
在Manifest中让Service运行在单独的进程中
客户端代码:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MessengerActivity"; private Messenger mService; private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { mService = new Messenger(iBinder); Message msg = Message.obtain(null,1); Bundle data = new Bundle(); data.putString("msg","hello,this is client."); msg.setData(data); msg.replyTo = messenger; try { mService.send(msg); }catch (RemoteException e){ e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName componentName) { } }; private Messenger messenger = new Messenger(new MessengerHandler()); private static class MessengerHandler extends Handler{ @Override public void handleMessage(Message msg) { switch (msg.what){ case 2: Log.d(TAG,msg.getData().getString("reply")); break; default: super.handleMessage(msg); } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(this,MessengerService.class); bindService(intent,mConnection,BIND_AUTO_CREATE); } }
在服务端被绑定调用onServiceConnected这个方法的时候,我们就可以得到Service的IBinder,然后得到Messenger,通过这个Messenger我们就可以在客户端给服务端发送消息
使用AIDL
1.服务端:
服务端首先要创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后爱Service中实现这个AIDL接口即可
2.客户端
首先需要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了
3.AIDL接口的创建:
创建一个后缀为AIDL的文件:
// IBookManager.aidl package com.example.asus1.androidprmoteflighting.AIDLTest; import com.example.asus1.androidprmoteflighting.AIDLTest.Book; interface IBookManager { List<Book> getBookList(); void addBook(in Book book); }
在AIDL文件中,并不是所有的数据类型都是可以使用的,AIDL文件支持的数据类型:
基本数据类型
String和CharSequence
List:只支持ArrayList,里面每个元素都必须能够被AIDL支持
Map:只支持HashMap,里面的每个元素都必须被AIDL支持,包括key和value
Parcelable:所有实现了Parcelable接口的对象
AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
其中自定义的Parcelable对象和AIDL对象必须要显示import进来,不管它们是否和当前的AIDL文件位于同一个包内。
还有需要注意,如果AIDL文件中用到了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件,并在其中声明它为Parelable类型
// Book.aidl package com.example.asus1.androidprmoteflighting.AIDLTest; parcelable Book;
除此之外,AIDL中除了基本数据类型,其他类型的参数必须标上方向:
in:表示输入型参数
out:表示输出型参数
inout:表示输入输出型参数
AIDL接口只支持方法,不支持声明静态常量
注意:AIDL的包结构在服务端和客户端要保持一致,否则运行会出错
4.远程服务端Service的实现
public class BookManagerService extends Service { private static final String TAG = "BMS"; private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>(); private Binder mBinder = new IBookManager.Stub() { @Override public List<Book> getBookList() throws RemoteException { return mBookList; } @Override public void addBook(Book book) throws RemoteException { mBookList.add(book); } }; public BookManagerService() { } @Override public void onCreate() { super.onCreate(); mBookList.add(new Book(1,"android")); mBookList.add(new Book(2,"Ios")); } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. return mBinder; } } <service android:name=".AIDLTest.BookManagerService" android:process=":remote" />
在上述代码中,我们创建一个Binder对象并在OnBind中返回上,这个对象继承自IBookManager.Stub并实现了它内部的AIDL方法。
这里采用了CopyOnWriteArrayList,它支持并发读写。因为AIDL方法是在服务端的Bind线程池汇总执行的,因此当多个客户端同时连接的时候,会存在多个线程同时访问的情形,所以我们要在AIDL方法中处理线程同步,所以这里直接使用CopyOnWriteArrayList来进行自动的线程同步。
虽然服务端返回的是CopyOnWriteArrayList,但是Binder中会按照List的规范去访问数据并最终形成一个新的ArrayList传递给客户端
5.客户端的实现:
public class BookManagerActivity extends AppCompatActivity { private static final String TAG = "BookManagerActivity"; private ServiceConnection mConnection = new ServiceConnection(){ @Override public void onServiceConnected(ComponentName name, IBinder service) { IBookManager manager = IBookManager.Stub.asInterface(service); try { List<Book> list = manager.getBookList(); Log.d(TAG,"list type: "+list.getClass().getCanonicalName()); Log.d(TAG,"book list: "+list.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_book_manager); Intent intent = new Intent(this,BookManagerService.class); bindService(intent,mConnection,BIND_AUTO_CREATE); } @Override protected void onDestroy() { unbindService(mConnection); super.onDestroy(); } }
在这个Activity中绑定成功后,通过manager去调用getBookList方法,然后打印信息。需要注意的是,服务端的方法有可能需要很久才能执行完毕,这个时候在下面的代码会导致ANR
BookManagerActivity: list type: java.util.ArrayList BookManagerActivity: book list: [com.example.asus1.androidprmoteflighting.AIDLTest.Book@8bf48e2, com.example.asus1.androidprmoteflighting.AIDLTest.Book@2731473]
如果想实现当有新书的到达的时候,通知用户有新书达到,那么我们可以另写一个接口,新建一个aidl文件
// IOnNewBookArrivedListener.aidl package com.example.asus1.androidprmoteflighting.AIDLTest; // Declare any non-default types here with import statements import com.example.asus1.androidprmoteflighting.AIDLTest.Book; interface IOnNewBookArrivedListener { void onNewBookArrived(in Book newBook); }
然后修改IBookManager.aidl
// IBookManager.aidl package com.example.asus1.androidprmoteflighting.AIDLTest; import com.example.asus1.androidprmoteflighting.AIDLTest.Book; import com.example.asus1.androidprmoteflighting.AIDLTest.IOnNewBookArrivedListener; interface IBookManager { List<Book> getBookList(); void addBook(in Book book); void registerListener(IOnNewBookArrivedListener listener); void unregisterListener(IOnNewBookArrivedListener listener); }
然后再修改BookManagerService
package com.example.asus1.androidprmoteflighting.AIDLTest; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import com.example.asus1.androidprmoteflighting.AIDLTest.Book; import com.example.asus1.androidprmoteflighting.AIDLTest.IBookManager; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; public class BookManagerService extends Service { private static final String TAG = "BMS"; private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false); private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>(); private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListeners = new CopyOnWriteArrayList<>(); private Binder mBinder = new IBookManager.Stub() { @Override public List<Book> getBookList() throws RemoteException { return mBookList; } @Override public void addBook(Book book) throws RemoteException { mBookList.add(book); } @Override public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException { if(!mListeners.contains(listener)){ mListeners.add(listener); } Log.d(TAG,"listener size :"+mListeners.size()); } @Override public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException { if(mListeners.contains(listener)){ mListeners.remove(listener); } Log.d(TAG,"listener size :"+mListeners.size()); } }; public BookManagerService() { } @Override public void onCreate() { super.onCreate(); mBookList.add(new Book(1,"android")); mBookList.add(new Book(2,"Ios")); new Thread(new ServiceWorker()).start(); } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. return mBinder; } private void onNewBookArrived(Book book) throws RemoteException{ mBookList.add(book); for(int i =0;i<mListeners.size();i++){ IOnNewBookArrivedListener listener = mListeners.get(i); listener.onNewBookArrived(book); } } private class ServiceWorker implements Runnable{ @Override public void run() { while (!mIsServiceDestoryed.get()){ try { Thread.sleep(5000); }catch (InterruptedException e){ e.printStackTrace(); } int bookId = mBookList.size()+1; Book book = new Book(bookId,"new Book#"+bookId); try { onNewBookArrived(book); }catch (RemoteException e){ e.printStackTrace(); } } } } }
然后在BookManagerActivity中改修一些代码:
public class BookManagerActivity extends AppCompatActivity { private static final String TAG = "BookManagerActivity"; private IBookManager mBookManager; private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 1: Log.d(TAG,"receive new book :"+msg.obj); break; default: super.handleMessage(msg); } } }; private ServiceConnection mConnection = new ServiceConnection(){ @Override public void onServiceConnected(ComponentName name, IBinder service) { IBookManager manager = IBookManager.Stub.asInterface(service); mBookManager = manager; try { List<Book> list = manager.getBookList(); Log.d(TAG,"list type: "+list.getClass().getCanonicalName()); Log.d(TAG,"book list: "+list.toString()); Book book = new Book(3,"web"); mBookManager.addBook(book); mBookManager.registerListener(mOnNewBookListener); }catch (RemoteException e){ e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { mOnNewBookListener = null; } }; private IOnNewBookArrivedListener mOnNewBookListener = new IOnNewBookArrivedListener.Stub() { @Override public void onNewBookArrived(Book newBook) throws RemoteException { mHandler.obtainMessage(1,newBook).sendToTarget(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_book_manager); Intent intent = new Intent(this,BookManagerService.class); bindService(intent,mConnection,BIND_AUTO_CREATE); } @Override protected void onDestroy() { if(mBookManager !=null &&mBookManager.asBinder().isBinderAlive()){ try { mBookManager.unregisterListener(mOnNewBookListener); }catch (RemoteException e){ e.printStackTrace(); } } unbindService(mConnection); super.onDestroy(); } }
但是有一个问题,就是在解注册的过程中,服务器无法找到我们之前注册的那个listener,因为Binder会把客户端传递过来对象重新转化并生成一个新的对象,虽然我们再注册和解注册的时候,用的是一个同一个客户端对象,但是通过Binder传递到服务器后,却会产生两个全新的对象。因为对象使不能跨进程直接传输的,对象的跨进程传输本质上都是反序列化的过程,这就是为什么AIDL中自定义对象都必须实现Parcelable接口的原因。
如果要成功的实现解注册,可以使用RemoteCallbackList
RemoteCallbackList是系统专门提供的用于删除跨进程listener的接口。RemoteCallbackList是一个泛型,支持管理任意的AIDL接口,这点从它的声明就可以看出,因为所有的AIDL接口都继承自IInterface接口
public class RemoteCallbackList<E extends IInterface>
它的内部有一个Map结构专门用来保存所有的AIDL回调,这个Map的key是IBinder类型,value是Callback类型
public class RemoteCallbackList<E extends IInterface> { /*package*/ ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>(); private Object[] mActiveBroadcast; private int mBroadcastCount = -1; private boolean mKilled = false;
其中Callback中封装了真正的远程listner,当客户端注册listener的时候,它会把这个listener的信息存入mCallbacks中
虽然说多次跨进程传输客户端的同一个对象会在服务端生成不同的对象,但是这些新生成的对象又一个共同点,那就是它们底层的Binder对象使同一个,利用这个特性,当客户端解注册的时候,只要遍历服务端所有的listener,找出那个和解注册listener具有相同Binder对象的服务端listener并把它删掉即可。
另外,当客户端进程终止后,它能自动移除客户端所注册的listener,而且他的内部自动实现了线程同步的功能,所以我们使用它来注册和解注册的时候,不需要做额外的线程同步工作。
private RemoteCallbackList<IOnNewBookArrivedListener> mListeners = new RemoteCallbackList<>(); @Override public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException { mListeners.register(listener); } @Override public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException { mListeners.unregister(listener); } private void onNewBookArrived(Book book) throws RemoteException{ mBookList.add(book); int n = mListeners.beginBroadcast(); for(int i =0;i<n;i++){ IOnNewBookArrivedListener listener = mListeners.getBroadcastItem(i); if (listener!=null){ listener.onNewBookArrived(book); } } mListeners.finishBroadcast(); }
AIDL中进行权限验证
1.在onBind中进行验证,验证不通过就直接返回null。比如使用permission验证,先在Mainfest中声明所需要的权限
然后在onBind中做权限验证:
2.可以在服务端的onTransact方法中进行权限验证
使用ContentProvider
ContentProvider是Android中提供的专门用于不同应用间进行数据共享的方式,和Messenger一样,ContentProvider的底层实现同样也是Binder
关于这个的使用,自己再看。。。。。
使用Socket
Socket也称套接字,是网络通信中的概念,它分为流式套接字和用户数据报套接字两种,分别对应网络的传输控制层中的TCP和UDP协议。
关于这个,可以想着之前写的聊天室。。。。
Binder连接池
AIDL使用流程:首先创建一个Service和一个AIDL接口,接着创建一个类继承自ADL接口汇总的Stub类并实现Stub中的抽象方法,在Service的onBind方法中返回这个类的对象,然后客户端就可以绑定服务端Service,建立连接后就可以访问远程服务端的方法了
当有大量业务模块需要用AIDL去进行进程间通信,不可能创建那么多个Service,针对上述问题,将所有的AIDL放在同一个Service中去管理
在这种模式下:整个工作机制是这样的:
每个业务模块创建自己的AIDL接口并实现此接口,这个时候不同业务模块之间是不能有耦合的,所有实现细节我们要单独开来,然后向服务端提供自己唯一标识和其对应的Binder对象:对于服务端开始,只需要一个Service就可以了,服务端提供一个queryBinder接口,这个接口能够根据业务模块的特征来返回相应的Binder对象给它们,不同的业务模块拿到所需的Binder对象后就可以进行远程方法调用了。由此可见,Binder连接池的主要作用就是将每个业务模块的Binder请求统一转发到远程Service中去执行,从而避免了重复创建Service的过程
举个例子:
假设现在有个两个业务逻辑,一个负责加密解密(ISecurityCenter),一个负责计算(ICompute),然后分别创建他们的aidl文件
// ISecurityCenter.aidl package com.example.asus1.androidprmoteflighting.AIDLTest; // Declare any non-default types here with import statements interface ISecurityCenter { String encrypt(String content); String decrypt(String password); } // ICompute.aidl package com.example.asus1.androidprmoteflighting.AIDLTest; // Declare any non-default types here with import statements interface ICompute { int add(int a,int b); }
然后再创建一个IBinderPool.aidl文件,用来查询Binder
// IBinderPool.aidl package com.example.asus1.androidprmoteflighting.AIDLTest; // Declare any non-default types here with import statements interface IBinderPool { IBinder queryBinder(int binderCode); }
然后我们知道,我们需要在服务器的onBind方法中返回IBinder,然后用它创建我们需要的接口,可是现在我们又两个接口,且我们需要都实现它的业务逻辑,因此我们新建两个java文件SecurityCenterImpl和ComputeImpl,分别继承它们的Stub
public class SecurityCenterImpl extends ISecurityCenter.Stub { private static final char SECRRT_CODE = '^'; @Override public String encrypt(String content) throws RemoteException { char[] chars = content.toCharArray(); for (int i =0;i<chars.length;i++){ chars[i] ^=SECRRT_CODE; } return new String(chars); } @Override public String decrypt(String password) throws RemoteException { return encrypt(password); } } public class ComputeImpl extends ICompute.Stub { @Override public int add(int a, int b) throws RemoteException { return a+b; } }
然后我们为了统一好看,再创建一个java文件,BinderPool,它负责服务器的连接和交互
public class BinderPool { private static final String TAG = "BinderPool"; public static final int BINDE_COMPUTE = 200; public static final int BINDE_SECRITY = 100; private Context mContext; private IBinderPool mBinderPool; private static volatile BinderPool sInstance; //为了控制线程,让service绑定成功后,才能进行其他操作 private CountDownLatch mConnectBinderPollCountDownLatacj; private BinderPool(Context context){ mContext = context.getApplicationContext(); connectBinderPoolSerivce(); } public static BinderPool getsInstance(Context context){ if(sInstance == null){ synchronized (BinderPool.class){ sInstance = new BinderPool(context); } } return sInstance; } private synchronized void connectBinderPoolSerivce(){ mConnectBinderPollCountDownLatacj = new CountDownLatch(1); Intent intent = new Intent(mContext,BinderPoolSerivce.class); mContext.bindService(intent,mBinderPoolConnection,Context.BIND_AUTO_CREATE); try { mConnectBinderPollCountDownLatacj.await(); //锁定当前线程 }catch (InterruptedException e){ e.printStackTrace(); } } public IBinder queryBinder(int binderCode){ IBinder binder = null; try { if(mBinderPool != null){ binder = mBinderPool.queryBinder(binderCode); } }catch (RemoteException e){ e.printStackTrace(); } return binder; } private ServiceConnection mBinderPoolConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mBinderPool = IBinderPool.Stub.asInterface(service);//得到BinderPool这个接口 try { mBinderPool.asBinder().linkToDeath(mBinderPollDeathRecipient,0);//设置死亡代理,当Binder死亡的时候,会调用binderDied方法 }catch (RemoteException e){ e.printStackTrace(); } //释放线程 mConnectBinderPollCountDownLatacj.countDown(); } @Override public void onServiceDisconnected(ComponentName name) { } }; private IBinder.DeathRecipient mBinderPollDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { mBinderPool.asBinder().unlinkToDeath(mBinderPollDeathRecipient,0);//解除死亡代理 mBinderPool = null; connectBinderPoolSerivce();//重新绑定 } }; //实现IBinderPool接口中的方法 public static class BinderPoolImpl extends IBinderPool.Stub{ @Override public IBinder queryBinder(int binderCode) throws RemoteException { IBinder binder = null; switch (binderCode){ case BINDE_SECRITY: binder = new SecurityCenterImpl(); break; case BINDE_COMPUTE: binder = new ComputeImpl(); break; } return binder; } } }
相关文章推荐
- android开发艺术探索笔记 IPC机制上
- 安卓艺术开发探索学习笔记之View的事件体系和工作原理
- Android开发艺术探索(研读笔记)——03-Android中的IPC机制(一)
- Android IPC机制学习(Android艺术开发探索)
- Android开发艺术探索笔记_第二章 IPC机制
- Android开发学习笔记2---HelloWord安卓程序结构简介
- 【Cocos2d-X开发学习笔记】第24期:事件处理机制之加速度传感器事件
- 《Spring 3.x 企业应用开发实战》学习笔记 第三章 IoC容器概述 3.2 相关Java基础知识 类装载器 反射机制
- Android IPC进程间通讯机制学习笔记
- (转载) 安卓开发学习笔记
- 学习笔记2-安卓开发环境搭建、创建虚拟机
- 安卓开发学习笔记
- 学习笔记2-安卓开发环境搭建、创建虚拟机
- ASP.Net MVC开发基础学习笔记:三、Razor视图引擎、控制器与路由机制学习
- Android应用开发学习笔记之多线程与Handler消息处理机制
- Android开发学习笔记4--安卓程序安装与启动过程剖析
- 《Spring 3.x 企业应用开发实战》学习笔记 第三章 IoC容器概述 3.2 相关Java基础知识 类装载器 反射机制
- Android应用开发学习笔记之多线程与Handler消息处理机制
- 【Cocos2d-X开发学习笔记】第23期:事件处理机制之按键事件
- Android开发学习笔记6--安卓程序调试方法