Android:除非我重新启动或手动卸载应用程序,为什么旧​​的PendingIntents不会被删除?

时间:2015-02-22 04:48:50

标签: android android-intent android-studio alarmmanager android-pendingintent

一些背景

我有一个持续通知的应用。我使用NotificationCompat.Builder来创建通知。我在构建器上调用setContentIntent()以向通知添加PendingIntent,以便在单击通知时运行MyActivity。

当MyActivity正在运行时,我点击正在进行的通知,我希望调用MyActivity onNewIntent()。为了实现这一点,我将MyActivity的launchMode设置为" singleTask",并将PendingIntent I传递给setContentIntent(),如下所示:

Intent notifyIntent = new Intent( this,
                                  MyActivity.class );
PendingIntent pendingIntent = PendingIntent.getActivity( this,
                                                         REQUEST_CODE,
                                                         notifyIntent,
                                                         PendingIntent.FLAG_UPDATE_CURRENT );

我在IntelliJ中调试我的应用程序,我看到一切都按预期工作。到目前为止没什么奇怪的。

问题

当我尝试将PendingIntent切换为使用TaskStackBuilder创建的PendingIntent时,会很奇怪。我知道这将不再导致调用onNewIntent(),只是忍受我:

Intent notifyIntent = new Intent( this,
                                  MyActivity.class );
TaskStackBuilder stackBuilder = TaskStackBuilder.create( this );
stackBuilder.addParentStack( MyActivity.class );
stackBuilder.addNextIntent( notifyIntent );
PendingIntent pendingIntent = stackBuilder.getPendingIntent( REQUEST_CODE,
                                                             PendingIntent.FLAG_UPDATE_CURRENT );

所以,让我们说我已经更新了我的PendingIntent以这种方式创建。我点击ctrl + S保存我的文件。我再次点击调试按钮,重建应用程序,替换我的设备上的旧apk等,并开始调试。当我点击正在进行的通知时,我期望看到的是现有的运行MyActivity被销毁并创建一个新的,即调用onCreate()。但是,我不明白。相反,我仍然像以前一样看到onNewIntent()被调用,好像我没有保存我的代码并重建它。

嗯,但我确实保存并重建它,因为同一个文件中的其他更改,例如新的Toast,正在显示。

让我重新启动设备并重试。嗯,现在onCreate()被调用而不是onNewIntent()。为什么它只在重启后才能工作?

我在不同的设备和模拟器上试过这个。我尝试了另一个IDE。同样的事情。

我甚至尝试过从使用TaskStackBuilder回到原来的PendingIntent,我仍然会遇到意想不到的行为。换句话说,我希望看到onNewIntent()被调用,因为我不再使用TaskStackBuilder,但我看到onCreate()被调用了。

因此,如果我重新启动设备,则会发生正确的预期行为。手动卸载应用程序也会产生正确的预期行为。

我错过了一些完全明显的东西吗?为什么会这样?好像Android正在记住上一个调试会话中的旧PendingIntent。

重现

我在此处的示例代码中创建了一个玩具示例:http://developer.android.com/guide/topics/ui/notifiers/notifications.html

以下是玩具示例:

MainActivity.java

package com.blah.pendingintent.example;

