Edited at

Android Q でアプリを強制的に再起動する方法


概要

Android Q では、従来よく使われていた AlarmManager によるアプリ強制再起動が使えなくなりました。

この記事ではその代替手段である Activity によるアプリ強制再起動について説明します。


問題

従来、アプリを強制的に再起動するために次のような方法がありました。


  1. AlarmManager を使ってメインの Activity を数秒後に起動するように設定

  2. アプリのプロセスを Kill する。

具体的には次のようなコードです。

private void restartApp() {

Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager am =
(AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
// 1秒後に MainActivity を起動
am.setExact(AlarmManager.RTC, System.currentTimeMillis() + 1000, pi);
// 現在のプロセスを Kill する
android.os.Process.killProcess(android.os.Process.myPid());
}

この方法は Android Q では使えなくなりました。

この制限は Android Q で動くすべてのアプリに適用されます。targetSdkVersion は関係ありません。

これは『Android Q におけるバックグラウンドでの Activity 起動の制限』が原因です。

Android Issues にこの問題に関するチケットが起票されています。これに対し、Android チームは「Android Q では AlarmManager による再起動は利用できなくなった」とコメントしています


解決方法

Android Q からは Activity によるアプリ強制再起動が利用できます。

概要は次の通りです。


  1. 再起動を実行する RestartActivity を定義する

  2. RestartActiivty が別プロセスで起動するように宣言する

  3. メインプロセスから RestartActivity を起動させる

  4. RestartActivity で再起動処理をする


    1. RestartActivity が起動したらメインプロセスを Kill する

    2. メインプロセスを Kill したら、MainActivity を起動する

    3. RestartActivity を終了する




RestartActivity を定義する

RestartActivity を定義します。

RestartActivity を起動する Intent にメインプロセスの PID を保持しておきます。

package com.x.androidapprestart;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

public class RestartActivity extends Activity {
public static final String EXTRA_MAIN_PID = "RestartActivity.main_pid";

public static Intent createIntent(Context context) {
Intent intent = new Intent();
intent.setClassName(context.getPackageName(), RestartActivity.class.getName());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// メインプロセスの PID を Intent に保存しておく
intent.putExtra(RestartActivity.EXTRA_MAIN_PID, android.os.Process.myPid());
return intent;
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ... (省略) ...
}
}


別プロセスで起動するように宣言する

AndroidManifest.xml に RestartActivity の宣言を追加します。

この時、別プロセスで起動させるために android:process に任意のプロセス名を指定します。

<activity

android:name=".RestartActivity"
android:excludeFromRecents="true"
android:exported="false"
android:launchMode="singleInstance"
android:process=":restart_process"
android:theme="@android:style/Theme.Translucent.NoTitleBar">
</activity>


RestartActivity を起動する

終了したいタイミングで RestartActivity を起動します。

private void restartApp() {

Context context = getApplicationContext();
Intent intent = RestartActivity.createIntent(context);
// RestartActivity を起動(AndroidManifest.xml での宣言により別プロセスで起動する
context.startActivity(intent);
}


再起動処理をする

1. メインプロセスを Kill する

RestartActivity の onCreate メソッドでメインプロセスを Kill します。

この時、PID は Intent で渡された値を利用します。

2. MainActivity を起動する

メインプロセスを Kill したら、再起動したい Activity(今回のケースでは MainActivity)を起動します。

3. RestartActivity を終了する

RestartActivity とそのプロセスを終了します。

これでアプリが再起動されるはずです。

以下に RestartActivity の onCreate のコードを記載します。

@Override

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// 1. メインプロセスを Kill する
Intent intent = getIntent();
int mainPid = intent.getIntExtra(EXTRA_MAIN_PID, -1);
android.os.Process.killProcess(mainPid);

// 2. MainActivity を再起動する
Context context = getApplicationContext();
Intent restartIntent = new Intent(Intent.ACTION_MAIN);
restartIntent.setClassName(context.getPackageName(), MainActivity.class.getName());
restartIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(restartIntent);

// 3. RestartActivity を終了する
finish();
android.os.Process.killProcess(android.os.Process.myPid());
}


すべてのコード

すべてのコードは github にありますので参考にしてください。


余談

この方法は Android 版 Chrome でも使われているようです。

Chrome にも BrowserRestartActivity というクラスがあり、ChromeLifetimeController から呼ばれています。