【问题标题】:How to implement Broadcast Receiver on Device Boot?如何在设备启动时实现广播接收器?
【发布时间】:2019-10-14 11:37:20
【问题描述】:

我有一个简单的应用程序可以在按下按钮 5 分钟后安排通知。这对我来说可以。但是,如果我在那 5 分钟内重新启动手机,我不会收到通知。我对设备重启时的警报管理器和调度通知进行了研究。我的应用中有 5 个类。

他们是:

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

我有 2 个扩展广播接收器的类。它们是:

  • NotificationPublisher.class
  • DeviceBootReceiver.class

从我的研究和其他堆栈溢出问题中,我了解到为了在设备重启后接收通知,我应该创建一个扩展广播接收器的类。每当手机启动时,它将运行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 中的 Button Click 上调用的类。

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>

据我了解,扩展广播接收器的类在设备重启时被调用。所以我尝试使用Intent.putExtra 将我的notificationnotificationID 从我的NotificationUtil.class 传递给我的DeviceBootReceiver.class,就像这样使用Intent.putExtra

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

然后在 onReceive 方法下的 DeviceBootReceiver.class 中,我使用 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 并且我将在启动时收到我的通知。相反,应用程序在onReceivemethod 中与我的旧实现和新实现一起崩溃。

有接收设备重启通知经验的人可以帮我解决这个问题吗?请仔细阅读我的代码并尝试在您的项目中实现它以了解我所做的事情。我已经上传了这个问题的所有代码。

这些是我的崩溃日志:

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

【问题讨论】:

  • 你能把你的崩溃日志放在这里吗?
  • 尝试以这种方式实现广播接收器github.com/devggaurav/…
  • 等一下
  • @No Body 我上传了我的崩溃日志

标签: android notifications broadcastreceiver alarmmanager


【解决方案1】:

Implicit Broadcast Exceptions

作为 Android 8.0 (API level 26) 后台执行限制的一部分,以 API 级别 26 或更高级别为目标的应用无法再注册广播接收器对于清单中的隐式广播。然而,一些广播目前不受这些限制。应用程序可以继续为以下广播注册侦听器,无论应用程序的目标 API 级别是什么。

即使这些隐式广播仍在后台运行, 你应该避免为他们注册监听器。

您可以改用scheduled jobs

仅供参考

   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 在应用程序尝试使用具有空值的对象引用时抛出。

【讨论】:

  • 重启设备时会抛出空指针。当我不重新启动它时它工作正常。我不确定我哪里出错了
  • @SaifThaha add try catch please
  • 我使用了 Try Catch。这次我没有收到错误“应用程序已停止工作”,但重启后我没有收到通知。当我检查 LogCat 时,它在 NotificationPublisher 第 25 行显示空指针异常
  • 分享行号25
  • 这是第 25 行中的代码 notificationManager.notify(notificationId, notification); 您可以查看我的 NotificationPublisher.class 了解更多详细信息。我已经上传了上面所有的课程
【解决方案2】:

ACTION_BOOT_COMPLETED 在隐式广播白名单中,因此即使在 Android 8.0 及之后,您也可以在清单中注册它。

这个答案最初是由@CommonsWare at this thread回答的

总而言之,实施后别忘了测试一下。

【讨论】:

  • 目前我正在使用 KitKat。那么我应该做些什么改变吗?
  • 别担心,Kit-Kat,你可以在Manifest注册Broadcast。但在我看来,Context Register Broadcast 为您提供了跨 API 级别的更好控制和安全性。
  • 我确实在 Manifest 中注册了广播。我顺便上传了我的清单文件
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-22
  • 1970-01-01
  • 1970-01-01
  • 2023-03-23
相关资源
最近更新 更多