Android中设备重启后广播接收器无法正常工作

Ari*_*Roy 49 android broadcastreceiver android-broadcast android-broadcastreceiver

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

是)我有的

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

<?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>
Run Code Online (Sandbox Code Playgroud)

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

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题

当我安装应用程序并启动一次时,所有这些接收器都能正常工作.但是在重新启动设备后,这些接收器根本无法工作.既没有BootCompleteReceiver也没有PhoneCallReceiver得到他们的onReceive()方法.

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

我的观察

我已经彻底测试过了.重新启动设备后,接收器在我的Nexus 5X(牛轧糖),Nexus 6P(牛轧糖),YU Yuphoria(棒棒糖)中运行良好,但在我的OnePlus 3(Nougat)和Mi 4i(Lollipop)中没有.

如何在一些设备上完美地运行相同的代码而在其他设备上完全无法工作?我没有改变任何东西.

我在这做错了什么?我的应用程序严重依赖于这些广播并基于这些启动服务.任何帮助将受到高度赞赏.

编辑1

要理解这个问题好,我刚刚创建了一个非常小的测试项目只是一个单一的活动和完全相同的BootCompleteReceiverPhoneCallReceiver.

但奇怪的是,这个项目完全适用于我的OnePlus 3,我的实际应用程序的接收器在重启后无法正常工作.我最初假设问题是在操作系统或设备中以某种方式,但事实并非如此.

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

这对我来说真的很困惑.我需要一些专家帮助.

编辑2

我试过@shadygoneinsane给出的建议.这是我的观察.

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

./adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -p com.app.myapp
Run Code Online (Sandbox Code Playgroud)

我得到了这个堆栈跟踪,

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)
Run Code Online (Sandbox Code Playgroud)

也许是因为我的设备没有根源.我无法以任何方式发送此广播.

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

./adb shell am broadcast -a android.intent.action.PROCESS_OUTGOING_CALLS -p com.app.myapp
Run Code Online (Sandbox Code Playgroud)

我懂了,

Broadcasting: Intent { act=android.intent.action.PROCESS_OUTGOING_CALLS pkg=com.app.myapp }
Broadcast completed: result=0
Run Code Online (Sandbox Code Playgroud)

看来广播是成功的,但我没有看到任何Toast或任何日志.然后我打开拨号器拨号,然后我可以看到Toast和日志.

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

编辑3

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

我使用了500,999甚至最高整数值的优先级,但没有任何作用.我的一些朋友应用程序也会出现此问题.它们在某些设备中工作,而在其他设备中不起作用.

编辑4

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

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

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

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

编辑5

我已经尝试过@Rahul Chowdhury提出的解决方案,它似乎真的很好用.添加辅助功能服务后,问题就会重新解决.

但是,如果用户在授予后撤销了辅助功能权限,那么我是否可以通过编程方式检查我的应用是否可以使用辅助功能?

Rah*_*ury 33

这是您提到的设备OnePlus和Mi的经过测试和运行的解决方案.

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

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

这个技巧的可能解释是,因为AccessibilityService是系统级服务,所以通过注册您自己的服务,您将通过这些制造商应用的特定过滤器,并且一旦您的自定义AccessibilityService被操作系统触发,您的应用就会变得活跃BroadcastReceiver您已注册的资格.

那么,这是怎么做的,

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

<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"/>
Run Code Online (Sandbox Code Playgroud)

这将允许您AccessibilityService在系统中注册您的应用程序.

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

<?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"/>
Run Code Online (Sandbox Code Playgroud)

还剩下一步,AccessibilityService在项目中定义自定义,

public class MyAccessibilityService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) { }

    @Override
    public void onInterrupt() {

    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,由于您不需要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>
Run Code Online (Sandbox Code Playgroud)

就这样.现在,在您的应用程序中,只需要求您的用户从设置中打开您的应用程序的辅助功能服务并保持打开状态!您的应用程序在所有设备上都能正常运行,即使操作系统会在应用程序应该在启动时自动启动哪些应用程序.

编辑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;
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.

  • 注意:每次强制停止应用程序时,可访问性权限都将丢失. (6认同)
  • @RahulChowdhury我今天收到了Google Play支持的电子邮件.它说,使用诸如`android.permission.BIND_ACCESSIBILITY_SERVICE`之类的辅助功能服务的应用只能用于帮助残障用户使用Android设备和应用.如果在30天内未能满足此要求的应用可能会从Google Play中删除. (4认同)
  • 您的发现很有用,但是如果您不随便建议使用伪造的AccessibilityService只是为了通过自动过滤器并接收`BOOT_COMPLETE`,那就更好了。如果每个人开始(滥用)此类做法,那么许多root应用程序,更重要的是,*面向残障人士的实际可访问性服务*可能受到负面影响。 (2认同)
  • @AritraRoy您好,请参阅上面的评论.您是否收到了在Google Play商店发布的应用中的此类电子邮件?有什么工作? (2认同)

Sun*_*nny 6

嗨,我迟到了,但我从这个问题的开始就是关注这个问题.我知道那里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'
Run Code Online (Sandbox Code Playgroud)

否则添加以下内容:

compile 'com.firebase:firebase-jobdispatcher-with-gcm-dep:0.5.2'
Run Code Online (Sandbox Code Playgroud)

这是一个来自团队的,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?"
    }
    
    Run Code Online (Sandbox Code Playgroud)

}

  1. 在清单文件中添加您的作业服务.

    <service
      android:exported="false"
      android:name=".MyJobService">
    <intent-filter>
        <action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
    </intent-filter>
    </service>
    
    Run Code Online (Sandbox Code Playgroud)
  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);
    
    Run Code Online (Sandbox Code Playgroud)

那就是它!现在,您可以在设备启动时执行任务或服务,无论您是否在白名单中.

有一点需要注意,Google Play服务必须安装在设备上,否则无法使用.


Cha*_*uri 5

@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>
Run Code Online (Sandbox Code Playgroud)

删除 QuickBoot 意图过滤器并尝试运行它,根据文档,我们只需要 BootCompleted 即可实现它。也许它正在打断这一点。

还有一点需要注意:

不要完全依赖或测试 Mi 设备,因为它们有自己的操作系统,会停止 Android 的基本功能,就像它们停止推送通知服务和后台服务只是为了优化电池使用一样。要在小米设备上测试此功能,请在安全应用程序中将您的应用程序标记为“自动启动”,然后尝试。

  • 不要依赖或测试 Mi 设备,因为它们有自己的操作系统,会停止 Android 的基本功能,就像它们停止推送通知服务和后台服务只是为了优化电池使用一样。要在小米设备上测试此功能,请在安全应用程序中将您的应用程序标记为“自动启动”,然后尝试。 (6认同)