第2章-IPC机制

Android多进程模式

一个应用使用多进程模式,就是给四大组件(Activity,Service,Receiver,ContentProvider)在清单文件中指定android:process属性。无法给一个线程或者一个实体类指定运行时的进程

开启多进程的配置

在清单文件中配置process属性

<activity android:name=".ThirdActivity"
            android:process="com.wzh.activitydemo.remote"
            />
        <activity android:name=".SecondActivity"
            android:process=":remote"
            />
        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|keyboardHidden|screenLayout|screenSize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
  • :remote
    冒号+名称,运行时,会加上当前包名,com.wzh.activitydemo:remote,这是一种简写,会自动带上包名,这种表示当前应用的私有进程,其他应用的组件不可以和它跑在同一进程中

  • 完整包名+remote
    com.wzh.activitydemo.remote,这种属于全局进程,其他应用通过ShareUID方式可以和它跑在同一进程中

  • 默认进程
    如果不指定process属性,则运行在当前应用的进程中,默认是当前包名

在这里插入图片描述

多进程模式的运行机制

每个进程都分配一个独立虚拟机,不同的虚拟机在内存分配上有不同地址空间,导致在不同虚拟机访问同一个类的对象产生多份副本

一般来说,多进程会造成的问题:

  1. 静态成员和单例完全失效
  2. 线程同步机制完全失效
  3. SharePreferences的可靠性下降
  4. Application会创建多次

IPC基础概念

主要包含三方面的内容:Serializable接口、Parcelable接口以及Binder。Serializable和Parcelable接口可以完成对象的序列化过程,当我们需要通过Intent和Binder传输数据时就需要使用Parcelable或者Serializable

Serializable接口

java提供的一个序列化接口,空接口

private static final long serialVersionUID = 1239893457234L; // 系统自动生成的id或者使用1L,两者没有区别

有serialVersionUID在很大程度上避免反序列化过程的失败,在删除某个变量或增加新的成员变量,这时反序列化过程依然能够成功。如果类结构发生了非常规性的改变,对类结构有毁灭性的改变,序列化依然失败

Parcelable接口

android提供的序列化接口

public class Book implements Parcelable {
    public int id;
    public String name;
    public String author;

    public Book(int id, String name, String author) {
        this.id = id;
        this.name = name;
        this.author = author;
    }

    protected Book(Parcel in) {
        id = in.readInt();
        name = in.readString();
        author = 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() {//内容描述,几乎都是返回0
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {//序列化
        dest.writeInt(id);
        dest.writeString(name);
        dest.writeString(author);
    }
}

Serializable和Parcelable如何选择?
serializable使用简单但是开销大,序列化和反序列化过程需要大量I/O操作。
Parcelable效率高,但是使用比较麻烦。
建议还是使用Serializable

Binder

Binder是Android的一个类,实现了IBinder接口,是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟物理设备,设备驱动/dev/binder,该通信方式在Linux中没有。
Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager)和相应ManagerService的桥梁。
进程间的通信,一个进程不是对另一个进程进行操作的,而是通过Binder工具进行进程间通信的

Android开发中,Binder主要用在Service中,包括AIDL和Messenger(底层是AIDL),普通的service中的Binder不涉及进程间通信。

使用AIDL分析Binder

  • 创建三个文件Book.java,Book.aidl,IBookManager.aidl
    Book实现了Parcelable接口
    Book.aidl是Book类在AIDL中声明
    IBookManager.aidl是定义的接口,里面可以自定义方法
    在这里插入图片描述
  • Book.aidl
package com.wzh.activitydemo;

parcelable Book;
  • IBookManager.aidl
// IBookManager.aidl
package com.wzh.activitydemo;

import com.wzh.activitydemo.Book;//需要引入Parcelable类

// 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.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

            List<Book> getBookList();

            void addBook(in Book book);//注意这里参数中需要写in或out
}

AIDL 支持的数据类型如下:

  1. 基本数据类型

  2. String 和charsequence

  3. ArrayList和HashMap, 并且其中包含的元素也必须被AIDL 支持

  4. 实现Parcelable 接口的对象。

  5. 其他AIDL 接口。

这里我要传递的是我自定义的类型, 所以必须自定义类必须实现Parcelable接口。

实现Parcelable 接口必须定义对象生成器CREATOR(注意只能命名为CREATOR),和实现以下方法:

public int describeContents()

public void writeToParcel(Parcel dest, int flags)

  • 编译后生成java类

在这里插入图片描述

编译后生成的Java类

例如上面生成的IBookManager接口,继承IInterface接口,所有可以在Binder中传输的接口都需要继承IInterface接口。

IBookManger接口的结构核心实现是:IBookManager内部静态抽象类Stub和Stub中的静态内部类Proxy

  • 在IBookManager中声明了一个内部类Stub
