看了一本书,上面有一章讲解了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: