Android IPC机制(1)

前言

本系列主要介绍Android的IPC机制,Android中多进程的概念以及多进程开发的注意事项,进程间通信的方式等。

1.IPC简介

IPC是Inter-Process Communication的缩写,含义为进程间通信或者跨进程通信,指两个进程间进行数据交换的过程。Android中一般指两个APP间的数据传递。

提到进程,首先要理解什么是进程,什么是线程:

  1. 线程: CPU调度的最小单元,有限的系统资源
  2. 进程: 一个执行单元
    关系图:(手动画的有点丑,见谅)
    在这里插入图片描述

IPC使用场景:多进程,多进程情况分为以下两种

  1. 一个应用本身因为某些原因需要采用多进程来实现(某些模块需要单独运行在单独的进程、Android中对单个应用使用的最大内存做了限制)
  2. 当前应用需要向其他应用获取数据

3.Android多进程模式

  1. 开启多进程模式(单个应用下)
    给四大组件(Activity;Service;Receiver;ContentProvider)在AndroidMenifest.xml中添加process属性
       <activity
            android:name=".Main2Activity"
            android:launchMode="singleTask"
            android:process=":remote"
            android:taskAffinity="com.wdl.test" />
        <activity android:name=".Main3Activity" 
            android:process="com.wdl.kt1.remote"
            />

上面的案例表示当Main2Activity启动时,系统新增一个om.wdl.kt1:remote的进程,默认进程进程名是包名。
案例中android:process分别为“:remote”,“com.wdl.kt1.remote”,两者区别

:指在当前进程名前面加上应用包名;属于当前应用的私有进程,其他应用组件不可与其跑在同一进程中
完整形式完整的命名方式,不会附加包名信息;属于全局进程,其他应用通过ShareUID可与其在同一进程中运行

Android为每个应用都分配一个UID,具有相同UID的应用才能共享数据。两个应用具有相同的ShareUID并且签名相同,才能互相访问对方的私有数据。(data目录;组件信息等)

4.多进程的运行机制

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

例:新建类,声明静态成员变量(静态变量在所有的地方共享),在第一个页面设置为2,跳转至第二个页面。

object UserManager {
    var sUserId = 1
}

第一个页面

  UserManager.sUserId = 2
        Log.e("wdl", "--onCreate-- ${UserManager.sUserId}")

第二个页面

Log.e(TAG, "--onCreate-- ${UserManager.sUserId}")

日志

2018-10-15 19:45:23.779 22832-22832/com.wdl.kt1 E/wdl: --onCreate-- 2
2018-10-15 19:45:45.658 22870-22870/? E/wdl: --onCreate-- 1

观察上面发现,位于不同进程访问同一个类会产生副本。

使用多进程造成的影响:

  1. 静态成员和单例模式失效
  2. 线程同步机制失效
  3. SharePreference可靠性降低(两个进程同时去执行写操作会造成数据丢失)
  4. Application多次创建

5.IPC基础概念

1. Serializable接口以及Parcelable接口实现数据序列化

  1. Binder
    简单介绍
    在这里插入图片描述

新建AIDL示例:

  1. 新建aidl包,新建User.java、User.aidl、IUserManager.aidl
package com.wdl.kt1.aidl

import android.os.Parcel
import android.os.Parcelable
import com.wdl.kt1.Book
import java.io.Serializable

/**
 * author:   wdl
 * time: 2018/10/12 18:05
 * des:    TODO
 */
class User : Parcelable,Serializable {
    private val serialVersionUID = 519067123721295773L
    private var age: Int? = null
    private var name: String? = null
    private var book: Book? = null

    constructor(age: Int?, name: String, book: Book) {
        this.age = age
        this.name = name
        this.book = book
    }

    /**
     * 从序列化后的对象中创建原始对象
     * 因为Book是另一个可序列化对象
     * 所以它的反序列化过程需要传递当前线程的上下文类加载器
     *
     * @param in Parcel
     */
    private constructor(`in`: Parcel) {
        age = `in`.readInt()
        name = `in`.readString()
        book = `in`.readParcelable(Thread.currentThread().contextClassLoader)
    }

    /**
     * 返回当前对象的内容描述
     *
     * @return 1:含描述符
     * 基本为0
     */
    override fun describeContents(): Int {
        return 0
    }

    /**
     * 将当前对象写入序列化结构中
     *
     * @param dest  Parcel
     * @param flags 1:标识当前对象需要作为返回值返回不能立即释放资源
     * 基本为0
     */
    override fun writeToParcel(dest: Parcel, flags: Int) {
        dest.writeInt(age!!)
        dest.writeString(name)
        dest.writeParcelable(book, 0)
    }

    /*
   * 完成反序列化的过程
   * */
    companion object CREATOR : Parcelable.Creator<User> {
        override fun createFromParcel(parcel: Parcel): User {
            return User(parcel)
        }

        override fun newArray(size: Int): Array<User?> {
            return arrayOfNulls(size)
        }
    }

