Activity 的多个实例
Multiple Instances of Activity
我们已经检查了 CATEGORY_MAIN
和 !isTaskRoot()
,但即便如此也启动了 2 个 activity 实例。
SplashActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("OnCreate method.");
if(checkIfActivityIsBroughtToFront() || checkIfActivityIsRootTask()) {
return; // Found that if we finish and don't return then it will run the code below, hence start the recovery task.
}
Log.i("Checking if Recovery is required ...");
new RecoveryTask(SplashActivity.this, this).execute();
}
private boolean checkIfActivityIsBroughtToFront() {
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
// Activity was brought to front and not created,
// Thus finishing this will get us to the last viewed activity
Log.i("Detecting a brought to front, no need for recovery.");
finish();
return true;
}
return false;
}
private boolean checkIfActivityIsRootTask() {
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
Log.i("Main Activity is not the root. " + "Finishing Main Activity instead of launching.");
finish();
return true;
}
}
return false;
}
日志
2015-10-22 13:42:25.581 +0300 SplashActivity INFO[main] - OnCreate method.
2015-10-22 13:42:25.587 +0300 SplashActivity INFO[main] - Checking if Recovery is required ...
2015-10-22 13:42:25.637 +0300 SplashActivity INFO[main] - OnCreate method.
2015-10-22 13:42:25.638 +0300 SplashActivity INFO[main] - Checking if Recovery is required ...
2015-10-22 13:42:25.828 +0300 GeoFenceManager INFO[pool-5-thread-1] - Removing geofences ...
2015-10-22 13:42:25.872 +0300 GeoFenceManager INFO[pool-5-thread-2] - Removing geofences ...
清单
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="x.y.z"
android:installLocation="internalOnly" >
<application
android:name=".global.GlobalInstance"
android:allowBackup="true"
android:allowClearUserData="true"
android:hardwareAccelerated="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:persistent="true" >
<activity
android:name=".activity.SplashActivity"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/Theme.Background" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".receiver.BootUpReceiver"
android:enabled="true"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
更新:
这是在重启后发生的,BOOT_COMPLETED 侦听器正在跟随
public class BootUpReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, SplashActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
知道如何避免这种情况吗?
private boolean checkIfActivityIsRootTask() {
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
Log.i("Main Activity is not the root. " + "Finishing Main Activity instead of launching.");
finish();
return true;
}
}
return false;
} ?
如果这个activity是roottask,它return false;
RecoveryTask 有什么用?
你说:
We have already the check of CATEGORY_MAIN and !isTaskRoot()
但是两次检查都失败了。在您附加的日志中,当创建两个实例时,Detecting a brought to front, no need for recovery.
和 Main Activity is not the root. " + "Finishing Main Activity instead of launching.
都不会打印在日志中。这意味着在这两种方法中都不会遇到 if
部分,因此 finish()
永远不会被执行。这两种方法都返回 false。
此外,仅仅因为您在 onCreate()
中调用 finish()
和 return
,并不意味着 onStart()
或 onResume()
不会打电话。
为什么会这样? 启动器 activity 永远不会被调用两次(考虑到您没有在内部启动 activity)。可能是用户使用的某些 SDK 中的错误。
可能的修复:
您可以尝试在 SplashActivity
的清单中设置 android:launchMode="singleTop"
。这将确保只维护一个实例。
将 android:launchMode="SingleTask" 添加到 xml 中的初始 activity 元素。然后离开 activity 即离开启动调用 finish()。您应该使用此模式而不是 "SingleInstance" 的原因是用户永远无法使用后退键导航回启动画面(因为这不是正常行为)。
我们已经检查了 CATEGORY_MAIN
和 !isTaskRoot()
,但即便如此也启动了 2 个 activity 实例。
SplashActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("OnCreate method.");
if(checkIfActivityIsBroughtToFront() || checkIfActivityIsRootTask()) {
return; // Found that if we finish and don't return then it will run the code below, hence start the recovery task.
}
Log.i("Checking if Recovery is required ...");
new RecoveryTask(SplashActivity.this, this).execute();
}
private boolean checkIfActivityIsBroughtToFront() {
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
// Activity was brought to front and not created,
// Thus finishing this will get us to the last viewed activity
Log.i("Detecting a brought to front, no need for recovery.");
finish();
return true;
}
return false;
}
private boolean checkIfActivityIsRootTask() {
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
Log.i("Main Activity is not the root. " + "Finishing Main Activity instead of launching.");
finish();
return true;
}
}
return false;
}
日志
2015-10-22 13:42:25.581 +0300 SplashActivity INFO[main] - OnCreate method.
2015-10-22 13:42:25.587 +0300 SplashActivity INFO[main] - Checking if Recovery is required ...
2015-10-22 13:42:25.637 +0300 SplashActivity INFO[main] - OnCreate method.
2015-10-22 13:42:25.638 +0300 SplashActivity INFO[main] - Checking if Recovery is required ...
2015-10-22 13:42:25.828 +0300 GeoFenceManager INFO[pool-5-thread-1] - Removing geofences ...
2015-10-22 13:42:25.872 +0300 GeoFenceManager INFO[pool-5-thread-2] - Removing geofences ...
清单
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="x.y.z"
android:installLocation="internalOnly" >
<application
android:name=".global.GlobalInstance"
android:allowBackup="true"
android:allowClearUserData="true"
android:hardwareAccelerated="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:persistent="true" >
<activity
android:name=".activity.SplashActivity"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/Theme.Background" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".receiver.BootUpReceiver"
android:enabled="true"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
更新: 这是在重启后发生的,BOOT_COMPLETED 侦听器正在跟随
public class BootUpReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, SplashActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
知道如何避免这种情况吗?
private boolean checkIfActivityIsRootTask() {
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
Log.i("Main Activity is not the root. " + "Finishing Main Activity instead of launching.");
finish();
return true;
}
}
return false;
} ?
如果这个activity是roottask,它return false; RecoveryTask 有什么用?
你说:
We have already the check of CATEGORY_MAIN and !isTaskRoot()
但是两次检查都失败了。在您附加的日志中,当创建两个实例时,Detecting a brought to front, no need for recovery.
和 Main Activity is not the root. " + "Finishing Main Activity instead of launching.
都不会打印在日志中。这意味着在这两种方法中都不会遇到 if
部分,因此 finish()
永远不会被执行。这两种方法都返回 false。
此外,仅仅因为您在 onCreate()
中调用 finish()
和 return
,并不意味着 onStart()
或 onResume()
不会打电话。
为什么会这样? 启动器 activity 永远不会被调用两次(考虑到您没有在内部启动 activity)。可能是用户使用的某些 SDK 中的错误。
可能的修复:
您可以尝试在 SplashActivity
的清单中设置 android:launchMode="singleTop"
。这将确保只维护一个实例。
将 android:launchMode="SingleTask" 添加到 xml 中的初始 activity 元素。然后离开 activity 即离开启动调用 finish()。您应该使用此模式而不是 "SingleInstance" 的原因是用户永远无法使用后退键导航回启动画面(因为这不是正常行为)。