广播接收器不工作,在Android设备重新启动后广播接收器不工作,在Android设备重新启动后(B

2019-05-13 09:00发布

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.

What I Have

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>

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

The Problem

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.

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.

My Observations

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.

EDIT 1

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.

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.

EDIT 2

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

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) 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

I got this,

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

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.

EDIT 3

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

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.

EDIT 4

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) 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) 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)?

EDIT 5

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?

Answer 1:

这里有两个你提到的设备,万普拉斯和糜一个测试和工作液

至于你说的万普拉斯装置自动启动预防功能,防止应用程序从系统启动时自动启动他们的服务完全以提高整体设备的开机速度和电池性能。 然而,有一个变通方法,让您的应用程序,即使该功能开启工作。

我注意到,如果你有一个AccessibilityService在您的应用程序,它是由用户开启,那么您的应用通过这些制造商应用过滤器和应用程序接收它的启动完成的事件和任何其他BroadcastReceiver正常工作。

这一招的可能的解释可以是,因为AccessibilityService是一个系统级的服务,因此通过注册自己的服务你路过这些制造商应用 ,只要你自定义的某些过滤器 AccessibilityService得到由操作系统引发的,你的应用程序变得活跃在收到符合条件的BroadcastReceiver ,你已注册。

所以,这里是如何做到这一点,

加入这个权限在你开始AndroidManifest.xml

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

这将允许您注册应用程序的AccessibilityService与系统。

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

<?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在你的项目中,

public class MyAccessibilityService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) { }

    @Override
    public void onInterrupt() {

    }
}

请注意,因为你不需要的AccessibilityService为任何目的而不是此解决方案,你可以离开覆盖的方法是空的。

最后,只是声明你的AccessibilityServiceAndroidManifest.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>

就这样。 现在你的应用程序中,只问你的用户从设置中开启无障碍服务,为您的应用程序,并把它留在瞧! 您的应用程序适用于即使在OS把一个过滤器上的应用程序应该在开机自动启动的所有设备的罚款。

编辑1

这里是你如何检查是否无障碍服务接通或不适合你的应用程序,

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

希望这可以帮助。



Answer 2:

嗨,我迟到了,但我是从以下它的开始这个问题。 我知道那里One-plus和其他一些OEM厂商保持应用程序的列表,它可以接收广播BOOT_COMPLETED。 如果您的应用并非白名单,然后你的应用程序将不会在开机启动。 现在我的解决方案是在内存和资源方面非常有效,并保证重新启动或硬启动也不必后,开始你的任务或服务AccessibilityService在此提出的答案 。 这不用..

  1. 添加<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>允许在manifest文件中。

2.如果你没有的依赖关系做com.google.android.gms:play-services-gcm ,以下内容添加到您的build.gradle的依赖部分:

compile 'com.firebase:firebase-jobdispatcher:0.5.2'

否则,添加以下内容:

compile 'com.firebase:firebase-jobdispatcher-with-gcm-dep:0.5.2'

这是一个图书馆 ,从firebase团队依赖于google-play-service库来安排您的工作,从我的观点点google-play-service必须在引导时启动的权限,以便代替系统google-play-service将运行工作尽快重启设备。

  1. 现在这一步是很容易只要定义一个类JobService

     public class MyJobService extends JobService { @Override public boolean onStartJob(JobParameters job) { Log.v("Running", "====>>>>MyJobService"); return false; // Answers the question: "Is there still work going on?" } @Override public boolean onStopJob(JobParameters job) { Log.v("Stopping", "====>>>>MyJobService"); return true; // Answers the question: "Should this job be retried?" } 

}

  1. 添加您的工作服务清单文件。

     <service android:exported="false" android:name=".MyJobService"> <intent-filter> <action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/> </intent-filter> </service> 
  2. 执行此作业,任何你想要的,例如当你的应用程序启动。

      FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(getApplicationContext())); Bundle myExtrasBundle = new Bundle(); myExtrasBundle.putString("some_key", "some_value"); Job myJob = dispatcher.newJobBuilder() // the JobService that will be called .setService(MyJobService.class) // uniquely identifies the job .setTag("my-unique-tag-test") // repeat the job .setRecurring(true) // persist past a device reboot .setLifetime(Lifetime.FOREVER) // start between 0 and 60 seconds from now .setTrigger(Trigger.executionWindow(0, 60)) // don't overwrite an existing job with the same tag .setReplaceCurrent(false) // retry with exponential backoff .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) // constraints that need to be satisfied for the job to run .setExtras(myExtrasBundle) .build(); dispatcher.mustSchedule(myJob); 

6那位就是它! 现在,您可以在设备启动执行你的任务或服务,无论您是在白名单与否。

有一点要注意的是谷歌Play业务必须在设备安装,否则将无法正常工作。



Answer 3:

@Aritra试试这个

<receiver
            android:name=".mics.BootReceiver"
            android:enabled="true"
            android:exported="true" >
            <intent-filter android:priority="500" >
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

