Я нашел простой и элегантный метод:
- НЕТ 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);
}