Android的IPC机制
由于自己搭建了博客最近都没有在csdn发过博客了,现在发一篇自己的学习笔记,具体内容参照:
Android的IPC机制
Inter-Process Communication => 进程间通信
在定义四大组件时添加如下属性:
android:process=":remote"android:process="io.github.grooters.remote"
:remote指当前app的包名.remote,为私有进程
由于开启多进程后,处于不同进程的组件会导致数据无法通信问题,故需要通过以下方式实现IPC
Serializable
实现Serializable接口的类可通过Intent和Binder进行传递
private static final long serialVersionUID = 42L;
该长整形静态常量用于辅助序列化与反序列化操作,通过识别该id可判断对象是否发生改变
eg:
public class Serializabler implement Serializable{ private static final long serialVersionUID = 42L; private int id; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; }}
传递对象:
bundle.putSerializable("key",serializabler);intent.putExtra("key",bundle);
写出/读入对象:
try { ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/test.text"))); outputStream.writeObject(serializabler);}catch (IOException e){ e.printStackTrace();}
Parceleable
需要重写四个方法:
内容描述,无描述返回0,有描述返回CONTENTS_FILE_DESCRIPTOR(1)
序列化方法
需要实现的接口,包括以下方法:
反序列化
创建一个Parceleable类型的新数组
eg:
package io.github.grooters.practicer;import android.os.Parcel;import android.os.Parcelable;/** * Create by 李林浪 in 2018/10/24 * Elegant Code... */public class Parceleabler implements Parcelable { private String name; private int id; private int sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getSex() { return sex; } public void setSex(int sex) { this.sex = sex; } public static final Parcelable.Creator<Parceleabler> CREATOR=new Parcelable.Creator<Parceleabler>(){ @Override public Parceleabler createFromParcel(Parcel source) { Parceleabler parceleabler=new Parceleabler(); //顺序要和序列化的write操作一样 parceleabler.setId(source.readInt()); parceleabler.setName(source.readString()); parceleabler.setSex(source.readInt()); return parceleabler; } @Override public Parceleabler[] newArray(int size) { return new Parceleabler[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); dest.writeString(name); dest.writeInt(sex); }}
传递和写出读入方法和Serializable方法相同
Binder
Android 接口定义语言 (AIDL)
新建一个AIDL文件系统会自动生成对应的java文件,eg:
IBookManager.aidl:
// IBookManager.aidlpackage io.github.grooters.practicer.BindeRer;import io.github.grooters.practicer.BindeRer.Book;// Declare any non-default types here with import statementsinterface IBookManager { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ List<Book> getBookList(); void addBook(in Book book);}
IBookManager.java:
/* * This file is auto-generated. DO NOT MODIFY. * Original file: D:\\Android\\Source\\Practicer\\app\\src\\main\\aidl\\io\\github\\grooters\\practicer\\BindeRer\\IBookManager.aidl */package io.github.grooters.practicer.BindeRer;// Declare any non-default types here with import statementspublic interface IBookManager extends android.os.IInterface {/** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements io.github.grooters.practicer.BindeRer.IBookManager { private static final java.lang.String DESCRIPTOR = "io.github.grooters.practicer.BindeRer.IBookManager"; /** Construct the stub at attach it to the interface. */ public Stub(){ this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an io.github.grooters.practicer.BindeRer.IBookManager interface, * generating a proxy if needed. */ public static io.github.grooters.practicer.BindeRer.IBookManager asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof io.github.grooters.practicer.BindeRer.IBookManager))) { return ((io.github.grooters.practicer.BindeRer.IBookManager)iin); } return new io.github.grooters.practicer.BindeRer.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<io.github.grooters.practicer.BindeRer.Book> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBook: { data.enforceInterface(DESCRIPTOR); io.github.grooters.practicer.BindeRer.Book _arg0; if ((0!=data.readInt())) { _arg0 = io.github.grooters.practicer.BindeRer.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 io.github.grooters.practicer.BindeRer.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; } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override public java.util.List<io.github.grooters.practicer.BindeRer.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<io.github.grooters.practicer.BindeRer.Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(io.github.grooters.practicer.BindeRer.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addBook(io.github.grooters.practicer.BindeRer.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<io.github.grooters.practicer.BindeRer.Book> getBookList() throws android.os.RemoteException; public void addBook(io.github.grooters.practicer.BindeRer.Book book) throws android.os.RemoteException;}
将服务端Binder对象转换成客户端所需的AIDL接口类型的对象,若客户端和服务端在不同进程,则返回的是Stub.Proxy :
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof io.github.grooters.practicer.BindeRer.IBookManager))) { return ((io.github.grooters.practicer.BindeRer.IBookManager)iin);}return new io.github.grooters.practicer.BindeRer.IBookManager.Stub.Proxy(obj);
返回当前的Binder对象(实现了IBinder接口)
运行在服务端的Binder线程池中,当跨进程访问时会通过code判断出使用哪个方法,当方法执行后,会将_result结果写入到reply中:
java.util.List<io.github.grooters.practicer.BindeRer.Book> _result = this.getBookList();reply.writeNoException();reply.writeTypedList(_result);return true;
当返回false时表示请求失败
方法中含有输入Paecel对象_data,输出Parcel对象_reply和返回值对象,其中方法参数会写入_data对象,然后调用transact方法发起RPC(远程过程调用)请求,此时该线程会挂起,直到onTransact中对应的方法执行并返回reply才会重写激活线程,此时可从_reply中获取对应的结果
Binder工作机制:
跨进程方式
Messenger
客户端:
Intent intent=new Intent(this,Servicer.class);bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);ServiceConnection serviceConnection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Messenger reply=new Messenger(new MessengerHandler()); Messenger messenger=new Messenger(service); Message msg=Message.obtain(); msg.replyTo=reply; try { messenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) {}};private static class MessengerHandler extends Handler{ @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.i(TAG,"handleMessage"); }}
创建一个绑定了Binder的Messenger对象用于发送消息,再创建一个绑定了MessengerHandler的Messenger对象并作为replyTo传过给服务端。
服务端:
public class Servicer extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } private static class MessagerHandler extends Handler{ @Override public void handleMessage(Message msg) { super.handleMessage(msg); Messenger client=msg.replyTo; Message message=Message.obtain(); try { client.send(message); } catch (RemoteException e) { e.printStackTrace(); } } } Messenger messenger=new Messenger(new MessagerHandler()); @Nullable @Override public IBinder onBind(Intent intent) { return messenger.getBinder(); } @Override public boolean onUnbind(Intent intent) { return super.onUnbind(intent); }}
服务端收到消息后从Message中取出客户端存入的replyTo即绑定了客户端Handler的Messenger对象,通过该对象服务端可以向客户端发送消息。
AIDL
服务端:
public class Servicer extends Service { IBinder iBookManager=new IBookManager.Stub() { @Override public List<Book> getBookList() throws RemoteException { Log.i(TAG,"getBookList()"); return null; } @Override public void addBook(Book book) throws RemoteException { Log.i(TAG,"book.id:"+book.getId()); } @Override public IBinder asBinder() { return this; } };}
实例化通过AIDL定义自动生成的Java类中的内部类IBookManager.Stub,实现提供给客户端调用的方法
客户端:
Intent intent=new Intent(this,Servicer.class);ServiceConnection serviceConnection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i(TAG,"onServiceConnected"); IBookManager bookManager=IBookManager.Stub.asInterface(service); try { final Book book=new Book(); book.setId(5); bookManager.addBook(book); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) {}};
通过调用IBookManager.Stub类中的asInterface方法传入服务端返回的IBinder对象获得IBookManager对象,通过该对象便可调用服务端方法
RemoteCallbackList
由于跨进程通讯,从客户端传到服务端的对象会被重建(序列化和反序列化),所以无法通过简单的对象判断来识别是否是同一个对象,针对某种情况需要注销对某个listener的监听(如监听者模式),该类就提供了解决方案
private RemoteCallbackList<BookArrivedListener> bookArrivedListeners=new RemoteCallbackList<>();@Overridepublic void registerListener(BookArrivedListener listener) throws RemoteException { bookArrivedListeners.register(listener);}@Overridepublic void unRegisterListener(BookArrivedListener listener) throws RemoteException { bookArrivedListeners.unregister(listener);}
该类为map结构实现对listnner的存储,通过IBinder作为key值,通过Callback保存listnner后作为value:
IBinder key=listner.asBinder()Callback value=new Callback(listener,cookies)
权限验证
服务端:
<permission android:name="io.github.grooters.practicer.BindeRer.ACCESS_BOOK" android:protectionLevel="normal"/>
@Nullable@Overridepublic IBinder onBind(Intent intent) { Log.i(TAG,"onBind"); int check=checkCallingOrSelfPermission("io.github.grooters.practicer.BindeRer.ACCESS_BOOK"); if(check==PackageManager.PERMISSION_DENIED){ Log.i(TAG,"check==PackageManager.PERMISSION_DENIED"); return null; } return iBookManager;}
checkCallingOrSelfPermission判断是否具有某项权限,有返回0,没有返回1
PackageManager.PERMISSION_DENIED指不具备该权限
PackageManager.PERMISSION_GRANTED指具备该权限
客户端:
<uses-permission android:name="io.github.grooters.practicer.BindeRer.ACCESS_BOOK"/>
服务端:
@Overridepublic boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { int check = checkCallingPermission("io.github.grooters.practicer.BindeRer.ACCESS_BOOK"); if(check == PackageManager.PERMISSION_DENIED){ return false; } return super.onTransact(code, data, reply, flags);}
深入了解Binder可参看这篇文章:
Android跨进程通信:图文详解 Binder机制 原理
ContentProvider
具体参考内容提供器的使用
更多相关文章
- Android中一个Activity多个intent-filter的调用方法
- Android模拟SD卡实现方法解析
- Android编程实现屏幕自适应方向尺寸与分辨率的方法
- Android Studio导入Project的方法
- Android使用AudioRecord遇到的问题与解决方法
- struts2服务端与android交互
- 另类方法屏蔽Android4.03的HOME按键
- android SDK更新方法总结
- android设置Activity背景色为透明的2种方法