public static abstract class Stub extends android.os.Binder implements com.wzh.activitydemo.IBookManager{

//Binder的唯一标识,一般用当前Binder类类名表示
 private static final java.lang.String DESCRIPTOR = "com.wzh.activitydemo.IBookManager";

//用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象。
//这种转换是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象
public static com.wzh.activitydemo.IBookManager asInterface(android.os.IBinder obj){
 if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      //判断客户端和服务端是否是同一进程
      if (((iin!=null)&&(iin instanceof com.wzh.activitydemo.IBookManager))) {
        return ((com.wzh.activitydemo.IBookManager)iin);
      }
      return new com.wzh.activitydemo.IBookManager.Stub.Proxy(obj);

}

//返回当前对象
 @Override public android.os.IBinder asBinder()
    {
      return this;
    }

/**
  *
  * @param code  服务端根据code确定客户端所请求的目标方法是什么
  * @param data  取出目标方法所需的参数
  * @param reply 执行完成后,向reply写入返回值
  * @param flags
  * @return  返回false时,客户端请求失败
  * @throws android.os.RemoteException
  */
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{}

}
  • Stub中定义了Proxy类
private static class Proxy implements com.wzh.activitydemo.IBookManager
    {
@Override public android.os.IBinder asBinder()
      {
        return mRemote;
      }
      public java.lang.String getInterfaceDescriptor()
      {
        return DESCRIPTOR;
      }
 @Override public java.util.List<com.wzh.activitydemo.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.wzh.activitydemo.Book> _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          //RPC远程请求,调用服务端的onTransact方法
          //当前线程会被挂起直至服务端进程返回数据,不能使用UI线程发起此远程请求
          boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().getBookList();
          }
          _reply.readException();
          //从_reply中取出结果,并返回
          _result = _reply.createTypedArrayList(com.wzh.activitydemo.Book.CREATOR);
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
}

@Override public void addBook(com.wzh.activitydemo.Book book) throws android.os.RemoteException{}
}

当客户端和服务端都位于同一进程,不会调用transact过程,当两者位于不同进程时,才会执行

在这里插入图片描述

IPC方式

Bundle

由于Bundle实现了Parcelable接口,可以在不同进程间传输,可以在Bundle中附加需要传输的信息,通过Intent发送数据,前提是数据类型必须是Bundle支持的

文件共享

两个进程通过读/写同一个文件夹交换数据。文件共享方式适合在对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读/写的问题。

从本质上说,SharedPreferences也属于文件的一种,但是由于系统对它的读/写有一定的缓存策略,即在内存中会有一份SharedPrefences文件的缓存,因此在多进程模式下,系统对它的读/写就变得不可靠,当面对高并发的读/写访问,有很大几率会丢失数据,不建议在进程间通信使用。

Messenger

Messenger翻译为信使,通过它可以在不同进程中传递Message对象。是一种轻量级的IPC方案,底层实现是AIDL。由于它一次处理一个请求,因此在服务端我们不用考虑线程同步问题

构造函数

    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

服务端和客户端交互

  • 创建Service服务端
/**
 * create by wzh
 * 服务端
 */
