【问题标题】:How to create an intent to an Activity but with updated content (not initial)?如何创建一个活动的意图但更新内容(不是初始)?
【发布时间】:2020-11-29 19:14:44
【问题描述】:

我是 Android SDK 的新手,我开发了一个使用 AlarmManager 每 30 分钟发送一次通知的应用程序。每个通知都包含一个操作按钮。在 MainActivity 中,我有一个 TextView,其中显示了一个数字,用于计算操作按钮被点击的次数。

存在显示计数不正确的用例。要重现该问题,我执行以下步骤:

  1. 应用已最小化,我等待通知。
  2. 点击操作按钮
  3. 点击通知转到应用程序

应用程序会自行打开,但 TextView 仍显示“0”(就像在初始状态下一样)。但是,当我单击设备上的 BACK 按钮时,计数变为正确的计数。

当我点击通知时,我收到一条警告,这可能是问题的原因: W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@c8e60ce

我试图用 MainActivity 的 onCreate() 方法中的这一行来解决这个问题: mCount.setText(String.valueOf(mCorrectPostureReceiver.getCount())); 但它不起作用。

我还应该提到,当我单击操作按钮,然后使用启动器(而不是通过点击通知)转到应用程序时,一切正常。

MainActivity.java

//
//imports
//

public class MainActivity extends AppCompatActivity {
    private static final String LOG_TAG = MainActivity.class.getSimpleName();
    private static String CORRECT_POSTURE_COUNT;
    private NotificationManager mNotifyManager;
    private static final int NOTIFICATION_ID = 0;
    private static final String PRIMARY_CHANNEL_ID = "primary_notification_channel";
    private CorrectPostureReceiver mCorrectPostureReceiver = new CorrectPostureReceiver(this);
    static final String POSTURE_YES_ACTION = BuildConfig.APPLICATION_ID + ".POSTURE_YES_ACTION";

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

        createNotificationChannel();

        ToggleButton mNotifToggle = findViewById(R.id.notify_toggle);
        TextView mCount = findViewById(R.id.txt_count);
        mCount.setText(String.valueOf(mCorrectPostureReceiver.getCount())); //doesn't work

        //set alarmPendingIntent to deliver repeating notifications
        final AlarmManager mAlarmManager = (AlarmManager) this.getSystemService(ALARM_SERVICE);
        Intent alarmIntent = new Intent(this, AlarmReceiver.class);
        mNotifToggle.setChecked(PendingIntent.getBroadcast(this, NOTIFICATION_ID, alarmIntent, PendingIntent.FLAG_NO_CREATE) != null); // check if notifications were turned on before the new MainActivity was stopped
        final PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(this, NOTIFICATION_ID, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        registerReceiver(mCorrectPostureReceiver, new IntentFilter(POSTURE_YES_ACTION));

        mNotifToggle.setOnCheckedChangeListener(
                new CompoundButton.OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                        String toastMessage = "error: AlarmManager is null";
                        if(isChecked) {
                            long repeatInterval;

                            //is emulator or device
                            if(Build.FINGERPRINT.startsWith("google/sdk_gphone_x86/generic")) {
                                repeatInterval = 30000; //short interval only for debug }                  
                            else { repeatInterval = AlarmManager.INTERVAL_HALF_HOUR; }

                            long triggerTime = SystemClock.elapsedRealtime() + repeatInterval;
                            if(mAlarmManager!=null) {
                                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, repeatInterval, alarmPendingIntent);
                                toastMessage = "Upright notifications on.";
                            }
                        }
                        else {
                            mNotifyManager.cancelAll();
                            if (mAlarmManager!=null) {
                                mAlarmManager.cancel(alarmPendingIntent);
                            }
                            toastMessage = "Upright notifications off.";
                        }
                        Toast.makeText(MainActivity.this, toastMessage, Toast.LENGTH_SHORT).show();
                    }
                }
        );
        Log.d(LOG_TAG, "A: created");
    }


    @Override
    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
//        super.onRestoreInstanceState(savedInstanceState);  //should be commmented?
        mCorrectPostureReceiver.getTxtCount().setText(savedInstanceState.getString(CORRECT_POSTURE_COUNT));
    }

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        outState.putString(CORRECT_POSTURE_COUNT, String.valueOf(mCorrectPostureReceiver.getCount()));
        super.onSaveInstanceState(outState);
    }

    private void createNotificationChannel() {
        mNotifyManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        long[] vibPattern = {0, 200, 200, 200, 200, 200}; //{delay1, vibDuration1, delay2, vibDuration2...}
        if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            NotificationChannel notificationChannel = new NotificationChannel(
                    PRIMARY_CHANNEL_ID,
                    "Check posture notification",
                    NotificationManager.IMPORTANCE_HIGH);
            notificationChannel.setDescription("notification check posture");
            notificationChannel.enableVibration(true);
            notificationChannel.setVibrationPattern(vibPattern);
            mNotifyManager.createNotificationChannel(notificationChannel);
        }
    }


}

