如何创建 Activity 的意图但具有更新的内容(不是初始的)?
How to create an intent to an Activity but with updated content (not initial)?
我是 Android SDK 的新手,我开发了一个使用 AlarmManager 每 30 分钟发送一次通知的应用程序。每个通知都包含一个操作按钮。在 MainActivity 中,我有一个 TextView,其中显示了一个数字,用于计算操作按钮被单击的次数。
有一个用例显示的计数不正确。为了重现该问题,我执行了以下步骤:
- 应用程序已最小化,我正在等待通知。
- 单击操作按钮
- 点击通知进入应用程序
应用程序自行打开,但 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>
如何解决?
如果您对我的代码有任何其他建议,请写信给我。
当您单击 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
);
另一种解决方案是在 AndroidManifest.xml 中设置 android:launchMode="singleInstance"
(在本例中为 MainActivity)。
这比设置标志 FLAG_ACTIVITY_SINGLE_TOP(如 )更好,因为当您有多个 Activity 而另一个 Activity 位于顶部时Activity 堆栈,然后单击通知,问题中的问题仍然会发生。
我是 Android SDK 的新手,我开发了一个使用 AlarmManager 每 30 分钟发送一次通知的应用程序。每个通知都包含一个操作按钮。在 MainActivity 中,我有一个 TextView,其中显示了一个数字,用于计算操作按钮被单击的次数。
有一个用例显示的计数不正确。为了重现该问题,我执行了以下步骤:
- 应用程序已最小化,我正在等待通知。
- 单击操作按钮
- 点击通知进入应用程序
应用程序自行打开,但 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>
如何解决? 如果您对我的代码有任何其他建议,请写信给我。
当您单击 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
);
另一种解决方案是在 AndroidManifest.xml 中设置 android:launchMode="singleInstance"
(在本例中为 MainActivity)。
这比设置标志 FLAG_ACTIVITY_SINGLE_TOP(如