Android多进程共享单例实现之——AIDL方式

来源:互联网 发布:excel网页数据抓取vba 编辑:程序博客网 时间:2024/06/10 19:58

Android多进程共享单例实现之——AIDL方式

概述

前一段时间说到单例在Android多进程中失效的问题(变成多例),最近花了一点时间用Android AIDL进程间通信的方式验证了一下可行性,结果表明,这种方式能实现单例在不同的进程中共享的功能。

实现思想

针对每一个单例,创建一个对应的Service,并在单独进程运行,在Service中创建唯一的实例,所有不同进程针对单例的操作均通过Service的AIDL接口调用,这样就保证了不同的进程操作的是同一个单例实例。

本测试应用使用的类和接口说明:

ProcessXActivity 运行在主进程的主Activity
ProcessYActivity 运行在:processY进程的Activity
ProcessXService 运行在:processService进程的Service
IProcessXService ProcessXService服务的AIDL接口
SingletonTest 单例实现类
BaseActivity Activity基类,实现Service绑定

具体实现类

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.pj.sh">    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity            android:name=".TestHomeActivity"            android:label="@string/app_name"            android:theme="@style/AppTheme.NoActionBar">        </activity>        <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" />        <service            android:name=".multiprocess.ProcessXService"            android:enabled="true"            android:exported="true"            android:process=":processService"></service>    </application></manifest>

BaseActivity.java

package com.pj.sh.multiprocess;import android.app.Activity;import android.app.ActivityManager;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.IBinder;/** * Created by sunhong on 2016/12/28. */public class BaseActivity extends Activity {    private boolean flag;    protected IProcessXService mService;    protected ServiceConnection connection;    protected int getProcessPid() {        return android.os.Process.myPid();    }    protected String getProcessName(Context context) {        String name = "";        int pid = android.os.Process.myPid();        ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);        for (ActivityManager.RunningAppProcessInfo process : activityManager.getRunningAppProcesses()) {            if(process.pid == pid) {                name = process.processName;            }        }        return name;    }    @Override    protected void onResume() {        super.onResume();        bindService();    }    @Override    protected void onPause() {        super.onPause();        unBind();    }    private void bindService(){        Intent intent = new Intent(BaseActivity.this, ProcessXService.class);        bindService(intent, conn, Context.BIND_AUTO_CREATE);    }    private void unBind(){        if(flag == true){            unbindService(conn);            flag = false;        }    }    private ServiceConnection conn = new ServiceConnection() {        @Override        public void onServiceDisconnected(ComponentName name) {            // TODO Auto-generated method stub        }        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            // TODO Auto-generated method stub            mService = IProcessXService.Stub.asInterface(service);            flag = true;        }    };}


ProcessXActivity.java

package com.pj.sh.multiprocess;import android.content.Intent;import android.os.Bundle;import android.app.Activity;import android.os.RemoteException;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.TextView;import com.pj.sh.R;public class ProcessXActivity extends BaseActivity implements View.OnClickListener {    private TextView mTXInstance;    private Button mBtnGetInstance;    private Button mBtnGo2ProcessY;    private Button mBtnGetInstanceByThread;    private Button mBtnGetCount;    private Button mBtnSetCount;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_process_x);        startService(new Intent(this, ProcessXService.class));        Log.d("process-test", "当前进程:" + getProcessPid() + "/" + getProcessName(this));        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);        mBtnGetCount = (Button) findViewById(R.id.id_btn_get_count);        mBtnSetCount = (Button) findViewById(R.id.id_btn_set_count);        mBtnGetInstance.setOnClickListener(this);        mBtnGo2ProcessY.setOnClickListener(this);        mBtnGetInstanceByThread.setOnClickListener(this);        mBtnGetCount.setOnClickListener(this);        mBtnSetCount.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;            case R.id.id_btn_get_count:                if(mService != null) {                    try {                        int count = mService.getCount();                        mTXInstance.setText("count:" + count);                        Log.d("process-test", "获取count值:" + count);                    } catch (RemoteException e) {                        e.printStackTrace();                    }                }                break;            case R.id.id_btn_set_count:                if(mService != null) {                    try {                        mService.setCount();                        int count = mService.getCount();                        mTXInstance.setText("count:" + count);                        Log.d("process-test", "设置count值:" + count);                    } catch (RemoteException e) {                        e.printStackTrace();                    }                }                break;            default:                break;        }    }}

ProcessYActivity.java