import android.app.Activity;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity
{
    private static final int REQUEST_CODE = 1;
    private static final int NOTIFICATION_ID = 2;
    private static final String TAG = "MainActivity";

    private NotificationCompat.Builder m_builder;
    private NotificationManager m_notificationMgr;

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

        m_builder = new NotificationCompat.Builder( this );
        m_notificationMgr = (NotificationManager)getSystemService( Context.NOTIFICATION_SERVICE );
    }

    @Override
    protected void onNewIntent( Intent intent )
    {
        super.onNewIntent( intent );
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
    }

    public void onClick( View v )
    {
        Intent notifyIntent = new Intent( this,
                                          MainActivity.class );

        // comment out one of these lines and rerun
        PendingIntent notifyPendingIntent = getPendingIntentWithActivity( notifyIntent );
        // PendingIntent notifyPendingIntent = getPendingIntentWithBackStack( notifyIntent );

        m_builder.setContentIntent( notifyPendingIntent );

        m_builder.setContentTitle( "Picture Download" )
                 .setContentText( "Download in progress" )
                 .setSmallIcon( R.drawable.abc_ic_go );

        new Thread( new Runnable()
        {
            @Override
            public void run()
            {
                int incr;
                for ( incr = 0; incr <= 100; incr += 5 )
                {
                    m_builder.setProgress( 0,
                                           0,
                                           true );
                    m_notificationMgr.notify( NOTIFICATION_ID,
                                              m_builder.build() );
                    try
                    {
                        Thread.sleep( 5 * 1000 );
                    }
                    catch ( InterruptedException e )
                    {
                        Log.d( TAG,
                               "sleep failure" );
                    }
                }
                m_builder.setContentText( "Download complete" )
                        .setProgress( 0,
                                      0,
                                      false );
                m_notificationMgr.notify( NOTIFICATION_ID,
                                          m_builder.build() );
            }
        }

        ).start();
    }

    private PendingIntent getPendingIntentWithBackStack( Intent notifyIntent )
    {
        Toast.makeText( this,
                        "Using TaskStackBuilder",
                        Toast.LENGTH_LONG )
             .show();
        TaskStackBuilder stackBuilder = TaskStackBuilder.create( this );
        stackBuilder.addParentStack( MainActivity.class );
        stackBuilder.addNextIntent( notifyIntent );
        return stackBuilder.getPendingIntent( REQUEST_CODE,
                                              PendingIntent.FLAG_UPDATE_CURRENT );
    }

    private PendingIntent getPendingIntentWithActivity( Intent notifyIntent )
    {
        Toast.makeText( this,
                        "Using PendingIntent",
                        Toast.LENGTH_LONG )
             .show();
        return PendingIntent.getActivity( this,
                                          REQUEST_CODE,
                                          notifyIntent,
                                          PendingIntent.FLAG_UPDATE_CURRENT );
    }
}

activity_main.xml中

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${packageName}.${activityClass}">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="pres butan"
        android:onClick="onClick"/>

</RelativeLayout>

的AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.blah.pendingintent.example" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.blah.pendingintent.example.MainActivity"
            android:launchMode="singleTask"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

build.gradle (IntelliJ向导中的默认文件)

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.9.+'
    }
}
apply plugin: 'android'

repositories {
    mavenCentral()
}

android {
    compileSdkVersion 19
    buildToolsVersion "19.1.0"

    defaultConfig {
        minSdkVersion 11
        targetSdkVersion 18
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:19.+'
}

1 个答案:

答案 0 :(得分:1)

  

因此,如果我重新启动设备,则会发生正确的预期行为。   手动卸载应用程序也会产生正确的预期   行为。

原因是,当您调试应用程序而不卸载它时,之前创建的PendingIntent仍然是AlarmManager。完全卸载应用程序后,之前的PendingIntent将被销毁。重新启动手机也会破坏所有现有的PendingIntent

  

好像Android正在记住之前的旧PendingIntent   调试会话。

正是正在发生的事情。因此,当您测试使用PendingIntent的应用程序时,您应该首先卸载应用程序并从IDE重新运行它以产生正确的预期行为。另请注意,正如您所观察到的,重新启动手机时会破坏现有的PendingIntent。因此,您的应用必须以编程方式重新创建在重新启动时被销毁的PendingIntent(因此,如果您的应用的用户重新启动他的手机,他们会收回他们安排的警报)。这是通过拦截BOOT_COMPLETE事件来完成的。有关详情,请参阅here

修改

要以编程方式检查先前创建的PendingIntent是否存在,请使用以下命令:

boolean pendingIntentExists = (PendingIntent.getBroadcast(context, requestCode, 
                               intent, 
                               PendingIntent.FLAG_NO_CREATE) != null);

其中requestCodeintent必须与您之前创建此PendingIntent时相同。

我希望能回答你的问题。

编辑2:

我相信你的情况,检查PendingIntent是否存在的测试需要:

// check PendingIntent for Activity
boolean pendingIntentExists = (PendingIntent.getActivity( this,
                                      REQUEST_CODE,
                                      notifyIntent,
                                      PendingIntent.FLAG_NO_CREATE) != null);

// check PendingIntent for backstack
TaskStackBuilder stackBuilder = TaskStackBuilder.create( this );
    stackBuilder.addParentStack( MainActivity.class );
    stackBuilder.addNextIntent( notifyIntent );
    boolean pendingIntentExists = (stackBuilder.getPendingIntent( REQUEST_CODE,
                                          PendingIntent.FLAG_NO_CREATE ) != null);

请试一试。