【问题标题】:Keep Activity state when starting from Notification Intent从 Notification Intent 开始时保持 Activity 状态
【发布时间】:2016-01-23 13:48:11
【问题描述】:

我有一个以 Activity 开头的应用程序,用于在显示主 Activity 之前加载内容。启动应用程序通常是MyApplication --> MyLoadingActivity --> MyMainActivity。在MyMainActivity 中有一个带有 RecyclerViews 和其他东西的 ViewPager。 MyMainActivity 中的状态在导航到应用程序中的其他 Activity 和从其他 Activity 导航时会被正确保存和恢复,但是当从 Notification 启动 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_***,但我无法使其正常工作。我无法弄清楚这一点,因此提出了这个问题。

我在打开通知时想要实现的是:

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

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

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

当使用这个标志时,如果一个任务已经在为这个活动运行 您现在正在开始,则不会开始新的活动; 相反,当前任务将简单地被带到前面 屏幕显示上次所处的状态。

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

我还尝试了哪些其他方法:

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

B.各种标志组合都无法提供所需的行为。

C. Stackoverflow 上的各种其他答案。

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

<?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>

更新:保存/恢复

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

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);
}

【问题讨论】:

  • 从/到另一个活动时,您如何恢复/保存 MyMainActicity 的状态?您是否使用数据库中的意图、sharedPreferences 和值?如果是这样,您是在 onCreate、onStart 还是 onResume 中恢复 MyMainActicity?
  • 您是否尝试过使用标志:FLAG_ACTIVITY_SINGLE_TOP 和 FLAG_ACTIVITY_CLEAR_TOP 比如:intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
  • 有关保存和恢复状态的信息,请参阅上面的更新。此过程中不涉及 Intent、SharedPreferences 或数据库调用。我将状态保存在onSaveInstanceState() 并在onCreate() 中恢复状态。 onResume()onStart() 均未实现。
  • 我编辑了我的答案,如果您需要任何关于如何使用 sharedPreferences 保存状态的具体示例,请告诉我:)

标签: android android-intent android-notifications


【解决方案1】:

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

关于 FLAG_ACTIVITY_CLEAR_TOP:

如果设置,并且正在启动的活动已经在 当前任务,而不是启动该任务的新实例 活动,除此之外的所有其他活动都将关闭,并且 此 Intent 将作为 新意图。

关于 FLAG_ACTIVITY_SINGLE_TOP:

如果设置,如果 Activity 已经在运行,则不会启动 历史堆栈的顶部。

这听起来几乎是您想要实现的目标。试试这个:

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

编辑:

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

不要将此方法与活动生命周期回调混淆,例如 onPause(),当一个活动被放置时总是被调用 背景或正在销毁的途中,或 onStop() 是 在破坏之前调用。 onPause() 和 onStop() 时的一个示例 被调用,而不是当用户从 活动B到活动A:无需调用 onSaveInstanceState(Bundle) 在 B 上,因为该特定实例将 永远不会被恢复,所以系统避免调用它。一个例子当 onPause() 被调用,而不是 onSaveInstanceState(Bundle) 是什么时候 活动 B 在活动 A 之前启动:系统可能会避免 如果活动 A 没有被杀死,则在活动 A 上调用 onSaveInstanceState(Bundle) 在 B 的生命周期内,从 A 的用户界面状态开始 将保持原样。

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

您也可以在此处查看我的答案:https://stackoverflow.com/a/35023304/5837758 以获得使用 sharedPreferences 的简单方法。

【讨论】:

  • 谢谢,你提到的标志看起来很有希望,我不知道我是如何在浏览标志时跳过它们的。我还覆盖了onNewIntent() 以捕获额外的意图,以便在从通知启动 Acticity 并且它似乎可以正常工作时执行一些操作。我会做更多的测试,以确保一切都按预期工作。是的,我可以看到自己在不久的将来会更改保存/恢复状态(我有点知道)。
  • 是的,标志按预期工作。感谢您对此进行调查。我不相信我需要比使用onSaveInstanceState() 更持久的储蓄。在我的情况下,如果用户杀死应用程序,它会丢失状态。在这种情况下,我会将我需要的数据保存到一个文件中(我已经实现了一些自动保存,然后可能会在这种情况下使用)。如果应用程序在后台被 Android 破坏,onSaveInstanceState() 在我的情况下也可以。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-01
  • 1970-01-01
  • 2018-02-22
  • 1970-01-01
  • 2015-10-06
  • 1970-01-01
相关资源
最近更新 更多