Encontrei um método simples e elegante:
- SEM Parcelável
- NÃO serializável
- SEM campo estático
- No Event Bus
Método 1
Código para a primeira atividade:
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);
Código para a segunda atividade:
final Object objReceived = ((ObjectWrapperForBinder)getIntent().getExtras().getBinder("object_value")).getData();
Log.d(TAG, "received object=" + objReceived);
você encontrará objSent
e objReceived
terá o mesmohashCode
, então eles são idênticos.
Mas por que podemos passar um objeto java dessa maneira?
Na verdade, o fichário do Android criará uma referência JNI global para o objeto java e liberará essa referência JNI global quando não houver uma referência para esse objeto java. O fichário salvará essa referência JNI global no objeto Binder.
* CUIDADO: esse método funciona apenas a menos que as duas atividades sejam executadas no mesmo processo; caso contrário, jogue ClassCastException em (ObjectWrapperForBinder) getIntent (). GetExtras (). GetBinder ("object_value") *
classe definição de ObjectWrapperForBinder
public class ObjectWrapperForBinder extends Binder {
private final Object mData;
public ObjectWrapperForBinder(Object data) {
mData = data;
}
public Object getData() {
return mData;
}
}
Método 2
- para o remetente,
- use o método nativo personalizado para adicionar seu objeto java à tabela de referência global da JNI (via JNIEnv :: NewGlobalRef)
- coloque o número inteiro de retorno (na verdade, JNIEnv :: NewGlobalRef return jobject, que é um ponteiro, podemos convertê-lo para int com segurança) no Intent (via Intent :: putExtra)
- para o receptor
- obter número inteiro de Intent (via Intent :: getInt)
- use o método nativo customizado para restaurar seu objeto java da tabela de referência global JNI (via JNIEnv :: NewLocalRef)
- remova o item da tabela de referência global JNI (via JNIEnv :: DeleteGlobalRef),
Mas o Método 2 tem um problema pequeno, mas sério, se o receptor falhar ao restaurar o objeto java (por exemplo, alguma exceção ocorre antes de restaurar o objeto java ou a Atividade do receptor não existir), o objeto java se tornará um órfão ou vazamento de memória, o método 1 não tem esse problema, porque o fichário do Android lidará com essa exceção
Método 3
Para chamar o objeto java remotamente, criaremos um contrato / interface de dados para descrever o objeto java, usaremos o arquivo aidl
IDataContract.aidl
package com.example.objectwrapper;
interface IDataContract {
int func1(String arg1);
int func2(String arg1);
}
Código para a primeira atividade
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);
Código para a segunda atividade:
altere o atributo android: process no AndroidManifest.xml para um nome de processo não vazio para garantir que a segunda atividade seja executada em outro processo
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();
}
Dessa maneira, podemos transmitir uma interface entre duas atividades, mesmo que elas sejam executadas em processos diferentes, e chamar o método da interface remotamente
Método 4
O método 3 parece não ser simples o suficiente, porque precisamos implementar uma interface de auxílio. Se você quiser apenas uma tarefa simples e o valor de retorno do método for desnecessário, podemos usar android.os.Messenger
Código da primeira atividade (remetente):
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)));
}
}
Código para a segunda atividade (destinatário):
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();
}
}
}
Todo o Messenger.send será executado em um manipulador de forma assíncrona e seqüencial.
Na verdade, android.os.Messenger também é uma interface de auxílio, se você tiver o código-fonte do Android, poderá encontrar um arquivo chamado IMessenger.aidl
package android.os;
import android.os.Message;
/** @hide */
oneway interface IMessenger {
void send(in Message msg);
}