Android 设备重启后广播接收器不工作 [英] Broadcast Receiver Not Working After Device Reboot in Android

查看:51
本文介绍了Android 设备重启后广播接收器不工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经检查了所有相关问题,但没有找到针对此问题的任何解决方案.所以这对我来说是一个全新的问题.

I have already checked all the related questions and have not found any solution for this problem. So this is an absolutely new problem for me.

我有什么

我有一个 Android 应用程序,它在其清单中注册了一些广播接收器.这就是我的清单的样子.

I have an Android app which registers a few broadcast receivers in its manifest. This is what my manifest looks like.

<?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.app.myapp">

    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.USE_FINGERPRINT" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_CALL_LOG" />
    <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
    <uses-permission android:name="com.android.vending.BILLING" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />

    <uses-feature
        android:name="android.hardware.telephony"
        android:required="false" />

    <uses-feature
        android:name="android.hardware.screen.portrait"
        android:required="false" />

    <application
        android:name=".base.MyApp"
        android:allowBackup="false"
        android:icon="@drawable/ic_launcher"
        android:label="@string/label_app_name"
        android:largeHeap="true"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:replace="label, allowBackup">

        <receiver android:name=".mics.BootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.QUICKBOOT_POWERON" />
            </intent-filter>
        </receiver>

        <receiver android:name=".PhoneCallReceiver">
            <intent-filter>
                <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
            </intent-filter>
        </receiver>

        <receiver
            android:name=".mics.DeviceAdminReceiver"
            android:permission="android.permission.BIND_DEVICE_ADMIN">
            <intent-filter>
                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
            </intent-filter>

            <meta-data
                android:name="android.app.device_admin"
                android:resource="@xml/device_admin" />
        </receiver>

        <receiver
            android:name="com.clevertap.android.sdk.InstallReferrerBroadcastReceiver"
            android:exported="true">
            <intent-filter>
                <action android:name="com.android.vending.INSTALL_REFERRER" />
            </intent-filter>
        </receiver>

        <meta-data
            android:name="com.app.myapp.utils.ImageLoaderModule"
            android:value="GlideModule" />

        <meta-data
            android:name="com.app.myapp.utils.AudioCoverLoaderModule"
            android:value="GlideModule" />

        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">

            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths" />
        </provider>

        <activity
            android:name=".core.activities.SplashActivity"
            android:excludeFromRecents="true"
            android:label="@string/label_app_name"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
            </intent-filter>
        </activity>

        <activity-alias
            android:name=".core.activities.SplashActivity-Alias"
            android:icon="@drawable/ic_launcher"
            android:label="@string/label_app_name"
            android:noHistory="true"
            android:targetActivity="com.app.myapp.core.activities.SplashActivity">

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        </activity-alias>

        <activity
            android:name=".core.flow.authFlow.activities.AuthFlowActivity"
            android:excludeFromRecents="true"
            android:label="@string/label_app_name"
            android:screenOrientation="portrait" />

        <service android:name=".features.fileCloudSync.KillNotificationService" />

    </application>

</manifest>

还有 10-15 个其他活动,但为简单起见已被删除.这是基本的引导接收器类.我从这里开始服务.

There are 10-15 other activities as well but have been removed for simplicity. And this is the basic boot receiver class. I start a service from here.

public class BootReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
            AlertUtils.showToast(context, "BOOT COMPLETED", Toast.LENGTH_LONG);
        }
    }
}

电话接收器类看起来像这样(它也被简化了),

and the phone call receiver class looks something like this (it has been simplified as well),

public class PhoneCallReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
            AlertUtils.showToast(context, "PHONE CALL RECEIVED", Toast.LENGTH_LONG);
            // Simplified for brevity
        }
    }
}

问题

当我安装应用程序并启动一次时,所有这些接收器都可以正常工作.但是在我重新启动设备后,这些接收器根本无法工作.BootCompleteReceiverPhoneCallReceiver 都没有调用它们的 onReceive() 方法.

All these receivers work fine when I install the app and start it once. But after I reboot my device these receivers don't work at all. Neither the BootCompleteReceiver nor the PhoneCallReceiver gets their onReceive() method called.

