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

发布于 2024-09-15 23:38:12 字数 666 浏览 6 评论 0原文

我正在开发一个包含大量活动的应用程序。我想创建一个持久通知(或多或少)说“AppName - 返回AppName”,每当我的后台服务运行时都会出现该通知。 创建和处理通知没有问题。

现在,用户可以在多个屏幕/活动中的任何一个上,离开应用程序,然后希望通过通知重新进入应用程序。 问题是,通知必须有一个意图,启动一个预定的 Activity。我希望通知重新进入应用程序历史堆栈顶部的任何 Activity

我第一次尝试一个丑陋的解决方法是创建一个活动(让我们称之为“returnFromNotify”),其唯一的工作是在“onCreate”中“完成”自身。该通知将在应用程序历史记录范围内打开“returnFromNotify”,然后该通知将立即删除自身,将用户发送回应用程序堆栈中的先前历史记录状态。这似乎有效......除非用户使用“后退”完全退出应用程序。然后,当他们点击通知时,“returnFromNotify”加载,然后完成,将它们发送回主屏幕(因为应用程序的历史堆栈中没有活动)。

我考虑尝试在“returnFromNotify”之前检测历史堆栈中是否有任何内容,如果没有,则启动我的主活动。我似乎也找不到办法做到这一点。

对于 Java/Android 新手有什么意见或建议吗?仅供参考,我的主要历史是基于脚本的语言。

I am developing an app with numerous Activities. I would like to create a persistent notification that (more or less) says, "AppName - Return to AppName" that will be present whenever my background services are running. Creating and disposing of the notification was no problem.

Now, the user could be on any of several screens/Activities, leave the application, then want to re-enter the app via the notification. The problem is, the notification must have an intent, which launches a predetermined Activity. I want the notification to re-enter the app in whatever Activity is at the top of the history stack.

My first attempt at an ugly workaround was to make an activity (let's call it "returnFromNotify") whose only job was to "finish" itself in it's "onCreate". The notification would open "returnFromNotify" in the scope of the applications history, which would then immediately remove itself, sending the user back to the previous history state in the application stack. This seems to work... unless the user has used "back" to completely back out of the app. Then, when they hit the notification, "returnFromNotify" loads, then finishes, sending them back out to the home screen (as there are no activities in the history stack for the app).

I considered trying to detect if there was anything in the history stack before "returnFromNotify", and if not, fire up my main Activity. I can't seem to find a way to do this, either.

Any input or suggestions for a Java/Android novice? FYI, My primary history is with script-based languages.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

夏雨凉 2024-09-22 23:38:12

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

操作方法如下:

将 ResumeActivity 添加到清单中并指定 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;
    }

}

我知道您在两年多前问过这个问题,但我提供这个答案以防其他人遇到这种特殊情况(正如我所做的那样)。我认为您最初致力于的解决方案走在正确的轨道上。

I like your original idea of creating a "returnFromNotify" activity better than your proposed workaround, as it is possible to detect if the ResumeActivity is at the bottom of the stack (and therefore the only activity in the stack).

Here's how you can do it:

Add your ResumeActivity to the manifest and specify the noHistory attribute:

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

Specifying noHistory will make sure this Activity won't stay in the stack as soon as it finishes. This way you know that only a currently running instance of the ResumeActivity will show up in the stack.

In order to check the application stack, you'll also have to ask for the GET_TASKS permission:

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

Now you can use ActivityManager::getRunningTasks() to determine if ResumeActivity is the only activity in the stack:

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

}

I know you asked this question over 2 years ago, but I'm providing this answer in case anyone else runs in to this particular situation (as I did). I think you were on the right track with the solution you were originally working towards.

野侃 2024-09-22 23:38:12

好的,我相信我已经针对我的具体案例找到了令人满意的解决方法。我在“mainActivity”中添加了一个静态整数,每次触发“onCreate”时,都会增加该整数。每次“onDestroy”被触发时,它都会减少。

在我的“returnFromNotify”中,我查看静态整数以查看它是否大于 0。如果是,我假设有一个活动的“mainActivity”,并且在“returnFromNotify”内运行“finish”将返回那里。否则,它假设用户已“退出”,完成自身,然后使用“startActivity”启动“mainActivity”的新实例。

这不是一个通用的解决方案,但就我的目的而言,我认为它就足够了。我仍然对其他答案持开放态度,如果有人可以在我的逻辑中戳破一个洞,请这样做 - 欢迎建设性批评。谢谢。

Okay, I believe that I have found a satisfactory work-around for my specific case. I've added a static integer to my "mainActivity", and each time it's "onCreate" is fired, it increments the integer. Each time it's "onDestroy" is fired, it decrements.

In my "returnFromNotify", I look at the static integer to see if it is greater than 0. If so, I assume there is an active "mainActivity", and that running "finish" inside "returnFromNotify" will return there. Otherwise, it assumes the users has "backed" out, finishes itself, then uses "startActivity" to fire up a new instance of "mainActivity".

This is not a universal solution, but for my purposes, I think it will suffice. I am still open to other answers, and if someone can punch a hole in my logic, please do so - constructive criticism is welcome. Thanks.

栖迟 2024-09-22 23:38:12

我想没有简单的方法可以做到这一点,但我不会在 mainActivity 中添加计数器,而是扩展 应用

为那些需要的人提供基础课程
维护全局应用程序状态。你
可以通过以下方式提供您自己的实现
在您的中指定其名称
AndroidManifest.xml 的
标签,这将导致该类
当流程时为您实例化
对于您的应用程序/包是
创建。

我会在那里管理逻辑,并有一个类似的方法:

public Intent getIntentForLastActivityShown();

单击通知项时调用。

I guess there is no easy way to do this but instead of adding a counter in the mainActivity I would extend Application:

Base class for those who need to
maintain global application state. You
can provide your own implementation by
specifying its name in your
AndroidManifest.xml's
tag, which will cause that class to be
instantiated for you when the process
for your application/package is
created.

I would mantein the logic there and have a method like:

public Intent getIntentForLastActivityShown();

to be called when the notification item is clicked.

残龙傲雪 2024-09-22 23:38:12

我的第一种方法是使用 SharedPreferences 并存储一个名为 LastDisplayedActivity 之类的键值对。然后,在每个 Activity 的 onResume(可能还有“onCreate”)中,您将有这样一行:

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

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

My first approach would be to use SharedPreferences and store a key value pair called something like lastDisplayedActivity. Then in each Activity's onResume (and possibly `onCreate') you would have a line like this:

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

In other words, you store an application-wide variable indicating which activity was last displayed. Then you just grab this variable from SharedPreferences and launch the corresponding activity.

无声无音无过去 2024-09-22 23:38:12

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

I usually use activity named "Launcher" that checks state of my application model and starts activities (or does other things) depending on model rules. I put Model object in my Application class. Model can use Preferences to store its state. I do it to avoid static fields in activities.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文