如何使通知意图恢复而不是创建新的意图?

40
我所拥有的是一个简单的 WebView 活动,当加载它时会自动显示一个持续通知。这样人们就可以从任何屏幕上拉下下拉菜单并选择它,以便从此活动中导航到其他屏幕。然后,他们只需按菜单按钮,然后按退出并清除通知即可关闭通知。这一切都很好用。但是,当点击通知时,它会启动该活动的新实例。我需要改变什么来让它看到该活动尚未被销毁,因此可以调用该实例(恢复它),因此不需要再次加载它,也不需要将另一个活动添加到我的堆栈中。有什么想法吗?任何帮助都将不胜感激。
package com.my.app;

import com.flurry.android.FlurryAgent;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent; 
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.webkit.CookieSyncManager;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;

public class Chat extends Activity { 
    private ProgressDialog progressBar;
    public WebView webview;
    private static final String TAG = "Main";

    private NotificationManager mNotificationManager;
    private int SIMPLE_NOTFICATION_ID;

    @Override
    public void onStart() {
       super.onStart();
       CookieSyncManager.getInstance().sync();
       FlurryAgent.onStartSession(this, "H9QGMRC46IPXB43GYWU1");
    }

    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.chat);

        mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

        final Notification notifyDetails = new Notification(R.drawable.chat_notification,"Chat Started",System.currentTimeMillis());

        notifyDetails.flags |= Notification.FLAG_ONGOING_EVENT;

        Context context = getApplicationContext();

        CharSequence contentTitle = "Chat";
        CharSequence contentText = "Press to return to chat";

        Intent notifyIntent = new Intent(context, Chat.class);

        PendingIntent intent =
        PendingIntent.getActivity(Chat.this, 0,
        notifyIntent, android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
        notifyDetails.setLatestEventInfo(context, contentTitle, contentText, intent);

        mNotificationManager.notify(SIMPLE_NOTFICATION_ID, notifyDetails);

        CookieSyncManager.createInstance(this);
        CookieSyncManager.getInstance().startSync();
        webview = (WebView) findViewById(R.id.webviewchat);
        webview.setWebViewClient(new chatClient());
        webview.getSettings().setJavaScriptEnabled(true);
        webview.getSettings().setPluginsEnabled(true);
        webview.loadUrl("http://google.com");

        progressBar = ProgressDialog.show(Chat.this, "", "Loading Chat...");  
    }

    private class chatClient extends WebViewClient { 
        @Override 
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            Log.i(TAG, "Processing webview url click...");
            view.loadUrl(url);
            return true;
        }

        public void onPageFinished(WebView view, String url) {
            Log.i(TAG, "Finished loading URL: " +url);
            if (progressBar.isShowing()) {
                progressBar.dismiss();
            }
        }
    }

    public boolean onKeyDown(int keyCode, KeyEvent event) { 
        if ((keyCode == KeyEvent.KEYCODE_BACK) && webview.canGoBack()) { 
            webview.goBack(); 
            return true; 
        }
        return super.onKeyDown(keyCode, event); 
    }

    @Override
    public boolean onCreateOptionsMenu (Menu menu) {
        super.onCreateOptionsMenu(menu);
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.chatmenu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected (MenuItem item) {
        switch (item.getItemId()) {
            case R.id.home:
                Intent a = new Intent(this, Home.class);
                startActivity(a);
                return true;
            case R.id.closechat:
                mNotificationManager.cancel(SIMPLE_NOTFICATION_ID);
                Intent v = new Intent(this, Home.class);
                startActivity(v);
                return true;
        }
        return false;
    }

    public void onStop() {
       super.onStop();
       CookieSyncManager.getInstance().sync();
       FlurryAgent.onEndSession(this);
    }
}

@Commonsware

只是为了确保我理解正确,您是这样建议的吗?

我有点担心这一行,

PendingIntent.getActivity(Chat.this, 0, notifyIntent, SIMPLE_NOTFICATION_ID);

