Ho trovato un metodo semplice ed elegante:
- NO parcelable
- NO serializzabile
- NESSUN campo statico
- Nessun bus eventi
Metodo 1
Codice per la prima attività:
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);
Codice per la seconda attività:
final Object objReceived = ((ObjectWrapperForBinder)getIntent().getExtras().getBinder("object_value")).getData();
Log.d(TAG, "received object=" + objReceived);
troverai objSente objReceivedavrai lo stesso hashCode, quindi sono identici.
Ma perché possiamo passare un oggetto Java in questo modo?
In realtà, Android Binder creerà un riferimento JNI globale per l'oggetto java e rilascerà questo riferimento JNI globale quando non ci sono riferimenti per questo oggetto java. binder salverà questo riferimento JNI globale nell'oggetto Binder.
* ATTENZIONE: questo metodo funziona SOLO a meno che le due attività non vengano eseguite nello stesso processo, altrimenti genera ClassCastException su (ObjectWrapperForBinder) getIntent (). GetExtras (). GetBinder ("object_value") *
definizione di ObjectWrapperForBinder di classe
public class ObjectWrapperForBinder extends Binder {
private final Object mData;
public ObjectWrapperForBinder(Object data) {
mData = data;
}
public Object getData() {
return mData;
}
}
Metodo 2
- per il mittente,
- utilizzare il metodo nativo personalizzato per aggiungere l'oggetto java alla tabella di riferimento globale JNI (tramite JNIEnv :: NewGlobalRef)
- metti il numero intero di ritorno (in realtà, JNIEnv :: NewGlobalRef return jobject, che è un puntatore, possiamo lanciarlo in int in modo sicuro) al tuo Intento (tramite Intent :: putExtra)
- per il ricevitore
- ottenere numeri interi da Intent (tramite Intent :: getInt)
- utilizzare il metodo nativo personalizzato per ripristinare l'oggetto java dalla tabella di riferimento globale JNI (tramite JNIEnv :: NewLocalRef)
- rimuove l'elemento dalla tabella di riferimento globale JNI (tramite JNIEnv :: DeleteGlobalRef),
Ma il Metodo 2 ha un piccolo ma grave problema, se il ricevitore non riesce a ripristinare l'oggetto java (ad esempio, si verifica un'eccezione prima di ripristinare l'oggetto java o l'attività del ricevitore non esiste affatto), allora l'oggetto java diventerà un orfano o perdita di memoria, il Metodo 1 non ha questo problema, perché Android Binder gestirà questa eccezione
Metodo 3
Per richiamare l'oggetto java in remoto, creeremo un contratto / interfaccia di dati per descrivere l'oggetto java, useremo il file aidl
IDataContract.aidl
package com.example.objectwrapper;
interface IDataContract {
int func1(String arg1);
int func2(String arg1);
}
Codice per la prima attività
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);
Codice per la seconda attività:
cambia l'attributo android: process in AndroidManifest.xml in un nome di processo non vuoto per assicurarti che la seconda attività venga eseguita in un altro 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();
}
In questo modo, possiamo passare un'interfaccia tra due attività anche se vengono eseguite in processi diversi e chiamare il metodo di interfaccia in remoto
Metodo 4
il metodo 3 non sembra abbastanza semplice perché dobbiamo implementare un'interfaccia di aiuto. Se vuoi semplicemente fare un semplice compito e il valore di ritorno del metodo non è necessario, possiamo usare android.os.Messenger
Codice per la prima attività (mittente):
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)));
}
}
Codice per la seconda attività (destinatario):
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();
}
}
}
Tutto il Messenger.send verrà eseguito in un gestore in modo asincrono e sequenziale.
In realtà, android.os.Messenger è anche un'interfaccia helpl, se hai il codice sorgente Android, puoi trovare un file chiamato IMessenger.aidl
package android.os;
import android.os.Message;
/** @hide */
oneway interface IMessenger {
void send(in Message msg);
}