删除QuickBoot工具的意图过滤器,并尝试运行它,按照文件,我们只需要BootCompleted到acheive它。 可它遮住这一点。

另外一个更重要的一点要注意:

因为他们有自己的操作系统,其暂停了Android的基本功能,如他们停止推送通知服务和后台服务只是为了优化电池的使用不完全或测试依靠弥设备。 为了检验这一弥设备上,标记你的应用程序中的安全应用程序“自动”,然后尝试。



Answer 4:

方式IntentFilter的工作是,每个<intent-filter></intent-filter>包含发射了该部件的一种方式。 如果你有烧起来(你要听一个像两个动作的多种方式BroadcastReceiver ),你需要一个独立的<intent-filter></intent-filter>定义每个。

因此,你可以尝试改变:

<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=".mics.BootReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>

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

在这里阅读更多: 意图和意图过滤器| Android开发者

编辑

如果仍不能正常工作,你可以尝试测试,如果你的舱单申报正确完成。 尝试在终端中执行下面的命令,保持连接到计算机,测试设备:

adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -n com.app.myapp/.mics.BootReceiver

如果这不起作用,你应该重新检查接收器的相对包声明在manifest文件。

编辑2

这可能听起来很奇怪,但可以尝试以下步骤:

  • 卸载从您的手机应用程序(确保其卸载所有用户)
  • 重新启动手机
  • 清洁工程
  • 建立在你的设备再次运行该项目


Answer 5:

我已经从将近一年疲于应付这个问题。 在我所有的应用程序,我展示一个通知用户禁用电池优化我的应用程序。

很多上一台加设备的测试之后,我能够收到时,电池优化关闭我的应用程序启动完成广播。 在我看来,它比上面讨论的辅助服务要好得多。

问用户禁用了您的应用程序的电池优化的最简单方法是显示某种通知,当用户点击它打开电池优化页面。 您可以使用下面的代码来做到这一点。

public void openPowerSettings(View v) {

    /* Make Sure to add below code to manifest
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
    */

    try {
        Intent i = new Intent(android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
        startActivityForResult(i, 1);
    }  catch (Exception e) {
        Log.e (TAG, "Exception: " + e.toString());
    }

}

而且你还可以隐藏通知若跌破函数返回true。

public static boolean is_ignoring_battery_optimizations(Context context) {
    String PACKAGE_NAME = context.getPackageName();
    PowerManager pm = (PowerManager) context.getSystemService(context.POWER_SERVICE);
    boolean status = true;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
        status = pm.isIgnoringBatteryOptimizations(PACKAGE_NAME);
    }
    return status;
}


Answer 6:

如何启动设备上启动服务(自动运行的应用程序等)

对于第一:因为版本的Android 3.1+您没有收到BOOT_COMPLETE如果用户从来没有开始您的应用程序至少一次或用户“强制关闭”应用程序。 这样做是为了防止恶意软件自动注册服务。 这个安全漏洞是Android中的较新版本关闭。

解:

创建活动的应用程序。 当用户运行一次应用程序能够接收BOOT_COMPLETE广播消息。

对于第二:外部存储被安装之前BOOT_COMPLETE被发送。 如果应用程序安装到外部存储也不会收到BOOT_COMPLETE广播消息。

在这种情况下,在两个解决方案:

  1. 将应用程序安装到内部存储
  2. 安装在内部存储的另一个小的应用程序。 这个应用程序接收BOOT_COMPLETE和外部存储运行第二应用程序。

如果您的应用程序已经安装在内部存储,然后下面的代码可以帮助您了解如何在设备开机启动服务。


在的Manifest.xml

允许:

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

注册您的BOOT_COMPLETED接收器:

<receiver android:name="org.yourapp.OnBoot">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
</receiver>

注册您的服务:

<service android:name="org.yourapp.YourCoolService" />

在接收器OnBoot.java:

public class OnBoot extends BroadcastReceiver
{

    @Override
    public void onReceive(Context context, Intent intent) 
    {
        // Create Intent
        Intent serviceIntent = new Intent(context, YourCoolService.class);
        // Start service
        context.startService(serviceIntent);

    }

 }

对于HTC也许你还需要添加在显示这个代码,如果设备不抓住项值:

<action android:name="android.intent.action.QUICKBOOT_POWERON" />

现在接收这个样子:

<receiver android:name="org.yourapp.OnBoot">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
        <action android:name="android.intent.action.QUICKBOOT_POWERON" />
    </intent-filter>
</receiver>

如何在没有重启模拟器或真实设备测试BOOT_COMPLETED? 这很简单。 试试这个:

adb -s device-or-emulator-id shell am broadcast -a android.intent.action.BOOT_COMPLETED

如何获得设备ID? 获取ID的连接设备的列表:

adb devices

亚洲开发银行在ADT在默认情况下,你可以找到在:

adt-installation-dir/sdk/platform-tools

请享用! )



文章来源: Broadcast Receiver Not Working After Device Reboot in Android