    override fun toString(): String {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\''.toString() +
                ", book=" + book +
                '}'.toString()
    }

}

右键new,AIDL file
在这里插入图片描述

IUserManager.aidl

// IUserManager.aidl
package com.wdl.kt1.aidl;
//尽管User与IUserManager同一个包,但是不导入会报错
import com.wdl.kt1.aidl.User;
// Declare any non-default types here with import statements

interface IUserManager {
   List<User> getUsers();
   void addUser(in User user);
}

User.aidl

// User.aidl
package com.wdl.kt1.aidl;

// Declare any non-default types here with import statements

parcelable User;

重新build项目:

Process 'command 'D:\SDK\build-tools\27.0.3\aidl.exe'' finished with non-zero exit value 1

1.包名问题
2.AIDL不支持同名不同参函数
3.使用自定义类型时需要在AIDL中声明

会报上面的错误,那是因为虽然User与IUserManager同一个包,但是不导入会报错,正确代码如上。

如图可以找到系统自动生成的IUserManager.java类
在这里插入图片描述

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: G:\\android_work\\Kt1\\app\\src\\main\\aidl\\com\\wdl\\kt1\\aidl\\IUserManager.aidl
 */
package com.wdl.kt1.aidl;
// Declare any non-default types here with import statements

public interface IUserManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.wdl.kt1.aidl.IUserManager {
        private static final java.lang.String DESCRIPTOR = "com.wdl.kt1.aidl.IUserManager";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.wdl.kt1.aidl.IUserManager interface,
         * generating a proxy if needed.
         */
        public static com.wdl.kt1.aidl.IUserManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.wdl.kt1.aidl.IUserManager))) {
                return ((com.wdl.kt1.aidl.IUserManager) iin);
            }
            return new com.wdl.kt1.aidl.IUserManager.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_getUsers: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.wdl.kt1.aidl.User> _result = this.getUsers();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addUser: {
                    data.enforceInterface(DESCRIPTOR);
                    com.wdl.kt1.aidl.User _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.wdl.kt1.aidl.User.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addUser(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.wdl.kt1.aidl.IUserManager {
            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.wdl.kt1.aidl.User> getUsers() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.wdl.kt1.aidl.User> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getUsers, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.wdl.kt1.aidl.User.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addUser(com.wdl.kt1.aidl.User user) 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 ((user != null)) {
                        _data.writeInt(1);
                        user.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getUsers = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public java.util.List<com.wdl.kt1.aidl.User> getUsers() throws android.os.RemoteException;

    public void addUser(com.wdl.kt1.aidl.User user) throws android.os.RemoteException;
}

观察上述代码,可以发现它继承了IInterface,自己也是个接口。所有可以在Binder中传输的接口都要继承IInterface。

  • 声明了在IUserManager.aidl中所声明的方法getUsers()和addUser(User user)
  • 声明两个整型ID用来标识这两个方法,transcat过程中客户端请求的是哪个方法
  • 声明Stub内部类,是一个Binder类,当客服端与服务端处于同一进程,不走跨进程的transcat,不同进程,走transcat,由Stub.Proxy完成
  • DESCRIPTOR:Binder唯一标识,当前类名表示
  • asInterface(android.os.IBinder obj)
    用于将服务端的Binder对象转为客户端所需的AIDL接口类型的对象,同一进程返回服务端Stub本身,否则返回Stub.Proxy
 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.wdl.kt1.aidl.IUserManager))) {
                return ((com.wdl.kt1.aidl.IUserManager) iin);
            }
            return new com.wdl.kt1.aidl.IUserManager.Stub.Proxy(obj);
  • asBinder()
    返回当前Binder对象

  • onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
    此方法运行在服务端的线程池中,当客户端发起跨进程请求时,交给此方法处理。服务端通过code判断请求目标方法是什么,通过data取出目标方法所需参数,执行完成后向reply写入返回值;如果返回false,客户端请求失败。

  • Proxy#getUsers()
    此方法运行在客户端,创建该方法所需输入型Parcel对象_data,输出型Parcel对象_reply,返回值List对象_result。将该方法参数信息写入_data中,接着调用transcat方法完成RPC(远程过程调用),当前线程挂起,服务端的transcat会被调用直到RPC过程返回后,当前线程继续执行,并从_reply中取出RPC返回结果;最后返回_reply数据

  • Proxy#addUser(User user)
    同上

Binder工作机制:
在这里插入图片描述

?:当客户端发起请求时,当前线程会被挂起,所以如果这个方法很耗时,则都应该采用同步的方式去实现。

Binder运行在服务端进程,由于服务端异常终止,服务端的连接断裂(Binder死亡),如果客户端不知道连接断裂,就会影响客户端的使用,所以Binder提供了如下两个方法判断连接是否断裂:

  1. linkToDeath:设置一个死亡代理,断裂时收到通知
  2. unlinkToDeath
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值