我的假设是这些接收器会在重启后自动注册,但它不起作用.我需要 BootCompleteReceiver 才能工作,以便我可以在我的应用中启动一项重要的服务.

My assumption was that these receivers would get registered automatically after reboot, but it just doesn't work. I need the BootCompleteReceiver to work so that I can start an important service in my app.

我的观察

我已经对此进行了彻底的测试.重新启动设备后,接收器在我的 Nexus 5X (Nougat)、Nexus 6P (Nougat)、YU Yuphoria (Lollipop) 中工作正常,但在我的 OnePlus 3 (Nougat) 和 Mi 4i 中无法正常工作(棒棒糖).

I have tested this thoroughly. After rebooting the device, the receivers work fine in my Nexus 5X (Nougat), Nexus 6P (Nougat), YU Yuphoria (Lollipop) but not in my OnePlus 3 (Nougat) and Mi 4i (Lollipop).

相同的代码如何在少数设备上完美运行而在其他设备上根本无法运行?我根本没有改变任何东西.

How can the same code work perfectly on a few devices and not work at all on the other devices? I haven't changed anything at all.

我在这里做错了什么?我的应用程序严重依赖这些广播并基于这些启动服务.任何帮助将不胜感激.

What am I doing wrong here? My app is heavily dependent on these broadcasts and starts services based on these. Any help will be highly appreciated.

编辑 1

为了更好地理解问题,我刚刚创建了一个非常小的测试项目,其中只有一个活动和完全相同的 BootCompleteReceiverPhoneCallReceiver.

To understand the problem better, I just created a very small test project with just a single activity and the exact same BootCompleteReceiver and PhoneCallReceiver.

但奇怪的是,这个项目在我的 OnePlus 3 上完美运行,我的实际应用程序的接收器在重新启动后不起作用.我最初假设问题出在操作系统或设备上,但事实并非如此.

But weirdly, this project works perfectly on my OnePlus 3 where my actual app's receivers don't work after a reboot. I was initially assuming that the problem is in the OS or the device somehow, but it is not.

那么真正的问题在哪里?它是在我的应用程序中(但它在其他设备上完美运行)还是在操作系统和设备中(小型测试项目在同一操作系统和同一设备上运行良好)?

So where is the actual problem? Is it in my app (but it works perfectly on other devices) or in the OS and device (the small test project works fine on the same OS and same device)?

这让我很困惑.我需要一些专家的帮助.

It is really confusing to me. I would need some expert help on this.

编辑 2

我已经尝试了@shadygoneinsane 给出的建议.这是我的观察.

I have tried the suggestion given by @shadygoneinsane. Here are my observations.

1) 我尝试通过 ADB 发送 BOOT_COMPLETED 广播.

1) I tried to send the BOOT_COMPLETED broadcast via ADB.

./adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -p com.app.myapp

我得到了这个堆栈跟踪,

And I got this stack trace,

Broadcasting: Intent { act=android.intent.action.BOOT_COMPLETED pkg=com.app.myapp }
java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.BOOT_COMPLETED from pid=25378, uid=2000
    at android.os.Parcel.readException(Parcel.java:1683)
    at android.os.Parcel.readException(Parcel.java:1636)
    at android.app.ActivityManagerProxy.broadcastIntent(ActivityManagerNative.java:3696)
    at com.android.commands.am.Am.sendBroadcast(Am.java:778)
    at com.android.commands.am.Am.onRun(Am.java:404)
    at com.android.internal.os.BaseCommand.run(BaseCommand.java:51)
    at com.android.commands.am.Am.main(Am.java:121)
    at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
    at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:276)

可能是因为我的设备没有root.我无法以任何方式发送此广播.

Maybe because my device is not rooted. I am unable to send this broadcast in any way.

2) 之后我尝试使用 PROCESS_OUTGOING_CALLS 广播.

2) I tried with the PROCESS_OUTGOING_CALLS broadcast after that.

./adb shell am broadcast -a android.intent.action.PROCESS_OUTGOING_CALLS -p com.app.myapp

我明白了,

Broadcasting: Intent { act=android.intent.action.PROCESS_OUTGOING_CALLS pkg=com.app.myapp }
Broadcast completed: result=0