public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.chat);

    mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

    final Notification notifyDetails = new Notification(R.drawable.chat_notification,"Chat Started",System.currentTimeMillis());

    notifyDetails.flags |= Notification.FLAG_ONGOING_EVENT;


    Context context = getApplicationContext();

    CharSequence contentTitle = "Chat";
    CharSequence contentText = "Press to return to chat";

    Intent notifyIntent = new Intent(context, Chat.class);

    PendingIntent intent =
    PendingIntent.getActivity(Chat.this, 0, notifyIntent, SIMPLE_NOTFICATION_ID);
    notifyDetails.setLatestEventInfo(context, contentTitle, contentText, intent);
    notifyIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);

    mNotificationManager.notify(SIMPLE_NOTFICATION_ID, notifyDetails);


    CookieSyncManager.createInstance(this);
    CookieSyncManager.getInstance().startSync();
    webview = (WebView) findViewById(R.id.webviewchat);
    webview.setWebViewClient(new chatClient());
    webview.getSettings().setJavaScriptEnabled(true);
    webview.getSettings().setPluginsEnabled(true);
    webview.loadUrl("http://google.com");

    progressBar = ProgressDialog.show(Chat.this, "", "Loading Chat...");        
}
5个回答

56

这个想法是让用户能够从此活动导航到任何屏幕,并通过下拉菜单选择它快速访问。

请将其设置为可选项。

但是,当通知被按下时,它会启动该活动的一个新实例。

这是默认行为。

我需要改变什么来判断该活动是否已被销毁并只需调用该实例(恢复它),因此不需要重新加载它,并且不需要将另一个活动添加到我的堆栈中。

去掉 FLAG_ACTIVITY_NEW_TASK,加入 notifyIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); -- 参见这个示例项目

Context context = getApplicationContext();
请不要这样做。只需将您的Activity作为Context使用。

非常感谢您查看这个!有机会的话,您能否看一下上面的编辑? - brybam
1
这对我也起作用了,但通知现在不会消失,有什么提示吗? - Marthyn Olthof
1
@MarthynOlthof:在“通知”上的FLAG_AUTO_CLEAR - CommonsWare
这个解决方案似乎对于只有一个任务的应用程序可以正常工作。但是,如果存在具有不同任务亲和性的多个启动器活动,则似乎无法正确工作。在这种情况下,要恢复的活动似乎会在新任务中重新创建,而不是从当前任务顶部的实例恢复。是否有扩展来进一步指定要恢复的任务,以便在这种情况下不会重新创建目标活动? - dhakim
@dhakim:你不能直接指定“要恢复哪个任务”。你需要尝试使用Intent标志和清单属性来达到想要的效果。我没有尝试过类似于你描述的任何操作,所以我无法为你提供解决方案,抱歉。 - CommonsWare
显示剩余5条评论

21

这是我展示通知的方法。希望能对你有所帮助。

private static void generateNotification(Context context, String message){
    Intent notificationIntent = new Intent(context, YOUR_ACTIVITY.class);
    notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    PendingIntent intent = PendingIntent.getActivity(context, 0, notificationIntent, 0);

    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
        .setSmallIcon(R.drawable.ic_launcher)
        .setContentTitle(context.getString(R.string.app_name))
        .setContentIntent(intent)
        .setPriority(PRIORITY_HIGH) //private static final PRIORITY_HIGH = 5;
        .setContentText(message)
        .setAutoCancel(true)
        .setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS);
    NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    mNotificationManager.notify(0, mBuilder.build());
}

9
点击通知后会创建一个新的实例。尽管作者询问的是恢复活动,而不是创建一个新的实例。奇怪的是这个答案获得了这么多分。 - Alexeev Valeriy

21

万一其他人有我曾经遇到的难题......

