安卓 IPC 跨进程通信有很多种方式,我们可以用 Bundle传递数据,通过 Intent 意图去打电话,在 Uri 里面传递电话号码
//手动拨号将Intent.ACTION_CALL改为Intent.ACTION_DIALIntent intent = new Intent(Intent.ACTION_CALL);Uri data = Uri.parse("tel:" + phoneNum);intent.setData(data);startActivity(intent);
ContentProvider 通过数据共享来获取另一个应用的数据,Socket 也可以用来进程通信,Messenger 支持一对多的串行实时通信, Linux 下特有的进程通信方式 pipe 。ContentProvider 、 Socket 以及 pipe 都没有一套严格的权限管理,以及他们需要拷贝内存两次,Messenger 是简化版的 AIDL,AIDL 只需要拷贝一次内存,并且对访问的进程做了严格的管理控制。如果我们在 AIDL 中需要传递自定义数据类型,那就必须有一个实现序列化的实体类,基本数据类型除了 Short 外,AIDL 都支持,List、Map也同样支持,前提是他们装载的元素是 AIDL 支持的。
1、首先编写我们的 aidl 实体类
2、Book 实现序列化
package demo.rzj.com.androiddemo.aidl;import android.os.Parcel;
import android.os.Parcelable;public class Book implements Parcelable {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public Book() {}protected Book(Parcel in) {name = in.readString();}public static final Creator<Book> CREATOR = new Creator<Book>() {@Overridepublic Book createFromParcel(Parcel in) {return new Book(in);}@Overridepublic Book[] newArray(int size) {return new Book[size];}};@Overridepublic int describeContents() {return 0;}public String readFromParcel(Parcel parcel){return parcel.readString();}@Overridepublic void writeToParcel(Parcel parcel, int i) {parcel.writeString(name);}
}
3、编写实体类 Book的 AIDL 映射
Book.aidl 的实现
// Book.aidl
package demo.rzj.com.androiddemo.aidl;// Declare any non-default types here with import statements
parcelable Book;
然后编写 AIDL 的通信方法
// IBookManager.aidl
package demo.rzj.com.androiddemo;
import demo.rzj.com.androiddemo.aidl.Book;interface IBookManager {Book getMyBook();void addBookInOut(inout Book book);void addBookIn(in Book book);void addBookOut(out Book book);
这里注意一点,即使是同一个包名,也必须手动 import aidl 的映射
然后我们再手动的 build project,切换到 project 模式可以看到 Android Studio 会自动生成一个实际的跨进程通信 AIDL 实现类
4、Service 的实现
public class DemoIpcService extends Service {private Book mBook;private String processId;private IBinder binder = new IBookManager.Stub() {@Overridepublic Book getMyBook() throws RemoteException {return mBook;}@Overridepublic void addBookInOut(Book book) throws RemoteException {if(null != book && !TextUtils.isEmpty(book.getName())){Log.e("addBookInOut", book.getName());}book.setName("addBookInOut");mBook = book;}@Overridepublic void addBookIn(Book book) throws RemoteException {if(null != book && !TextUtils.isEmpty(book.getName())){Log.e("addBookIn", book.getName());}book.setName("addBookIn");mBook = book;}@Overridepublic void addBookOut(Book book) throws RemoteException {if(null == book || TextUtils.isEmpty(book.getName())){Log.e("addBookOut", "is null");}String str = null;if(null == book.getName()){str = "book name is null";}book.setName(str + "addBookOut");mBook = book;}};@Nullable@Overridepublic IBinder onBind(Intent intent) {processId = String.valueOf(android.os.Process.myPid());return binder;}
}
这里我偷懒了一下,没有新建两个安卓工程,只是把这个 Service 设置到另一个进程运行,如果是两个应用要通信,只需要把相关的实体类和 AIDL 映射及通讯的 AIDL 拷贝就行,切记服务端和客户端实体类和 AIDL 包名路径要保持一致,就是说客户端的 Book 实体类包名路径是 demo.rzj.com.androiddemo.aidl ,那么服务端也必须是这个包名路径,这里简单说一下 AIDL 定向 Tag 的作用。
in 表示客户端传递数据给服务端,然后就不管了,没有声明默认也是这种
out 表示即使客户端传递了一个非空的对象给服务端,服务端依然会自己实例化一个空对象(里面的参数没有值而已)
inout 这个修饰符有争议,很多博客上写当服务端修改客户端传递的数据后会把这个修改同步到移动端,但是经过我的反复测试,数据是不会同步的,查阅了安卓官方文档,里面也没有对这三种定向 Tag 的具体用法和含义做具体的说明,不过一般用 in 就行了