一、介绍IPC
IPC(Inter-Process Communication)是指进程间通信,是系统中至关重要的一部分,尤其在Android系统的多进程设计中更是不可或缺的。Android系统的进程间通信方式主要有:Bundle、Messenger、AIDL、ContentProvider和Socket。
二、Bundle
Bundle是一个存储单元,可以在不同组件之间传递数据。例如,Activity之间通过Intent传递数据常使用Bundle。在Activity A中:
Intent intent = new Intent(A.this, B.class);
Bundle bundle = new Bundle();
bundle.putString("key", "value");
intent.putExtras(bundle);
startActivity(intent);
在Activity B中:
Bundle bundle = getIntent().getExtras();
String value = bundle.getString("key");
Bundle的使用简单、灵活,但只适用于进程内通信,不适用于跨进程通信。
三、Messenger
Messenger是一种轻量型的进程间通信方式,基于Message传递数据。它是一对一的通信方式,需要一个客户端和服务端。在客户端中:
Messenger mService = null;
ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
Intent intent = new Intent();
intent.setAction("com.example.service.MessengerService");
// 绑定 MessengerService
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
// 发送数据到服务端
Message msg = Message.obtain(null, MessengerService.MSG_HELLO, 0, 0);
Bundle bundle = new Bundle();
bundle.putString("data", "Hello, this is from client.");
msg.setData(bundle);
msg.replyTo = new Messenger(new MessengerHandler());
mService.send(msg);
服务端实现一个Handler接收来自客户端的数据:
class MessengerHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_HELLO:
Bundle bundle = msg.getData();
String data = bundle.getString("data");
Log.i(TAG, "receive message from client: " + data);
Messenger client = msg.replyTo;
Message replyMsg = Message.obtain(null, MessengerService.MSG_REPLY_HELLO, 0, 0);
Bundle replyData = new Bundle();
replyData.putString("replyData", "Hello, this is from service.");
replyMsg.setData(replyData);
try {
client.send(replyMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
Messenger的优点在于简单易用,能够在进程间传递数据,但也有一些缺点,如只适用于进程间一对一通信。
四、AIDL
AIDL(Aandroid Interface Definition Language)是Android中一种语言结构定义语言,是一种基于IPC机制的远程服务调用方式。这种通信方式可以让客户端调用服务端提供的接口,并进行跨进程通信。客户端和服务端的通信抽象成了“代理-运行时数据交换-存根”几个步骤。客户端通过AIDL接口向服务端发送数据,服务端对数据进行处理,通过AIDL返回数据给客户端。在客户端中:
// 定义ServiceConnection,用于绑定服务
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// 转成服务端的AIDL接口
mService = ServiceAIDL.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
// 绑定服务
Intent intent = new Intent();
intent.setAction("com.example.service.AIDLService");
// 绑定 AIDLService
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
// 发送数据到服务端,并接收返回值
try {
int result = mService.add(1, 2);
Log.i(TAG, "add result is " + result);
} catch (RemoteException e) {
e.printStackTrace();
}
服务端实现AIDL接口:
public class AIDLService extends Service {
private final ServiceAIDL.Stub mBinder = new ServiceAIDL.Stub() {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
AIDL通信方式可以实现进程间通信,支持单向、双向、in、out和inout等多种数据类型,但使用过程复杂。
五、ContentProvider
ContentProvider是一种跨进程数据共享的方法,是一种标准的Android数据管理方式,可以对数据进行CRUD(增删改查)操作。在ContentProvider中,提供了一些对外公开的接口,供其他应用程序查询和修改数据。在一个APP中,通过query/update/insert/delete方法,直接对数据库进行操作;在其他APP中,通过ContentResolver中的query/update/insert/delete方法实现对ContentProvider中数据的操作。
在ContentProvider中,需要实现以下方法:
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
SQLiteDatabase database = mDatabaseHelper.getWritableDatabase();
long rowId = database.insert(TABLE_NAME, null, values);
if (rowId > 0) {
Uri noteUri = ContentUris.withAppendedId(CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(noteUri, null);
return noteUri;
}
return null;
}
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase database = mDatabaseHelper.getReadableDatabase();
Cursor cursor = null;
switch (uriMatcher.match(uri)) {
case NOTES:
cursor = database.query(TABLE_NAME, projection, selection, selectionArgs,
null, null, sortOrder);
break;
case NOTE_ID:
String id = uri.getPathSegments().get(1);
cursor = database.query(TABLE_NAME, projection, "_id=?", new String[]{id},
null, null, sortOrder);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
SQLiteDatabase database = mDatabaseHelper.getWritableDatabase();
int count;
switch (uriMatcher.match(uri)) {
case NOTES:
count = database.update(TABLE_NAME, values, selection, selectionArgs);
break;
case NOTE_ID:
String id = uri.getPathSegments().get(1);
count = database.update(TABLE_NAME, values, "_id=?", new String[]{id});
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase database = mDatabaseHelper.getWritableDatabase();
int count;
switch (uriMatcher.match(uri)) {
case NOTES:
count = database.delete(TABLE_NAME, selection, selectionArgs);
break;
case NOTE_ID:
String id = uri.getPathSegments().get(1);
count = database.delete(TABLE_NAME, "_id=?", new String[]{id});
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
其他应用程序可以通过ContentResolver调用ContentProvider的方法。例如,在其他APP中查询数据:
Uri uri = Uri.parse("content://com.example.myprovider/notes");
Cursor cursor = getContentResolver().query(uri, new String[]{"_id", "title", "content"},
null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
int id = cursor.getInt(cursor.getColumnIndex("_id"));
String title = cursor.getString(cursor.getColumnIndex("title"));
String content = cursor.getString(cursor.getColumnIndex("content"));
Log.i(TAG, "id: " + id + ", title: " + title + ", content: " + content);
}
cursor.close();
}
ContentProvider是Android通信机制中比较底层的一种,使用较为灵活,适用于数据共享、安全控制等场景。
六、Socket
Socket是一种基于网络的IPC机制,是最为底层的IPC通信方式,需要自己启动服务端进程进行监听。在服务端中:
// 启动服务端,监听客户端的请求
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
Socket socket = serverSocket.accept();
new Thread(new SocketHandler(socket)).start();
}
在客户端中:
// 发送数据到服务端
Socket socket = new Socket("127.0.0.1", 8080);
OutputStream os = socket.getOutputStream();
os.write("Hello server".getBytes());
// 读取服务端返回的数据
InputStream is = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
String msg = new String(baos.toByteArray());
Log.i(TAG, "receive message from server: " + msg);
Socket可以实现跨进程、跨网络通信,但需要自己实现数据的序列化和反序列化,同时也存在一些安全风险。
七、总结
Android系统提供了多种进程间通信方式,开发者可以根据实际需求选择合适的方式实现进程间通信。Bundle和Messenger容易掌握但适用范围有限、AIDL功能强大但使用较为复杂、ContentProvider能够实现数据共享控制等高级功能、Socket可以跨进程和网络,但安全风险较大。开发者应根据具体场景选择最合适的进程间通信方式。
原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/286474.html
微信扫一扫
支付宝扫一扫