Broadcast Receiver Not Working After Device Reboot in Android

前端 未结 7 1695
花落未央
花落未央 2020-11-29 17:12

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

相关标签:
7条回答
  • 2020-11-29 17:27

    Here's a tested and working solution on both the devices that you mentioned, OnePlus and 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.

    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.

    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.

    So, here's how to do it,

    Start by adding this permission to your AndroidManifest.xml,

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

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

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

    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() {
    
        }
    }
    

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

    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.

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

    Hope this helps.

    0 讨论(0)
  • 2020-11-29 17:29

    You can ask user for autostart permission, and direct them to the required settings page:

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

    After doing this, receiver always got triggered on reboot.

    0 讨论(0)
  • 2020-11-29 17:35

    Hi I am late to the party but I was following this question from it's start. I know there that One-plus and some other OEMs maintain a list of apps which can receive BOOT_COMPLETED broadcast. If your app is not white listed then your app won't be started on boot. Now I've a solution which is very efficient in terms of memory and resources and guaranteed to start your task or service after reboot or hard boot also does not need AccessibilityService as proposed in this answer. Here it goes..

    1. Add <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> permission in your manifest file.

    2.If you don't have a dependency on com.google.android.gms:play-services-gcm, add the following to your build.gradle's dependencies section:

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

    Otherwise add the following:

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

    This is a library from firebase team which depends on google-play-service library to schedule your jobs and from my point of view google-play-service has the permission to start at boot so instead of system google-play-service will run your job as soon as device rebooted.

    1. Now this step is easy Just define a JobService class

      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. Add your Job Service in manifest file.

      <service
        android:exported="false"
        android:name=".MyJobService">
      <intent-filter>
          <action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
      </intent-filter>
      </service>
      
    2. Schedule this job anywhere you want for e.g when your app start.

             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.That's it!! Now you can execute your task or service on device boot no matter you are in white list or not.

    There is one point to note that Google Play Service must be installed on device otherwise it won't work.

    0 讨论(0)
  • 2020-11-29 17:36

    @Aritra, Try this

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

    Remove quickBoot intent filter and try running it, as per the documentation we only required BootCompleted to acheive it. May be it is interrupting this.

    Also one more important point to Note :

    Don't rely completely or test on Mi devices as they have their own OS which halts the basic features of Android, like they stop the Push notifications services and background services just to optimize battery usage. To test this on Mi device, mark your app as "AutoStart" in Security app and then try.

    0 讨论(0)
  • 2020-11-29 17:45

    How to start service on device boot(autorun app, etc.)

    For first: since version Android 3.1+ you don't receive BOOT_COMPLETE if user never started your app at least once or user "force closed" application. This was done to prevent malware automatically register service. This security hole was closed in newer versions of Android.

    Solution:

    Create app with activity. When user run it once app can receive BOOT_COMPLETE broadcast message.

    For second: BOOT_COMPLETE is sent before external storage is mounted. If app is installed to external storage it won't receive BOOT_COMPLETE broadcast message.

    In this case there is two solution:

    1. Install your app to internal storage
    2. Install another small app in internal storage. This app receives BOOT_COMPLETE and run second app on external storage.

    If your app already installed in internal storage then code below can help you understand how to start service on device boot.


    In Manifest.xml

    Permission:

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

    Register your BOOT_COMPLETED receiver:

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

    Register your service:

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

    In receiver 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);
    
        }
    
     }
    

    For HTC you maybe need also add in Manifest this code if device don't catch RECEIVE_BOOT_COMPLETED:

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

    Receiver now look like this:

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

    How to test BOOT_COMPLETED without restart emulator or real device? It's easy. Try this:

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

    How to get device id? Get list of connected devices with id's:

    adb devices
    

    adb in ADT by default you can find in:

    adt-installation-dir/sdk/platform-tools
    

    Enjoy! )

    0 讨论(0)
  • 2020-11-29 17:48

    I have been struggling with this issue from almost a year. In all my apps, I show a notice to users to disable battery optimization for my app.

    After a lot of testing on One Plus devices, I am able to receive boot completed broadcast when battery optimization is turned off for my app. In my opinion, it is much better than the accessibility service discussed above.

    The simplest way to ask the user to disable the battery optimization for your app is to show some kind of notice, and open the battery optimization page when the user clicks on it. You can use the below code to do that.

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

    And you can also hide the notice if below function returns 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;
    }
    
    0 讨论(0)
提交回复
热议问题