使用持久通知允许用户返回正在运行的Android应用程序。

9
我正在开发一个包含多个活动的应用程序。我想创建一个持久的通知,当我的后台服务运行时,它(或多或少)会显示“AppName - 返回到 AppName”。创建和清除通知没有问题。 现在,用户可能在任何一个屏幕/活动上,离开应用程序,然后希望通过通知重新进入应用程序。问题是,通知必须具有意图,该意图启动预定活动。我希望通知能够在历史堆栈顶部的任何活动中重新进入应用程序。
我的第一次尝试是一个丑陋的解决方法,制作一个活动(我们称之为“returnFromNotify”),它的唯一工作就是在其“onCreate”中“完成”自己。通知将在应用程序历史记录范围内打开“returnFromNotify”,然后立即删除自身,将用户发送回应用程序堆栈中的先前历史状态。这似乎有效...除非用户使用“返回”完全退出应用程序。然后,当他们点击通知时,“returnFromNotify”会加载,然后完成,将他们发送回主屏幕(因为应用程序的历史堆栈中没有活动)。
我考虑尝试检测“returnFromNotify”之前是否有任何历史记录堆栈,如果没有,则启动我的主要活动。但我似乎找不到这样做的方法。
对于一个Java/Android新手来说,有什么建议或意见吗?顺便说一下,我的主要历史记录是基于脚本语言的。
5个回答

6
我喜欢您最初提出的创建“returnFromNotify”活动的想法,而不是您提出的解决方法,因为有可能检测到ResumeActivity是否在堆栈底部(因此是堆栈中唯一的活动)。

以下是如何操作:

将ResumeActivity添加到清单中并指定noHistory属性:noHistory

<activity android:name=".ResumeActivity" android:noHistory="true" />

指定noHistory后,当Activity结束时,它将不会保留在栈中。这样,您就知道只有当前正在运行的ResumeActivity实例才会显示在堆栈中。

为了检查应用程序堆栈,您还需要请求GET_TASKS权限:

<uses-permission android:name="android.permission.GET_TASKS" />

现在您可以使用 ActivityManager::getRunningTasks() 来确定 ResumeActivity 是否是堆栈中唯一的活动:
public class ResumeActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if(isOnlyActivityInStack()) { //check the application stack
            //This activity is the only activity in the application stack, so we need to launch the main activity
            Intent main = new Intent(this, MainActivity.class);
            main.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(main);
        } else {
            //Return the user to the last activity they had open
            this.finish();
        }
    }

    /**
     * Checks the currently running tasks. If this activity is the base activity, we know it's the only activity in the stack
     *
     * @return  boolean  This activity is the only activity in the stack?
     **/
    private boolean isOnlyActivityInStack() {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        boolean onlyActivityInStack = false;

        for(RunningTaskInfo tasks : manager.getRunningTasks(Integer.MAX_VALUE)) {
            if(tasks.baseActivity.getPackageName().equals(this.getPackageName())) { //find this package's application stack
                if(tasks.baseActivity.getClassName().equals(this.getClass().getName())) {
                    //If the ResumeActivity is the base activity, we know that it is the only activity in the stack
                    onlyActivityInStack = true;
                    break;
                }
            }
        }

        return onlyActivityInStack;
    }

}

我知道您在2年前就已经提出了这个问题,但是我还是提供这个答案,以便其他人遇到类似的问题(就像我一样)。我认为您最初想要解决的方案是正确的。


是的!这个项目是为了一份我已经不在的工作,所以我完全不需要它...但它听起来正是我正在寻找的,所以谢谢!希望其他人也会发现它有用。 - Slobaum

3
好的,我相信我已经找到了一个令人满意的解决方法适用于我的特定情况。我在我的“mainActivity”中添加了一个静态整数,每次它的“onCreate”被触发时,它都会增加该整数。每次它的“onDestroy”被触发时,它都会减少。
在我的“returnFromNotify”中,我查看静态整数是否大于0。如果是,则假设有一个活动的“mainActivity”,并且在“returnFromNotify”内运行“finish”将返回那里。否则,它假设用户已经“退出”,完成自身,然后使用“startActivity”启动“mainActivity”的新实例。
这不是一个通用的解决方案,但对于我的目的,我认为它足够了。我仍然愿意接受其他答案,如果有人能打破我的逻辑,请这样做 - 欢迎建设性的批评。谢谢。

1

我猜没有简单的方法来做到这一点,但是我会扩展Application而不是在mainActivity中添加计数器:

为那些需要维护全局应用程序状态的人提供基类。您可以通过在AndroidManifest.xml的中指定其名称来提供自己的实现,这将导致在创建应用程序/包的进程时为您实例化该类。

我会在那里维护逻辑,并有一个像这样的方法:

public Intent getIntentForLastActivityShown();

当通知项被点击时调用。


1
那么你如何使用getIntentForLastActivityShown()呢?难道不是更简单的方法是在Application类中保持对通知的控制,然后每次启动新的意图时从notificaiton调用setLatestEventInfo,并将pendingIntent更新为当前可见活动吗? - Nathan Schwermann

1

我的第一种方法是使用SharedPreferences,并存储一个名为lastDisplayedActivity的键值对。然后在每个活动的onResume(可能还有`onCreate')中,您将有这样一行代码:

sharedPreferences.edit().putInteger("lastDisplayedActivity", ReturnFromNotify.THIS_ACTIVITY_NAME);

换句话说,您可以存储一个应用程序范围内的变量,指示上次显示的活动是哪个。然后,您只需从SharedPreferences中获取此变量并启动相应的活动。

这不是一个坏主意,但它需要我在每个活动的生命周期中添加代码(我宁愿不这样做)。此外,启动相应的活动是否会保留先前活动实例的状态,还是会创建该活动的新实例?我真的需要它保留他们之前的任何状态。 - Slobaum
它可能会在某些情况下自动恢复状态,但如果您的 Activity 目前未被显示,Android 可以随时将其终止以回收额外的内存。换句话说,如果要确保保存 Activity 的状态,您必须将其存储在其他地方。有许多方法可以做到这一点,但是如果您有相当数量的数据,我建议使用 SQLite 数据库: http://developer.android.com/guide/topics/data/data-storage.html#db - Computerish

0
通常我使用名为“Launcher”的活动来检查我的应用程序模型的状态,并根据模型规则启动活动(或执行其他操作)。我将模型对象放在我的应用程序类中。模型可以使用首选项来存储其状态。我这样做是为了避免在活动中使用静态字段。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接