我找到了一个简单而优雅的方法:
方法一
第一个活动的代码:
final Object objSent = new Object();
final Bundle bundle = new Bundle();
bundle.putBinder("object_value", new ObjectWrapperForBinder(objSent));
startActivity(new Intent(this, SecondActivity.class).putExtras(bundle));
Log.d(TAG, "original object=" + objSent);
第二个活动的代码:
final Object objReceived = ((ObjectWrapperForBinder)getIntent().getExtras().getBinder("object_value")).getData();
Log.d(TAG, "received object=" + objReceived);
您会发现objSent 和objReceived 具有相同的hashCode,因此它们是相同的。
但是为什么我们可以这样传递一个java对象呢?
实际上,android binder 会为 java 对象创建全局 JNI 引用,并在该 java 对象没有引用时释放这个全局 JNI 引用。 binder 会将这个全局 JNI 引用保存在 Binder 对象中。
*注意:此方法仅在两个活动在同一进程中运行时才有效,否则在 (ObjectWrapperForBinder)getIntent().getExtras().getBinder("object_value") *
类 ObjectWrapperForBinder 定义
public class ObjectWrapperForBinder extends Binder {
private final Object mData;
public ObjectWrapperForBinder(Object data) {
mData = data;
}
public Object getData() {
return mData;
}
}
方法二
- 对于发件人,
- 使用自定义本地方法将您的 java 对象添加到 JNI 全局引用表(通过 JNIEnv::NewGlobalRef)
- 将返回的整数(实际上是JNIEnv::NewGlobalRef 返回的jobject,它是一个指针,我们可以安全地将其转换为int)到你的Intent(通过Intent::putExtra)
- 用于接收器
- 从 Intent 中获取整数(通过 Intent::getInt)
- 使用自定义本地方法从 JNI 全局引用表中恢复您的 java 对象(通过 JNIEnv::NewLocalRef)
- 从 JNI 全局引用表中删除项目(通过
JNIEnv::DeleteGlobalRef),
但是方法2有个小问题,如果receiver恢复java对象失败(比如恢复java对象之前发生异常,或者receiver Activity根本不存在),那么java对象将成为孤儿或内存泄漏,
方法1没有这个问题,因为android binder会处理这个异常
方法3
要远程调用java对象,我们将创建一个数据契约/接口来描述java对象,我们将使用aidl文件
IDataContract.aidl
package com.example.objectwrapper;
interface IDataContract {
int func1(String arg1);
int func2(String arg1);
}
第一个活动的代码
final IDataContract objSent = new IDataContract.Stub() {
@Override
public int func2(String arg1) throws RemoteException {
// TODO Auto-generated method stub
Log.d(TAG, "func2:: arg1=" + arg1);
return 102;
}
@Override
public int func1(String arg1) throws RemoteException {
// TODO Auto-generated method stub
Log.d(TAG, "func1:: arg1=" + arg1);
return 101;
}
};
final Bundle bundle = new Bundle();
bundle.putBinder("object_value", objSent.asBinder());
startActivity(new Intent(this, SecondActivity.class).putExtras(bundle));
Log.d(TAG, "original object=" + objSent);
第二个活动的代码:
将 AndroidManifest.xml 中的 android:process 属性更改为非空进程名称,以确保第二个活动在另一个进程中运行
final IDataContract objReceived = IDataContract.Stub.asInterface(getIntent().getExtras().getBinder("object_value"));
try {
Log.d(TAG, "received object=" + objReceived + ", func1()=" + objReceived.func1("test1") + ", func2()=" + objReceived.func2("test2"));
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
这样,我们可以在两个activity之间传递一个接口,即使它们运行在不同的进程中,并远程调用接口方法
方法四
方法3似乎不够简单,因为我们必须实现一个aidl接口。
如果你只是想做简单的任务,不需要方法返回值,我们可以使用android.os.Messenger
第一个活动的代码(发件人):
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
public static final int MSG_OP1 = 1;
public static final int MSG_OP2 = 2;
public static final String EXTRA_MESSENGER = "messenger";
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
Log.e(TAG, "handleMessage:: msg=" + msg);
switch (msg.what) {
case MSG_OP1:
break;
case MSG_OP2:
break;
default:
break;
}
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startActivity(new Intent(this, SecondActivity.class).putExtra(EXTRA_MESSENGER, new Messenger(mHandler)));
}
}
第二个活动的代码(接收者):
public class SecondActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
final Messenger messenger = getIntent().getParcelableExtra(MainActivity.EXTRA_MESSENGER);
try {
messenger.send(Message.obtain(null, MainActivity.MSG_OP1, 101, 1001, "10001"));
messenger.send(Message.obtain(null, MainActivity.MSG_OP2, 102, 1002, "10002"));
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
所有的 Messenger.send 都会在一个 Handler 中异步顺序执行。
其实android.os.Messenger也是一个aidl接口,如果你有android源码的话,可以找到一个名为IMessenger.aidl的文件
package android.os;
import android.os.Message;
/** @hide */
oneway interface IMessenger {
void send(in Message msg);
}