【问题标题】:Broadcast Receiver Not Working After Device Reboot in AndroidAndroid 设备重启后广播接收器不工作
【发布时间】:2017-05-22 07:45:49
【问题描述】:

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

我有什么

我有一个 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>

还有 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);
        }
    }
}

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

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() 方法。

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

我的观察

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

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

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

编辑 1

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

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

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

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

编辑 2

我已经尝试过@shadygoneinsane 给出的建议。以下是我的观察。

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

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

我得到了这个堆栈跟踪,

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)

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

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

./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 和日志。

看来通过 ADB 发送广播不起作用,但实际上打开拨号器并拨打了一个号码。

编辑 3

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

我使用了诸如 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 提出的解决方案,它似乎真的很有效。添加无障碍服务后问题得到解决。

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

【问题讨论】:

  • 尝试将这些添加到清单声明中:android:enabled="true"android:exported="true"。同样在启动接收器类中,删除intent.getAction 条件,因为没有理由检查它,因为广播将被触发的唯一时间是在启动时。
  • 我刚试过你的建议,结果还是一样。实际上,这些可以被视为最佳实践,不会以任何方式改变实施。
  • 不同的路径。也许它确实有效,但你没有看到吐司。也许它失败了,而你没有看到它失败。更改代码以保存您以后可以检测到的内容。记住广播接收器会在 10 秒内完成,否则应用会失败。
  • 我没有测试过一次,把这个问题放在这里。经过几次测试,我可以确认这些接收器在上述某些设备中重新启动后无法正常工作。正如您所提到的,我如何检测它是否失败?
  • 接受的答案解决了我所面临的实际问题并得到了解决。谢谢。

标签: android broadcastreceiver android-broadcast android-broadcastreceiver


【解决方案1】:

IntentFilters 的工作方式是每个&lt;intent-filter&gt;&lt;/intent-filter&gt; 都包含一种启动组件的方式。如果您有多种触发方式(例如您想在一个 BroadcastReceiver 中收听两个动作),则需要为每个动作单独定义一个 &lt;intent-filter&gt;&lt;/intent-filter&gt;

因此,您可以尝试更改:

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

在此处阅读更多信息:Intents and Intent Filters | Android Developers

编辑

如果仍然无法正常工作,您可以尝试测试您的清单声明是否正确完成。尝试在终端中执行以下命令,保持测试设备与计算机的连接:

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

如果这不起作用,您应该重新检查清单文件中接收器的相关包声明。

编辑 2

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

  • 从您的手机中卸载应用程序(确保已为所有用户卸载)
  • 重启手机
  • 清理项目
  • 再次在您的设备中构建并运行该项目

【讨论】:

  • 试过了,不行。如果多个操作是问题所在,那么 PhoneCallReceiver 应该已经在工作了。它只有一个动作。
  • 您可以尝试执行此操作吗:adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -n com.app.myapp/.mics.BootReceiver
  • 试过了,得到相同的SecurityException。可能是因为我的设备没有root。
  • 这可能听起来很奇怪,但请尝试按照以下步骤操作:从手机中卸载应用程序(确保已为所有用户卸载)> 重新启动手机 > 清理项目 >​​ 在您的设备中构建并运行项目
  • 这与调试版本中的问题无关。我的应用程序正在生产中,有几个设备面临这个问题。甚至它发生在我拥有的一些设备中。
【解决方案2】:

@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 来实现它。可能是它打断了这个。

还有一点需要注意:

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

【讨论】:

  • 试过了,但效果不佳。如果问题出在快速启动过滤器上,那么 PhoneCallReceiver 至少应该可以工作。我认为问题出在其他地方。
  • 不要依赖或测试小米设备,因为它们有自己的操作系统,这会停止 Android 的基本功能,比如他们停止推送通知服务和后台服务只是为了优化电池使用。要在小米设备上进行测试,请在安全应用中将您的应用标记为“自动启动”,然后尝试。
  • 我正在牛轧糖上的 OnePlus 3 上测试所有这些。我知道关于 Mi 的这一事实,这就是为什么我不再对其进行测试的原因。但它不适用于我的 OnePlus 3,我可以确认。
  • 检查了 MI 和 Stock Android 手机(Moto G3),它工作正常!仍然如果它不起作用,那么请参考这个 ans 的编辑 2:stackoverflow.com/a/41562672/7271231
【解决方案3】:

如何在设备启动时启动服务(自动运行应用等)

首先:从 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,如果设备未捕获 RECEIVE_BOOT_COMPLETED,您可能还需要在清单中添加此代码:

<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 中的 adb 默认可以在以下位置找到:

adt-installation-dir/sdk/platform-tools

享受吧! )

【讨论】:

【解决方案4】:

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

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

我注意到,如果您的应用中有 AccessibilityService 并且它已被用户打开,那么您的应用会通过这些制造商应用的过滤器,并且应用会收到它的启动完成事件和任何其他 BroadcastReceiver按预期工作。

这个技巧的可能解释是,由于AccessibilityService 是系统级服务,因此通过注册您自己的服务,您通过了这些制造商应用的特定过滤器,并且一旦您的自定义AccessibilityService 由操作系统触发,您的应用会主动接收您已注册的符合条件的BroadcastReceiver

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

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

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

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

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

