在Android中每个app都由Activity和Service组成的,这而Activity和Service可能运行在同一个进程中,也可能在不同的进程中。
那么,不在同一个进程的Activity或者Service是如何通信的呢?这时候就要用到Binder进程间通信机制了。
而接下来要使用的一切都是基于Binder完成的,在Android中,无Binder不Andorid.
Bundle
public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}
Bundle已经实现了Parcelable接口,在它的序列化与反序列化特性下,他可以在不同的进程中进行数据的传输.
常规的是Intent又可以对Bundle进行传递数据,因此我们经常在一个activity,service中利用intent传输数据就是Bundle的利用之一
- ProcessActivity数据传输方
Bundle bundle = new Bundle();
bundle.putString("ipc_bundle", "BundleName");
Intent intent=new Intent(ProcessActivity.this,ProcessActivity2.class);
intent.putExtras(bundle);
startActivity(intent);
- ProcessActivity2数据接收方
Bundle bundle=getIntent().getExtras();
String ipc=bundle.getString("ipc_bundle");
Messenger
Messenger属于轻量化的IPC通信,他的底层是基于AIDL实现.可以在不同的进程中传输Message对象,客户端发送一个Message到服务端,服务端借到消息后对消息进行处理在包装成Message再次反馈到客户端.
- 服务端
public class MessengerService extends Service {
public static final String TAG = "MessengerService";
private HandlerThread handlerThread;
private Handler handler;
//Messenger
private Messenger messenger;
@Override
public void onCreate() {
super.onCreate();
int pid = android.os.Process.myPid();
Log.d(TAG, "进程Id:" + pid + "");
//HandlerThread,使用Looper处理队列消息
handlerThread = new HandlerThread("messenger_server");
handlerThread.start();
//获取Looper
Looper looper = handlerThread.getLooper();
//让Handler 运行在HandlerThread中
handler = new Handler(looper) {
@Override
public void handleMessage(Message msg) {
replyToClientMsg(msg);//回复客户端消息
super.handleMessage(msg);
}
};
messenger = new Messenger(handler);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
private void replyToClientMsg(Message msg) {
switch (msg.what) {
case Constants.MSG_FROM_CLIENT://接受消息处理
//模拟器服务器响应过程
Log.d(TAG, "msg what= [" + msg.what + "" + "]");
Log.d(TAG, "msg arg1= [" + msg.arg1 + "" + "]");
Log.d(TAG, "msg arg2= [" + msg.arg2 + "" + "]");
Log.d(TAG,"客户端发送的消息:"+msg.getData().getString(Constants.MESSENGER_KEY));
Toast.makeText(MessengerService.this, msg.getData().getString(Constants.MESSENGER_KEY), Toast.LENGTH_SHORT).show();
try {
Messenger msgFromClient = msg.replyTo;//客户端回调
Message replyMsgToClient = Message.obtain(null, Constants.MSG_FROM_SERVICE);//回复给客户端的消息
Bundle bundle = new Bundle();
bundle.putString(Constants.MESSENGER_KEY, "我也爱你");
replyMsgToClient.setData(bundle);
msgFromClient.send(replyMsgToClient);//发送Message消息体给客户端
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
服务端通过Service来回应客户端的请求,通过一个Handler来实例化一个Messenger对象,在onBind中返回底层的Binder对象.
因为客户端可能一次性或者多次发送消息,在服务端需要一个消息队列处理数据信息,因此使用HandlerThread,实现使用Looper来维护消息队列.以此保证不会出现并发的情况。
- 客户端
public class MessengerActivity extends AppCompatActivity {
private static final String TAG = MessengerActivity.class.getSimpleName();
private Button button;
private Messenger messengerService;//服务端Service
private Messenger messengerClient = new Messenger(new MessageHandler());//客户端Messenger
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
int pid = android.os.Process.myPid();
Log.d(TAG, "进程Id:" + pid + "");
bindMessengerService();
button = (Button) findViewById(R.id.btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
sendMessage();
}
}).start();
}
});
}
//绑定服务
public void bindMessengerService() {
Intent mIntent = new Intent(this, MessengerService.class);
bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
Log.e(TAG, "bindService invoked !");
}
//发送消息
public void sendMessage() {
Message msgFromClient = Message.obtain(null, Constants.MSG_FROM_CLIENT, 1, 2);
Bundle data = new Bundle();
data.putString(Constants.MESSENGER_KEY, "我爱你,你爱我吗?");
msgFromClient.setData(data);
msgFromClient.replyTo = messengerClient;
try {
messengerService.send(msgFromClient);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
unbindService(mServiceConnection);
super.onDestroy();
}
private class MessageHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constants.MSG_FROM_SERVICE:
Log.d(TAG,"服务端回复消息:"+msg.getData().getString(Constants.MESSENGER_KEY));
Toast.makeText(MessengerActivity.this, msg.getData().getString(Constants.MESSENGER_KEY) , Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
/**
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
messengerService = new Messenger(service);
}
/**
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
messengerService = null;
}
};
}
- 图示
09-08 03:12:19.052 15181-15228/com.allure.study D/MessengerService: 客户端发送的消息:我爱你,你爱我吗?
09-08 03:12:19.056 15181-15181/com.allure.study D/MessengerActivity: 服务端回复消息:我也爱你
客户端主要用于绑定服务端,绑定之后生成一个Messenger,通过这个Messenger可以实现想服务端的消息发送.
服务端收到消息之后的反馈需要客户端使用Message.reply回调给服务端,服务端在同样使用Message.reply即可实现双向的通信.
特别注意在客户端的2个Messenger,一个是通过绑定服务端的获取的,一个是自身创建给服务端的回调.
AIDL
ADIL可以处理并发和跨进程.
前面的Messenger的底层也是基于AIDL,但是在客户端有多个消息同时发送到客户端,如果按照Messenger的方式只能单独的在消息队列一个个处理,在处理这种场景时就更推荐使用AIDL进行解决.
- 服务端
创建一个AIDL文件,结尾为.aidl
AIDL的数据类型
- 基本的数据类型
- HashMap,ArrayList,实现Parcelable的对象
- AIDL接口
如果AIDL中使用了自定义的Parcelable,必须建一个同名的AIDL文件
public class AIDLService extends Service {
private static final String TAG = "AIDLService";
public AIDLService() {
}
@Override
public void onCreate() {
Log.e(TAG, "onCreate");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.e(TAG, "onDestroy");
super.onDestroy();
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
Log.e(TAG, "onRebind");
}
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
Log.e(TAG, "onTaskRemoved");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind");
return binder;
}
private Binder binder = new TestAIDL.Stub() {
@Override
public String reply(String s) throws RemoteException {
if(s.equals("你爱我吗")){
return "我爱你";
}else {
return "没有你爱我,我怎么爱你";
}
}
};
}
- 客户端
public class AIDLActivity extends AppCompatActivity {
private static final String TAG = AIDLActivity.class.getSimpleName();
private Button button;
private TestAIDL testAIDL;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
//绑定AIDL服务
bindService();
button = (Button) findViewById(R.id.btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
String s = testAIDL.reply("你爱我吗");
Log.d(TAG,"你爱我吗");
Log.d(TAG,s);
Toast.makeText(AIDLActivity.this, "你爱我吗", Toast.LENGTH_SHORT).show();
Toast.makeText(AIDLActivity.this, s, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
unBindServcie();
}
private void unBindServcie() {
unbindService(mServiceConnection);
}
private void bindService() {
Intent mIntent = new Intent(this, AIDLService.class);
bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
Log.e(TAG, "bindService invoked !");
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
testAIDL = TestAIDL.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
testAIDL = null;
}
};
}
- 图示
和Messenger类似,绑定服务,获取aidl对象,接口回调得到相应的服务响应信息.
源码中可以看到AIDL.Stub属于Binder子类,所以AIDL也是基于Binder来完成的
ContentProvider
ContentProvider和Messenger一样底层也是基于AIDL Binder,和Messenger一样在系统都做了一定程度封装,便于上层的调用.最常见的便是我们获取通讯录使用ContentProvider
简单实现一个ContentProvider的联系人增删改查的例子.
- 数据库
public class DBOpenHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "contacts.db";
public static final String CONTACTS_TABLE_NAME = "contacts";
private static final String CREATE_CONTACTS_TABLE = "CREATE TABLE IF NOT EXISTS "
+ CONTACTS_TABLE_NAME + " (phonenumber CHAR(11) PRIMARY KEY, name TEXT)";
public static final int DB_VESION = 1;
public DBOpenHelper(Context context) {
super(context, DB_NAME, null, DB_VESION);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL(CREATE_CONTACTS_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
- ContentProvider
public class TestContentProvider extends ContentProvider {
private static final String TAG = TestContentProvider.class.getSimpleName();
private static final String AUTHORITY = "com.allure.study.interprocesscommunication.contentprovider";
public static final String CONTACTS_URI = "content://" + AUTHORITY + "/" + DBOpenHelper.CONTACTS_TABLE_NAME;
private static final int CONTACTS_TABLE_CODE = 0;
private UriMatcher uriMatcher;
private SQLiteDatabase sqLiteDatabase;
@Override
public boolean onCreate() {
Log.v(TAG, "ContactsProvider进程Id " + android.os.Process.myPid());
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, DBOpenHelper.CONTACTS_TABLE_NAME, CONTACTS_TABLE_CODE);
sqLiteDatabase = new DBOpenHelper(getContext()).getWritableDatabase();
return false;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
String tableName = getTableName(uri);
if(tableName == null) {
throw new IllegalArgumentException("Uri错误: " + uri);
}
Cursor cursor = sqLiteDatabase.query(tableName, strings, s, strings1, s1, null, null);
return cursor;
}
/**
* @param uri
* @return
*/
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
String tableName = getTableName(uri);
if(tableName == null) {
throw new IllegalArgumentException("Erro Uri: " + uri);
}
sqLiteDatabase.insert(tableName, null, contentValues);
getContext().getContentResolver().notifyChange(uri, null);
return null;
}
@Override
public int delete(Uri uri, String s, String[] strings) {
String tableName = getTableName(uri);
if(tableName == null) {
throw new IllegalArgumentException("Erro Uri: " + uri);
}
int count = sqLiteDatabase.delete(tableName, s, strings);
if(count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return 0;
}
@Override
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
return 0;
}
private String getTableName(Uri uri) {
String tableName = null;
switch (uriMatcher.match(uri)) {
case CONTACTS_TABLE_CODE:
tableName = DBOpenHelper.CONTACTS_TABLE_NAME;
break;
}
return tableName;
}
}
由于ContentProvider属于四大组件因此我们在清单文件中需要对其进行注册
** authorities和TestContentProvider类里的AUTHORITY徐亚保持一致**
** name代表ContentProvider类**
- Activity 调用
ublic class ContentProviderActivity extends AppCompatActivity {
private static final String TAG = ContentProviderActivity.class.getSimpleName();
private Uri uri;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content_provider);
Log.v(TAG, "Activity进程Id " + android.os.Process.myPid());
uri = Uri.parse(TestContentProvider.CONTACTS_URI);
}
public void insert(View v) {
ContentValues values = new ContentValues();
values.put("phoneNumber", "15000000001");
values.put("name", "Jacye");
getContentResolver().insert(uri, values);
Log.v(TAG, "插入了 " + "Jacye " + "电话号码: 15000000001");
}
public void delete(View v) {
getContentResolver().delete(uri, "name=?", new String[]{"Jacye"});
}
public void update(View v) {
}
public void query(View v) {
String[] colum = new String[]{"phoneNumber", "name"};
Cursor cursor = getContentResolver().query(uri, colum, null, null, null);
while (cursor.moveToNext()) {
String phoneNumber = cursor.getString(0);
String name = cursor.getString(1);
Log.v(TAG, "获取到联系人 " + phoneNumber + " " + name);
}
cursor.close();
}
}
显示
09-08 05:04:40.978 31726-31726/com.allure.study V/ContentProviderActivity: 插入了 Jacye 电话号码: 13333333333
09-08 05:04:42.870 31726-31726/com.allure.study V/ContentProviderActivity: 获取到联系人 13333333333 Jacye
总结
到此,Android中常用的IPC方式就已经实现了,当然还有其他的实现方式,比如说文件共享,Scoket通信等,在这里只讲了Android 常用的.
- Messenger适用于单一无并发的场景,也就是说同一个app的不同进程
- AIDL多用于并发跨进程的场景(比如说2个app之间的交互),稳定快速高效是他的优点,但是在通信约束上需要客户端服务端都把AIDL加入自身项目。
- contentprovider多用于跨进程和数据共享方面,相比较于AIDL来说更轻便