Android系统分析之进程间消息通信机制

本文深入讲解Android中的进程间通信(IPC)机制,包括IPC的概念、多进程模式、基础IPC概念如Serializable、Parcelable和Binder的工作机制等内容。此外,还介绍了Android中的多种IPC方式,如使用Bundle、文件共享、Messenger、AIDL、ContentProvider和Socket等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 IPC机制基础

1.1 Android IPC简介

(1)IPC是Inter-Process Communication的缩写,含义是进程间通信或者跨进程通信,是指两个进程间进行数据交互的一个过程
(2)线程是CPU调度的最小单元,同时线程是一种有限的系统资源。而进程一般指一个执行单元,在PC和移动设备上指一个程序或者一个应用一个进程可以包含多个线程,因此进程和线程是包含与被包含的关系。

1.2 Android 中的多进程模式

Android中的多进程模式,通过给四大组件指定android:process属性,可以轻易的开启多进程模式。

1.2.1 开启多进程模式

(1)正常情况下,在Android中多进程是指一个应用中存在多个进程的情况,因此这里不讨论两个应用之间的情况,首先在Android中使用多进程只有一种方法,那就是给四大组件指定android:process。还有一种非常规的多进程方法,那就是通过JNI在native层去fork一个新的进程。
(2)示例如下:

       <activity
            android:name=".MainActivity"
            android:configChanges="orientation|screenSize"
            android:launchMode="standard">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.seniorlibs.binder.file.SecondActivity"
            android:process=":remote" />
        <activity
            android:name="com.seniorlibs.binder.file.ThirdActivity"
            android:process="com.seniorlibs.binderclient.remote" />

(3)“:remote”和包名“.remote”的区别?
①":remote"属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中,常用
②".remote"属于全局进程,其他应用通过ShareUID方式可以和它跑在同一进程中

1.2.2 多进程模式的运行机制

使用多进程会造成如下几方面的问题:

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

(1)第1问题原因:Android为每一个应用都分配了一个独立的虚拟机,或者说为每个进程都分配一个独立的虚拟机,不同的虚拟机在内存上有不同的地址空间,这就导致在不同的虚拟机中访问同一个类的对象会产生多份副本。举例,在两个进程中都存在一个UserManager类,并且这两个类是互不干扰的,在一个进程中修改mUseld的值只会影响当前进程,对其他进程不会造成任何影响。所有运行在不同进程中的四大组件,只要它们之间需要通过内存来共享数据,都会共享失败这也是多进程带来的主要影响
(2)第2问题原因:本质上和第一个问题是类似的,既然都不是一块内存了,那么不管是锁对象还是锁全局类都无法保证线程同步,因为不同进程锁都不是同一个对象
(3)第3问题原因:SharedPreferences底层是通过读/写XML文件来实现的,并发写显然是可能出问题的,甚至并发读/写都有可能出问题;所以不支持两个进程同时去执行写操作,否则会导致一定几率的数据丢失
(4)第4问题原因:由于系统要在创建新的进程同时分配独立的虚拟机,所以这个过程其实就是启动一个应用的过程。因此,相当于系统又把这个应用重启动了一遍,既然重新启动了,那么自然会创建新的Application。

1.3 IPC基础概念

1.3.1 Serializable

(1)Serializable是java所提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化的操作。
(2)想让一个对象实现序列化,只需要这个类实现Serializable接口并且声明一个serialversionUID即可:

private static final long serialVersionUID = 8711368828010083044L;

(3)serialversionUID的工作机制?
序列化的时候会把当前类的serialversionUID写进序列化的文件中(也可能是其他中介),当反序列化的时候系统会去检测文件中的serialversionUID,看它是否和当前类的serialversionUID一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化;否则就说明当前类和序列化的类相比发生了某些变换,比如成员,类型可能发生了变化,这个时候是无法正常的反序列化的。
(3)为什么建议手动指定serialversionUID的值?
如果不手动指定serialversionUID的值,反序列化时当前类有所改变,比如增加或者删除了某些成员变量,那么系统就会重新计算当前类的hasj值并把它赋值给serialversionUID,这个时候当前类的serialversionUID就和序列化数据中的serialversionUID不一致,于是反序列化失败,程序就会出现crash。

1.3.2 Parcelable

(1)在序列化过程中需要实现的功能有序列化、反序列化和内容描述

public class User implements Parcelable {
    public int userId;
    public String userName;
    public boolean isMale;
    public Book book;

    public User(int userId, String userName, boolean isMale) {
        this.userId = userId;
        this.userName = userName;
        this.isMale = isMale;
    }
 
