#android
#Android
Вопрос:
Я знаю, что если я использую android:process
в манифесте, то класс, который расширяет Application, будет вызываться дважды. Один раз для приложения и второй раз для нового процесса.
Проблема в том, что у меня есть код в MyApplication (расширяет приложение), который не следует вызывать дважды. Я хочу иметь отдельный процесс, но я хочу, чтобы код в MyApplication#onCreate запускался только один раз за загрузку.
Я попытался установить флаг в общих настройках, но он не работает, вероятно, из-за проблемы с различными процессами
Есть идеи?
Комментарии:
1. Общие настройки кажутся нормальными. Почему это не сработало?
2. Почему бы не написать простой файл (например, как это делают стандартные процессы Linux, файл .pid) и проверить его существование перед запуском кода. Возможно, вам придется позаботиться об очистке
Ответ №1:
Основная причина
Из документации SharedPreferences:
Примечание: этот класс не поддерживает использование в нескольких процессах.
Это объясняет, почему ваш SharePreferences
не работает.
Решение 1: использование Context.MODE_MULTI_PROCESS, но оно устарело на уровне API 23
public static final int MODE_MULTI_PROCESS
Эта константа устарела на уровне API 23.
MODE_MULTI_PROCESS работает ненадежно в некоторых версиях Android и, кроме того, не предоставляет никакого механизма для согласования одновременных изменений между процессами. Приложения не должны пытаться его использовать. Вместо этого они должны использовать явный межпроцессный подход к управлению данными, такой как ContentProvider.
Решение 2: использование ContentProvider
2.1. Напишите свой собственный ContentProvider
- Нам просто нужен способ сохранить логическую переменную, которая указывает
onCreate()
MyApplication
, вызывается метод в первый раз или нет, использование этого решения кажется неэффективным.
2.2. Используйте предопределенные ContentProvider
из Android
- Приложение должно объявлять разрешения на чтение / запись во внешнее хранилище, а также запрашивать разрешения времени выполнения. Кстати, это сбивает пользователей с толку при первом открытии приложения.
Решение 3: мы можем использовать приведенный ниже поток для реализации
- Объявите a
BroadcastReceiver
, который выполняется в том же процессе, что и ваше приложение. Потому что они выполняются в одном и том же процессе, поэтому у них одинаковый идентификатор процесса. - Компонент (activity, service, receiver, provider), который выполняется в отдельном процессе (частном или глобальном), поэтому у них будет другой идентификатор процесса.
Реализация
Шаг 1. Создайте класс, который расширяется от BoardcastReceiver
, named AppReceiver
.
public class AppReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Messenger messenger = intent.getParcelableExtra(MyApplication.EXTRA_MESSENGER);
int processId = intent.getIntExtra(MyApplication.EXTRA_PROCESS_ID, -1);
boolean isOnCreateCalledFirstTime = processId == Process.myPid();
Message message = Message.obtain();
message.arg1 = isOnCreateCalledFirstTime ? 1 : 0;
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
Шаг 2. Добавьте этот класс в AndroidManifest.xml
<receiver
android:name=".AppReceiver"
android:enabled="true"
android:exported="true" />
Шаг 3. Измените свой MyApplication
класс
public class MyApplication extends Application {
public static final String EXTRA_MESSENGER = "EXTRA_MESSENGER";
public static final String EXTRA_PROCESS_ID = "EXTRA_PROCESS_ID";
@Override
public void onCreate() {
super.onCreate();
Intent intent = new Intent();
String pkg = getPackageName();
intent.setComponent(new ComponentName(pkg, pkg ".AppReceiver"));
Handler handler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
boolean isOnCreateCalledFirstTime = msg.arg1 != 0;
if (isOnCreateCalledFirstTime) {
// TODO: First time onCreate() is called
} else {
// TODO: Next time onCreate() is called
}
return true;
}
});
Messenger messenger = new Messenger(handler);
intent.putExtra(EXTRA_MESSENGER, messenger);
intent.putExtra(EXTRA_PROCESS_ID, Process.myPid());
sendBroadcast(intent);
}
}