似乎广播成功了,但我没有看到任何 Toast 或任何日志.然后我打开拨号器拨打一个号码,然后我可以看到 Toast 和日志.

It seems that the broadcast was successful, but I do not see any Toast or any log. I then opened my dialer to dial a number and I can then see the Toast and the log both.

所以似乎通过 ADB 发送广播不起作用,但实际上打开拨号器并拨打一个号码确实有效.

So it seems that sending the broadcast via ADB didn't work, but actually opening the dialer and dialing a number did.

编辑 3

根据@ChaitanyaAtkuri 的建议,我也尝试为意图过滤器添加优先级,但效果不佳.

As per the suggestion from @ChaitanyaAtkuri, I have also tried adding priority to the intent-filters but that didn't work as well.

我使用了 500、999 之类的优先级,甚至是最高的整数值,但没有任何效果.这个问题也出现在我的一些朋友的应用程序中.它们适用于某些设备,不适用于其他设备.

I have used priorities like 500, 999 and even the highest integer value, but nothing works. This problem is also occurring in some of my friends apps as well. They work in some devices and doesn't work in others.

编辑 4

终于找到了我的 OnePlus 3 出现问题的根本原因.我的 OnePlus 3 最近更新为牛轧糖,他们引入了类似于 Mi 设备的功能,可防止某些应用在重启后自动启动.

I have finally found out the root cause of the problem happening in my OnePlus 3. My OnePlus 3 recently got updated to Nougat and they introduced a feature similar to Mi devices which prevent certain apps from auto-starting after reboot.

禁用此功能后,我的应用程序在完美重启后开始接收广播.但这仍然不能解释两件事.

Upon disabling this feature my app started receiving broadcasts after reboot perfectly. But this still doesn't explain two things.

1) 我的小测试项目在 AutoLaunch 应用程序列表中自动列入白名单,这就是它按预期工作的原因.但这怎么可能呢?为什么操作系统认为这个小应用值得自动启动?

1) My small test project is whitelisted automatically in the list of AutoLaunch apps and that is why it works as expected. But how is this possible? Why the OS considers this small app worthy to be auto-started?

2) 有一些应用程序,如 LockDown Pro、500 Firepaper,它们在 AutoLaunch 应用程序屏幕中被列入黑名单,但它仍然在我的 OnePlus 3 和 Mi 4i 重新启动后接收广播.现在怎么可能?是否有可能以编程方式允许我的应用在这些设备(OnePlus 和 Mi)中自动启动?

2) There are some apps like LockDown Pro, 500 Firepaper which is blacklisted in the AutoLaunch apps screen but still, it receives broadcasts after reboot in my OnePlus 3 and Mi 4i. How is that possible now? Is it somehow possible to programmatically allow my app to auto launch in these devices (OnePlus and Mi)?

编辑 5

我已经尝试了@Rahul Chowdhury 提出的解决方案,它似乎真的很好用.添加无障碍服务后,问题重新解决.

I have tried the solution proposed by @Rahul Chowdhury and it really seems to work very well. After adding the accessibility service the problem is re-solved.

但是如果用户在授予无障碍权限后撤销了它,那么我有没有办法以编程方式检查无障碍权限是否可用于我的应用?

But if the user revokes the accessibility permission after granting it then is there a way for me to programmatically check if the accessibility permission is available to my app?

推荐答案

这是在您提到的 OnePlus 和 Mi 设备上经过测试且有效的解决方案.

Here's a tested and working solution on both the devices that you mentioned, OnePlus and Mi.

正如您所说,OnePlusMi 设备上的自动启动预防功能可防止应用在启动完成时自动启动其服务,以改善整体设备启动速度和电池性能.但是,即使启用此功能,也有一种解决方法可以让您的应用正常运行.

As you said the auto-start prevention feature on OnePlus and Mi devices prevent apps from starting up their services automatically on boot complete so as to improve the overall device boot speed and battery performance. However, there's a workaround to get your app working even when this feature is turned on.

我注意到,如果您的应用程序中有一个 AccessibilityService 并且它是由用户打开的,那么您的应用程序将通过这些制造商应用的过滤器,并且该应用程序会收到它的启动完成事件和任何其他 BroadcastReceiver 都按预期工作.