    // 内容描述功能,几乎在所有情况下这个方法都应该返回0,仅当当前对象中存在文件描述符时,此方法返回1
    @Override
    public int describeContents() {
        return 0;
    }
    // 序列化功能,最终是通过Parcel中的一系列write方法来完成的
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(userId);
        dest.writeString(userName);
        dest.writeByte((byte) (isMale ? 1 : 0));
    }
    // 反序列化功能,其内部标明了如何创建序列化对象和数组,并通过Parcel的一系列read方法来完成反序列化过程
    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    protected User(Parcel in) {
        userId = in.readInt();
        userName = in.readString();
        isMale = in.readByte() != 0;
    }
}

(2)Parcelatle和Serializable之间该如何选取?
①Parcelatle和Serializable都能实现序列化并且都可用于Inient间的数据传递;
②Serializable是Java中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化过程需要大量I/O操作。
③Parcelable是Andrord推荐的序列化方式,更适合用在Android平台上,它的缺点就是使用起来稍微麻烦点,但是它的效率很高,因此我们要首选Parcelatle。
④Parcelable主要用在内存序列化上;通过Parcelable将对象序列化到存储设备中,或者将对象序列化后通过网络传输这个过程会稍显复杂,因此建议使用Serializabie

1.3.3 Binder

(1)概念
①直观来说,Binder是Android中的一个类,它实现了IBinder接口;
②Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在Linux中没有;
③从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager,等等)和相应Managerservice的桥梁:
④从IPC角度来说,Binder是Android中的一种跨进程通信方式;
⑤从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务(包括普通服务和基于AIDL的服务)或者数据。
(2)进程间通信的整体架构

1.4 Binder工作机制分析

1.4.1 通过AIDL来分析Binder工作机制

(1)创建一个工程,在工程中点击右键New->AIDL->AIDL File,默认直接点确定,这时会在工程中出现一个aidl文件。
这里写图片描述
(2)由于AS是要手动编译才能生成对应AIDL的java文件,既然aidl文件是个接口,那就必须存在着实现这个接口的类,点击编译,系统自动生成一个java类,该java类的代码就是整个Binder机制的原理所在
这里写图片描述
(3)写服务端的代码,就要开始写服务了,创建一个类,继承Service

/**
 * 服务端---服务类
 */
public class MyService extends Service {
  
    @Override
    public IBinder onBind(Intent intent) {
        return iBinder;
    }
    
    private IBinder iBinder = new IMyAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }
        @Override
        public int add(int num1, int num2) throws RemoteException {
            Log.i("TAG", "从客户端发来的AIDL请求:num1->" + num1 + "::num2->" + num2);
            return num1 + num2;
        }
    };
}

(4)必须在manifests文件中配置

<service
     android:name=".MyService"
     android:process=":remote" />

(5)写客户端的代码,放一个”AIDL“的按钮,先绑定服务,然后点击按钮调用

public class MainActivity extends AppCompatActivity {
    private IMyAidlInterface iMyAidlInterface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 绑定服务
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }
    /**
     * 点击“AIDL”按钮事件
     */
    public void add(View view) {
        try {
            int res = iMyAidlInterface.add(1, 2);
            Log.i("TAG", "从服务端调用成功的结果:" + res);
        } catch (Exception e) {
            e.printStackTrace();
            Log.i("TAG", "服务端未开启");
        }
    }
    /**
     * 服务回调方法
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            iMyAidlInterface = null;
        }
    };
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //解绑服务,回收资源
        unbindService(mConnection);
    }
}

(6)测试结果(先开启服务端,开启服务后,接着开启客户端,绑定远程服务)

02-22 17:21:33.651 19492-19552/demo.binder I/TAG: 从客户端发来的AIDL请求:num1->1::num2->2

02-22 17:21:33.651 22610-22610/demo.binderb I/TAG: 从服务端调用成功的结果:3
1.4.2 Binder的工作机制原理分析-AIDL类源码
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\project\\project_my\\BinderB\\app\\src\\main\\aidl\\demo\\binder\\IMyAidlInterface.aidl
 */
package demo.binder;
// Declare any non-default types here with import statements
// 接口

public interface IMyAidlInterface extends android.os.IInterface {
    /**
     * 服务端.
     */
    public static abstract class Stub extends android.os.Binder implements demo.binder.IMyAidlInterface {
        // 当前Binder类的全名 
        private static final java.lang.String DESCRIPTOR = "demo.binder.IMyAidlInterface";

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

       /**
         * 将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,obj是当前服务端的Binder对象
         */
        public static demo.binder.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }

            // 如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,下面方法转换
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof demo.binder.IMyAidlInterface))) {
                return ((demo.binder.IMyAidlInterface) iin);
            }
            // 如果客户端和服务端位不位于同一进程,返回的是系统封装后的Stub.Proxy(obj)
            return new demo.binder.IMyAidlInterface.Stub.Proxy(obj);
        }

        /**
         * 当前服务端的Binder对象
         */
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

       /**
        * 运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理
        */
        @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_basicTypes: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值