Android 中的IPC机制
- IPC是跨进程,这并非Android系统独有的,像一般的跨进程通信有Socket,文件读写,管道等等。而Android独有的跨进程则是Binder机制。
1. 进程和线程
- 线程
1.1 什么是线程呢?
是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。线程有就绪、阻塞和运行三种基本状态。(网上搜的)
1.2 如何创建新的线程
- 继承Thread
private class MyThread extends Thread{
@Override
public void run() {
super.run();
//run;
}
}
MyThread myThread= new MyThread();
myThread.start();
- 实现Runnable接口
class MyThread implements Runnable {
@Override
public void run() {
}
}
public void test() {
Thread t = new Thread(new MyThread());
t.start();
}
- FutureTask方式
Thread t = new Thread();
FutureTask<Integer> futureTask = new FutureTask<Integer>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return 10;
}
});
try {
Integer integer = futureTask.get(); //返回call()返回的值,这里会阻塞,知道线程结束了才会执行
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
new Thread(futureTask).start();
说明 这里的FutureTask是Java5提供的一种新的线程实现方式,它的特点是有返回值,而且call()方法会抛出异常,FutureTask主要是内部的Callable来实现线程的这里可以在它的源码run()方法中发现
- 线程池方法
线程创建的方法就是以上四种方式,就先说到这,稍后会专门写一篇文章来总结线程,毕竟本文的重点是Binder。
- 进程
2.1 什么是进程?
系统正在运行的程序就是一个进程,进程里可以有多个线程。
2.2 Android中创建进程的方式
就是四大组件在AndroidManifest文件中指定android:process属性即可。
例如以下2种方式
<activity
android:name=".ui.activity.WebActivity"
android:process=":process" />
<activity
android:name=".ui.activity.WebActivity"
android:process="com.test.tb.remote" />
第一种 :结尾的 系统会在前面拼接上包名,并且属于当前应用独立进程,第二种则是全局进程,其他应用则可以通过ShareUID以及包名相同的方式跑在同一进程
2.多进程的优缺点
- 优点
- 对于同一个APP内如果所有模块都跑在同一进程则可能导致该进程内存分配过大,容易被系统回收。
2.多模块进程,可以保证该模块崩溃不会导致主进程崩溃从而导致APP退出。
- 缺点
- 静态成员和单例模式失效(多进程会有多个内存模块,放在内存中唯一的效果都会失效)
- Application会被创建多次
3.线程同步失效
4.SharePreference失效(底层通过读写XML文件实现,对于并发读写可能会出现问题)
3. Android中的多进程数据交互方式
- 使用Bundle,通过Intent传递过去
- 使用文件共享
- 使用Messenger
- 使用AIDL
- 使用Socket通信
- 使用ContentProvider
对于2,5 这里就不做详细介绍了,这个是万能的实现方式。
接下来详细解释每一种实现方式
第1种:使用Intent传递Bundle实现
因为对于Activity,Service,Receiver,都可以通过Intent启动,在启动的时候直接将值传过去。
第3种:使用Messenger信使实现
这里Messenger是Android替我们实现好的AIDL方式,具体的Messenger用法如下
- 客户端
public class ClientActivity extends Activity {
private Messenger mClient;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mClient = new Messenger(iBinder);
Message message = Message.obtain(null, ServerService.TAG_MESSAGE);
Bundle bundle = new Bundle();
bundle.putString("key", "value from client");
message.setData(bundle);
try {
mClient.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, ServerService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
public void sendMessage(String str) {
if (mClient != null) {
Message message = Message.obtain(null, ServerService.TAG_MESSAGE);
Bundle bundle = new Bundle();
bundle.putString("key", str);
message.setData(bundle);
try {
mClient.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
}
- 服务端
//记得把service放在新的进程里
public class ServerService extends Service {
public static final int TAG_MESSAGE = 1000;
private Handler mServerHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TAG_MESSAGE:
Bundle data = msg.getData();
String key = data.getString("key");
Log.e("tag", key);
break;
default:
super.handleMessage(msg);
break;
}
}
};
private Messenger mMessenger = new Messenger(mServerHandler);
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
以上就是Messenger的使用实例:客户端向服务器发送数据。
这里如果要服务器向客户端发送数据呢?就需要如下
- 客户端
增加了Messenger和Handler用于接收服务器发来的消息
public class ClientActivity extends Activity {
Messenger mClientMessenger;
private static class ClientHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case ServerService.TAG_MESSAGE_SERVER:
String serverKey = msg.getData().getString("serverKey");
Log.e("ServerService", serverKey);
break;
}
}
}
private Messenger mClient;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mClient = new Messenger(iBinder);
Message message = Message.obtain(null, ServerService.TAG_MESSAGE_CLIENT);
Bundle bundle = new Bundle();
bundle.putString("key", "value from client");
message.setData(bundle);
if (mClientMessenger == null) {
mClientMessenger = new Messenger(new ClientHandler());
message.replyTo = mClientMessenger; //这里很重要,这里就是将client的Messenger传递给了Server,建立了连接。
}
try {
mClient.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
public void sendMessage(String str) {
if (mClient != null) {
Message message = Message.obtain(null, ServerService.TAG_MESSAGE_CLIENT);
Bundle bundle = new Bundle();
bundle.putString("key", str);
message.setData(bundle);
try {
mClient.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Intent intent = new Intent(this, ServerService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
}
- 服务端
服务端通过客户端传过来的replyMsg来向客户端发送消息
public class ServerService extends Service {
private static final String TAG = "ServerService";
public static final int TAG_MESSAGE_CLIENT = 1000;
public static final int TAG_MESSAGE_SERVER = 1001;
private static class ServerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TAG_MESSAGE_CLIENT:
Bundle data = msg.getData();
String key = data.getString("key");
Log.e(TAG, key);
Messenger replyTo = msg.replyTo;
Message message = Message.obtain(null, TAG_MESSAGE_SERVER);
Bundle bundle = new Bundle();
bundle.putString("serverKey", "hello, I got message & send you a new message");
message.setData(bundle);
try {
replyTo.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
break;
}
}
}
;
private Messenger mMessenger = new Messenger(new ServerHandler());
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
以上就是Messenger具体使用,接下来就看看Messenger源码上的实现。
先从服务端开始
Messenger msger = new Messenger(new Handler()); // 1
public IBinder onBind(Intent intent) { //2
return mMessenger.getBinder();
}
这里看下Messenger的源码
private final IMessenger mTarget;
/**
* Create a new Messenger pointing to the given Handler. Any Message
* objects sent through this Messenger will appear in the Handler as if
* {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
* been called directly.
*
* @param target The Handler that will receive sent messages.
*/
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public IBinder getBinder() {
return mTarget.asBinder();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
这里是Handler的源码
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
private final class MessengerImpl extends IMessenger.Stub { //这里可以发现 实现是用AIDL方式
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
根据源码可以知道
步骤1根据Messenger构造函数知道,则是将target初始化,来自Handler中MessengerImpl类,对于当客户端调用Messenger的send()方法就是调用MessengerImpl内的send方法,这里然后在通过Handler的sendMessage将消息回调出来,这里可以看出来 Messenger中的handler还是Handler的本职工作,消息的回调
步骤2这里是对AIDL的返回Binder操作
对于回调,其实就是通过传递Messenger构建连接操作
第4种AIDL
可以移步我的这篇文章aidl
第6种 ContentProvider实现跨进程
-
首先是ContentProvider为什么支持跨进程呢?
ContentProvider:内容提供者,是Android专门为了不同应用间数据共享的一种方式。其实你有没有注意到,平常我们获取系统联系人,相册中的照片等时候 就是一种跨进程获取数据的方式,APP本身是一个进程,联系人,相册是在另个进程中,通过ContentProvider可以获取到。 -
自定义ContentProvider实现通信
ContentProvider主要是以表格方式存储,它暴露出来给我们的实现方法,而底层我们可以通过SQLite数据库,其他别的数据库,XML,文件,哪怕是内存中的类也可以,只要是满足它的格式即可。
接下来实现一个自己的ContentProvider
public class BookProvider extends ContentProvider {
private static final String TAG = "BookProvider";
@Override
public boolean onCreate() {
Log.d(TAG, "onCreate() called");
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Log.d(TAG, "query() called ");
return null;
}
@Override
public String getType(Uri uri) {
Log.d(TAG, "getType() called ");
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.d(TAG, "insert() called ");
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.d(TAG, "delete() called ");
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
Log.d(TAG, "update() called ");
return 0;
}
}
manifest文件生命如下
<provider
android:name=".ipc.BookProvider"
android:authorities="com.test.ipc.authorities" //这个是唯一标识
android:process=":providerpro" />
其实以上就完成了一个自定义的Provider,你可以在自己的Provider方法内进行数据存储查询
为了实现数据存储,这里可以用数据进行简单的存储
public class BookSQLite extends SQLiteOpenHelper {
private static final String DB_NAME = "book_provider.db";
public static final String BOOK_TABLE_NAME = "book";
public static final String USER_TABLE_NAME = "user";
private static final int DB_VERSION = 1;
static final String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS"
+ BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";
static final String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS"
+ USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT,sex INT)";
public BookSQLite(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK_TABLE);
db.execSQL(CREATE_USER_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
provider改后如下
public class BookProvider extends ContentProvider {
private static final String TAG = "BookProvider";
public static final String AUTHORITY = "com.test.ipc.authorities";
public static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
public static final Uri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");
public static final int BOOK_URI_CODE = 0;
public static final int USER_URI_CODE = 1;
private static final UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private SQLiteDatabase mSQLite;
private Context mContext;
static {
sMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE); //这里就将URI与对应的Code绑定,这样方便我们以后对不同的URI进行判断
sMatcher.addURI(AUTHORITY, "user", USER_URI_CODE);
}
private String getTBName(Uri uri) {
String name = null;
switch (sMatcher.match(uri)) {
case BOOK_URI_CODE:
name = BookSQLite.BOOK_TABLE_NAME;
break;
case USER_URI_CODE:
name = BookSQLite.USER_TABLE_NAME;
break;
default:
break;
}
return name;
}
@Override
public boolean onCreate() {
Log.d(TAG, "onCreate() called");
mContext = getContext();
mSQLite = new BookSQLite(getContext()).getWritableDatabase();
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Log.d(TAG, "query() called ");
String name = getTBName(uri);
if (name == null) {
return null;
}
return mSQLite.query(name, projection, selection, selectionArgs, null, null, sortOrder, null);
}
@Override
public String getType(Uri uri) {
Log.d(TAG, "getType() called ");
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.d(TAG, "insert() called ");
String name = getTBName(uri);
if (name == null) {
return null;
}
mSQLite.insert(name, null, values);
mContext.getContentResolver().notifyChange(uri, null);
return uri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.d(TAG, "delete() called ");
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
Log.d(TAG, "update() called ");
return 0;
}
}
这样就是实现了数据的存储与查找
以上就是Android中的IPC机制实现
总结
这篇文章主要是一个总章,对进程与线程进行一个总的介绍,稍后会逐一介绍线程,Binder的具体实现细节,ContentProvider的底层实现。
参考《Android开发艺术探索》&Android源码