一、介紹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/zh-tw/n/286474.html
微信掃一掃
支付寶掃一掃