看了一本書,上面有一章講解了IPC(Inter-Process Communication,進程間通訊)通訊,決定結合之前的一篇博客android 兩個應用之間的通訊與調用和本身的理解來好好整理總結一下這塊的知識,因爲內容較多,這部分會分上中下三篇博客來仔細分析講解,第一篇上篇要講解的是sharedUserId和Messenger的使用方式。
android IPC通訊(中)-ContentProvider&&Socket
android IPC通訊(下)-AIDL
html
sharedUserId的做用是讓兩個應用程序共享一個user id,咱們都知道linux進程給每個應用程序分配了一個獨立的user id,因此若是兩個或多個應用程序的簽名相同而且設置了同樣的sharedUserId,他們將會共享一個user id,相同user id的應用程序能夠訪問對方的數據(也就是說若是應用程序中的一個文件的權限是600,相同uid能夠直接訪問,反之則沒法訪問),而且設置成一個android:process就可以運行在一個進程中了。
sharedUserId方式主要就是使用createPackageContext (String packageName, int flags)函數,該函數用來返回指定包名應用的上下文,注意是application的context。
這個方法有兩個參數:java
<manifest package="com.android.shareduserid_a"
xmlns:android="schemas.android.com/apk/res/and…"
android:sharedUserId="com.android.test"
android:sharedUserLabel="@string/share_label">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity android:name="com.android.shareuserid_a.Server">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
複製代碼
B應用:
<manifest package="com.android.shareduserid_b"
xmlns:android="schemas.android.com/apk/res/and…"
android:sharedUserId="com.android.test"
android:sharedUserLabel="@string/share_label">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity android:name="com.android.shareuserid_b.Client"
android:exported="true">
</activity>
</application>
</manifest>
複製代碼
B應用的Client Activity加上了android:exported=」true」用來與A應用交互,這個在 android permission權限與安全機制解析(上)已經有詳細的介紹,在這就略過了。
A應用有一個Server的Activity,B應用有一個Client的Activity,先來看看交互的效果,左側爲A應用,右側爲B應用:
A應用中的Server activity經過下面代碼獲取到B應用的application context:
linux
context = createPackageContext("com.android.shareduserid_b", CONTEXT_INCLUDE_CODE|CONTEXT_IGNORE_SECURITY);複製代碼
//獲取B應用圖片
int id = context.getResources().getIdentifier("share", "mipmap", "com.android.shareduserid_b");
iv_pic.setImageDrawable(ContextCompat.getDrawable(context, id));複製代碼
id = context.getResources().getIdentifier("share_string", "string", "com.android.shareduserid_b");
tv_string.setText(context.getString(id));複製代碼
Intent intent = new Intent();
ComponentName componentName = new ComponentName("com.android.shareduserid_b",
"com.android.shareuserid_b.Client");
intent.setComponent(componentName);
//intent.setClassName(context, "com.android.shareuserid_b.Client");
//intent.setClassName("com.android.shareduserid_b",
// "com.android.shareuserid_b.Client");
startActivity(intent);複製代碼
try {
Class clazz = context.getClassLoader().loadClass("com.android.shareuserid_b.Method");
Object object = clazz.newInstance();
int[] ints = new int[]{1,2,3};
int sum = (int) clazz.getMethod("add", int[].class).invoke(object, ints);
tv_sum.setText("sum is :"+sum);
} catch (Exception e) {
L.e(e);
e.printStackTrace();
}複製代碼
獲取B應用SharedPreferences,這個須要特殊說明一下,因爲SharedPreferences是有緩存機制的,因此若是在B應用中修改了該SharedPreferences文件,接着A應用去讀取該文件中修改的那個值,這時你會發現仍是修改前的值,這就是緩存機制致使的問題,不過有一個flag能夠解決這個問題:MODE_MULTI_PROCESS,可是很是不幸的是api23已經將該標識deprecated了,緣由是在一些版本上不可靠,有興趣的能夠去了解一下,看代碼:android
//注意Context.MODE_MULTI_PROCESS不可靠
SharedPreferences sharedPreferences = context.getSharedPreferences("permanent", MODE_MULTI_PROCESS);
String time = sharedPreferences.getString("time", "get time error");
tv_shared_preference.setText(time);複製代碼
String DBPath = context.getDatabasePath("permanentCache.db").getAbsolutePath();
SQLiteDatabase sqLiteDatabase = SQLiteDatabase.openDatabase(DBPath, null, SQLiteDatabase.OPEN_READONLY);
Cursor cursor = sqLiteDatabase.query("cache_1", null, "key=?", new String[]{"time"}, null, null, null, null);
cursor.moveToNext();
tv_DB.setText(cursor.getString(1));
cursor.close();
sqLiteDatabase.close();複製代碼
用途:這種方式能夠用來進行輕量級的補丁操做,例如皮膚,第一步從服務器獲取全部皮膚的包名,第二步看用戶選擇的皮膚包是否已經安裝到手機上,若是沒有從服務器下載安裝,若是有直接第三步;第三步固然就是從該皮膚包中獲取資源等等等了。git
源碼地址:github.com/zhaozepeng/…github
Messenger能夠用來在不一樣進程中傳遞對象,在Messenger中放入咱們須要傳遞的對象,就能輕鬆地實現數據的進程間傳遞了。Messenger是一種輕量級的IPC方案,它對AIDL進行封裝,因此使用起來很是的方便,固然AIDL通訊的底層實現也是對Binder的封裝,須要特別注意的是這個Binder類並不會影響系統對進程生命週期的管理(你須要使用一些更高等級的組件來告訴系統你的進程須要繼續執行而不被系統回收內存),若是因爲某些緣由被系統殺死回收,鏈接就會斷開。
由於是進程之間的通訊,因此能夠在一個應用中開兩個進程通訊,也能夠在兩個應用中實現通訊,我就以兩個應用之間的通訊爲例。兩個應用之間的通訊可使用兩種方式:數據庫
handler方式最主要是經過Messenger+message進行兩個應用的通訊,先來看看服務端Service代碼:api
Messenger messenger = null;
private static class MessengerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
L.i("i receive '" + msg.getData().getString("message")+"'");
Messenger client = msg.replyTo;
//迴應客戶端
if (client != null){
Message reply = Message.obtain();
Bundle message = new Bundle();
message.putString("message", "i have received your message");
L.i("i have received your message");
reply.setData(message);
try {
client.send(reply);
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
case 2:
L.i("i receive '" + msg.getData().getString("message")+"'");
L.i("client has disconnect this connection, bye~");
break;
default:
break;
}
}
}
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
@Override
public void onCreate() {
super.onCreate();
messenger = new Messenger(new MessengerHandler());
}複製代碼
客戶端代碼:數組
private Button connect_handler;
private TextView tv_handler;
private Button connect_binder;
private TextView tv_binder;
private ServiceConnection serviceConnection;
private Messenger serverMessenger;
private Messenger messenger;
private boolean hasBindService = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.client_handler);
connect_handler = (Button) findViewById(R.id.connect_handler);
connect_handler.setOnClickListener(this);
tv_handler = (TextView) findViewById(R.id.tv_handler);
connect_binder = (Button) findViewById(R.id.connect_binder);
connect_binder.setOnClickListener(this);
tv_binder = (TextView) findViewById(R.id.tv_binder);
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
serverMessenger = new Messenger(service);
communicate();
}
@Override
public void onServiceDisconnected(ComponentName name) {
serverMessenger = null;
}
};
messenger = new Messenger(new Handler(){
@Override
public void handleMessage(Message msg) {
L.i("i have received '" + msg.getData().getString("message") + "'");
Message message = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("message", "OK, bye bye~");
message.setData(bundle);
L.i("i have send '" + message.getData().getString("message") + "'");
message.what = 2;
if (serverMessenger != null){
try {
serverMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
private void communicate(){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
Message message = Message.obtain();
Bundle msg = new Bundle();
msg.putString("message", "i have send handler a message at " + simpleDateFormat.format(System.currentTimeMillis()));
message.setData(msg);
L.i("i have send '" + message.getData().getString("message") + "'");
message.what = 1;
message.replyTo = messenger;
if (serverMessenger != null){
try {
serverMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.connect_handler:
if (!hasBindService) {
Intent intent = new Intent();
intent.setClassName("com.android.messenger_a", "com.android.messenger_a.ServerWithHandler");
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
hasBindService = true;
}else{
if (serverMessenger == null){
return;
}
communicate();
}
break;
case R.id.connect_binder:
startActivity(new Intent(this, ClientForBinder.class));
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (serverMessenger != null)
unbindService(serviceConnection);
}複製代碼
運行結果以下圖:
服務器端Service先須要自定義一個Handler類用來處理客戶端發過來的消息,接着用一個Handler對象新建一個Messenger對象,這樣就至關於把發送給Messenger的message處理交給了該Handler,最後就在onBind函數中返回該Messenger的IBinder便可。
客戶端用bindService綁定該服務端service,而且在onServiceConnected回調中用IBinder參數構造一個Messenger,這個Messenger就是客戶端用來和服務端通訊的中介了,還有一點須要注意的是,爲了服務端接收到客戶端的消息以後可以回覆客戶端,在客戶端也須要新建一個Messenger,而且將其經過message.replyTo變量傳遞給服務端,服務端就可以經過該replyTo變量傳遞消息給客戶端了。緩存
該模式須要使用sharedUserId+android:permission的方式將兩個應用置於一個進程才能使用(這樣想想好像就不是跨進程通訊了呢-, -),要否則是沒法反射到相關函數的。
服務端代碼:
private final InnerBinder binder = new InnerBinder();
public class InnerBinder extends Binder {
public ServerWithBinder getServer(){
return ServerWithBinder.this;
}
}
public int add(int... ints){
int sum = 0;
for (int temp : ints){
sum += temp;
}
return sum;
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}複製代碼
客戶端代碼:
private ServiceConnection serviceConnection;
private Messenger serverMessenger;
private IBinder mBoundService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.client_binder);
findViewById(R.id.connect_binder).setOnClickListener(this);
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
mBoundService = service;
Class clazz = mBoundService.getClass();
//若是兩個應用程序沒有運行在同一個進程中,則沒法反射到該函數
Method method = clazz.getDeclaredMethod("getServer");
Object object = method.invoke(mBoundService);
Class messenger = object.getClass();
Method add = messenger.getDeclaredMethod("add", int[].class);
L.e("1+2+3=" + add.invoke(object, new int[]{1,2,3}));
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
serverMessenger = null;
}
};
}
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClassName("com.android.messenger_a", "com.android.messenger_a.ServerWithBinder");
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
if (serverMessenger != null)
unbindService(serviceConnection);
super.onDestroy();
}複製代碼
運行結果以下圖所示:
客戶端經過onServiceConnected函數回調獲取到該Binder對象,經過該Binder對象反射服務端Binder類的相關方法,在服務端的該相關方法getServer()中,直接返回該Service對象,獲取到該Service對象以後就可以成功反射該類的全部方法,也就可以成功進行兩個應用之間(雖說不是兩個進程)的通訊了。
關於Service的詳細介紹能夠看我之前的一篇博客:
blog.csdn.net/self_study/…
這裏簡單介紹Service的相關flag標識,每次經過startService(Intent)函數啓動Service都會調用到onStartCommand (Intent intent, int flags, int startId)函數,該函數第二個參數能夠爲 0,START_FLAG_REDELIVERY或者START_FLAG_RETRY: