一、介紹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