Android IPC机制之开启多进程模式

            IPC是Inter-Process Communication的简写,意为进程间通信或者跨进程间通信,是指两个进程间进行数据交换的过程。在说道进程间通信,首先,我们要理解什么是进程?什么线程?进程跟线程的关系又是什么?搞清楚这些东西,对我们来说,是很有必要的,当然了,这个进程与线程的关系,你们肯定是知道的,但是我还是要在这里啰嗦两句,哈哈,不要喷我啊。首先来说,什么是线程?线程其实就是程序中单独执行的流控制。那么什么又是多线程呢?那么他呢其实就是单个程序中,可以同时运行多个不同线程执行不同的任务。多线的作用就是最大限度的使用CPU资源。而进程呢?一般指的是一个执行单元。比如:一个程序或者在Android上来说,它是一个应用。进程可以有多个线程,所以说,进程与线程是包含与被包含的关系。

           那么我们为什么要知道这个多进程呢?多进程对我们来说又有什么好处呢?这个多进程的使用场景很多,我们知道在Android早些时候的版本,每个应用分配的内存大小是16MB,有时候,我为了解决这个应用内存大小的问题,我就采用了多进程来解决这个内存不足的问题。当然,还有很多使用场景,比如说还有我们比较常见的跨进程通讯,ContentProvider对吧,他也是一种跨进程通信。好了,既然说到了多进程,那么我们下面就来说说如何开启多进程?当然开启多进程很简单,我们给四大组件定义一个process属性,就好了。但是,开启多进程对我们来说真的很简单吗?答案是否定的,如果多进程应用的不好,那就犹如黄河之水泛滥一发不可收拾。如果控制的不好,将会给你的程序带来灾难性的后果。下面我们来看个例子,然后来分析分析,在开启多进程之后会带来那些问题。如下:



我们在清单文件里配置,如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    package="com.example.ipcdemo"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:name=".MyApplication"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
            android:name=".SencondActivity"
            android:label="@string/app_name"
            android:process=":remote">
        </activity>
        <activity
            android:name=".ThirdActivity"
            android:label="@string/app_name"
            android:process="com.example.ipcdemo.remote">
        </activity>
    </application>

</manifest>
好,现在我们运行一下程序,看看:

在上面的清单文件中,我们会发现,我们SencondActivity与ThirdActivity所声明的process方式是不一样的?一个是“  :remote”,一个是加上全部包名,“com.example.ipcdemo.remote”,那么他们之间有什么区别呢?当然是有区别的,对于,ThirdActivity中的声明方式,它是一个i额完整的声明方式,并且它不会附加包名信息,而SencondActivity则会附加包名信息。如果是以“:”开头的进程属于当前应用的私有进程,其他应用组件不可以和它跑到一个进程里,如果不是以“:”开头的进程属于全局的进程,其他的应用可以通过ShareUID可以和它跑到同一进程当中,我们知道Android系统会为每个应用分配一个唯一的UID,相同应用的UID的应用才能共享数据。但是这里要讲明的是,如果两个应用通过SharedUID跑到同一个进程中需要这两个应用的SharedUID相同并且签名也要相同才可以。当然,在这种情况下,他们可以互相访问对方的私有数据,比如:data目录,组件信息等,不管它们是否是跑在同一个进程当中,还有就是它们如果跑在同一个进程当中的话,除了能访问data目下、组件信息,还可以共享内存信息。

         之前我们说道,开启多进程,会出现各种奇奇怪怪的问题,那么下面我们就来看一下这个例子,比如我们创建一个UserManger类,里面声明userId,int类型的:

/**
 * Author : Created by rookie 
 */
public class UserManager {

    public static int sUserId = 1;

}
接下来,我们在MainActivity里,修改这个值,然后在打印,接着在SecondActivity里打印一下userId的值,看看他是不是我们之前在MainActivity里面改过来的数据。代码如下:

//MainActivity:
public class MainActivity extends AppCompatActivity {


    private static final String TAG = "MainActivity";

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

        UserManager.sUserId = 2;

        Log.d(TAG,"sUserId = "+UserManager.sUserId);


        Intent intent = new Intent(this,SencondActivity.class);
        startActivity(intent);

    }
}


//SencondActivity:
public class SencondActivity extends AppCompatActivity {

    private static final String TAG = "SencondActivity";

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


        Log.d(TAG, "sUserId = " + UserManager.sUserId);
        Intent intent = new Intent(this,ThirdActivity.class);
        startActivity(intent);


    }
}




又上图可知,他们打印出来的值居然不一样,我们明明在MainActivity里改了,可是值在SedondActivity里打印出来并没有发生改变,这又是为什么呢?我们带着这个问题,来分析解答一下:

出现上述问题的原因很简单,那就是Android为我们每个应用都分配一个独立的虚拟机,或者你也可以这么去理解,Android系统为我们每个进程都分配了一个独立的虚拟机,不同的虚拟机将导致分配的内存空间也不一样,这也就是,在开启多进程的时候,每个进程对应一个独立的虚拟机,并且在访问同一个类的对象时,会产生多个副本。就拿这个例子来说吧,SedondActivity和ThirdActivity这两个进程中都会存在一个UserManger类,并且这两个类还是互不干扰的,那么你在前者的进程中修改值,在后一个进程中是不会收到影响的。这只是其中一种,那么我们在使用多进程的时候,他可能还会带来以下几个问题:

1>静态成员和单例模式完全失效。

2>线程同步机制完全失效。

3>SPreferences的可靠性下降。

4>Application会创建多次。

第一、第二个问题,类似,既然都不在同一个内存块了,无论你是锁对象还是所类都将无法保证线程同步,因为不同进程锁的对象不同。第三个问题,它不支持两个进程同时进行读写操作,否则会导致数据一定机率的丢失,好,到了第四个问题,我们创建一个Applictaion的子类来测试一下,代码如下:

public class MyApplication extends Application {


    private static final String TAG = "MyApplication";

    @Override
    public void onCreate() {
        super.onCreate();


        Log.d(TAG,"进程名字 : "+ Utils.getProcessName(getApplicationContext(),android.os.Process.myPid()));



    }
}
工具类:

public class Utils {


    public static String getProcessName(Context context,int processId){

        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

        for(ActivityManager.RunningAppProcessInfo appProcess : am.getRunningAppProcesses()){
            if(appProcess.pid == processId){
                return appProcess.processName;
            }
        }

        return null;
    }


}
运行效果:



从上面的结果,我们可以证实,不同进程的组件的确会拥有独立的虚拟机、Application以及内存空间,对吧,这也无形给我们日常开发中带来非常大的困扰,这篇我们说了,在使用多进程时所带来的问题,既然有问题,我们不能视若无睹,当然要想办法去解决,当然,Android系统,为我们提供很多跨进程的通信方式,虽然不能直接共享内存,但是交换数据还是没问题的。那么通信方式有哪些呢?这里可以预告一下,我们将会在下一篇讲到。比如:Intent来传递数据,共享文件和SharedPreferences,基于Binder的Messager和AIDL以及Socket,当然还有内容提供者(ContentProvider),但是,为了让我们更好的理解IPC的通信方式,我们要熟悉一些基础概念,比如:Serializable和Parcelable接口,以及Binder概念。我们将会在下一篇讲到这些基础概念,以及一种IPC机制通信方式。未完待续......


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值