ProcessPhoenix
ProcessPhoenix copied to clipboard
not always restart activity on Android 12
I am using v2.1.2 and found that the activity won't be restarted on Android 12 sometimes.
When the problem happens, I notice the following errors:
I/ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10008000 pkg=<my_pkg_name> cmp=<my_activity_class_name>} from uid 10293
W/ActivityTaskManager: Tried to set launchTime (0) < mLastActivityLaunchTime (202472441)
W/ActivityTaskManager: Exception when starting activity <my_activity_class_name>
android.os.DeadObjectException
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(BinderProxy.java:571)
at android.app.IApplicationThread$Stub$Proxy.scheduleTransaction(IApplicationThread.java:2732)
W/ActivityTaskManager: Failed to send top-resumed=true to ActivityRecord{9199e95 u0 <my_activity_class_name> t1186}
android.os.DeadObjectException
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(BinderProxy.java:571)
at android.app.IApplicationThread$Stub$Proxy.scheduleTransaction(IApplicationThread.java:2732)
W/ActivityTaskManager: Failed to send top-resumed=false to ActivityRecord{9199e95 u0 <my_activity_class_name> t1186}
android.os.DeadObjectException
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(BinderProxy.java:571)
at android.app.IApplicationThread$Stub$Proxy.scheduleTransaction(IApplicationThread.java:2732)
I/ActivityTaskManager: Restarting because process died: ActivityRecord{9199e95 u0 <my_activity_class_name> t1186}
E/ActivityTaskManager: Second failure launching <my_activity_class_name>, giving up
android.os.DeadObjectException
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(BinderProxy.java:571)
at android.app.IApplicationThread$Stub$Proxy.scheduleTransaction(IApplicationThread.java:2732)
I noticed this issue as well @samlu did you find a suitable workaround?
can you please state if you noticed this issue on an actual device or emulator? I tested on an emulator with Android 12 and it works.
here as well, on emulator works fine, but on actuals devices (specifically Samsung Galaxy A72) sometimes doesn't restart the app, and for UX looks like app crash.
Emulator is working well but frequently samsung Galaxy Note 10 Lite cannot restart the app. Is there any way to workaround solution for this?
Workaround solution for me is to use following implementation project(':process-phoenix') instead of implementation 'com.jakewharton:process-phoenix:2.1.2'
It works well in Android 12 Samsung phones.
Here is my solution. Search "ysl" to see the changes. I have tested it on several Android 12 devices without any issues.
/*
* Copyright (C) 2014 Jake Wharton
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jakewharton.processphoenix;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Process;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
/**
* 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 {
// added by ysl
private final Handler m_handler = new Handler(Looper.getMainLooper());
private Intent[] m_itLaunchs;
// end of ysl
private static final String KEY_RESTART_INTENTS = "phoenix_restart_intents";
private static final String KEY_MAIN_PROCESS_PID = "phoenix_main_process_pid";
/**
* Call to restart the application process using the {@linkplain Intent#CATEGORY_DEFAULT default}
* activity as an intent.
* <p>
* Behavior of the current process after invoking this method is undefined.
*/
public static void triggerRebirth(Context context) {
triggerRebirth(context, getRestartIntent(context));
}
/**
* Call to restart the application process using the specified intents.
* <p>
* Behavior of the current process after invoking this method is undefined.
*/
public static void triggerRebirth(Context context, Intent... nextIntents) {
if (nextIntents.length < 1) {
throw new IllegalArgumentException("intents cannot be empty");
}
// create a new task for the first activity.
nextIntents[0].addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
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)));
intent.putExtra(KEY_MAIN_PROCESS_PID, Process.myPid());
context.startActivity(intent);
}
private static Intent getRestartIntent(Context context) {
String packageName = context.getPackageName();
Intent defaultIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
if (defaultIntent != null) {
return defaultIntent;
}
throw new IllegalStateException("Unable to determine default activity for "
+ packageName
+ ". Does an activity specify the DEFAULT category in its intent filter?");
}
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Process.killProcess(getIntent().getIntExtra(KEY_MAIN_PROCESS_PID, -1)); // Kill original main process
ArrayList<Intent> intents = getIntent().getParcelableArrayListExtra(KEY_RESTART_INTENTS);
/* ysl: move to onPostCreate()
startActivities(intents.toArray(new Intent[intents.size()]));
finish();
*/
// added by ysl
setContentView(new FrameLayout(this)); // not sure if this line is necessary
m_itLaunchs = intents.toArray(new Intent[0]);
// end of ysl
/* ysl: move to onDestroy()
Runtime.getRuntime().exit(0); // Kill kill kill!
*/
}
// added by ysl
@Override protected void
onPostCreate(Bundle savedInstanceState)
{
super.onPostCreate(savedInstanceState);
m_handler.post(new Runnable() {
@Override public void
run()
{
startActivities(m_itLaunchs);
finish();
}
});
}
@Override protected void
onDestroy()
{
m_handler.removeCallbacks(null);
Runtime.getRuntime().exit(0); // Kill kill kill!
super.onDestroy();
}
// end of ysl
/**
* Checks if the current process is a temporary Phoenix Process.
* This can be used to avoid initialisation of unused resources or to prevent running code that
* is not multi-process ready.
*
* @return true if the current process is a temporary Phoenix Process
*/
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;
}
}
Thank you so much for your help.
On Tue, May 17, 2022, 10:49 AM Sam Lu @.***> wrote:
Here is my solution. Search "ysl" to see the changes. I have tested it on several Android 12 devices without any issues.
/*
- Copyright (C) 2014 Jake Wharton
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License. */ package com.jakewharton.processphoenix;
import android.app.Activity; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Process; import java.util.ArrayList; import java.util.Arrays; import java.util.List;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
/**
- 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).
- Trigger process recreation by calling @.*** #triggerRebirth} with a @.*** Context} instance. */ public final class ProcessPhoenix extends Activity { // added by ysl private final Handler m_handler = new Handler(Looper.getMainLooper()); private Intent[] m_itLaunchs; // end of ysl
private static final String KEY_RESTART_INTENTS = "phoenix_restart_intents"; private static final String KEY_MAIN_PROCESS_PID = "phoenix_main_process_pid";
/**
- Call to restart the application process using the @.*** Intent#CATEGORY_DEFAULT default}
- activity as an intent.
- Behavior of the current process after invoking this method is undefined. */ public static void triggerRebirth(Context context) { triggerRebirth(context, getRestartIntent(context)); }
/**
- Call to restart the application process using the specified intents.
- Behavior of the current process after invoking this method is undefined. */ public static void triggerRebirth(Context context, Intent... nextIntents) { if (nextIntents.length < 1) { throw new IllegalArgumentException("intents cannot be empty"); } // create a new task for the first activity. nextIntents[0].addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
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))); intent.putExtra(KEY_MAIN_PROCESS_PID, Process.myPid()); context.startActivity(intent);
}
private static Intent getRestartIntent(Context context) { String packageName = context.getPackageName(); Intent defaultIntent = context.getPackageManager().getLaunchIntentForPackage(packageName); if (defaultIntent != null) { return defaultIntent; }
throw new IllegalStateException("Unable to determine default activity for " + packageName + ". Does an activity specify the DEFAULT category in its intent filter?");
}
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
Process.killProcess(getIntent().getIntExtra(KEY_MAIN_PROCESS_PID, -1)); // Kill original main process ArrayList<Intent> intents = getIntent().getParcelableArrayListExtra(KEY_RESTART_INTENTS); /* ysl: move to onPostCreate() startActivities(intents.toArray(new Intent[intents.size()])); finish(); */ // added by ysl setContentView(new FrameLayout(this)); // not sure if this line is necessary m_itLaunchs = intents.toArray(new Intent[0]); // end of ysl /* ysl: move to onDestroy() Runtime.getRuntime().exit(0); // Kill kill kill! */
}
// added by ysl @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState);
m_handler.post(new Runnable() { @Override public void run() { startActivities(m_itLaunchs); finish(); } });
}
@Override protected void onDestroy() { m_handler.removeCallbacks(null);
Runtime.getRuntime().exit(0); // Kill kill kill! super.onDestroy();
} // end of ysl
/**
- Checks if the current process is a temporary Phoenix Process.
- This can be used to avoid initialisation of unused resources or to prevent running code that
- is not multi-process ready.
- @return true if the current process is a temporary Phoenix Process */ 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; } }
— Reply to this email directly, view it on GitHub https://github.com/JakeWharton/ProcessPhoenix/issues/47#issuecomment-1128377615, or unsubscribe https://github.com/notifications/unsubscribe-auth/AGVIVH7ZWA3Q2ANSQRGFL3TVKMJONANCNFSM5HXMRI3A . You are receiving this because you commented.Message ID: @.***>
Also noticed 2.1.2 isn't completely restarting the app from scratch. When I use triggerRebirth(applicationContext), app is crashed and closed, but it's still running in the background. 2.0.0 doesn't have this issue and I'm running an Android 12 device.
It's happening in my app targeting api level 33
I don't see any issues with the app that targets the sdk 33. Did you apply my patch code (https://github.com/JakeWharton/ProcessPhoenix/issues/47#issuecomment-1128377615)?
@samlu what am I supposed to do? Create a new class which replaces the original (library-provided) ProccessPhoenix
?