public class MessageService extends Service {

    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(@NonNull @NotNull Message msg) {
            if (msg.what == Contants.MSG_FROM_CLIENT) {
                Bundle bundle = msg.getData();
                String clientMsg = bundle.getString("client msg");
                System.out.println("服务端收到信息----:" + clientMsg);
                //获取客户端的信使
                Messenger replyTo = msg.replyTo;
                if (replyTo != null) {
                    Message replyMsg = Message.obtain();
                    replyMsg.what = Contants.MSG_FROM_SERVER;
                    replyMsg.arg1 = 888;
                    Bundle replyBundle = new Bundle();
                    replyBundle.putString("service msg", "我是服务端,我收到你发送的消息了");
                    replyMsg.setData(replyBundle);
                    //发送给客户端
                    try {
                        replyTo.send(replyMsg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    //创建信使对象
    private final Messenger mMessenger = new Messenger(new MessengerHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {//返回Binder对象
        return mMessenger.getBinder();
    }
}
  • 服务端配置为另一个进程
<service android:name="com.wzh.messenger.MessageService"
    android:process=":remote"
    />
  • 客户端绑定服务
//接收服务端发送的消息
private val replyHandler :Handler by lazy {
   Handler(Looper.getMainLooper(),object :Handler.Callback{
       override fun handleMessage(msg: Message): Boolean {
           if (msg.what ==Contants.MSG_FROM_SERVER){//收到服务端发送过来的信息
               val arg1 = msg.arg1
               val data = msg.data
               val serviceMsg = data.getString("service msg")
               println("客户端收到信息----:${arg1},${serviceMsg}")
           }
           return true
       }
   })
}

//接收服务端发送消息的信使
private val replyMessenger:Messenger by lazy {
   Messenger(replyHandler)
}

private val serviceConnection by lazy {
        object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                //创建信使
                val messenger = Messenger(service)
                //封装信息
                val message = Message.obtain(null, Contants.MSG_FROM_CLIENT)
                
                val bundle = Bundle()
                bundle.putString("client msg","我是客户端,跨进程发送信息")

                //可以用Bundle,arg1,arg2,what,replyTo传递数据,不能使用obj
                message.data = bundle

                //如果需要服务端发送信息到客户端时,需要设置replyTo
                message.replyTo = replyMessenger

                try {
                    //发送
                    messenger.send(message)
                } catch (e: RemoteException) {
                    e.printStackTrace()
                }
            }

            override fun onServiceDisconnected(name: ComponentName?) {
            }
        }
    }



    private fun startMessenger() {
        val intent = Intent(this, MessageService::class.java)
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
    }

上面的操作即可实现客户端和服务端在不同进程中进行交互。

Messenger中进行数据传递必须将数据放入Message中,Messenger和Message都实现了Parcelable接口,因此可以跨进程传输。Message中能使用的载体有what,arg1,arg2,Bundle以及replyTo,不支持使用obj。

Messenger是以串行的方式处理客户端发来的消息,如果有大量的并发请求,那么Messenger就不合适了。Messenger主要为了传递消息,如果需要跨进程调用服务端的方法,Messenger就无法做到了

使用AIDL

  1. AIDL支持的数据类型
    在这里插入图片描述
  2. 其中自定义Parcelable对象和AIDL对象必须显示import。
    如果AIDL文件中用到了自定义Parcelable对象,那么必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型
package com.wzh.activitydemo;

parcelable Book;
  1. AIDL中除了基本数据类型,其他类型的参数必须标上方向:in(输入参数),out(输出参数)或者inout(输入输出型参数)
  2. AIDL中只支持方法,不支持声明静态常量,与传统接口有区别,AIDL中无法使用普通接口,只能是有AIDL接口
  3. 如果需要使用观察者模式时,定义的存储观察者列表使用RemoteCallbackList(本质不是List),因为普通的List,通过跨进程后,都是新的对象,无法进行remove相同的观察者

开发步骤

  1. 创建aidl文件

例如IBookManager.aidl

// IBookManager.aidl
package com.wzh.activitydemo;

//记得显式导入Parcelable类
import com.wzh.activitydemo.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.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

//自定义的方法,自定义Book类型,需要创建同名的Book.aidl文件
//在Book.aidl文件中标注parcelable类型
            List<Book> getBookList();

            void addBook(in Book book);
}
  1. 新建与Parcelable对象的同名文件Book.aidl
package com.wzh.activitydemo;

parcelable Book;
  1. 新建服务端,BookService
public class BookService extends Service {
    private IBookManager.Stub mBinder = new IBookManager.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public List<Book> getBookList() throws RemoteException {
            //编写相应的逻辑
            return null;
        }

        @Override
        public void addBook(Book book) throws RemoteException {

        }
    };


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;//返回Binder对象
    }
}
  1. 客户端的实现

绑定service,在ServiceConnection中获取IBookManager对象

private val serviceConnection by lazy {
        object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                val bookManager: IBookManager = IBookManager.Stub.asInterface(service)
                //调用bookManager相应方法,如果调用方法,服务端耗时过久,会导致ANR,耗时的方法需要在非UI线程中调用
}

ContentProvider

抽象类,底层实现是Binder,系统预置了很多ContentProvider,比如通讯录,日程信息等。子类需要实现6个方法。创建时机在Application执行onCreate之前

public class BookProvider extends ContentProvider {
    //contentProvider创建
    @Override
    public boolean onCreate() {
        //主线程
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        //Binder线程池中
        return null;
    }

    /**
     * 用来返回一个uri请求所对应的MIME类型,比如图片,视频等
     * 如果不关心这个类型,可以返回null或*/*
     * @param uri
     * @return
     */
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        //Binder线程池中
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        //Binder线程池中
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        //Binder线程池中
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        //Binder线程池中
        return 0;
    }
}

ContentProvider主要以表格的形式来组织数据,并且可以包含多个表,行代表一条记录,列代表字段,和数据库类似。除了表格数据外,ContentProvider还支持文件数据,比如图片,视频等,文件数据和表格数据的结构不同。Android系统提供的MediaStore就是文件类型的ContentProvider。

ContentProvider对底层数据的存储方式,既可以使用SQLite数据库,文件存储,内存存储等

清单文件注册

<provider
  android:authorities="com.wzh.activitydemo.provider"
  android:name="com.wzh.contentProvider.BookProvider"
  android:permission="com.wzh.PROVIDER"
  android:process=":wzh_provider"
  />

authorities是contentProvider的唯一标识,必须唯一
permission标识provider的权限,访问此provider必须带上权限才能方法,否则异常

Socket

socket是网络通信的概念,对应网络传输控制的TCP和UDP协议

Binder连接池

AIDL是一种最常用的进程间通信方式

在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值