上一篇文章中我们已经实现了进程间的通信,但是文章最后留了一个问题,就是实现进程间通信是不是只用通过aidl?答案是no,这篇文章我就带大家通过纯代码的方法实现以下进程间的通信。
1,服务端,我们新建一个Service,取名为MyCodeService.java,代码如下:
package com.wms.github.aidl.server;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
/**
* Created by 王梦思 on 2017/5/25.
*/
public class MyCodeService extends Service {
private static final String TAG = "MyService";
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind...");
//这里不能返回null,必须要返回我们创建的Binder对象
return new MyBinder();
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(TAG, "onUnbind...");
return super.onUnbind(intent);
}
@Override
public void onStart(Intent intent, int startId) {
Log.e(TAG, "onStart...");
super.onStart(intent, startId);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand...");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onCreate() {
Log.e(TAG, "onCreate...");
super.onCreate();
}
@Override
public void onDestroy() {
Log.e(TAG, "onDestroy...");
super.onDestroy();
}
/**
* 这里一定要继承自String2UpperCase.Stub
*/
class MyBinder extends Binder {
private static final String DESCRIPTOR = "com.wms.MyBinder";
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
//code是和操作对应,这个code和客户端code对应
switch (code) {
case 0x001:
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = _arg0.toUpperCase();
reply.writeNoException();
reply.writeString(_result);
return true;
}
return super.onTransact(code, data, reply, flags);
}
}
}
在MyCodeService中我们将MyBinder直接继承自Binder对象,aidl中式继承自Android Studio自动生成的java文件。在MyBinder里面,我们重写了onTransact方法,这个方法是在客户端发送请求的时候会调用transact方法,而transact方法内部代码如下:
/**
* Default implementation rewinds the parcels and calls onTransact. On
* the remote side, transact calls into the binder to do the IPC.
*/
public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
在transact内部回调了onTransact方法,所以在客户端发送消息后,就会调用到MyCodeService中的 onTransact方法,处理完数据后直接返回给客户端。
注意别忘记了在AndroidManifest.xml文件中注册MyCodeService.java
<service
android:name="com.wms.github.aidl.server.MyCodeService"
android:exported="true">
<intent-filter>
<action android:name="com.wms.github.aidl.server.MyCodeService"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
这样服务端的代码就完成了,其实很简单。
2,客户端,新建一个CodeActivity,代码如下:
package com.wms.github.aidl.client;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
/**
* Created by 王梦思 on 2017/5/25.
*/
public class CodeActivity extends MainActivity {
private EditText mEditText;
private IBinder mBinder;
private static final String DESCRIPTOR = "com.wms.MyBinder";
private ServiceConnection mServiceConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//当绑定成功后调用
mBinder = service;
Log.e("MainActivity", "onServiceConnected...");
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBinder = null;
Log.e("MainActivity", "onServiceDisconnected...");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEditText = (EditText) findViewById(R.id.id_edittext);
findViewById(R.id.bind).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bindService();
}
});
findViewById(R.id.unbind).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unBindService();
}
});
findViewById(R.id.invokeServer).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
invokeServer();
}
});
}
/**
* 绑定服务
*/
public void bindService() {
Intent intent = new Intent();
intent.setAction("com.wms.github.aidl.server.MyCodeService");
//Android 5.0以上必须要加这句代码,不然报错
intent.setPackage("com.wms.github.aidl.server");
bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
}
public void unBindService() {
unbindService(mServiceConn);
}
public void invokeServer() {
String inputStr = mEditText.getText().toString().trim();
try {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String result = "";
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(inputStr);
mBinder.transact(0x001, _data, _reply, 0);
_reply.readException();
result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
//重新显示到Edittext
mEditText.setText(result);
} catch (RemoteException e) {
//这里会抛出远程异常
e.printStackTrace();
}
}
}
这里需要注意的是,在onServiceConnected中,aidl实现进程通信的时候是调用Stub内部方法asInterface来生成一个String2Uppercase对象的,这里没有这么做,因为我们没有String2Uppercase类,这里直接在CodeActivity中保存onServiceConnected回调回来的ibinder,大家可能会疑问这个ibinder是什么呢?其实就是我们上一篇文章中所说的binder驱动,不明白的可以去看上一篇文章。
当点击调用服务端转换按钮时,会执行如下代码
String inputStr = mEditText.getText().toString().trim();
try {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String result = "";
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(inputStr);
mBinder.transact(0x001, _data, _reply, 0);
_reply.readException();
result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
//重新显示到Edittext
mEditText.setText(result);
} catch (RemoteException e) {
//这里会抛出远程异常
e.printStackTrace();
}
关键的代码就是mBinder.transact(0x001, _data, _reply, 0);
这行代码其实就是让Binder驱动传送数据给服务端,其实内部和aidl实现一模一样,只是这些操作aidl利用apt工具自动给我们生成了而已。大家可以去查看下aidl文件生成的java文件就明白了。
通过本文的分析,大家是不是更加清晰了aidl的机制呢?也解决了我们文章开头的问题,解决进程通信没必要非要写aidl,aidl只是一个辅助我们更简单实现通信的。其实android系统中很多地方都用到了代码的方式来获取系统服务。这里我就不展开说了,如果有兴趣可以去查看framework代码。