package com.pj.sh.multiprocess;import android.content.Intent;import android.os.Bundle;import android.app.Activity;import android.os.RemoteException;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.TextView;import com.pj.sh.R;public class ProcessYActivity extends BaseActivity implements View.OnClickListener{    private TextView mTXInstance;    private Button mBtnGetInstance;    private Button mBtnGo2ProcessX;    private Button mBtnGetCount;    private Button mBtnSetCount;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_process_y);        Log.d("process-test", "当前进程:" + getProcessPid() + "/" + getProcessName(this));        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);        mBtnGetCount = (Button) findViewById(R.id.id_btn_get_count);        mBtnSetCount = (Button) findViewById(R.id.id_btn_set_count);        mBtnGetInstance.setOnClickListener(this);        mBtnGo2ProcessX.setOnClickListener(this);        mBtnGetCount.setOnClickListener(this);        mBtnSetCount.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;            case R.id.id_btn_get_count:                if(mService != null) {                    try {                        int count = mService.getCount();                        mTXInstance.setText("count:" + count);                        Log.d("process-test", "获取count值:" + count);                    } catch (RemoteException e) {                        e.printStackTrace();                    }                }                break;            case R.id.id_btn_set_count:                if(mService != null) {                    try {                        mService.setCount();                        int count = mService.getCount();                        mTXInstance.setText("count:" + count);                        Log.d("process-test", "设置count值:" + count);                    } catch (RemoteException e) {                        e.printStackTrace();                    }                }                break;            default:                break;        }    }}

ProcessXService.java
package com.pj.sh.multiprocess;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.os.RemoteException;public class ProcessXService extends Service {    private ProcessXServiceBinder mServiceBinder = null;    private SingletonTest mSingletonInstance = null;    private ProcessXService mServiceInstance = null;    public ProcessXService() {    }    private ServiceBinder singleBinder = new ServiceBinder();    @Override    public IBinder onBind(Intent intent) {        // TODO: Return the communication channel to the service.        return mServiceBinder;    }    @Override    public void onDestroy() {        super.onDestroy();        mServiceBinder = null;        mSingletonInstance = null;        mServiceInstance = null;    }    @Override    public void onCreate() {        super.onCreate();        mServiceBinder = new ProcessXServiceBinder();        mSingletonInstance = SingletonTest.getInstance();    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        mServiceInstance = this;        return super.onStartCommand(intent, flags, startId);    }    private int getCount() {        return mSingletonInstance.getCount();    }    private void setCount() {        mSingletonInstance.setCount();    }    public class ProcessXServiceBinder extends IProcessXService.Stub {        @Override        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {        }        @Override        public int getCount() throws RemoteException {            return mServiceInstance.getCount();        }        @Override        public void setCount() throws RemoteException {            mServiceInstance.setCount();        }    }    public class ServiceBinder extends Binder {        public ProcessXService getService() {            return ProcessXService.this;        }    }}

IProcessXService.aidl

// IProcessXService.aidlpackage com.pj.sh.multiprocess;// Declare any non-default types here with import statementsinterface IProcessXService {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,            double aDouble, String aString);    int getCount();    void setCount();}

SingletonTest.java

package com.pj.sh.multiprocess;/** * Created by sunhong on 2016/12/28. * Singleton Test */public class SingletonTest {    volatile private static SingletonTest mInstance;    volatile private int count = 0;    private SingletonTest() {    }    public static SingletonTest getInstance() {        if (mInstance == null) {            synchronized (SingletonTest.class) {                if (mInstance == null) {                    mInstance = new SingletonTest();                }            }        }        return mInstance;    }    public void setCount() {        count++;    }    public int getCount() {        return count;    }}

运行结果

Log运行结果表明,一个进程对count值的修改对另一个进程可见,说明操作的是同一个实例。

12-28 17:35:20.574 13677-13677/com.pj.sh D/process-test: 当前进程:13677/com.pj.sh12-28 17:35:43.074 13677-13677/com.pj.sh D/process-test: 获取count值:012-28 17:35:44.984 13677-13677/com.pj.sh D/process-test: 设置count值:112-28 17:35:46.614 13677-13677/com.pj.sh D/process-test: 设置count值:212-28 17:35:47.254 13677-13677/com.pj.sh D/process-test: 设置count值:312-28 17:35:50.064 14484-14484/com.pj.sh:processY D/process-test: 当前进程:14484/com.pj.sh:processY12-28 17:35:55.059 14484-14484/com.pj.sh:processY D/process-test: 获取count值:312-28 17:35:56.794 14484-14484/com.pj.sh:processY D/process-test: 设置count值:412-28 17:35:57.069 14484-14484/com.pj.sh:processY D/process-test: 设置count值:512-28 17:35:57.254 14484-14484/com.pj.sh:processY D/process-test: 设置count值:612-28 17:35:59.289 13677-13677/com.pj.sh D/process-test: 当前进程:13677/com.pj.sh12-28 17:36:02.749 13677-13677/com.pj.sh D/process-test: 获取count值:612-28 17:36:03.939 13677-13677/com.pj.sh D/process-test: 设置count值:712-28 17:36:04.149 13677-13677/com.pj.sh D/process-test: 设置count值:8

优缺点

优点:实现直观,容易理解,做过AIDL远程调用的同学都不陌生

缺点:需要一个单独的Service包装单例实现的类


其它实现方式有没有? 欢迎大家一起讨论分享


0 0