由于自己搭建了博客最近都没有在csdn发过博客了,现在发一篇自己的学习笔记,具体内容参照:
Android的IPC机制
Inter-Process Communication => 进程间通信
在定义四大组件时添加如下属性:
android:process=":remote"
android:process="io.github.grooters.remote"
:remote指当前app的包名.remote,为私有进程
由于开启多进程后,处于不同进程的组件会导致数据无法通信问题,故需要通过以下方式实现IPC
实现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();
}
需要重写四个方法:
内容描述,无描述返回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方法相同
新建一个AIDL文件系统会自动生成对应的java文件,eg:
IBookManager.aidl:
// IBookManager.aidl
package io.github.grooters.practicer.BindeRer;
import io.github.grooters.practicer.BindeRer.Book;
// Declare any non-default types here with import statements
interface 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 statements
public 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工作机制:
客户端:
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对象,通过该对象服务端可以向客户端发送消息。
服务端:
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<>();
@Override
public void registerListener(BookArrivedListener listener) throws RemoteException {
bookArrivedListeners.register(listener);
}
@Override
public 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
@Override
public 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"/>
服务端:
@Override
public 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机制 原理
具体参考内容提供器的使用