ProcessPhoenix - 优化Android应用进程重启

1,418 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

ProcessPhoenix是大神jakewharton开源的一款Android平台开源库, 主要用于优化Android应用的进程重启. 该库只应该用于在测试构建阶段时基础状态的改变, 比如从staging阶段向生产阶段的转变.

ProcessPhoenix本意凤凰进程, 所谓涅槃重生, 即指重启进程.

Usage

启动应用内默认的Launcher Activity:

ProcessPhoenix.triggerRebirth(context);

或是不想重启应用, 只想重启特定的组件, 相关API为:

Intent nextIntent = //...
ProcessPhoenix.triggerRebirth(context, nextIntent);

因为Phoenix处于单独的进程, 若App启动时处于Phoenix进程, App的相关初始化操作应该在Application#onCreate中跳过该进程.

if (ProcessPhoenix.isPhoenixProcess(this)) {
  return;
}

Under the hood

ProcessPhoenix作为一个Activity, 运行于单独的进程. 它的核心作用是在ProcessPhoenix#onCreate中启动别的应用的默认Launcher或者其它基础组件, 之后再杀死自身进程.

这部分信息从ProcessPhoenix源码可知:


/**
 * Process Phoenix facilitates restarting your application process. This should only be used for
 * things like fundamental state changes in your debug builds (e.g., changing from staging to
 * production).
 * <p>
 * Trigger process recreation by calling {@link #triggerRebirth} with a {@link Context} instance.
 */
public final class ProcessPhoenix extends Activity {
  
  ...
  //利用Activity#startActivities(Intent[] intents)启动多个组件.
  
  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ArrayList<Intent> intents = getIntent().getParcelableArrayListExtra(KEY_RESTART_INTENTS);
    startActivities(intents.toArray(new Intent[intents.size()]));
    finish();
    Runtime.getRuntime().exit(0); // Kill ProcessPhoenix process.
  }
}

ProcessPhoenix#AndroidManifest.xml, ProcessPhoenix声明为:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.jakewharton.processphoenix" >

    <uses-sdk android:minSdkVersion="11" />

    <application>
        <activity
            android:name="com.jakewharton.processphoenix.ProcessPhoenix"
            android:process=":phoenix"
            android:theme="@android:style/Theme.Translucent.NoTitleBar" />
    </application>

</manifest>

由此可知, ProcessPhoenix是一个背景透明且无UI的Activity, 其所在进程名称为: com.jakewharton.processphoenix:phoenix

此外, ProcessPhoenix剩余源码为:

public final class ProcessPhoenix extends Activity {
  private static final String KEY_RESTART_INTENTS = "phoenix_restart_intents";

  /**
   * 启动App的默认Launcher Activity
   */
  public static void triggerRebirth(Context context) {
    triggerRebirth(context, getRestartIntent(context));
  }

  /**
   * 启动Context所在进程的一个或多个特定组件
   */
  public static void triggerRebirth(Context context, Intent... nextIntents) {
    Intent intent = new Intent(context, ProcessPhoenix.class);
    intent.addFlags(FLAG_ACTIVITY_NEW_TASK); // In case we are called with non-Activity context.
    intent.putParcelableArrayListExtra(KEY_RESTART_INTENTS, new ArrayList<>(Arrays.asList(nextIntents)));
    context.startActivity(intent);
    if (context instanceof Activity) {
      ((Activity) context).finish();
    }
    Runtime.getRuntime().exit(0); // Kill kill kill!
  }
  //获取Context所在App的默认Launcher
  private static Intent getRestartIntent(Context context) {
    Intent defaultIntent = new Intent(ACTION_MAIN, null);
    defaultIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
    defaultIntent.addCategory(CATEGORY_DEFAULT);

    String packageName = context.getPackageName();
    PackageManager packageManager = context.getPackageManager();
    for (ResolveInfo resolveInfo : packageManager.queryIntentActivities(defaultIntent, 0)) {
      ActivityInfo activityInfo = resolveInfo.activityInfo;
      if (activityInfo.packageName.equals(packageName)) {
        defaultIntent.setComponent(new ComponentName(packageName, activityInfo.name));
        return defaultIntent;
      }
    }

    throw new IllegalStateException("Unable to determine default activity for "
        + packageName
        + ". Does an activity specify the DEFAULT category in its intent filter?");
  }

  ...

  /**
   * 判断当前进程是否是ProcessPhoenix临时进程.
   * 因此需要在自己应用中Application#onCreate时, 判断当前进程为ProcessPhoenix临时进程时, 主动跳过原本属于应用自身初始化相关资源的过程.
   */
  public static boolean isPhoenixProcess(Context context) {
    int currentPid = Process.myPid();
    ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningAppProcessInfo> runningProcesses = manager.getRunningAppProcesses();
    if (runningProcesses != null) {
      for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
        if (processInfo.pid == currentPid && processInfo.processName.endsWith(":phoenix")) {
          return true;
        }
      }
    }
    return false;
  }
}

Yeah, jakewharton is absolutely a m...ker! //这绝对是一句赞扬的话