最近将之前工作做本地的学习笔记上传一下
这里是Android艺术开发探索的前三章内容
文章目录
所谓的IPC,是Inter-Process Communication的缩写,含义为在进程之间的数据交互。常见的方案有管道、命名管道、共享内存、消息队列、信号量。android中常见的方案是binder、socket。
1. android的多进程模式
可以通过给四大组件指定android:process来开启多进程模式。如下所示,设置可以通过 ":xxx"的方式,此时会自动创建一个名为"包名+:remote"的进程名,也直接输入自定义进程名,如果不设置该值,默认跑在进程名为包名的进程中。
此外通过:创建的进程属于当前应用的私有进程,而不通过:创建的进程属于全局进程,其他应用可以通过ShareUID方式可以和它跑在同一个进程中。
android:process=":remote"
但是会带来一个问题。由于 每创建一个进程,系统会为他分配一个单独的虚拟机,不同虚拟机有不同的内存空间。这导致当两个进程访问同一个对象时,会创建两个该对象的副本,会导致共享数据失败。
public class User{
public static int age=1;
}
比如现有静态成员变量age=1,如果在一个进程中将age+1,那么按照正常的逻辑,静态成员变量属于类共有的,所有的User中age都相同。但是在另一个进程中age还是等于1,这说明同时存在了两个age副本,它们各自独立互不干扰。
这样多进程模式下通常会导致以下问题:
(1)静态成员和单例模式完全失效
这一点已经在上面介绍过了。
(2)线程同步机制完全失效
这一点由于是不同的内存空间,因此不管是锁对象还是锁全局类,都无法保证线程同步,因为锁的不是一个东西。这边进程里的类锁了,但是那边进程的类独立并没有被上锁。
(3)SharePreference的可靠性下降
这一点是因为SharePreference不允许两个进程同时进行读写操作。显然并发进行文件读写肯定是很容易出现问题的。
(4)Application会多次创建
这一点也很好理解,不同进程的组件分别属于对应的虚拟机和application。当创建新进程时,会同时创建一个新的application。
为了解决这些问题,提供了一些IPC的方法,能够进行不同进程之间数据的共享。
2. IPC基础概念介绍
序列化是将数据转化成可传输的数据形式进行传输。总的来说就是离开内存的数据都需要进行序列化才能传输。java中就是传输成字节流。
2.1 Serializable
Serializable是java提供的一个序列化的接口,是一个空接口。使用时十分简单。
a. 实现这个接口
b. 声明一个serialVersionUID即可(实际上,不添加也可以。序列化时一切正常,但是在反序列化时可能出现问题). 如下所示代码就已经完成了。
public class User implements Serializable {
private static final long serialVersionUID=123456789L;
public int age;
public String name;
}
接下来只需要使用ObjectOutputStream和ObjectInputStream进行序列化和反序列化即可。
//序列化
User user=new User();
ObjectOutputStream outputStream=new ObjectOutputStream(new FileOutputStream("file.txt"));
outputStream.writeObject(user);
outputStream.close();
//反序列化
ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("file.txt"));
User newUser= (User) inputStream.readObject();
inputStream.close();
上面提到如果不添加serialVersionUID的话,可能会出现问题。原理是这样的:序列化时,会将该类的serialVersionUID一起写入到file或者别的中介中,然后反序列化的时候会把原serialVersionUID(user)和现有的serialVersionUID(newUser)比较是否相同,如果不相同的时候就会报错。
当我们不手动声明serialVersionUID的时候,会自动根据当前类的结果生成它的hash值,这样一旦类的结构发生变化,比如增减字段时,就会导致serialVersionUID改变,此时在反序列化的时候就会出现问题。如果手动设置了serialVersionUID,那么此时serialVersionUID始终为最初设置的那个serialVersionUID不会发生变化,因此不会出现报错的现象,从而最大程度地恢复了数据。此外如果类名或者成员变量的类型发生变化时,仍然会导致序列化失败。
2.2Parcelabe
除了Serializable意以外,Android还提供了一个Parcelabe。只要实现了这个接口,同样能够进行序列化。我们都知道,可以通过intent和bundle进行传递数据,而它们实际上都是实现了这个接口。
public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
//省略
}
public class Intent implements Parcelable, Cloneable {
//省略
}
用法其实在源码中也有写到。
/**
*源码示例
* <pre>
* public class MyParcelable implements Parcelable {
* private int mData;
*
* public int describeContents() {
* return 0;
* }
*
* public void writeToParcel(Parcel out, int flags) {
* out.writeInt(mData);
* }
*
* public static final Parcelable.Creator<MyParcelable> CREATOR
* = new Parcelable.Creator<MyParcelable>() {
* public MyParcelable createFromParcel(Parcel in) {
* return new MyParcelable(in);
* }
*
* public MyParcelable[] newArray(int size) {
* return new MyParcelable[size];
* }
* };
*
* private MyParcelable(Parcel in) {
* mData = in.readInt();
* }
* }</pre>
*/
public interface Parcelable {
//...
}
以下介绍这些方法
(1)public int describeContents()
/**
* Describe the kinds of special objects contained in this Parcelable
* instance's marshaled representation. For example, if the object will
* include a file descriptor in the output of {@link #writeToParcel(Parcel, int)},
* the return value of this method must include the
* {@link #CONTENTS_FILE_DESCRIPTOR} bit.
*
* @return a bitmask indicating the set of special object types marshaled
* by this Parcelable object instance.
*/
public @ContentsFlags int describeContents();
有点看不懂,大概就是描述这个对象的类型。大部分情况返回0就行了。如
@Override
public int describeContents() {
return 0;
}
(2)public void writeToParcel()
/**
* Flatten this object in to a Parcel.
*
* @param dest The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
* May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
*/
public void writeToParcel(Parcel dest, @WriteFlags int flags);
将要转化的对象转化成parcel类型,第一个参数是数据保存的parcel,第二个参数是标志位,代表是否需要将对象作为返回值,一般都为0。实现方式如下所示,将对象的每个成员变量分别写入序列化的结构中。
@Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(music_name);//好像要按照顺序写,否则会出错 dest.writeString(music_author); dest.writeInt(play_time); }
(3)静态的Parcelable.Creator接口,有两个方法。
- createFromParcel(Parcel in)
/** * Create a new instance of the Parcelable class, instantiating it * from the given Parcel whose data had previously been written by * {@link Parcelable#writeToParcel Parcelable.writeToParcel()}. * * @param source The Parcel to read the object's data from. * @return Returns a new instance of the Parcelable class. */ public T createFromParcel(Parcel source);
创建一个music的对象,并通过之前writeToParce()已经赋好值的parcel类实例化这个music对象。
- newArray(int size)
/** * Create a new array of the Parcelable class. * * @param size Size of the array. * @return Returns an array of the Parcelable class, with every entry * initialized to null. */ public T[] newArray(int size);
返回一个music类型的数组,大小为size 。如
@Override public Music createFromParcel(Parcel in) { return new Music(in); } @Override public Music[] newArray(int size) { return new Music[size]; }
**(4)私有的构造方法 **
private Music(Parcel in) { music_name=in.readString(); music_author=in.readString(); play_time=in.readInt(); }
注:1局部变量序列化和反序列化的顺序必须一致否则会出错
2 如果成员变量是另一个可序列化的对象时,需要通过传递当前线程的上下文类加载器,否则会报无法找到类的错误。
如下所示。
Author author=in.readParcelable(Thread.currentThread().getContextClassLoader());
Q&A
1.Parcelabe和Serializable有什么区别?什么场景下应该使用什么?
Serializable是java中的序列化接口,使用简单但是开销大,序列化和反序列化的过程都需要大量的io操作。而Parcelable是Android中的序列化方式,因此更适合用在Android平台上,缺点是使用较前者复杂,但是效率较高。Parcelable主要用在内存序列化上,而序列化到存储设备以及通过网络传输推荐使用Serializable。
3.AIDL
AIDL是Android中IPC方式中的一种,AIDL是Android Interface definition language的缩写,可以很方便的实现客户端和服务端的交互。因为这块内容比较多,这里只是暂时简单的介绍,后续会单独写一篇有关于AIDL的学习笔记。
3.1使用
(1)创建aidl文件
一共有三个,注意创建Book.aidl的时候,可能会提示Book已存在,可以先随便取个名字,之后再重命名为Book。
Book实体类
package com.example.myapplication.aidl;import android.os.Parcel;import android.os.Parcelable;public class Book implements Parcelable { private int bookId; private int price; private String bookName; public Book(int bookId, String bookName,int price) { this.bookId = bookId; this.bookName = bookName; this.price=price; } private Book(Parcel in){ bookId=in.readInt(); price=in.readInt(); bookName=in.readString(); } public static final Creator<Book> CREATOR = new Creator<Book>() { @Override public Book createFromParcel(Parcel in) { return new Book(in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(bookId); dest.writeInt(price); dest.writeString(bookName); } public int getBookId() { return bookId; } public void setBookId(int bookId) { this.bookId = bookId; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } @Override public String toString() { return "Book{" + "bookId=" + bookId + ", price=" + price + ", bookName='" + bookName + '\'' + '}'; }}
AIDL文件中不是所有数据类型都支持,只有以下类型支持。
- 基本数据类型(除了short)
- String和CharSequence
- ArrayList
- HashMap
- Parcelable
- AIDL
// IBookManager.aidlpackage com.example.myapplication.aidl;// Declare any non-default types here with import statementsimport com.example.myapplication.aidl.Book;interface IBookManager { List<Book> getBookList(); void addBook(in Book book);}
// Book.aidlpackage com.example.myapplication.aidl;// Declare any non-default types here with import statementsparcelable Book;
(2)创建服务端服务供远程绑定
package com.example.myapplication.aidl;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class AIDLService extends Service {
private List<Book> list=new ArrayList<>();
public AIDLService() {
}
@Nullable @Override public IBinder onBind(Intent intent) {
return new MyBinder();
}
private final class MyBinder extends IBookManager.Stub {
@Override public List<Book> getBookList() throws RemoteException {
return list;
}
@Override public void addBook(Book book) throws RemoteException {
if(book!=null){
list.add(book);
}
}
}
}
(3)创建客户端
重新创建一个工程,然后将服务端到aidl文件整个复制到客户端中,注意位置要相同。
之后,需要创建和服务端Book类所在的相同包名来存放 Book类。如图所示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lGMXLkNS-1627456913968)(/Users/bjhl/Library/Application Support/typora-user-images/image-20200810160502220.png)]
最后绑定远程服务。
package com.example.myclient;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.example.myapplication.aidl.Book;
import com.example.myapplication.aidl.IBookManager;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
private static final String TAG="MainActivity1";
private IBookManager iBookManager;
private boolean isConnected=false;
private ServiceConnection sc=new ServiceConnection() {
@Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
isConnected=true;
iBookManager=IBookManager.Stub.asInterface(iBinder);
Log.d(TAG, "连接成功");
}
@Override public void onServiceDisconnected(ComponentName componentName) {
isConnected=false;
Log.d(TAG, "连接失败");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent=new Intent();
intent.setPackage("com.example.myapplication");
intent.setAction("com.example.aidl");
Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) {
if(isConnected){
Book book=new Book(101,"书本1",10);
try {
iBookManager.addBook(book);
Log.d(TAG, iBookManager.getBookList().toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
Log.d(TAG,bindService(intent,sc, Context.BIND_AUTO_CREATE)+"");
}
@Override protected void onDestroy() {
super.onDestroy();
unbindService(sc);
}
}
最终输出结果
2020-08-10 15:58:50.472 8552-8552/com.example.myclient D/MainActivity1: true
2020-08-10 15:58:50.509 8552-8552/com.example.myclient D/MainActivity1: 连接成功
2020-08-10 15:58:56.498 8552-8552/com.example.myclient D/MainActivity1: [Book{bookId=101, price=10, bookName='书本1'}]
2020-08-10 15:59:00.188 8552-8552/com.example.myclient D/MainActivity1: [Book{bookId=101, price=10, bookName='书本1'}, Book{bookId=101, price=10, bookName='书本1'}]
3.2IBookManager.java类源码分析
先贴一下完整代码
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.example.myapplication.aidl;
public interface IBookManager extends android.os.IInterface
{
/** Default implementation for IBookManager. */
public static class Default implements com.example.myapplication.aidl.IBookManager
{
@Override public java.util.List<com.example.myapplication.aidl.Book> getBookList() throws android.os.RemoteException
{
return null;
}
@Override public void addBook(com.example.myapplication.aidl.Book book) throws android.os.RemoteException
{
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.myapplication.aidl.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.example.myapplication.aidl.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.myapplication.aidl.IBookManager interface,
* generating a proxy if needed.
*/
public static com.example.myapplication.aidl.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.myapplication.aidl.IBookManager))) {
return ((com.example.myapplication.aidl.IBookManager)iin);
}
return new com.example.myapplication.aidl.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList:
{
data.enforceInterface(descriptor);
java.util.List<com.example.myapplication.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(descriptor);
com.example.myapplication.aidl.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.myapplication.aidl.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.myapplication.aidl.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.myapplication.aidl.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.myapplication.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getBookList();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.example.myapplication.aidl.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.example.myapplication.aidl.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);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addBook(book);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.example.myapplication.aidl.IBookManager sDefaultImpl;
}
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 static boolean setDefaultImpl(com.example.myapplication.aidl.IBookManager impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.myapplication.aidl.IBookManager getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public java.util.List<com.example.myapplication.aidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.example.myapplication.aidl.Book book) throws android.os.RemoteException;
}
Default类不做分析,就是返回一个默认的实现,里面什么事情也没做。
public static class Default implements com.example.myapplication.aidl.IBookManager{ @Override public java.util.List<com.example.myapplication.aidl.Book> getBookList() throws android.os.RemoteException { return null; } @Override public void addBook(com.example.myapplication.aidl.Book book) throws android.os.RemoteException { } @Override public android.os.IBinder asBinder() { return null; }}
接下来是 stub类。
DESCRIPTOR:首先是创建Binder的唯一标识,一般用Binder当前类名表示。
private static final java.lang.String DESCRIPTOR = "com.example.myapplication.aidl.IBookManager";
asInterface: 就是利用DESCRIPTOR将binder转化为AIDL接口类型的对象。并且如果客户端和服务端在同一进程,则直接返回IBookManager,否则返回Stub.proxy对象。
public static com.example.myapplication.aidl.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.myapplication.aidl.IBookManager))) {
return ((com.example.myapplication.aidl.IBookManager)iin);
}
return new com.example.myapplication.aidl.IBookManager.Stub.Proxy(obj);
}
asBinder:返回当前binder对象。
@Override public android.os.IBinder asBinder()
{
return this;
}
onTransact:这个方法运行在服务端Binder线程池中。如果客户端和服务端同一进程时,不调用此方法;如果不同进程时,服务端通过code、确定客户端请求的目标方法是什么,紧接着从data中取出目标方法所需要的参数(如果有),然后执行目标方法。当目标方法执行完成后,就向reply中写入返回值(如果有),这个逻辑由stub内部代理类Proxy完成。如果此方法返回false,那么客户端请求就会失败,因此可以利用这个做权限验证。
举例来说,可以看到case TRANSACTION_getBookList对应的方法是 List getBookList(),因为没有输入参数,且返回值是 List,所以调用目标方法_result = this.getBookList()后,将_result写入reply作为返回值。
case TRANSACTION_addBook 对应的方法是void addBook(in Book book),输入参数是Book类型,没有返回值,所以首先获取_arg0,然后作为参数传入目标方法this.addBook(_arg0)
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList:
{
data.enforceInterface(descriptor);
java.util.List<com.example.myapplication.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(descriptor);
com.example.myapplication.aidl.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.myapplication.aidl.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
接下来是内部类Proxy。
首先创建了一个远程binder供客户端使用。
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;}
Proxy#getBookList
这个方法运行客户端,它的执行过程是这样的:当客户端远程调用时,首先创建包含输入参数的Parcel对象_data、包含返回值当Parcel对象_reply和返回值 _result;接着将sharer参数写入_data(如果有),然后通过remote调用transact发起RPC请求,并将当前线程挂起,等到服务端onTransact被调用返回后,当前线程才继续执行,并从_reply中取出值赋值给__result,最后返回_result。
@Override public java.util.List<com.example.myapplication.aidl.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.myapplication.aidl.Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { return getDefaultImpl().getBookList(); } _reply.readException(); _result = _reply.createTypedArrayList(com.example.myapplication.aidl.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result;}
Proxy#addBook
这个方法运行在客户端,它的执行过程等同于getBookList。和上一个方法不同的是,这个方法因为没有返回值,因此没有_result,但是有输入参数,所以需要将book写入data中。
@Override public void addBook(com.example.myapplication.aidl.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);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addBook(book);
return;
}
_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);
默认实现的get和set方法。
public static boolean setDefaultImpl(com.example.myapplication.aidl.IBookManager impl) { if (Stub.Proxy.sDefaultImpl == null && impl != null) { Stub.Proxy.sDefaultImpl = impl; return true; } return false;}public static com.example.myapplication.aidl.IBookManager getDefaultImpl() { return Stub.Proxy.sDefaultImpl;}
有两点需要注意的地方。第一点就是上面已经说过的,由于客户端调用服务端onTransact时,会挂起等到服务端结束返回时才会继续执行,因此如果服务端执行事件过长时,不能在ui线程中发起本次请求。第二点,由于Binder的方法时运行在binder线程池中,所以Binder方法不论是否耗时,都应该使用同步的方式去实现,因为它已经运行在一个线程中了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h7Dxkbwd-1627456913982)(/Users/bjhl/Library/Application Support/typora-user-images/image-20200810112257074.png)]
3.3定向Tag
分为in、out、inOut三类。
in:只能由客户端向服务端输入,服务端对数据的修改无法影响客户端。
out:只能由服务端向客户端输出,客户端对方法即使有输入也是为空,服务端的数据修改能影响到客户端。
inOut:双向。
4. Android中的IPC方式
4.1 Bundle
由于Bundle实现了parcelable接口,所以可以方便地在不同的进程间传递数据。三大组件(Activity、Service、Receiver)都支持在Intent中传递Bundle数据。
使用方式
非常简单,通过键值对的方式进行存储。
存储
Bundle bundle=new Bundle();
bundle.putString("name","张三");
bundle.putInt("age",18);
bundle.putStringArray("技能",new String[]{"唱歌","跳舞","打篮球"});
Intent intent=new Intent();
intent.putExtras(bundle);
获取
Bundle bundle=getIntent().getExtras();
if(bundle!=null) {
String name=bundle.getString("name");
int age=bundle.getInt("age",10);
}
这样一来就可以通过bundle在进程间传递数据。
4.2 文件共享
通过向同一文件进行读写时,可以实现跨进程的数据传输。但是并发读写可能存在问题。因此适合在对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读写的问题。
4.3 Messenger
Messenger是一种轻量级的IPC方案,底层时AIDL。并且一次只处理一个请求,因此在服务端不用考虑线程同步的问题,因为服务端不存在并发访问的情况。用法有点类似handler。
使用步骤
1服务端进程
创建服务端的服务供远程绑定。创建一个hander的子类,传入messenger,并通过onBinder方法返回messenger的binder。
public class MessengerService extends Service {
private static final String TAG="MessengerService";
private static class MessengerHandler extends Handler{
@Override public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 1:
Log.d(TAG,msg.getData().getString("name")+"");
break;
default:break;
} super.handleMessage(msg);
}
}
private final Messenger messenger=new Messenger(new MessengerHandler());
@Nullable @Override public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
此外注册一下这个服务,另外开启一个进程。
<service
android:name=".messenger.MessengerService"
android:process=":remote" />
2客户端进程
通过bindService的方式和远程服务进行绑定,通过messenger传递message的方式,进行数据的传输。
public class MessengerActivity extends AppCompatActivity {
private Messenger messenger;
private ServiceConnection serviceConnection=new ServiceConnection() {
@Override public void onServiceConnected(ComponentName name, IBinder service) {
messenger=new Messenger(service);
Message message=Message.obtain(null,1);
Bundle data=new Bundle();
data.putString("name","张三");
message.setData(data);
try {
messenger.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(this,MessengerService.class);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
}
}
最终结果
2020-08-11 08:26:29.428 11947-11947/com.example.myapplication D/MessengerService: 张三
此外如果需要服务端返回信息给客户端,比如通知消息收到等,需要接触relpyTo。
首先将服务端这边handleMessage修改一下
@Override public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 1:
Log.d(TAG,msg.getData().getString("name")+"");
//通过相同的方法返回数据
Messenger messenger=msg.replyTo;
Message message=Message.obtain(null,2);
Bundle data=new Bundle();
data.putString("reply","已经收到");
message.setData(data);
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:break;
} super.handleMessage(msg);
}
客户端这边需要添加一个接收消息的handler以及messenger
private static class ClientHandler extends Handler{
@Override public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 2:
Log.d(TAG,msg.getData().getString("reply")+"");
break;
default:
super.handleMessage(msg);
}
}
}
private Messenger mReplyMessenger=new Messenger(new ClientHandler());
然后修改一下上面的代码,给replyTo输入用来接收服务端返回数据等messenger。
@Override public void onServiceConnected(ComponentName name, IBinder service) {
messenger=new Messenger(service);
Message message=Message.obtain(null,1);
Bundle data=new Bundle();
data.putString("name","张三");
message.setData(data);
//要将replyTo赋值
message.replyTo=mReplyMessenger;
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
2020-08-11 09:09:58.774 12510-12510/com.example.myapplication D/MessengerActivity: 已经收到
4.4 ContentProvider
ContentProvider是Android中提供的专门用于不同应用间进行数据共享的方式,底层同样是binder实现。ContentProvider主要以表格的形式来组织数据,并且可以包含多个表,对于每个表格来说,它们都具有行和列的层次性,行代表每一条记录,列代表一条记录中的一个字段。除了表格以外,还支持其他类型诸如图片、视频等。
4.5 Socket套接字
后面的因为感觉有点太深了,暂时先跳过,以后再补。
5. Binder连接池
略
6. 选择合适的IPC方案
略