How to create an intent to an Activity but with updated content (not initial)?

huangapple go评论94阅读模式
英文:

How to create an intent to an Activity but with updated content (not initial)?

问题

I'll provide a summary of your code and the issue you're facing, along with a suggestion on how to fix it.

你的代码主要实现了一个Android应用,它使用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()));
但它没有起作用。

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

解决此问题的建议是确保在通知被点击时更新TextView。你可以在通知的点击操作中添加一个广播,然后在广播接收器中更新TextView的内容。

具体步骤如下:

  1. 在MainActivity中,创建一个方法来更新TextView的内容。
  2. 在CorrectPostureReceiver广播接收器中,当操作按钮被点击时,发送一个广播。
  3. 在MainActivity中注册这个广播,并在广播接收器中调用第一步中创建的方法以更新TextView。

这样,当你点击通知上的操作按钮时,TextView会得到正确的更新。

希望这可以帮助你解决问题。如果你对代码有其他建议,也可以分享。

英文:

I'm new in Android SDK and develop an app which sends notifications every 30 minutes using AlarmManager. Each notification includes an action button. In the MainActivity, I have a TextView where I display a number counting how many times the action button was clicked.

There is a use case where displayed count number is not correct. To reproduce the issue I perform following steps:

  1. The app is minimized and I wait for notification.
  2. Click the action button
  3. go to the app tapping on the notification

The app opens itself however, the TextView displays still "0" (like in the initial state). However when I click BACK button on the device, the count number turns into a proper one.

When I click the notification, I get a warning which may be a reason of the problem:
W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@c8e60ce

I was trying to fix that with this line in onCreate() method in MainActivity:
mCount.setText(String.valueOf(mCorrectPostureReceiver.getCount()));
But it doesn't work.

I should also mention that when I click the action button and then go to the app using launcher (not by tapping on the notification), everything works fine.

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 - receives repeating intent messages from AlarmManager and delivers notifications

//
//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 - gets an intent message when the action button on notification was clicked.

//
//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 fragment

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

How to fix that?
If you have any other suggestions to my code, please write me about it.

答案1

得分: 1

点击“通知”时,Android会启动一个新的MainActivity实例。这是一个完全不同的对象,与原始的MainActivity实例不同,原始实例仍然存在,并被新实例完全覆盖。这就是为什么计数显示为零的原因,也是为什么当您按下返回按钮时,计数会正确显示的原因(在按下返回按钮后,新的MainActivity实例被销毁,显示出位于其下方的先前原始MainActivity实例)。

在创建“通知”时,您应该在Intent中添加Intent.FLAG_ACTIVITY_SINGLE_TOP

//此意图导致点击通知时启动MainActivity
Intent notifClickIntent = new Intent(context, MainActivity.class);
notifClickIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent notifClickPendingIntent = PendingIntent.getActivity(
        context,
        NOTIFICATION_ID,
        notifClickIntent,
        PendingIntent.FLAG_UPDATE_CURRENT
);
英文:

When you click on the Notification, Android is launching a new instance of MainActivity. This is a completely different object than the original instance of MainActivity, which still exists and is being completely covered by the new one. That's the reason that the count shows zero, and also the reason why, when you press BACK, the count appears correct (after pressing BACK, the new instance of MainActivity is destroyed, revealing the previous original instance of MainActivity underneath it).

You should add Intent.FLAG_ACTIVITY_SINGLE_TOP to the Intent when you create the Notification:

    //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

得分: 0

另一种解决方案是在AndroidManifest.xml中设置android:launchMode="singleInstance"(在这种情况下是为了MainActivity)。

这比设置FLAG_ACTIVITY_SINGLE_TOP标志要好(如David Wasser的答案中所述),因为当你有多个Activity并且其他Activity在Activity堆栈的顶部时,然后你点击通知时,问题仍然会发生。

英文:

Another solution is setting android:launchMode="singleInstance" in AndroidManifest.xml (in this case for MainActivity).

This is better than setting a flag FLAG_ACTIVITY_SINGLE_TOP (as in David Wasser's answer) because when you have more than one Activity and the other Activity is on the top of Activity stack and then you click on the notification, the issue from question will still occur.

huangapple
  • 本文由 发表于 2020年8月10日 15:29:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/63335858.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定