在前面的文章Android进程间通信机制之AIDL中我们简单的介绍了AIDL的使用。在Android中我们还可以使用其他的进程间通信方式:
我们可以看到要实现进程间的通信方式有很多种,下面我们就来介绍一下使用它们如何实现进程间的通信。
Bundle通常与Intent在一起使用,它们也常用于Activity、Service和Receiver之间的数据传递。
那么为什么Bundle可以在进程间传递数据,因为Bundle实现了Parcelable接口,拥有了在进程间传递数据的能力。
public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
//......
}
它支持基本类型的数据和其他实现了Parcelable接口的对象的传递。在代码中使用它们:
在第一个Activity中:
Intent intent = new Intent(this,OtherProcessActivity.class);
Bundle extras = new Bundle();
extras.putString("test0", "string");
extras.putParcelable("parcelable", new Book(0, "bookName 0"));
intent.putExtras(extras);
startActivity(intent);
在跳转到的Activity中获取数据:
Intent intent = getIntent();
Bundle extras = intent.getExtras();
String extrasString = extras.getString("test0");
Book book = ((Book) extras.getParcelable("parcelable"));
Log.e("bundle", extrasString + " === " + book.toString());
如此,就完成了进程间数据的传递。
Messenger即为信使,用来传递Message,Message是我们要传递的数据的载体。Messenger通过在不同的进程间传递Message就可以实现进程间的通信。
Messenger其实就是用AIDL来实现的,Messenger是对AIDL的封装,所以Messenger和AIDL其实是一样的。Google建议我们在Messenger和AIDL都可以使用的情况下优先使用Messenger,因为它使用起来更加便捷。
服务端
新建一个Service类,在内部创建一个Messenger对象,用来接收Client传递过来的信息,并在处理完信息后把结果反馈给Client。
服务端代码:
public class MessengerService extends Service {
@SuppressLint("HandlerLeak")
private Messenger mMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(Message msg) {
//创建一个Message用于存放发送回Client的消息
Message sendToClientMsg = Message.obtain(msg);
switch (msg.what) {
case Constant.CLIENT_TO_SERVICE_WHAT:
try {
Bundle bundle = msg.getData();
bundle.setClassLoader(getClass().getClassLoader());
Log.e("debug", "从客户端来的消息:"+bundle.getParcelable("book").toString());
sendToClientMsg.what = Constant.SERVICE_TO_CLIENT_WHAT;
Bundle data = new Bundle();
data.putParcelable("bundle",new Book(1,"Service To Client book"));
sendToClientMsg.setData(data);
msg.replyTo.send(sendToClientMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
super.handleMessage(msg);
}
});
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger != null ? mMessenger.getBinder() : null;
}
}
客户端需要创建一个Messenger对象用来接收服务端反馈给客户端的数据,还要创建一个ServiceConnection对象获取Message对象用来向服务端发送数据。
public class OtherProcessActivity extends AppCompatActivity {
public Messenger mServiceMessenger;
@SuppressLint("HandlerLeak")
Messenger mMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constant.SERVICE_TO_CLIENT_WHAT:
Bundle bundle = msg.getData();
bundle.setClassLoader(getClass().getClassLoader());
Log.e("debug", "从服务端来的反馈"+msg.getData().getParcelable("bundle").toString());
break;
default:
break;
}
super.handleMessage(msg);
}
});
ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mServiceMessenger = new Messenger(service);
Log.e("debug", "连接成功");
}
@Override
public void onServiceDisconnected(ComponentName name) {
mServiceMessenger = null;
Log.e("debug", "连接断开");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_other_process);
getData();
bindMessengerService();
}
@Override
protected void onDestroy() {
super.onDestroy();
//解绑service
unbindService(mConnection);
}
private void bindMessengerService() {
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
Log.e("debug", "服务已绑定");
}
private void getData() {
Intent intent = getIntent();
Bundle extras = intent.getExtras();
String extrasString = extras.getString("test0");
Book book = ((Book) extras.getParcelable("parcelable"));
Log.e("bundle", extrasString + " === " + book.toString());
}
public void sendMessage(View view) {
if (mServiceMessenger == null) return;
try {
Message message = Message.obtain();
message.what = Constant.CLIENT_TO_SERVICE_WHAT;
// message.obj = new Book(2, "Client-To-Service Book");
Bundle bundle = new Bundle();
bundle.putParcelable("book", new Book(2, "Client-To-Service Book"));
message.setData(bundle);
//不要忘记此处代码
message.replyTo = mMessenger;
mServiceMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
注意:
1、在sendMessage()方法中,如果我们需要在服务端反馈结果给客户端那么必须要写上这行代码message.replyTo = mMessenger;
,否则在服务端会报空指针异常。
2、在使用Bundle传递Parcelable数据,正常情况下我们就收数据时会报错ClassNotFoundException异常。对于这种情况我们要重新设置类加载方式。
Android有两种ClassLoader:FrameWork ClassLoader和APK ClassLoader,FrameWork加载Android的class,APK可以加载用户自定义的类,apk继承自FrameWork所以都可以加载。在应用刚启动时,默认启用APK ClassLoader。当系统被回收,默认会变成FrameWork classloader。
所以我们在获取数据前需要设置一下类加载方式:
Bundle bundle = msg.getData();
bundle.setClassLoader(getClass().getClassLoader());
3、跨进程通信方式不支持msg.obj。
通过读取SD卡中的文件,获取不同进程的文件数据。
ContentProvider的底层也是用Binder实现的。
通过服务端的ContentProvider建立数据库,然后向外提供接口,在客户端使用getContentResolver()获取到的ContentResolver去对数据库进行增删改查的操作,以此来实现进程间的通信。
代码实现:
1. 继承SQLiteOpenHelper创建DbHelper类
ContentProvider与ContentResolver之间的通信其实是通过操作数据表实现的,所以先创建一个DBHelper类。
public class ProviderDbHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "ipc_provider.db";
private static final String TABLE_NAME = "book";
private static final int DB_VERSION = 1;
private final String SQL_CREATE_TABLE = "create table if not exists " + TABLE_NAME + " (_id integer primary key, name TEXT, description TEXT)";
public ProviderDbHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
2. 继承ContentProvider创建自定义Provider
ContentProvider使用Uri标识数据,它包括两个部分:
内容Uri的形式为:
content://authority/path
在我们使用ContentResolver方法来访问ContentProvider的数据表时,需要传递URI来区分要操作的内容。Android中提供了UriMatcher来解析Uri。
UriMatcher用来匹配Uri:
- 注册需要匹配的Uri路径,可以添加匹配码;
- 使用uriMatcher.match(uri)方法对输入的uri进行匹配,使用返回的匹配码进行匹配。
public class IPCProvider extends ContentProvider {
//ContentProvider 的授权字符串
public static final String AUTHORITY = "com.kanlulu.ipc_contentprovider.provider.IPCProvider";
// 内容 URI 用于在 ContentProvider 中标识数据的 URI,可以使用 content:// + authority 作为 ContentProvider 的 URI
public static final Uri uri = Uri.parse("content://" + AUTHORITY + "/book");
//在 ContentProvider 中可以通过 UriMatcher 来为不同的 URI 关联不同的 code,便于后续根据 URI 找到对应的表
private static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
public static final int CODE_BOOK = 1;
static {
uriMatcher.addURI(AUTHORITY, "book", CODE_BOOK);
}
public Context mContext;
public ProviderDbHelper dbHelper;
public SQLiteDatabase mDatabase;
public String mTableName;
@Override
public boolean onCreate() {
mContext = getContext();
initProvider();
return false;
}
private void initProvider() {
mTableName = ProviderDbHelper.TABLE_NAME;
dbHelper = new ProviderDbHelper(mContext);
mDatabase = dbHelper.getWritableDatabase();
new Thread(new Runnable() {
@Override
public void run() {
mDatabase.execSQL("delete from " + mTableName);
mDatabase.execSQL("insert into " + mTableName + " values(1,'test_book_name','test_book_desc')");
}
}).start();
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
String tableName = getTableName(uri);
Log.e("debug", tableName + " 查询数据");
return mDatabase.query(tableName, projection, selection, selectionArgs, null, sortOrder, null);
}
private String getTableName(Uri uri) {
String tableName = "";
int match = uriMatcher.match(uri);
switch (match) {
case CODE_BOOK:
tableName = ProviderDbHelper.TABLE_NAME;
}
return tableName;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
String tableName = getTableName(uri);
Log.e("debug", tableName + " 插入数据");
mDatabase.insert(tableName, null, values);
mContext.getContentResolver().notifyChange(uri, null);
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
String tableName = getTableName(uri);
Log.e("debug", tableName + " 删除数据");
int deleteCount = mDatabase.delete(tableName, selection, selectionArgs);
if (deleteCount > 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return deleteCount;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
String tableName = getTableName(uri);
int updateCount = mDatabase.update(tableName, values, selection, selectionArgs);
if (updateCount > 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return updateCount;
}
}
ContentProvider是Android中的四大组件之一,我们还需要在AndroidManifest.xml文件中声明provider标签:
<provider
android:name=".provider.IPCProvider"
android:authorities="com.kanlulu.ipc_contentprovider.provider.IPCProvider"
android:exported="false"
android:grantUriPermissions="true"
android:process=":ipc_provider" />
3. 在其他进程中通过getContentResolver对数据表进行增删改查
public class MainActivity extends AppCompatActivity {
public TextView mQueryResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mQueryResult = (TextView) findViewById(R.id.tv_query_result);
}
public void insert(View view) {
ContentResolver contentResolver = getContentResolver();
ContentValues contentValues = new ContentValues();
int id = (int) (Math.random() * 100);
contentValues.put("_id", id);
contentValues.put("name", "book-name-" + id);
contentValues.put("description", "book-description-" + id);
contentResolver.insert(IPCProvider.uri, contentValues);
}
public void query(View view) {
mQueryResult.setText("");
StringBuilder sb = new StringBuilder();
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(IPCProvider.uri, new String[]{"name", "description"}, null, null, null);
if (cursor == null) return;
while (cursor.moveToNext()) {
String result = cursor.getString(0) + " === " + cursor.getString(1);
Log.e("debug", result);
sb.append(result).append("\n");
}
mQueryResult.setText(sb.toString());
cursor.close();
}
}
如此便完成了使用ContentProvider进行进程间的通信。