Как реализовать Broadcast Receiver при загрузке устройства? - PullRequest
0 голосов
/ 14 октября 2019

У меня есть простое приложение для планирования уведомлений через 5 минут после нажатия кнопки. Он отлично работает для меня. Но если я перезагружаю телефон в течение этих 5 минут, я не получаю уведомление. Я провел исследование Alarm Manager и планирование уведомлений при перезагрузке устройства. У меня в приложении 5 классов.

Это:

  • MainActivity.class
  • NotificationUtil.class
  • NotificationPublisher.class
  • NotificationView.class
  • DeviceBootReceiver.class

У меня есть 2 класса, расширяющих приемники BroadCast. Это:

  • NotificationPublisher.class
  • DeviceBootReceiver.class

Из моих исследований и других вопросов о переполнении стека я узнал, что для получения уведомлений послеустройство перезагружено, я должен создать класс, который расширяет BroadCast Receiver. Он будет запускать любой код в методе onReceive() при каждой загрузке телефона.

Это мой класс MainActivity.class

public class MainActivity extends AppCompatActivity {
    private Button button;
    private Context context;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = findViewById(R.id.notification);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                NotificationUtil.createNotification(MainActivity.this,NotificationView.class,"Notification","You have a new Task");
            }
        });
    }
}

Это мой класс NotificationUtil.class. Это класс, который обрабатывает все уведомления, и это класс, который я вызываю по нажатию кнопки в MainActivity.

public class NotificationUtil
{
    public static void createNotification(Context context,Class<?> cls, String title, String content)
    {
        Intent intent = new Intent(context,cls);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

        sendNotification(getNotification(pendingIntent,context,title,content),context);
    }

    private static void sendNotification(Notification notification,Context context)
    {
        Intent notificationIntent = new Intent(context, NotificationPublisher.class);
        Intent deviceBootIntent = new Intent(context,DeviceBootReceiver.class);

        notificationIntent.putExtra(NotificationPublisher.NOTIFICATION_ID,1);
        notificationIntent.putExtra(NotificationPublisher.NOTIFICATION,notification);

        deviceBootIntent.putExtra(DeviceBootReceiver.NOTIFICATION_ID, 1);
        deviceBootIntent.putExtra(DeviceBootReceiver.NOTIFICATION, notification);

        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0,notificationIntent,PendingIntent.FLAG_UPDATE_CURRENT);

        AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 60 * 1000,pendingIntent);
    }

    private static Notification getNotification(PendingIntent pendingIntent, Context context, String title, String content)
    {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context,"ChannelID");
        builder.setSmallIcon(R.drawable.notification_bell);
        builder.setContentTitle(title);
        builder.setContentText("You have a Notification");
        builder.setSubText("Tap To View");
        builder.setStyle(new NotificationCompat.BigTextStyle().bigText(content));
        builder.setContentIntent(pendingIntent);
        return builder.build();
    }
}

Это мой NotificationPublisher.class. Это класс, отвечающий за отправку Уведомления.

public class NotificationPublisher extends BroadcastReceiver {

    public static String NOTIFICATION_ID = "notification_id";
    public static String NOTIFICATION = "notification";

