一、介绍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可以跨进程和网络,但安全风险较大。开发者应根据具体场景选择最合适的进程间通信方式。