Android多进程实现及常见问题

Android多进程实现及常见问题

 

1、为什么需要多个进程?

默认情况下,一个Android应用中所有的组件都会运行在以包名为进程名的单个进程中,但是由于Android自身平台的一些限制或者多进程固有的一些好处,导致很多应用在实现的时候不得不选择多进程的实现方式:

1.1. Android系统对每一个应用进程的内存占用有限制,视具体设备的情况,我的测试机的单个应用的内存限制为128M,比较大了,早期的Android设备由于总的内存大小限制,对单个应用的内存限制的比较小24M或者更小。所以如果应用需要占用很大的内存,可以考虑将一些组件单独运行在独立的进程中,减小OOM和被系统kill的概率。

 

1.2. 由于每一个进程都是运行在独立的虚拟机中,所以子进程的崩溃不会影响到其它的进程

1.3. 主进程退出后,子进程仍然可以运行,对voip和推送服务类的应用,可以说是必备的

1.4. 还有一些另类的应用用多个进程之间相互监听,防止自己的服务被系统杀死,保证被杀死或者崩溃后能拉起服务

2android多进程实现方式

说了多进程的那么多好处,在Android中如何实现呢?可通过两种设置process属性的方式实现多进程:

2.1. android:process=":processY" 实际的进程名是包名:processY,这种形式表示该进程为当前应用的私有进程,其它应用的组件不可以和它跑在同一个进程中

2.2. android:process="com.pj.sh.processY" 其它应用可以通过设置相同SharedUID且签名完全一致,可以和它跑在同一个进程

代码

<activity
    android:name=".multiprocess.ProcessXActivity"
    android:label="@string/title_activity_process_x">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<activity
    android:name=".multiprocess.ProcessYActivity"
    android:label="@string/title_activity_process_y"
    android:process=":processY"></activity>

运行结果:

shell@ja3g:/ $ ps | grep com.pj.sh
u0_a191   7078  2604  1875580 61288 ffffffff 00000000 S com.pj.sh
u0_a191   13412 2604  1892492 59280 ffffffff 00000000 S com.pj.sh:processY

2.3. Application也可以设置android:process属性,来修改所有组件运行进程的名称

代码:

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:process="application.test">
        <activity
            android:name=".multiprocess.ProcessXActivity"
            android:label="@string/title_activity_process_x">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".multiprocess.ProcessYActivity"
            android:label="@string/title_activity_process_y"
            android:process=":processY"></activity>
    </application>

运行结果:用第一种方式指定的进程的进程名称依然是包名:processY

shell@ja3g:/ $ ps | grep application.test
u0_a191   3142  2604  1877644 60508 ffffffff 00000000 S application.test
shell@ja3g:/ $ ps | grep com.pj.sh
u0_a191   4651  2604  1890428 59232 ffffffff 00000000 S com.pj.sh:processY

3、多进程常见的问题

多进程是不是就这么简单?有没有其它单进程不会有的问题呢?答案是肯定的,而且经常很头疼。

3.1. 不同的进程会创建不同的Application实例,导致我们在Application中全局初始化资源的时候失效

解决方法:可以在ApplicationonCreate方法中判断当前运行的进程做不同的处理解决资源重复初始化的问题。

 

3.2. 单例和静态成员变量失效,不同的进程中获取的单例实例不是同一块内存,静态变量也是不同的副本

代码:

public class SingletonTest {

    volatile private static SingletonTest mInstance;

    private SingletonTest() {
    }

    public static SingletonTest getInstance() {
        if (mInstance == null) {
            synchronized (SingletonTest.class) {
                if (mInstance == null) {
                    mInstance = new SingletonTest();
                }
            }
        }
        return mInstance;
    }
}

public class ProcessXActivity extends Activity implements View.OnClickListener {

    private TextView mTXInstance;
    private Button mBtnGetInstance;
    private Button mBtnGo2ProcessY;
    private Button mBtnGetInstanceByThread;

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

        mTXInstance = (TextView) findViewById(R.id.id_tx_singleton);
        mBtnGetInstance = (Button) findViewById(R.id.id_btn_get_instance);
        mBtnGo2ProcessY = (Button) findViewById(R.id.id_btn_goto_processy);
        mBtnGetInstanceByThread = (Button) findViewById(R.id.id_btn_thread_get_instance);

        mBtnGetInstance.setOnClickListener(this);
        mBtnGo2ProcessY.setOnClickListener(this);
        mBtnGetInstanceByThread.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch(v.getId()) {
            case R.id.id_btn_get_instance:
                mTXInstance.setText("processX:" + SingletonTest.getInstance());
                break;
            case R.id.id_btn_goto_processy:
                startActivity(new Intent(this, ProcessYActivity.class));
                break;
            case R.id.id_btn_thread_get_instance:
                final String tmp = mTXInstance.getText().toString();
                new Thread() {
                    @Override
                    public void run() {
                        super.run();
                        final String tx = tmp + "\n" + "processX:" + Thread.currentThread().getId() + ":" + SingletonTest.getInstance();
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                mTXInstance.setText(tx);
                            }
                        });
                    }
                }.start();
                break;
            default:
                break;
        }
    }
}



 

public class ProcessYActivity extends Activity implements View.OnClickListener{
    private TextView mTXInstance;
    private Button mBtnGetInstance;
    private Button mBtnGo2ProcessX;

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

        mTXInstance = (TextView) findViewById(R.id.id_tx_singleton);
        mBtnGetInstance = (Button) findViewById(R.id.id_btn_get_instance);
        mBtnGo2ProcessX = (Button) findViewById(R.id.id_btn_goto_processx);

        mBtnGetInstance.setOnClickListener(this);
        mBtnGo2ProcessX.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch(v.getId()) {
            case R.id.id_btn_get_instance:
                mTXInstance.setText("processY:" + SingletonTest.getInstance());
                break;
            case R.id.id_btn_goto_processx:
                startActivity(new Intent(this, ProcessXActivity.class));
                break;
            default:
                break;
        }
    }
}


运行结果:




3.3. 文件共享问题,线程同步机制失效,SharedPreferences可靠性下降,不支持多进程同时写,有一定的概率丢失数据

多个进程同时读写数据库等共享数据,造成资源竞争,导致数据库损坏或者数据丢失等情况

 

3.4. 断点调试

多进程给断点调试带来了很大的不便,一般在调试的时候会把多进程的配置先去掉,这样堆栈信息就是连贯的。

 

这么多的问题,使用多进程的时候一定要小心,特别是第23两个问题,笔者曾经犯过类似的错误,写了一个单例的数据库工具类,信心满满的做了线程同步机制,保证不会出现同时读写的情况,保证数据的一致,由于项目需要,后期把应用拆分成三个进程运行,没有考虑到之前写的单例数据库工具类,导致单例失效,数据的一致性也遭到破坏,整体提心吊胆,生怕出现问题,决定对这个问题进行深入研究,如何实现单例在多个进程之间共享?还没有验证,后期单独文章和进程间通讯一起分析这部分内容。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值