    @Override
    public void onReceive(final Context context, Intent intent) {

        try
        {
            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

            Notification notification = intent.getParcelableExtra(NOTIFICATION);
            int notificationId = intent.getIntExtra(NOTIFICATION_ID, 1);
            notificationManager.notify(notificationId, notification);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

Это мой DeviceBootReceiver.class. Я пытаюсь реализовать свой код здесь, чтобы получать уведомления после перезагрузки устройства.

public class DeviceBootReceiver extends BroadcastReceiver {

    public static String NOTIFICATION_ID = "notification_id";
    public static String NOTIFICATION = "notification";

    public void onReceive(Context context, Intent intent)
    {
        if(Objects.requireNonNull(intent.getAction()).equalsIgnoreCase("android.intent.action.BOOT_COMPLETED"))
        {
            Notification notification = intent.getParcelableExtra(NOTIFICATION);
            int notificationID = intent.getIntExtra(NOTIFICATION_ID,1);

            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            notificationManager.notify(notificationID, notification);
        }
    }
}

После перезагрузки телефона я получаю сообщение «Приложение перестало работать». Я не уверен, где я ошибаюсь.

Это мой файл манифеста:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.notificationtest">

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <activity android:name=".NotificationView"
            android:parentActivityName=".MainActivity"/>

        <receiver android:name=".NotificationPublisher"/>


        <receiver android:name=".DeviceBootReceiver"
            android:enabled="true"
            android:exported="true"
            android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.ACTION_BOOT_COMPLETED" />
                <action android:name="android.intent.action.REBOOT" />
                <action android:name="android.intent.action.QUICKBOOT_POWERON" />
                <action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
                <action android:name="android.intent.action.ACTION_SHUTDOWN" />
            </intent-filter>
        </receiver>


    </application>

</manifest>

Из того, что я понял, класс, который расширяет BroadCast Receivers, вызывается при перезагрузке устройства. Поэтому я попытался передать мои notification и notificationID из моего NotificationUtil.class в мой DeviceBootReceiver.class следующим образом, используя Intent.putExtra

Intent deviceBootIntent = new Intent(context,DeviceBootReceiver.class);
deviceBootIntent.putExtra(DeviceBootReceiver.NOTIFICATION_ID, 1);
deviceBootIntent.putExtra(DeviceBootReceiver.NOTIFICATION, notification);

Затем в моем DeviceBootReceiver.class по методу onReceive, который яполучить значения, используя getExtra. Далее я передаю 2 значения, которые я получил, в свой NotificationPublisher.class.

Это «БЫЛО» моя более ранняя реализация моего DeviceBootReceiver.class. Так как это не сработало, я попробовал новую реализацию, как указано выше.

public class DeviceBootReceiver extends BroadcastReceiver {

    public static String NOTIFICATION_ID = "notification_id";
    public static String NOTIFICATION = "notification";

    public void onReceive(Context context, Intent intent)
    {
        if(Objects.requireNonNull(intent.getAction()).equals("android.intent.action.BOOT_COMPLETED"))
        {
            Intent pushIntent = new Intent(context, NotificationPublisher.class);

            Notification notification = intent.getParcelableExtra(NOTIFICATION);
            int notificationID = intent.getIntExtra(NOTIFICATION_ID,1);

            pushIntent.putExtra(NotificationPublisher.NOTIFICATION_ID,notificationID);
            pushIntent.putExtra(NotificationPublisher.NOTIFICATION,notification);

            PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0,pushIntent,PendingIntent.FLAG_UPDATE_CURRENT);

            AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
            alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 60 * 1000,pendingIntent);
        }

    }
}

То, что я ожидал, было то, что, поскольку DeviceBootReceiver.class вызывается при запуске, 2 значения будут отправлены на мой NotificationPublisher.class, и я получу уведомление при запуске. Вместо этого приложение падает с моей старой и новой реализацией в методе onReceive.

Может ли кто-нибудь, кто имеет опыт получения уведомлений о перезагрузке устройства, помочь мне с этим? Пожалуйста, пройдите мой код и попробуйте внедрить его в свой проект, чтобы понять, что я сделал. Я загрузил все коды здесь в этом вопросе.

Это мои журналы сбоев:

10-14 17:26:03.088 5438-5438/? I/SELinux: Function: selinux_android_load_priority [0], There is no sepolicy file.

10-14 17:26:03.088 5438-5438/? I/SELinux: Function: selinux_android_load_priority [1], There is no sepolicy version file.

10-14 17:26:03.088 5438-5438/? I/SELinux: Function: selinux_android_load_priority , priority is 3. priority version is VE=SEPF_SM-G357FZ_4.4.4_A042


10-14 17:26:03.088 5438-5438/? E/SELinux: [DEBUG] get_category: variable seinfocat: default sensitivity: NULL, cateogry: NULL
10-14 17:26:03.088 5438-5438/? E/SELinux: seapp_context_lookup: str_security_ctx is null
10-14 17:26:03.088 5438-5438/? E/dalvikvm: >>>>> Normal User
10-14 17:26:03.088 5438-5438/? E/dalvikvm: >>>>> com.example.notificationtest [ userId:0 | appId:10181 ]
10-14 17:26:03.088 5438-5438/? E/SELinux: [DEBUG] get_category: variable seinfocat: default sensitivity: NULL, cateogry: NULL
10-14 17:26:03.088 5438-5438/? E/SELinux: seapp_context_lookup: str_security_ctx is null
10-14 17:26:03.088 5438-5438/? D/dalvikvm: Late-enabling CheckJNI
10-14 17:26:03.088 5438-5438/? I/libpersona: KNOX_SDCARD checking this for 10181
10-14 17:26:03.088 5438-5438/? I/libpersona: KNOX_SDCARD not a persona
10-14 17:26:03.138 5438-5438/? D/TimaKeyStoreProvider: in addTimaSignatureService
10-14 17:26:03.148 5438-5438/? D/TimaKeyStoreProvider: Cannot add TimaSignature Service, License check Failed
10-14 17:26:03.148 5438-5438/? D/ActivityThread: Added TimaKesytore provider
10-14 17:26:03.168 5438-5438/com.example.notificationtest D/DisplayManagerGlobal: getDisplayInfo: displayId=0, info=DisplayInfo{"Built-in Screen", app 480 x 800, real 480 x 800, largest app 800 x 762, smallest app 480 x 442, 60.0 fps, rotation0, density 240 (217.714 x 216.17) dpi, layerStack 0, type BUILT_IN, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}
10-14 17:26:03.168 5438-5438/com.example.notificationtest D/DisplayManagerGlobal: getDisplayInfo: displayId=0, info=DisplayInfo{"Built-in Screen", app 480 x 800, real 480 x 800, largest app 800 x 762, smallest app 480 x 442, 60.0 fps, rotation0, density 240 (217.714 x 216.17) dpi, layerStack 0, type BUILT_IN, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}
10-14 17:26:03.168 5438-5438/com.example.notificationtest D/ActivityThread: handleBindApplication:com.example.notificationtest
10-14 17:26:03.168 5438-5438/com.example.notificationtest D/DisplayManagerGlobal: getDisplayInfo: displayId=0, info=DisplayInfo{"Built-in Screen", app 480 x 800, real 480 x 800, largest app 800 x 762, smallest app 480 x 442, 60.0 fps, rotation0, density 240 (217.714 x 216.17) dpi, layerStack 0, type BUILT_IN, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}
10-14 17:26:03.168 5438-5438/com.example.notificationtest D/DisplayManagerGlobal: getDisplayInfo: displayId=0, info=DisplayInfo{"Built-in Screen", app 480 x 800, real 480 x 800, largest app 800 x 762, smallest app 480 x 442, 60.0 fps, rotation0, density 240 (217.714 x 216.17) dpi, layerStack 0, type BUILT_IN, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}
10-14 17:26:03.238 5438-5438/com.example.notificationtest D/AndroidRuntime: Shutting down VM
10-14 17:26:03.238 5438-5438/com.example.notificationtest W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x416a7d58)
10-14 17:26:03.238 5438-5438/com.example.notificationtest E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.notificationtest, PID: 5438
    java.lang.RuntimeException: Unable to start receiver com.example.notificationtest.NotificationPublisher: java.lang.NullPointerException
        at android.app.ActivityThread.handleReceiver(ActivityThread.java:2551)
        at android.app.ActivityThread.access$1700(ActivityThread.java:155)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1319)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:136)
        at android.app.ActivityThread.main(ActivityThread.java:5426)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084)
        at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.NullPointerException
        at android.app.NotificationManager.notify(NotificationManager.java:150)
        at android.app.NotificationManager.notify(NotificationManager.java:120)
        at com.example.notificationtest.NotificationPublisher.onReceive(NotificationPublisher.java:23)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:2544)
        at android.app.ActivityThread.access$1700(ActivityThread.java:155) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1319) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:136) 
        at android.app.ActivityThread.main(ActivityThread.java:5426) 
        at java.lang.reflect.Method.invokeNative(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:515) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084) 
        at dalvik.system.NativeStart.main(Native Method) 
