阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

多进程通信系列问题

82次阅读
没有评论

共计 3149 个字符,预计需要花费 8 分钟才能阅读完成。

导读 Android 中开启多进程只有一种方法,就是在 AndroidManifest.xml 中注册 Service、Activity、Receiver、ContentProvider 时指定 android:process 属性。今天来讲解下:多进程通信方式以及带来的问题,方便在项目中遇到问题及时的处理;
一、Android 中多进程详解
1、定义
    Android 的多进程通信即 IPC 是指两个进程之间进行数据交换;
    进程一般指一个执行单元,在 PC 和移动设备中指一个程序或应用;
    最简单的情况下,Android 应用中只有一个进程,包含一个线程,即主线程,也叫作 UI 线程,只能在此线程更新操作 UI;
    普通情况下是不需要多进程的,但是当应用需要更多的内存或者某些特殊的 Module 或特殊的需求需要运行在多进程条件下;
2、开启多进程

Android 中开启多进程只有一种方法,就是在 AndroidManifest.xml 中注册 Service、Activity、Receiver、ContentProvider 时指定 android:process 属性,例如:

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

<activity 
    android:name=".MyActivity" 
    android:process="com.test.remote2"> 
</activity>

我们为 MyService 和 MyActivity 指定的 android:process 属性值有所不同,它们的区别如下:

    :remote:以: 开头是一种简写,系统会在当前进程名前附件当前包名,完整的进程名为:com.test:remote,同时以: 开头的进程属于当前应用的私有进程,其它应用的组件不能和它跑在同一进程;
    com.test.remote2:这是完整的命名方式,不会附加包名,其它应用如果和该进程的 ShareUID、签名相同,则可以和它跑在同一个进程,实现数据共享;
3、Android 中的多进程通信方式

多进程通信方式主要有以下几种,它们之间各有优缺点,可根据使用场景选择选择:

    AIDL:功能强大,支持进程间一对多的实时并发通信,并可实现 RPC (远程过程调用);
    Messenger:支持一对多的串行实时通信,AIDL 的简化版本;
    Bundle:四大组件的进程通信方式,只能传输 Bundle 支持的数据类型;
    ContentProvider:强大的数据源访问支持,主要支持 CRUD 操作,一对多的进程间数据共享,例如我们的应用访问系统的通讯录数据;
    BroadcastReceiver:即广播,但只能单向通信,接收者只能被动的接收消息;
    文件共享:在非高并发情况下共享简单的数据;
    Socket:通过网络传输数据;

多进程通信系列问题

二、多进程带来的问题
1、静态变量失效

在一个 Activity 中新建一个静态变量 TEST_STATIC,并在 RemoteActivity1 中的 onStartOtherRemoteActivity 方法中自增,之后启动 RemoteActivity2,并在 2 中打印 TEST_STATIC 的值;

public static int TEST_STATIC = 21; 
public void onStartOtherRemoteActivity(View view) { 
    TEST_STATIC++; 
    Log.e(TAG, "onStartOtherRemoteActivity:" + TEST_STATIC); 
    startActivity(new Intent(this, RemoteActivity2.class)); 
} 
结果:// RemoteActivity1 log 
E/RemoteActivity1: onStartOtherRemoteActivity: 22 
// RemoteActivity2 log 
E/RemoteActivity2: onCreate: 21

并不相同的数值说明在多进程中静态变量是失效的,同样的因为静态变量带来的问题是单例模式的失效;

原因就是多进程时 Android 为其他进程分配了一个新的虚拟机,导致不同的虚拟机在内存上有不同的内存地址,当在新的进程访问变量时,访问的其实是这个类在新的虚拟机中的副本,也就是相当于在:remote 和.remote 中各有一个 RemoteActivity1 类,而.remote 访问的那个副本中的 TEST_STATIC 是没有进行自增操作的,所以还是会打印出 21 的初始数值,而在:remote 中是自增过的 22;

单例模式也是同样的解释,当在另一个进程中访问单例类时,在此进程中其实并没有进行初始化,所以才会失效;

2、线程同步机制失效

本质上跟静态变量类似,在一个进程锁住的是副本的对象,而在另一个副本中,内存都不同,所以肯定是无效的;

3、SharedPreferences 可靠性下降

SharedPreferences 不支持两个进程同时去执行写操作,否则会导致一定几率的数据丢失;

SharedPreferences 的底层是通过读写 XML 文件实现的,并发写很可能导致问题,并发读写都不能保证不会出问题;

4、Application 会被创建多次

当一个组件跑在一个新的进程中时,系统给新的进程分配一个新的虚拟机,就相当于应用又一次的重新启动,Application 作为应用基础肯定也会被重新创建;

新建 Application 类,继承自 Application,并在 onCreate 方法中输出当前进程的 PID:

public class LApplication extends Application { 
    private static final String TAG = "LApplication"; 
    @Override 
    public void onCreate() {super.onCreate(); 
        Log.e(TAG, "onCreate:" + android.os.Process.myPid()); 
    } 
}

当依次开启进程后输出如下:

// Main 
E/LApplication: onCreate: 16031 
// RemoteActivity1 
E/LApplication: onCreate: 16127 
// RemoteActivity2 
E/LApplication: onCreate: 16202

Application 被创建多次带来的问题是,有些时候会需要在 Application 中初始化些依赖,但是多进程就会随着 Application 的创建而重复初始化,可以在 Application 中设置一些条件跳过重复初始化部分;

// 根据 pid 获取进程名 
private String getAppName(int pid) { 
    String processName = null; 
    ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE); 
    List list = am.getRunningAppProcesses(); 
    for (ActivityManager.RunningAppProcessInfo info : list) { 
        try {if (info.pid == pid) { 
                processName = info.processName; 
                return processName; 
            } 
        } catch (Exception e) {e.printStackTrace(); 
            return null; 
        } 
    } 
    return null; 
}

通过 PID 获取进程名,与包名做对比,只有跟包名一致时才做一些初始化工作;

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-25发表,共计3149字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中