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 objSent
e objReceived
avrai 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);
}