在Android中重新启动设备后,广播接收器无法正常工作 [英] Broadcast Receiver Not Working After Device Reboot in Android

查看:68
本文介绍了在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(牛轧糖),Nexus 6P(牛轧糖),Yu Yuphoria(棒棒糖)中正常工作,但不能在我的 OnePlus 3(牛轧糖)和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)

可能是因为我的设备未植根.我无法以任何方式发送此广播.

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.

因此似乎无法通过亚行发送广播,但实际上打开拨号器并拨打号码确实可行.

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

编辑3

根据@ChaitanyaAtkuri的建议,我也尝试过为intent-filters添加优先级,但是效果不佳.

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最近更新为Nougat,他们引入了类似于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.

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

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天全站免登陆