Я нашел простой и элегантный метод:
- НЕТ Parcelable
- НЕТ Сериализуемый
- Нет статического поля
- No Event Bus
Способ 1
Код для первого занятия:
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);
Код для второго занятия:
final Object objReceived = ((ObjectWrapperForBinder)getIntent().getExtras().getBinder("object_value")).getData();
Log.d(TAG, "received object=" + objReceived);
вы найдете objSentи objReceivedполучите то же самое hashCode, поэтому они идентичны.
Но почему мы можем передать объект Java таким образом?
На самом деле, связыватель Android создаст глобальную ссылку JNI для объекта Java и выпустит эту глобальную ссылку JNI, если для этого объекта Java нет ссылки. binder сохранит эту глобальную ссылку JNI в объекте Binder.
* ВНИМАНИЕ: этот метод работает ТОЛЬКО в том случае, если два действия выполняются в одном и том же процессе, в противном случае генерируется исключение ClassCastException в (ObjectWrapperForBinder) getIntent (). GetExtras (). GetBinder ("object_value") *
определение класса ObjectWrapperForBinder
public class ObjectWrapperForBinder extends Binder {
private final Object mData;
public ObjectWrapperForBinder(Object data) {
mData = data;
}
public Object getData() {
return mData;
}
}
Способ 2
- для отправителя,
- используйте собственный нативный метод для добавления вашего Java-объекта в глобальную справочную таблицу JNI (через JNIEnv :: NewGlobalRef)
- поместите возвращаемое целое число (фактически, JNIEnv :: NewGlobalRef возвращаемое задание, которое является указателем, мы можем безопасно преобразовать его в int) в ваше намерение (через Intent :: putExtra)
- для получателя
- получить целое число из Intent (через Intent :: getInt)
- используйте собственный нативный метод для восстановления вашего Java-объекта из глобальной справочной таблицы JNI (через JNIEnv :: NewLocalRef)
- удалить элемент из глобальной справочной таблицы JNI (через JNIEnv :: DeleteGlobalRef),
Но у метода 2 есть небольшая, но серьезная проблема: если получателю не удается восстановить объект Java (например, какое-то исключение происходит до восстановления объекта Java, или действие получателя вообще не существует), тогда объект Java станет утечка памяти или утечка памяти, метод 1 не имеет этой проблемы, потому что Android Binder будет обрабатывать это исключение
Способ 3
Для удаленного вызова Java-объекта мы создадим контракт данных / интерфейс для описания Java-объекта. Мы будем использовать файл aidl.
IDataContract.aidl
package com.example.objectwrapper;
interface IDataContract {
int func1(String arg1);
int func2(String arg1);
}
Код для первого занятия
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);
Код для второго занятия:
измените атрибут android: process в AndroidManifest.xml на непустое имя процесса, чтобы убедиться, что второе действие выполняется в другом процессе
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();
}
Таким образом, мы можем передавать интерфейс между двумя действиями, даже если они выполняются в разных процессах, и вызывать метод интерфейса удаленно
Способ 4
Метод 3 кажется недостаточно простым, потому что мы должны реализовать вспомогательный интерфейс. Если вы просто хотите выполнить простую задачу, а возвращаемое значение метода не нужно, мы можем использовать android.os.Messenger
Код для первого действия (отправитель):
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)));
}
}
Код для второго действия (получатель):
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();
}
}
}
Весь Messenger.send будет выполняться в обработчике асинхронно и последовательно.
На самом деле, android.os.Messenger также является вспомогательным интерфейсом, если у вас есть исходный код Android, вы можете найти файл с именем IMessenger.aidl
package android.os;
import android.os.Message;
/** @hide */
oneway interface IMessenger {
void send(in Message msg);
}