私はシンプルでエレガントな方法を見つけました:
- 小包不可
- シリアライズ不可
- 静的フィールドなし
- イベントバスなし
方法1
最初のアクティビティのコード:
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);
2番目のアクティビティのコード:
final Object objReceived = ((ObjectWrapperForBinder)getIntent().getExtras().getBinder("object_value")).getData();
Log.d(TAG, "received object=" + objReceived);
あなたは見つけるでしょうobjSent
&objReceived
同じを持っていますhashCode
するので、彼らは同じです。
しかし、なぜこの方法でJavaオブジェクトを渡すことができるのでしょうか。
実際、Androidバインダーは、JavaオブジェクトのグローバルJNI参照を作成し、このJavaオブジェクトの参照がない場合、このグローバルJNI参照を解放します。バインダーは、このグローバルJNI参照をバインダーオブジェクトに保存します。
*注意:このメソッドは、2つのアクティビティが同じプロセスで実行されない限り機能します。それ以外の場合は、(ObjectWrapperForBinder)getIntent()。getExtras()。getBinder( "object_value")*でClassCastExceptionをスローします。
クラスObjectWrapperForBinder defination
public class ObjectWrapperForBinder extends Binder {
private final Object mData;
public ObjectWrapperForBinder(Object data) {
mData = data;
}
public Object getData() {
return mData;
}
}
方法2
- 送信者にとって
- カスタムネイティブメソッドを使用して、JavaオブジェクトをJNIグローバル参照テーブルに追加します(JNIEnv :: NewGlobalRefを使用)。
- 戻り整数(実際には、JNIEnv :: NewGlobalRef return jobject、これはポインターです。これをintに安全にキャストできます)をIntent :: putExtraを介して
- 受信機用
- Intentから整数を取得する(Intent :: getIntを使用)
- カスタムネイティブメソッドを使用して、JNIグローバル参照テーブルからJNIEnv :: NewLocalRefを介してJavaオブジェクトを復元します
- JNIグローバル参照テーブルからアイテムを削除します(JNIEnv :: DeleteGlobalRefを使用)。
ただし、方法2には少し深刻な問題があります。レシーバーがJavaオブジェクトの復元に失敗した場合(たとえば、Javaオブジェクトを復元する前に何らかの例外が発生した場合、またはレシーバーアクティビティがまったく存在しない場合)、Javaオブジェクトは孤立またはメモリリーク、方法1にはこの問題はありません。Androidバインダーがこの例外を処理するためです。
方法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);
2番目のアクティビティのコード:
AndroidManifest.xmlのandroid:process属性を空でないプロセス名に変更して、2番目のアクティビティが別のプロセスで実行されるようにします
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();
}
このようにして、2つのアクティビティが異なるプロセスで実行されている場合でも、それらの間でインターフェースを渡し、インターフェースメソッドをリモートで呼び出すことができます。
方法4
方法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)));
}
}
2番目のアクティビティ(レシーバー)のコード:
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は、ハンドラーで非同期かつ順次実行されます。
実際には、android.os.Messengerも補助インターフェイスです。androidソースコードがある場合は、IMessenger.aidlという名前のファイルを見つけることができます。
package android.os;
import android.os.Message;
/** @hide */
oneway interface IMessenger {
void send(in Message msg);
}