AlarmReceiver.java - 从 AlarmManager 接收重复的意图消息并传递通知

//
//imports
//

public class AlarmReceiver extends BroadcastReceiver {

    private NotificationManager mNotificationManager;
    private static final int NOTIFICATION_ID = 0;
    private static final String PRIMARY_CHANNEL_ID = "primary_notification_channel";
    private static final String LOG_TAG = AlarmReceiver.class.getSimpleName();

    @Override
    public void onReceive(Context context, Intent intent) {
        mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        deliverNotification(context);
        Log.d(LOG_TAG, "notification fired!");
    }

    private void deliverNotification(Context context) {

        //this intent causes that when the notification is clicked, the MainActivity is launched
        Intent notifClickIntent = new Intent(context, MainActivity.class); //when the notification is clicked, the MainActivity is launched
        PendingIntent notifClickPendingIntent = PendingIntent.getActivity(
                context,
                NOTIFICATION_ID,
                notifClickIntent,
                PendingIntent.FLAG_UPDATE_CURRENT
        );
     
        // after you click the action button on notification, this intent will be sent
        Intent postureYesIntent = new Intent(MainActivity.POSTURE_YES_ACTION);
        PendingIntent yesPendingIntent = PendingIntent.getBroadcast(context, NOTIFICATION_ID, postureYesIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, PRIMARY_CHANNEL_ID)
                .setContentTitle("Are you straighten up?")
                .setSmallIcon(R.drawable.ic_notify)
                .setAutoCancel(true)
                .setContentIntent(notifClickPendingIntent)
                .addAction(R.drawable.ic_posture_yes, "yes", yesPendingIntent)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setDefaults(NotificationCompat.DEFAULT_ALL);
        mNotificationManager.notify(NOTIFICATION_ID, builder.build());
    }
}

CorrectPostureReceiver.java - 单击通知上的操作按钮时获取意图消息。

//
//imports
//

class CorrectPostureReceiver extends BroadcastReceiver {

    private int mCount = 0;
    private TextView mTxtCount;
    private final String LOG_TAG = CorrectPostureReceiver.class.getSimpleName();
    private MainActivity mainActivity;
    public CorrectPostureReceiver(MainActivity mainActivity) {
        this.mainActivity = mainActivity;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(LOG_TAG, "yes option clicked");
        mTxtCount = mainActivity.findViewById(R.id.txt_count);
        if (mTxtCount != null) {
            mCount++;
            mTxtCount.setText(Integer.toString(mCount));
        }
    }

    public int getCount() {
        return mCount;
    }
    public TextView getTxtCount() {
        return mTxtCount;
    }
}

AndroidManifest.xml 片段

<activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
</activity>

如何解决这个问题? 如果您对我的代码有任何其他建议,请写信告诉我。

【问题讨论】:

  • 请编辑您的问题并为MainActivity 添加清单内容。

标签: java android android-intent android-activity android-notifications


【解决方案1】:

当您单击Notification 时,Android 将启动MainActivity 的新实例。这是一个与MainActivity 的原始实例完全不同的对象,它仍然存在并且完全被新的实例覆盖。这就是计数显示为零的原因,也是当您按 BACK 时,计数显示正确的原因(按 BACK 后,MainActivity 的新实例被破坏,在其下方显示MainActivity 的先前原始实例)。

您应该在创建Notification 时将Intent.FLAG_ACTIVITY_SINGLE_TOP 添加到Intent

    //this intent causes that when the notification is clicked, the MainActivity is launched
    Intent notifClickIntent = new Intent(context, MainActivity.class); //when the notification is clicked, the MainActivity is launched
    notifClickIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    PendingIntent notifClickPendingIntent = PendingIntent.getActivity(
            context,
            NOTIFICATION_ID,
            notifClickIntent,
            PendingIntent.FLAG_UPDATE_CURRENT
    );

【讨论】:

    【解决方案2】:

    另一种解决方案是在 AndroidManifest.xml 中设置 android:launchMode="singleInstance"(在本例中为 MainActivity)。

    这比设置一个标志 FLAG_ACTIVITY_SINGLE_TOP(如David Wasser's answer)要好,因为当您有多个 Activity 并且另一个 Activity 位于 Activity 堆栈的顶部,然后您单击通知时,问题中的问题将仍然发生。

    【讨论】:

      猜你喜欢
      • 2014-10-20
      • 1970-01-01
      • 2015-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多