Znalazłem prostą i elegancką metodę:
- NIE Paczkowany
- NO Serializowalny
- BEZ pola statycznego
- Brak magistrali zdarzeń
Metoda 1
Kod pierwszego działania:
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);
Kod drugiego działania:
final Object objReceived = ((ObjectWrapperForBinder)getIntent().getExtras().getBinder("object_value")).getData();
Log.d(TAG, "received object=" + objReceived);
znajdziesz objSent
i objReceived
masz to samo hashCode
, więc są identyczne.
Ale dlaczego możemy w ten sposób przekazać obiekt Java?
W rzeczywistości Android Binder utworzy globalne odwołanie JNI dla obiektu Java i zwolni to globalne odwołanie JNI, gdy nie będzie odniesienia do tego obiektu Java. binder zapisze to globalne odniesienie JNI w obiekcie Binder.
* UWAGA: ta metoda działa TYLKO, chyba że dwie czynności działają w tym samym procesie, w przeciwnym razie wyrzuć ClassCastException na (ObjectWrapperForBinder) getIntent (). GetExtras (). GetBinder („wartość_obiektu”) *
klasa Definiowanie obiektu ObjectWrapperForBinder
public class ObjectWrapperForBinder extends Binder {
private final Object mData;
public ObjectWrapperForBinder(Object data) {
mData = data;
}
public Object getData() {
return mData;
}
}
Metoda 2
- dla nadawcy
- użyj niestandardowej metody rodzimej, aby dodać obiekt Java do globalnej tabeli referencyjnej JNI (za pośrednictwem JNIEnv :: NewGlobalRef)
- umieść zwracaną liczbę całkowitą (faktycznie, JNIEnv :: NewGlobalRef return jobject, który jest wskaźnikiem, możemy rzucić go bezpiecznie na int) do twojej intencji (poprzez Intent :: putExtra)
- dla odbiornika
- pobierz liczbę całkowitą z Intent (przez Intent :: getInt)
- użyj niestandardowej metody rodzimej, aby przywrócić obiekt Java z globalnej tabeli referencyjnej JNI (przez JNIEnv :: NewLocalRef)
- usuń element z globalnej tabeli referencyjnej JNI (przez JNIEnv :: DeleteGlobalRef),
Ale Metoda 2 ma mały, ale poważny problem, jeśli odbiornik nie przywróci obiektu java (na przykład zdarzy się jakiś wyjątek przed przywróceniem obiektu java lub działanie odbiorcy w ogóle nie istnieje), wówczas obiekt java stanie się wyciek sieroty lub pamięci, Metoda 1 nie ma tego problemu, ponieważ Android Binder poradzi sobie z tym wyjątkiem
Metoda 3
Aby zdalnie wywołać obiekt Java, utworzymy umowę / interfejs danych opisujący obiekt Java, użyjemy pliku aidl
IDataContract.aidl
package com.example.objectwrapper;
interface IDataContract {
int func1(String arg1);
int func2(String arg1);
}
Kod pierwszej aktywności
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);
Kod drugiego działania:
zmień atrybut android: proces w AndroidManifest.xml na niepustą nazwę procesu, aby mieć pewność, że drugie działanie zostanie uruchomione w innym procesie
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();
}
W ten sposób możemy przekazać interfejs między dwoma działaniami, nawet jeśli działają one w innym procesie, i wywołać metodę interfejsu zdalnie
Metoda 4
metoda 3 wydaje się nie dość prosta, ponieważ musimy wdrożyć interfejs pomocniczy. Jeśli chcesz po prostu wykonać proste zadanie, a wartość zwracana przez metodę jest niepotrzebna, możemy użyć android.os.Messenger
Kod pierwszego działania (nadawcy):
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)));
}
}
Kod drugiej czynności (odbiorcy):
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();
}
}
}
Wszystkie Messenger.send będą wykonywane w module obsługi asynchronicznie i sekwencyjnie.
W rzeczywistości android.os.Messenger jest także interfejsem pomocniczym, jeśli masz kod źródłowy Androida, możesz znaleźć plik o nazwie IMessenger.aidl
package android.os;
import android.os.Message;
/** @hide */
oneway interface IMessenger {
void send(in Message msg);
}