如果应用程序已关闭,我想创建一个带有推送通知的Android应用程序。我设法用JobSceduler做到了这一点,如果应用程序仅在后台运行,它可以完美地工作。但是,如果我关闭该应用程序(将其擦除),它将无法正常工作。错误日志仍会定期触发该事件,但该应用无法创建通知:
错误:
java.lang.RuntimeException: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.app.NotificationManagerCompat.notify(int, android.app.Notification)' on a null object reference
at android.app.job.JobService$JobHandler.handleMessage(JobService.java:147)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6623)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.app.NotificationManagerCompat.notify(int, android.app.Notification)' on a null object reference
at xxx.MainActivity.buildNotification(MainActivity.java:484)
发生错误的MainActivity.java代码:
Intent notificationIntent = new Intent(MyApplication.getContext(), MainActivity.class);
notificationIntent.putExtra("sendSomeData", sendSomeData); // not necessary at the moment
PendingIntent contentIntent = PendingIntent.getActivity(MyApplication.getContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification1Summary = new NotificationCompat.Builder(MyApplication.getContext(), CHANNEL_1_ID)
.setSmallIcon(R.drawable.noti_icon)
.setContentTitle("Truth or Date")
.setContentText("You have new messages")
.setPriority(NotificationCompat.PRIORITY_MAX)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setSound(Settings.System.DEFAULT_NOTIFICATION_URI)
.setVibrate(new long[]{0, 400, 200, 400}) // sleep, vibrate, in milliseconds
.setAutoCancel(true) // delete after user clicks on it
//.setOngoing(true) // keep it alive even if clicked on notification
.setGroup(groupid) // new group key.. all comments will be grouped in one shout
.setGroupSummary(true)
//.setContentIntent(contentIntent) // when user clicks on notification
.build();
notification1 = new NotificationCompat.Builder(MyApplication.getContext(), CHANNEL_1_ID)
.setSmallIcon(R.drawable.noti_icon)
.setContentTitle(title)
.setContentText(text)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(text))
.setPriority(NotificationCompat.PRIORITY_MAX)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setSound(Settings.System.DEFAULT_NOTIFICATION_URI)
.setVibrate(new long[]{0, 400, 200, 400}) // sleep, vibrate, in milliseconds
.setAutoCancel(true) // delete after user clicks on it
//.setOngoing(true) // keep it alive even if clicked on notification
.setGroup(groupid) // new group key.. all comments will be grouped in one shout
// .setGroupSummary(true)
.setContentIntent(contentIntent) // when user clicks on notification
.build();
notificationManager.notify(Integer.parseInt(groupid), notification1Summary);
notificationManager.notify(commentid, notification1);
来自MyApplication.java的代码(从中获得上下文):
private static MyApplication instance;
private static Context mContext;
public static MyApplication getInstance() {
return instance;
}
public static Context getContext() {
return mContext;
}
我的理论
我的理论是问题在于通知的上下文。我认为如果应用程序完全关闭,则该应用程序无法从MyApplication.getContext()获取上下文。
...new NotificationCompat.Builder(MyApplication.getContext()...
我试图用getApplicationContext()和this替换它,但是它也给出了其他错误。您是否有解决此问题的方法?您对导致问题的原因还有其他建议吗? (我也尝试使用AlarmManager和BroadcastReciever来做到这一点,但错误是相同的)
谢谢!
答案 0 :(得分:0)
我正在使用Android Jetpack的体系结构组件中包含的新WorkManager API做类似的事情:
https://developer.android.com/topic/libraries/architecture/workmanager/basics
这是我的实现方式
class ReminderWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): Result {
val id = inputData.getLong(DATA_EXTRA_ID, -1)
showNotification(applicationContext, id)
return Result.success()
}
private fun showNotification(context: Context, id: Long) {
val item: ContentItem? = ContentItem(id)
item?.run {
val notificationManager: NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Utils.isOreo()) {
val notificationChannel = NotificationChannel(NOTIFICATIONCHANNEL_ID, context.getString(R.string.notificationchannel_reminder_title), NotificationManager.IMPORTANCE_DEFAULT)
notificationChannel.description = context.getString(R.string.notificationchannel_reminder_description)
notificationManager.createNotificationChannel(notificationChannel)
}
val notificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, NOTIFICATIONCHANNEL_ID)
val contentIntent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
notificationBuilder.setSmallIcon(R.drawable.ic_notification)
notificationBuilder.setContentTitle(context.getString(R.string.notification_reminder_title))
notificationBuilder.setContentText(context.getString(R.string.notification_reminder_content))
notificationBuilder.setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT))
notificationBuilder.setAutoCancel(true)
notificationManager.notify(id.toInt(), notificationBuilder.build())
}
}
companion object {
const val DATA_EXTRA_ID = "data_extra_id"
const val NOTIFICATIONCHANNEL_ID = "notification_channel_reminder"
private const val REMINDERWORKER_NAME = "reminderworker_%s"
const val DELAY_FIVE_DAYS: Long = 5 * 3600 * 24 * 1000
//region Manage Reminders
fun createReminder(context: Context, id: Long, fireDate: Date? = null) {
create(context, id, fireDate ?: computeFireDate())
}
fun updateReminder(context: Context, id: Long) {
cancel(context, id, true)
create(context, id, computeFireDate(), true)
}
fun cancelReminder(context: Context, id: Long) {
cancel(context, id)
}
fun cancelNotification(context: Context, id: Long) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancel(id.toInt())
}
private fun create(context: Context, id: Long, fireDate: Date, update: Boolean = false) {
val reminderWorker = OneTimeWorkRequestBuilder<ReminderWorker>()
.setInitialDelay(fireDate.time - System.currentTimeMillis(), TimeUnit.MILLISECONDS)
.setInputData(Data.Builder().putLong(DATA_EXTRA_ID, id).build())
.build()
WorkManager.getInstance().beginUniqueWork(uniqueWorkName(id), ExistingWorkPolicy.REPLACE, reminderWorker).enqueue()
}
private fun cancel(context: Context, id: Long, update: Boolean = false) {
WorkManager.getInstance().cancelUniqueWork(uniqueWorkName(id))
}
fun computeFireDate(delay: Long? = null): Date {
val calendar = Calendar.getInstance()
calendar.timeInMillis += delay ?: DELAY_FIVE_DAYS
calendar.apply {
set(Calendar.HOUR_OF_DAY, 12)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
return calendar.time
}
private fun uniqueWorkName(bookEntryId: Long) = REMINDERWORKER_NAME.format(bookEntryId)
//endregion
}
}
答案 1 :(得分:0)
我设法解决了这个问题:
我将创建通知的代码更改为:
NotificationManager notificationManager2 = (NotificationManager) MyApplication.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager2.notify(Integer.parseInt(groupid), notification1Summary);
notificationManager2.notify(commentid, notification1);
如果应用程序完全关闭,则似乎需要重新定义NotificationManager。
感谢the_dani回答得这么快,我仍然会尝试使用您的解决方案,看看它是否也有效;)
5个小时的研究时间:一行代码:D