<?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 用于任何目的而不是此解决方法,您可以将覆盖的方法留空。

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

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

就是这样。现在在您的应用程序中,只需要求您的用户从设置中为您的应用程序打开无障碍服务并保持打开状态,瞧!您的应用在所有设备上都能正常运行,即使操作系统设置了哪些应用应在启动时自动启动的过滤器。

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

希望这会有所帮助。

【讨论】:

  • 您的发现很有用,但如果您不是随便建议使用虚假的 AccessibilityService 只是为了通过自动过滤器并接收BOOT_COMPLETE,那就更好了。如果每个人都开始(误)​​使用这种做法,许多根应用程序,更重要的是,为残障人士提供的实际无障碍服务可能会受到负面影响。
  • 注意:每次强制停止应用都会丢失访问权限。
  • @user1643723 这是我能想到的最好的解决方案,但我愿意接受比这种方法更简单且需要更少权限的任何更好的替代方案。 :-)
  • @RahulChowdhury 我今天收到了来自 Google Play 支持的电子邮件。它表示,使用android.permission.BIND_ACCESSIBILITY_SERVICE 等无障碍服务的应用程序只能用于帮助残障用户使用 Android 设备和应用程序。未能在 30 天内满足此要求的应用可能会从 Google Play 下架。
  • @AritraRoy 嗨,请参阅上面的评论。您在 Google Play 商店上发布的应用程序是否也收到了此类电子邮件?解决方法是什么?
【解决方案5】:

您好,我迟到了,但我从一开始就关注这个问题。我知道One-plus 和其他一些 OEM 维护了一个可以接收 BOOT_COMPLETED 广播的应用程序列表。如果您的应用未列入白名单,则您的应用将不会在启动时启动。现在我有一个解决方案,它在内存和资源方面非常有效,并且保证在重新启动或硬启动后启动您的任务或服务也不需要AccessibilityService ,正如answer 中所建议的那样。就这样吧。。

  1. 在您的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'

这是来自firebaselibrary 团队,它依赖google-play-service 库来安排您的工作,从我的角度来看,google-play-service 有权在启动时启动,所以google-play-service 将在启动时启动而不是系统设备重启后立即运行您的作业。

  1. 现在这一步很简单,只需定义一个 JobService 类

公共类 MyJobService 扩展 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. 在清单文件中添加您的作业服务。

  2. 将此作业安排在您想要的任何位置,例如当您的应用启动时。

    FirebaseJobDispatcher 调度程序 = 新 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.就是这样!!现在无论您是否在白名单中,您都可以在设备启动时执行您的任务或服务。

有一点需要注意,Google Play Service 必须安装在设备上,否则将无法正常工作。

【讨论】:

  • 这似乎是一个很好的解决方案。我会试一试,然后回复你。
  • @tejshah,它工作正常,我在 MI note 4 和 OnePlus3 上进行了测试。
  • 是的。该应用程序被禁用以在列表中启动。你在 Job 中设置了.setLifetime(Lifetime.FOREVER) 吗?
  • 只有当setRecurring 为真时,您才能说它在 1 分钟后循环。并且Lifetime.FOREVER 也将在重新启动后使其工作。可能有一些设备在重启后出现问题,但对我来说,它可以在 op 所说的设备上运行。
  • FirebaseJobDispatcher 已弃用
【解决方案6】:

近一年来,我一直在努力解决这个问题。在我的所有应用程序中,我都会向用户显示禁用我的应用程序电池优化的通知。

在一加设备上进行大量测试后,当我的应用关闭电池优化时,我能够接收启动完成广播。在我看来,它比上面讨论的无障碍服务要好得多。

要求用户为您的应用禁用电池优化的最简单方法是显示某种通知,并在用户点击时打开电池优化页面。你可以使用下面的代码来做到这一点。

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

【讨论】:

  • 现在情况并非如此,因为一加再次将所有应用程序都投入到优化中。所以它不起作用
【解决方案7】:

您可以向用户请求自动启动权限,并将他们引导到所需的设置页面:

 private void autoStart() {
    try {
        Intent intent = new Intent();
        String manufacturer = android.os.Build.MANUFACTURER;
        if ("xiaomi".equalsIgnoreCase(manufacturer)) {
            intent.setComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity"));
        } else if ("oppo".equalsIgnoreCase(manufacturer)) {
            intent.setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity"));
        } else if ("vivo".equalsIgnoreCase(manufacturer)) {
            intent.setComponent(new ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity"));
        } else if ("Letv".equalsIgnoreCase(manufacturer)) {
            intent.setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity"));
        } else if ("Honor".equalsIgnoreCase(manufacturer)) {
            intent.setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity"));
        } else if ("oneplus".equalsIgnoreCase(manufacturer)) {
            intent.setComponent(new ComponentName("com.oneplus.security", "com.oneplus.security.chainlaunch.view.ChainLaunchAppListAct‌​ivity"));
        }

        List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        if (list.size() > 0) {
            startActivity(intent);
        }

    } catch (Exception e) {
        Log.e("exc", String.valueOf(e));
    }
}

执行此操作后,接收器总是在重新启动时触发。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多