从 Notification Intent 开始时保持 Activity 状态

Keep Activity state when starting from Notification Intent

我有一个以 Activity 开头的应用程序,用于在显示主要 Activity 之前加载内容。启动应用程序通常会 MyApplication --> MyLoadingActivity --> MyMainActivity。在 MyMainActivity 中有一个带有 RecyclerViews 和其他东西的 ViewPager。 MyMainActivity 中的状态在导航到应用程序中的其他 Activity 和从其他 Activity 导航时被正确保存和恢复,但是当从通知启动 MyMainActivity 时,所有状态都被清除,因为 Android 重新启动 Activity.

通知是从检查远程服务器更新的服务创建的。一旦找到更新,新数据将存储在 SQLite 数据库中,并创建一个关于此的通知。当我单击此通知时,MyMainActicity 已启动,但状态丢失。

official guide 之后,这里是我如何在服务中创建通知:

NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
    .setSmallIcon(R.drawable.foo)
    .setAutoCancel(true)
    .setContentTitle("title")
    .setContentText("text")
    .setStyle(new NotificationCompat.BigTextStyle().bigText("A long text will go here if we are on Lollipop or above."));

Intent notifyIntent = new Intent(context, MyMainActivity.class);
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

PendingIntent notifyPendingIntent = PendingIntent.getActivity(context, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(notifyPendingIntent);

NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(666, builder.build());

我尝试过以各种方式组合 Intent.FLAG_***,但无法使其正常工作。我无法弄清楚这一点,因此提出了这个问题。

打开Notification我想实现的是:

  1. 如果应用程序不是运行ning,正常启动应用程序并转到MyMainActivity
  2. 如果应用程序 运行ning 并且 MyMainActivity 处于活动状态,只需将其置于前面 并保持现有状态 和 运行几行代码。
  3. 如果应用程序是 运行ning 而另一个 Activity 是 运行ning,则返回到 MyMainActivity,最好使用现有堆栈。大多数其他活动都是 MyMainActivity.
  4. 的子活动

使用当前代码会发生什么:

  1. 使用 Intent.FLAG_ACTIVITY_NEW_TASK 创建 MyMainActivitynew 实例,它具有空状态,即使它的描述是

When using this flag, if a task is already running for the activity you are now starting, then a new activity will not be started; instead, the current task will simply be brought to the front of the screen with the state it was last in.

  1. 在创建新的 MyMainActivity 后,还使用 ​​Intent.FLAG_ACTIVITY_CLEAR_TASK 在现有 MyMainActivity 上调用 onDestroy()。这并没有多大帮助。

我尝试过的其他东西:

一个。如 here 所述,将 Intent 设置为像应用程序通常那样启动是行不通的。

乙。标志的各种组合未能提供所需的行为。

C。 Whosebug 上的各种其他答案。

作为参考,这是我的清单文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.my.app"
    android:versionCode="1"
    android:versionName="1.0" >

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

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/my_app_name"
        android:theme="@style/MyTheme"
        android:name=".MyApplication" >
        <activity
            android:name=".activities.MyLoadingActivity"
            android:icon="@drawable/ic_launcher"
            android:screenOrientation="portrait" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".activities.MyMainActivity"
            android:icon="@drawable/ic_launcher"
            android:label="@string/my_app_name"
            android:screenOrientation="portrait" >
        </activity>
        <activity
            android:name=".activities.AnotherActivity"
            android:label="@string/aa_title"
            android:icon="@drawable/ic_launcher"
            android:parentActivityName=".activities.MyMainActivity"
            android:screenOrientation="portrait">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value=".activities.MyMainActivity" >
            </meta-data>
        </activity>

        <!-- More children activities of MyMainActivity here -->

        <receiver android:name=".services.ScheduleUpdateReciever">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="com.my.app.action.ScheduleUpdateReceiver"/>
            </intent-filter>
        </receiver>
        <receiver android:name=".services.StartUpdateReceiver" />

        <service android:enabled="true" android:name=".services.UpdateService" />

    </application>

</manifest>

更新:saving/restoring

下面是 MyMainActivity 的保存和恢复方法。我意识到这可能需要一些清洁。也许这与问题有关。为简洁起见,省略了 class 的其他部分。如上所述,在应用程序内部导航时,所有保存和恢复工作正常。

private ShoppingList mShoppingList;
private FilteredRecipes mFilteredRecipes;
private MainFragment mMainFragment;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_activity_layout);

    // Start the Broadcast receiver that creates notifications if new data is found.
    startService(new Intent(this, ScheduleUpdateReciever.class));
    sendBroadcast(new Intent(SCHEDULE_UPDATE_RECEIVER_BROADCAST));

    // Setup Toolbar, NavigationView, DrawerLayout etc. here. No state restored for these widgets.

    if (mShoppingList == null)
    {
        mShoppingList = new ShoppingList(this);
    }

    if (mFilteredRecipes == null)
    {
        mFilteredRecipes = new FilteredRecipes(this);
    }

    if (savedInstanceState == null)
    {
        mMainFragment = new MainFragment();
    }
    else
    {
        restoreState(savedInstanceState);
    }

    getSupportFragmentManager().beginTransaction()
            .replace(R.id.content_frame, mMainFragment, FragmentConstants.MAIN_FRAGMENT_TAG)
            .commit();


}

@Override
protected void onSaveInstanceState(Bundle outState)
{
    super.onSaveInstanceState(outState);

    // These calls adds stuff directly to the Bundle instance, be it basic variables, lists or Parcelable classes.
    mShoppingList.onSaveInstanceState(outState);
    mFilteredRecipes.onSaveInstanceState(outState);
    getSupportFragmentManager().putFragment(outState, SAVE_RESTORE_MAIN_FRAGMENT, mMainFragment);
}


private void restoreState(Bundle savedInstanceState)
{
    // These calls restores stuff directly from the Bundle instance, be it basic variables, lists or Parcelable classes.
    mShoppingList.onRestoreInstanceState(savedInstanceState);
    mFilteredRecipes.onRestoreInstanceState(savedInstanceState);
    mMainFragment = (MainFragment) getSupportFragmentManager().getFragment(savedInstanceState, SAVE_RESTORE_MAIN_FRAGMENT);
}

来自 android 文档:http://developer.android.com/reference/android/content/Intent.html

关于FLAG_ACTIVITY_CLEAR_TOP:

If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent.

关于FLAG_ACTIVITY_SINGLE_TOP:

If set, the activity will not be launched if it is already running at the top of the history stack.

这听起来很像您想要实现的目标。试试这个:

notifyIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);

编辑:

您正在使用 savedInstanceState 来保存来自您的 activity 的数据,我不建议使用这种方法,因为它不可靠,从文档 (http://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState%28android.os.Bundle%29) 您可以推断根本无法调用 onSaveInstanceState():

Do not confuse this method with activity lifecycle callbacks such as onPause(), which is always called when an activity is being placed in the background or on its way to destruction, or onStop() which is called before destruction. One example of when onPause() and onStop() is called and not this method is when a user navigates back from activity B to activity A: there is no need to call onSaveInstanceState(Bundle) on B because that particular instance will never be restored, so the system avoids calling it. An example when onPause() is called and not onSaveInstanceState(Bundle) is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState(Bundle) on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact.

您应该使用更持久的方式来存储您的 key/value 信息,例如 sharedPreferences 或数据库,并在您的 onCreate 方法中检索这些值。此处有更多相关信息:http://developer.android.com/training/basics/data-storage/index.html

您也可以在此处查看我的回答: 了解使用 sharedPreferences 的简单方法。