Android进程中通信的方式

     本文来自刘兆贤的博客_CSDN博客-Java高级,Android旅行,Android基础领域博主 ,引用必须注明出处!

大的方向上

第一、使用进程共享的方式,往往使用android:process=remote,这样开启一个新的进程,使得所有进程都可以访问这个进程,使服务可以在多进程共享;而android:process=:remote相当于给当前进程一个私有进程,用来维护其自身的业务处理。开启新进程可以用在activity、service、broadcastReceiver、ContentProvider等组件。

If the name assigned to this attribute begins with a colon (':'), a new process, private to the application, is created when it's needed and the service runs in that process.  If the process name begins with a lowercase character, the service will run in a global process of that name, provided that it has permission to do so. This allows components in different applications to share a process, reducing resource usage.

第二、使用广播的方式,可以设置category、component、package来区别广播,同时使用自定义权限来做限制

方法1、使用AIDL的方式进行

第三、使用Message+Binder

第四、使用Socket

第五、共享内存,如ContentProvider

linux有管道(Pipe)、信号(Signal)、报文(Message)、Trace、ShareMemory等。

Binder能支持跨进程通信的原因:是它实现IBinder接口,系统定义实现此接口即赋予进程通信的功能。

优势:做数据拷贝只用一次,则Pipe、Socket都需要两次;其次安全性高,不会像Socket会暴露地址,被人替换;

通信机制:Service向ServiceManager注册,得到虚拟的Uid和Pid;Client向ServiceManager请求,得到虚拟的Uid和Pid,以及目标对象的Proxy,底层通过硬件协议传输,使用Binder通讯。

通信时Client手持Proxy,ServiceManger进行转换,调用到Service。三者运行在三个独立进程中。Client/Sever全双工,互为Sever/Client。

ServiceManager的主要作用:注册和查询,将Service注册服务,根据参数和名字查询服务。

获得进程是否位于前台:

    public static boolean isRunningForeground(Application application) {
        ActivityManager activityManager = (ActivityManager) application.getSystemService("activity");
        List appProcessInfos = activityManager.getRunningAppProcesses();
        Iterator var3 = appProcessInfos.iterator();
        ActivityManager.RunningAppProcessInfo appProcessInfo;
        do {
            if (!var3.hasNext()) {
                return false;
            }
            appProcessInfo = (ActivityManager.RunningAppProcessInfo) var3.next();
        }
        while (appProcessInfo.importance != 100 || !appProcessInfo.processName.equals(application.getApplicationInfo().processName));
        return true;
    }

讲一个实例:

场景:小米手机,使用okhttp下载56M的安装包,每当下载至45%时,抛出异常:java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 521092 bytes

异常:android.os.TransactionTooLargeException: data parcel size 521092 bytes
    at android.os.BinderProxy.transactNative(Native Method)
    at android.os.BinderProxy.transact(Binder.java:769)
    at android.app.INotificationManager$Stub$Proxy.enqueueNotificationWithTag(INotificationManager.java:1394)
    at android.app.NotificationManager.notifyAsUser(NotificationManager.java:323)
    at android.app.NotificationManager.notify(NotificationManager.java:292)
    at android.app.NotificationManager.notify(NotificationManager.java:276)

分析:下载任务在子线程中执行,更新进度通过调用Notification显示,有时进度未更新也notify一下,结果导致调用系统服务次数过多,Binder的B/S服务响应不及时,出现此异常。

方案:减少不必要的调用。

BinderProxy:

void signalExceptionForError(JNIEnv* env, jobject obj, status_t err,
        bool canThrowRemoteException, int parcelSize)
{
    switch (err) {
        case UNKNOWN_ERROR:
            jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
            break;
        ......
        case FAILED_TRANSACTION: {
            ALOGE("!!! FAILED BINDER TRANSACTION !!!  (parcel size = %d)", parcelSize);
            const char* exceptionToThrow;
            char msg[128];
            // TransactionTooLargeException is a checked exception, only throw from certain methods.
            // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
            //        but it is not the only one.  The Binder driver can return BR_FAILED_REPLY
            //        for other reasons also, such as if the transaction is malformed or
            //        refers to an FD that has been closed.  We should change the driver
            //        to enable us to distinguish these cases in the future.
            if (canThrowRemoteException && parcelSize > 200*1024) {
                // bona fide large payload
                exceptionToThrow = "android/os/TransactionTooLargeException";
                snprintf(msg, sizeof(msg)-1, "data parcel size %d bytes", parcelSize);
            } else {
                // Heuristic: a payload smaller than this threshold "shouldn't" be too
                // big, so it's probably some other, more subtle problem.  In practice
                // it seems to always mean that the remote process died while the binder
                // transaction was already in flight.
                exceptionToThrow = (canThrowRemoteException)
                        ? "android/os/DeadObjectException"
                        : "java/lang/RuntimeException";
                snprintf(msg, sizeof(msg)-1,
                        "Transaction failed on small parcel; remote process probably died");
            }
            jniThrowException(env, exceptionToThrow, msg);
        } break;
        .......
    }
}
大于200k即报android/os/TransactionTooLargeException,而我们需要传输的数据已经达到500k。

另外通知不可以在主线程中持续更新,否则容易导致应用卡顿。

进程间通信方式:Intent/aidl/messenger/broadcastreceiver/file/socket

Linux进程隔离,进行IPC方式:socket/pipe/semaphone/fifo等,Android只保留了前两种。

IPC通信机制:先将应用进程的数据放到数据缓存区,然后在内核空间开辟一块缓存区,使用copy_from_user将数据从应用缓存区拷贝到内核缓存区;接方的服务进程,同样在用户空间开辟一块缓存区,然后将内核缓存区的数据拷贝过来。应用->内核->服务,计拷贝2次。

2次拷贝,就像寄快递的过程,寄的人和收的人处在用户空间,快递员处在内核空间。

Linux利用加载内核模块(Loadable Kernal Module)机制,将Binder模块动态加载进来,用于IPC通信。

Socket两种实现方式:一、listen某个端口,通过read获得数据的阻塞式,二、select某个文件操作符,等有数据再去读的非阻塞式。可以说一个监听端口,一个监听文件。

Android多数IPC调用使用Binder,Zygote使用Socket,Kill Process使用Signal

为什么Zygote使用Socket,不用Binder?Zygote进行fork的作用是,在单线程条件下,将父进程的资源和内存拷贝到子进程,而Binder是多线程操作的,容易造成死锁;比如子进程在等待Zygote进程的资源,但Zygote进程有锁,并未拷贝过来。

使用Binder的原因:

1、数据拷贝1次(一、应用进程到内核传递数据指针。二、该块数据在应用进程中申请内存。三、从内核到应用层,再次传递指针,即只有第1次发生数据拷贝,为什么不直接映射同一物理空间,因为多线程资源有同步问题);

2、安全系数高(通过分配的pid和uid,可以对应用进行身份验证,比如权限;另一方面不像socket开放端口,使有知道协议就可以访问)

3、稳定性好,Client和Server,通过ServiceManager进行通信,可以有效降低死锁、请求过载等情况。

应用空间之间不能共享资源,而内核空间可以

使用Signal的场景:杀进程Process.killProcess(),通知

Binder原理:架构分为三层:业务层、IPC层和驱动层

Binder通信协议分为两类:BC(Binder Command Protocol,请求码,从IPC层传递到驱动层)和BR(Binder Response Procotol,响应码,从驱动层传递到IPC层)

所有注册的服务,在死亡时会告知ServiceManager,从而可以降低Client直接检测造成的负载过重。

硬件开发:

Android架构分为四级,应用程序、Framework、Hal层(Hardware Abstraction Layer 硬件厂商可以把算法等核心技术放在这一层,不需要开源)、内核驱动层。

Android移植包含两方面:应用移植和系统移植。前者指应用程序,针对不同版本和硬件的系统,做兼容处理;后者指Android系统,在不同硬件如CPU架构、蓝牙等进行适配,必要时还要移植Linux内核驱动和HAL层代码。

监测应用工具:Profiler(CPU、内存、电量),App Inspection(数据库、网络和后台服务),LeakCanary。

参考:

https://zhuanlan.zhihu.com/p/138357525

http://gityuan.com/2015/11/01/binder-driver/

http://gityuan.com/2015/10/31/binder-prepare/

https://blog.csdn.net/qq_39037047/article/details/88066589

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘兆贤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值