J'ai trouvé une méthode simple et élégante:
- PAS de colis
- PAS sérialisable
- AUCUN champ statique
- Aucun bus d'événement
Méthode 1
Code pour la première activité:
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);
Code pour la deuxième activité:
final Object objReceived = ((ObjectWrapperForBinder)getIntent().getExtras().getBinder("object_value")).getData();
Log.d(TAG, "received object=" + objReceived);
vous trouverez objSent
et objReceived
aurez les mêmes hashCode
, ils sont donc identiques.
Mais pourquoi peut-on passer un objet java de cette façon?
En fait, Android Binder créera une référence JNI globale pour l'objet Java et libérera cette référence JNI globale lorsqu'il n'y a pas de référence pour cet objet Java. binder enregistrera cette référence JNI globale dans l'objet Binder.
* ATTENTION: cette méthode fonctionne UNIQUEMENT sauf si les deux activités s'exécutent dans le même processus, sinon lancez ClassCastException à (ObjectWrapperForBinder) getIntent (). GetExtras (). GetBinder ("object_value") *
classe ObjectWrapperForBinder, définition
public class ObjectWrapperForBinder extends Binder {
private final Object mData;
public ObjectWrapperForBinder(Object data) {
mData = data;
}
public Object getData() {
return mData;
}
}
Méthode 2
- pour l'expéditeur,
- utiliser une méthode native personnalisée pour ajouter votre objet java à la table de référence globale JNI (via JNIEnv :: NewGlobalRef)
- mettez l'entier de retour (en fait, JNIEnv :: NewGlobalRef return jobject, qui est un pointeur, nous pouvons le convertir en int en toute sécurité) dans votre intention (via Intent :: putExtra)
- pour le récepteur
- récupère l'entier de Intent (via Intent :: getInt)
- utiliser une méthode native personnalisée pour restaurer votre objet java à partir de la table de référence globale JNI (via JNIEnv :: NewLocalRef)
- supprimer l'élément de la table de référence globale JNI (via JNIEnv :: DeleteGlobalRef),
Mais la méthode 2 a un petit mais grave problème, si le récepteur ne parvient pas à restaurer l'objet java (par exemple, une exception se produit avant de restaurer l'objet java, ou l'activité du récepteur n'existe pas du tout), alors l'objet java deviendra un orphelin ou fuite de mémoire, la méthode 1 n'a pas ce problème, car Android Binder gérera cette exception
Méthode 3
Pour invoquer l'objet java à distance, nous allons créer un contrat / interface de données pour décrire l'objet java, nous utiliserons le fichier aidl
IDataContract.aidl
package com.example.objectwrapper;
interface IDataContract {
int func1(String arg1);
int func2(String arg1);
}
Code pour la première activité
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);
Code pour la deuxième activité:
changez l'attribut android: process dans AndroidManifest.xml en un nom de processus non vide pour vous assurer que la deuxième activité s'exécute dans un autre processus
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 cette façon, nous pouvons passer une interface entre deux activités même si elles s'exécutent dans un processus différent, et appeler la méthode d'interface à distance
Méthode 4
la méthode 3 ne semble pas assez simple car nous devons implémenter une interface aidl. Si vous voulez simplement faire une tâche simple et que la valeur de retour de la méthode n'est pas nécessaire, nous pouvons utiliser android.os.Messenger
Code de la première activité (expéditeur):
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)));
}
}
Code de la deuxième activité (récepteur):
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();
}
}
}
Tous les Messenger.send s'exécuteront dans un gestionnaire de manière asynchrone et séquentielle.
En fait, android.os.Messenger est également une interface aidl, si vous avez le code source android, vous pouvez trouver un fichier nommé IMessenger.aidl
package android.os;
import android.os.Message;
/** @hide */
oneway interface IMessenger {
void send(in Message msg);
}