10-14 17:26:07.708 5438-5438/com.example.notificationtest I/Process: Sending signal. PID: 5438 SIG: 9

Ответы [ 2 ]

1 голос
/ 14 октября 2019

Неявные исключения широковещания

Как часть Android 8.0 (API level 26) Пределов выполнения фона, приложений, нацеленных на Уровень API 26 или выше больше не может регистрировать широковещательные приемники для неявных широковещательных передач в их манифесте. Однако несколько передач в настоящее время освобождены от этих ограничений. Приложения могут продолжать регистрировать прослушиватели для следующих трансляций, независимо от того, какой уровень API предназначен для приложений.

Даже если эти неявные трансляции все еще работают в фоновом режиме, вам следует избегать регистрации прослушивателей для них.

Вместо этого можно использовать scheduled jobs.

FYI

   java.lang.RuntimeException: Unable to start receiver com.example.notificationtest.NotificationPublisher: java.lang.NullPointerException
        at android.app.ActivityThread.handleReceiver(ActivityThread.java:2551)
        at android.app.ActivityThread.access$1700(ActivityThread.java:155)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1319)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:136)

NullPointerException генерируется, когда приложение пытается использовать ссылку на объект с нулевым значением.

0 голосов
/ 14 октября 2019

ACTION_BOOT_COMPLETED находится в неявном белом списке рассылки , поэтому вы можете зарегистрироваться для него в манифесте даже на Android 8.0 и позже.

На этот ответ первоначально ответил @CommonsWare в этой теме

В общем, не забудьте протестировать его, как только оно будет реализовано.

...