我尝试了以上所有代码都没有成功——即使已经运行一个应用程序,通知项仍然会启动完全新的实例。(我只希望在任何时候只有一个实例可用。)最后注意到这个其他的线程(https://stackoverflow.com/a/20724199/4307281),它需要在清单中添加一个简单的额外条目:

android:launchMode="singleInstance"

我将其放置在我的应用程序的主活动部分下面的清单中,然后通知项重新使用了已经进行中的实例。


1
这对我有用。如果你建立一个GPS活动,这将是正确的方法。 - Pierre Maoui
这对我很有效,也是最简单和最直接的解决方案。感谢分享。 - Mijo
很棒的解决方案,修改清单后我找不到任何错误。 - FRANCIS FERNANDO

3

没有任何解决方案适用于我,所以我找到了自己的方法。我的所有活动都是从一个BaseActivity扩展出来的。我遵循了一系列简单的步骤,使我的推送通知点击可以恢复应用程序:

第一步:常量记录当前活动:

public static Context currentContext;

步骤2:在继承自BaseActivity的活动的onCreate()onResume()中执行

currentContext = this;

第三步:在创建通知时使用“Intent”代码。
Class activity = BaseActivity.commonContext.getClass();
Intent intent = new Intent(this, activity);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

//FLAG_UPDATE_CURRENT is important
PendingIntent pendingIntent = PendingIntent.getActivity(this, (int)System.currentTimeMillis(), intent, PendingIntent.FLAG_UPDATE_CURRENT);

1
我发现对于多任务应用程序,没有任何答案可行。可以理解的是,在多个任务的情况下,Intent.FLAG_ACTIVITY_CLEAR_TOP和Intent.FLAG_ACTIVITY_SINGLE_TOP并不完全相同。在我的应用程序中,有多个任务根活动。在某些时候,用户将收到通知,其中一个任务需要完成工作。与OP不同,我只需要返回到其中一个任务的顶部,而不是特定的活动,这可能被埋在其中一个任务中。我认为这足够类似于提出的问题,可能对其他人有用。
我的解决方案包括静态任务ID来跟踪当前任务,广播接收器来处理挂起意图,新的使用权限标签以及一些简单的ActivityManager使用。
private static int taskID;

public static void setResumeTask(int tID)
{
    Log.e("TaskReturn", "Setting Task ID: " + tID);
    taskID = tID;
}

public static PendingIntent getResumeTask(Context appContext)
{
    return PendingIntent.getBroadcast(appContext, 0, new Intent(appContext, TaskReturn.class).putExtra(TaskReturn.EXTRA_TASK_ID, taskID), PendingIntent.FLAG_UPDATE_CURRENT);
}

public void onReceive(Context context, Intent intent)
{
    Log.e("TaskReturn", "Task Return!");
    if (intent.hasExtra(EXTRA_TASK_ID))
    {
        int taskToStart = intent.getIntExtra(EXTRA_TASK_ID, -1);
        Log.e("TaskReturn", "Returning To Task: " + taskToStart);
        ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.AppTask> allAppTasks = activityManager.getAppTasks();

        for (ActivityManager.AppTask task : allAppTasks)
        {
            Log.e("TaskReturn", "AllTasks: " + task.getTaskInfo().id + " " + task.getTaskInfo().affiliatedTaskId + " " + task.getTaskInfo().persistentId);
        }

        activityManager.moveTaskToFront(taskToStart, ActivityManager.MOVE_TASK_WITH_HOME);
    }
    else
    {
        Log.e("TaskReturn", "WHERE IS MY EXTRA!?!?!");
        //Boo...
    }
}

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

...

<receiver android:name=".TaskReturn"/>

基本上的想法是将调用 Activity.getTaskId() 的结果标记为状态。稍后,当创建通知以恢复到该活动的任务时,获取任务 ID 并将其放入额外内容中。然后,将待处理意图设置为指向广播接收器,该广播接收器提取出任务 ID 并启动该任务。
看起来这需要使用我使用的 API 21,并且如果任务已经退出,则可能需要进行一些附加错误检查,但它符合我的目的。

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