Android应用程序在后台显示通知,但如果应用程序关闭则给出N​​ullpointerException

时间:2018-12-29 21:37:18

标签: java android nullpointerexception android-notifications android-context

如果应用程序已关闭,我想创建一个带有推送通知的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来做到这一点,但错误是相同的)

谢谢!

2 个答案:

答案 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