Android-приложение показывает уведомление в фоновом режиме, но дает NullpointerException, если приложение закрыто - PullRequest
0 голосов
/ 30 декабря 2018

Я хочу создать приложение для Android с push-уведомлениями, которые также показывают, закрыто ли приложение.Мне удалось сделать это с 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 () и этим, но он также дает другие ошибки.Есть ли способ узнать об этой проблеме?Есть ли у вас какие-либо другие предложения о том, что вызывает проблему?(Я также пытался сделать это с AlarmManager и BroadcastReciever, но ошибка та же)

Спасибо!

Ответы [ 2 ]

0 голосов
/ 30 декабря 2018

Мне удалось решить эту проблему:

Я изменил код, в котором создается уведомление, на:

 NotificationManager notificationManager2 = (NotificationManager) MyApplication.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager2.notify(Integer.parseInt(groupid), notification1Summary);
    notificationManager2.notify(commentid, notification1);

Похоже, что приложение требует повторного определения NotificationManager, еслиПриложение полностью закрыто.

Спасибо the_dani за столь быстрый ответ, я все еще попытаюсь использовать ваше решение и посмотреть, работает ли оно тоже;)

5 часов исследований для ОДНОГОстрока кода: D

0 голосов
/ 30 декабря 2018

Я делаю то же самое, используя новый API WorkManager, упакованный в компоненты архитектуры Android Jetpack:

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
    }
}
...