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

# 概要
Android Q では、従来よく使われていた AlarmManager によるアプリ強制再起動が使えなくなりました。
この記事ではその代替手段である Activity によるアプリ強制再起動について説明します。

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

1. AlarmManager を使ってメインの Activity を数秒後に起動するように設定
2. アプリのプロセスを Kill する。

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

```java
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);
    // １秒後に 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 起動の制限](https://developer.android.com/guide/components/activities/background-starts)』が原因です。

Android Issues に[この問題に関するチケット](https://issuetracker.google.com/issues/138888752)が起票されています。これに対し、Android チームは「Android Q では AlarmManager による再起動は利用できなくなった」と[コメントしています](https://issuetracker.google.com/issues/138888752#comment3)。

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

概要は次の通りです。

1. 再起動を実行する RestartActivity を定義する
2. RestartActivity が別プロセスで起動するように宣言する
3. メインプロセスから RestartActivity を起動させる
4. RestartActivity で再起動処理をする
   1. RestartActivity が起動したらメインプロセスを Kill する
   2. メインプロセスを Kill したら、MainActivity を起動する
   3. RestartActivity を終了する

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

```java
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 に任意のプロセス名を指定します。**

```xml
<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 を起動します。

```java
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 のコードを記載します。

```java
@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](https://github.com/hshiozawa/AndroidAppRestart) にありますので参考にしてください。

# 余談
この方法は Android 版 Chrome でも使われているようです。
Chrome にも [BrowserRestartActivity](https://chromium.googlesource.com/chromium/src/+/master/chrome/android/java/src/org/chromium/chrome/browser/BrowserRestartActivity.java) というクラスがあり、[ChromeLifetimeController](https://chromium.googlesource.com/chromium/src/+/master/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeLifetimeController.java) から呼ばれています。