Как правильно инициализировать приложение Android?

Проблема: мне нужно запускать код при каждом запуске, прежде чем мое приложение будет готово к использованию.

Сначала я пробовал делать это в отдельном занятии.

AndroidManifest.xml

<activity android:name = ".MainActivity" />

<activity android:name = ".StarterActivity">
    <intent-filter>
        <action android:name = "android.intent.action.MAIN" />

        <category android:name = "android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

AppLoader.java

public class AppLoader {

    private static Object someInstance;

    public static void load(Runnable onCompleteCallback) {
        try {
            someInstance = new Object();

            //potentially long operation to initialize the app
            Thread.sleep(5000);

            onCompleteCallback.run();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public static void checkInitialized() {
        if (someInstance == null) {
            throw new RuntimeException("Not initialized");
        }
    }
}

StarterActivity.java

public class StarterActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        AppLoader.load(() -> {
            MainActivity.start(this);
            finish();
        });
    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    public static void start(Context context) {
        Intent starter = new Intent(context, MainActivity.class);
        context.startActivity(starter);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        AppLoader.checkInitialized();
    }
}

Это отлично работает, если приложение запускается холодным способом с помощью значка запуска, но дает сбой во всех остальных случаях. Простой способ воспроизвести проблему:

  1. Перейдите в настройки разработчика на своем устройстве и установите для параметра «Ограничение фонового процесса» значение «Без фонового процесса».
  2. Открыть приложение
  3. Откройте какое-нибудь другое приложение
  4. Снова откройте приложение. Результат: вылетает.

Вот статья, описывающая похожую проблему: Смерть процесса Android - и (большие) последствия для вашего приложения

Возможные решения:

  1. Ленивая загрузка / реактивный подход. Я стараюсь использовать его как можно чаще, но всегда есть код, который мне нужно запустить в режиме блокировки, прежде чем пользователь сможет взаимодействовать с приложением, поэтому этого недостаточно.
  2. Помещаем весь этот код в App.onCreate (). Это, вероятно, сработает для небольших приложений, но я видел большие приложения, для инициализации которых требуется 5-10 секунд, и я сомневаюсь, что они используют для этого onCreate (). Возможные недостатки: ANR и / или чрезмерное время запуска в Android Vitals?
  3. Проверка, инициализировано ли приложение в BaseActivity, но для этого потребуется либо блокировать onCreate, либо управлять обратными вызовами жизненного цикла вручную, что не похоже на хорошую идею.

Итак, как правильно запускать код каждый раз при запуске приложения?

Примечание. Обычно StarterActivity представляет собой заставку, вводится AppLoader и т. д., Но я оставил это для простоты.

2
0
1 440
3

Ответы 3

AndroidManifest.xml

<application
    android:name = ".AppLoader"

AppLoader.java

public class AppLoader extends Application {

private static Object someInstance;

@Override
public void onCreate() {
    super.onCreate();
    // DO YOUR STUFF 
}
}

Обновлять - Используйте Handler с заставкой.

public class StarterActivity extends AppCompatActivity {

    private Handler handler;
    private Runnable myStuffRunnable;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handler = new Handler();
        myStuffRunnable = new Runnable(){
            public void run(){
                // DO MY STUFF
                MainActivity.start(this);
            }
        };
    }

    @Override
    protected void onPause() {
        handler.removeCallbacks(myStuffRunnable);
        super.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        handler.post(myStuffRunnable);
    }

    @Override
    protected void onDestroy() {
        handler.removeCallbacks(myStuffRunnable);
        super.onDestroy();
    }
}

Но что, если "делать свое дело" займет 5-10 секунд? Я подозреваю, что это, по крайней мере, вызовет «чрезмерное время запуска» в Android Vitals и, возможно, ANR тоже. Или нет? Я предполагаю, что я пытаюсь сделать это 1) Сделать так, чтобы приложение было отзывчивым при его загрузке (экран-заставка) 2) Но не знаю, как обрабатывать случаи, когда процесс завершается в фоновом режиме.

user9782202 17.07.2018 00:21

Если вы ожидаете, что ваш материал займет много времени, используйте IntentService и зарегистрируйтесь на baseActivity. Но если всего 5-10 секунд, то используйте Handler на заставке.

Khaled Lela 17.07.2018 00:27

Проверьте мое обновление ответа с помощью Handler, и это позволит вам сделать некоторую анимацию прогресса для хорошего взаимодействия с пользователем, пока вы не сделаете свою работу.

Khaled Lela 17.07.2018 00:45

Возможно, я неправильно сформулировал свой вопрос, но речь не шла о потоковой передаче / обновлении пользовательского интерфейса и т. д., Я пытался найти лучший способ выполнить некоторый код при каждом запуске (но не в onCreate of App). Решение Handler все равно выйдет из строя, если пользователь напрямую откроет MainActivity. По этой причине я просто перенаправлялся на StarterActivity, если это было необходимо. Спасибо за вашу помощь!

user9782202 17.07.2018 13:03

Ваше приложение генерирует исключение RuntimeException, которое вы установили в методе AppLoader.checkInitialized (), потому что ваш объект someInstance теряет свое состояние, когда приложение переходит в фоновый режим и его убивает система (потому что вы настроили свое устройство на нулевые фоновые потоки) . Итак, когда вы пытаетесь повторно открыть приложение, система запускает MainActivity напрямую (а не StarterActivity), потому что пытается восстановить его предыдущее состояние. Но переменные не восстанавливаются, даже статические переменные.

Итак, если вам нужен Object someInstance в вашей MainActivity, вы должны интегрировать его создание в жизненный цикл MainActivitie, переопределив такие методы, как onSavedInstanceState, onRestoreInstanceState и т. д., Чтобы правильно обрабатывать и повторно загружать этот объект, если ваше приложение будет убито системой.

Взгляните на этот https://developer.android.com/guide/components/activities/activity-lifecycle

Если кому-то интересно, я просто перенаправлял пользователя на StarterActivity, если это необходимо, чтобы убедиться, что необходимый код выполняется при каждом запуске.

public abstract class BaseActivity extends AppCompatActivity {

    private boolean isCreated;

    @Override
    protected final void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (!appLoader.isLoaded()) {
            StarterActivity.start(this);
            finish();
            return;
        }

        onCreateActivity(savedInstanceState);
        isCreated = true;
    }

    protected void onCreateActivity(@Nullable Bundle savedInstanceState) {
    }

    @Override
    protected final void onDestroy() {
        super.onDestroy();
        if (isCreated) {
            onDestroyActivity();
        }
    }

    protected void onDestroyActivity() {
    }

}

Все активности расширяют BaseActivity (кроме StarterActivity) и перекрывают onCreateActivity / onDestroyActivity вместо onCreate / onDestroy.

Другие вопросы по теме