Android开发艺术探索学习笔记 第二章IPC

最近将之前工作做本地的学习笔记上传一下
这里是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方案

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值