I have noticed that if you have an AccessibilityService in your app and it is turned on by the user, then your app passes the filter that these manufacturers apply and the app receives it's boot complete event and any other BroadcastReceiver works as expected.

这个技巧的可能解释是,由于AccessibilityService 是系统级服务,所以通过注册您自己的服务,您通过了这些制造商应用的某些过滤器一旦您的自定义 AccessibilityService 被操作系统触发,您的应用就会激活接收您注册的符合条件的 BroadcastReceiver.

The possible explanation of this trick can be that since AccessibilityService is a system level service, so by registering your own service you are passing the certain filter applied by these manufacturers and as soon as your custom AccessibilityService gets triggered by the OS, your app becomes active in receiving the eligible BroadcastReceiver that you had registered.

所以,这是怎么做的,

首先将此权限添加到您的 AndroidManifest.xml

Start by adding this permission to your AndroidManifest.xml,

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

这将允许您向系统注册您应用的AccessibilityService.

This will allow you to register your app's AccessibilityService with the system.

现在,通过在项目的 res 文件夹下的 XML 文件夹中创建一个文件,例如 my_accessibility_service.xml,为您的 AccessibilityService 添加一个非常基本的配置.

Now, add a very basic configuration for your AccessibilityService by creating a file for example my_accessibility_service.xml inside XML folder under your res folder in your project.

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityFeedbackType="feedbackSpoken"
    android:description="@string/service_desc"
    android:notificationTimeout="100"/>

还有一步要做,在您的项目中定义您的自定义AccessibilityService

There's just one more step left to do, define your custom AccessibilityService in your project,

public class MyAccessibilityService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) { }

    @Override
    public void onInterrupt() {

    }
}

请注意,由于您不需要 AccessibilityService 用于任何目的而不是此解决方法,因此您可以将覆盖的方法留空.

Note, since you're not needing the AccessibilityService for any purpose rather than this workaround, you can leave the overridden methods empty.

最后,只需在您的 AndroidManifest.xml 中声明您的 AccessibilityService

Finally, just declare your AccessibilityService in your AndroidManifest.xml,

<service
    android:name=".MyAccessibilityService"
    android:label="@string/app_name"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService"/>
    </intent-filter>

    <meta-data
        android:name="android.accessibilityservice"
        android:resource="@xml/my_accessibility_service"/>
</service>

仅此而已.现在在您的应用程序中,只需让您的用户从设置中为您的应用程序打开无障碍服务,然后将其保持开启,瞧!您的应用在所有设备上都能正常运行,即使操作系统对哪些应用应在启动时自动启动设置了过滤器.

That's all. Now within your app, just ask your users to turn on the accessibility service for your app from the settings and leave it on and voila! Your app works fine on all devices even where the OS puts a filter on which apps should auto-start on boot.

编辑 1

您可以通过以下方法检查是否为您的应用开启了无障碍服务,

Here's how you can check if accessibility service is turned ON or not for your app,

private static final int ACCESSIBILITY_ENABLED = 1;

public static boolean isAccessibilitySettingsOn(Context context) {
    int accessibilityEnabled = 0;
    final String service = context.getPackageName() + "/" + MyAccessibilityService.class.getCanonicalName();
    try {
        accessibilityEnabled = Settings.Secure.getInt(
                context.getApplicationContext().getContentResolver(),
                android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
    } catch (Settings.SettingNotFoundException e) {
        Log.e("AU", "Error finding setting, default accessibility to not found: "
                + e.getMessage());
    }
    TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');

    if (accessibilityEnabled == ACCESSIBILITY_ENABLED) {
        String settingValue = Settings.Secure.getString(
                context.getApplicationContext().getContentResolver(),
                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
        if (settingValue != null) {
            mStringColonSplitter.setString(settingValue);
            while (mStringColonSplitter.hasNext()) {
                String accessibilityService = mStringColonSplitter.next();

                if (accessibilityService.equalsIgnoreCase(service)) {
                    return true;
                }
            }
        }
    }

    return false;
}

希望这会有所帮助.

这篇关于Android 设备重启后广播接收器不工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