Encontré un método simple y elegante:
- NO parcelable
- NO serializable
- NO campo estático
- No hay bus de eventos
Método 1
Código para la primera actividad:
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 la segunda actividad:
final Object objReceived = ((ObjectWrapperForBinder)getIntent().getExtras().getBinder("object_value")).getData();
Log.d(TAG, "received object=" + objReceived);
encontrarás objSent
y objReceived
tendrás lo mismohashCode
, por lo que son idénticos.
Pero, ¿por qué podemos pasar un objeto Java de esta manera?
En realidad, Android Binder creará una referencia JNI global para el objeto Java y liberará esta referencia JNI global cuando no haya ninguna referencia para este objeto Java. Binder guardará esta referencia JNI global en el objeto Binder.
* PRECAUCIÓN: este método SOLO funciona a menos que las dos actividades se ejecuten en el mismo proceso; de lo contrario, arroje ClassCastException en (ObjectWrapperForBinder) getIntent (). GetExtras (). GetBinder ("object_value") *
clase ObjectWrapperForBinder definición
public class ObjectWrapperForBinder extends Binder {
private final Object mData;
public ObjectWrapperForBinder(Object data) {
mData = data;
}
public Object getData() {
return mData;
}
}
Método 2
- para el remitente
- use un método nativo personalizado para agregar su objeto java a la tabla de referencia global JNI (a través de JNIEnv :: NewGlobalRef)
- coloque el entero de retorno (en realidad, JNIEnv :: NewGlobalRef return jobject, que es un puntero, podemos enviarlo a int de forma segura) a su Intención (a través de Intent :: putExtra)
- para el receptor
- obtener entero de Intent (a través de Intent :: getInt)
- use un método nativo personalizado para restaurar su objeto java desde la tabla de referencia global JNI (a través de JNIEnv :: NewLocalRef)
- eliminar elemento de la tabla de referencia global JNI (a través de JNIEnv :: DeleteGlobalRef),
Pero el Método 2 tiene un problema pequeño pero grave, si el receptor no puede restaurar el objeto java (por ejemplo, ocurre alguna excepción antes de restaurar el objeto java, o la Actividad del receptor no existe en absoluto), entonces el objeto java se convertirá en un huérfano o pérdida de memoria, el Método 1 no tiene este problema, ya que Android Binder manejará esta excepción
Método 3
Para invocar el objeto java de forma remota, crearemos un contrato / interfaz de datos para describir el objeto java, usaremos el archivo aidl
IDataContract.aidl
package com.example.objectwrapper;
interface IDataContract {
int func1(String arg1);
int func2(String arg1);
}
Código para la primera actividad
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 la segunda actividad:
cambie el atributo android: process en AndroidManifest.xml a un nombre de proceso no vacío para asegurarse de que la segunda actividad se ejecute en otro proceso
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();
}
De esta manera, podemos pasar una interfaz entre dos actividades a pesar de que se ejecutan en un proceso diferente, y llamar al método de la interfaz de forma remota
Método 4
El método 3 no parece lo suficientemente simple porque debemos implementar una interfaz de ayuda. Si solo desea realizar una tarea simple y el valor de retorno del método es innecesario, podemos usar android.os.Messenger
Código para la primera actividad (remitente):
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 la segunda actividad (receptor):
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();
}
}
}
Todos los Messenger.send se ejecutarán en un controlador de forma asincrónica y secuencial.
En realidad, android.os.Messenger también es una interfaz de ayuda, si tiene el código fuente de Android, puede encontrar un archivo llamado IMessenger.aidl
package android.os;
import android.os.Message;
/** @hide */
oneway interface IMessenger {
void send(in Message msg);
}