Android 8.0: java.lang.IllegalStateException: Not allowed to start service Intent

前端 未结 17 1030
借酒劲吻你
借酒劲吻你 2020-11-22 09:15

On application launch, app starts the service that should to do some network task. After targeting API level 26, my application fails to start service on Android 8.0 on back

相关标签:
17条回答
  • 2020-11-22 09:49

    Alternate solution by using JobScheduler, it can start service in background in regular interval of time.

    Firstly make class named as Util.java

    import android.app.job.JobInfo;
    import android.app.job.JobScheduler;
    import android.content.ComponentName;
    import android.content.Context;
    
    public class Util {
    // schedule the start of the service every 10 - 30 seconds
    public static void schedulerJob(Context context) {
        ComponentName serviceComponent = new ComponentName(context,TestJobService.class);
        JobInfo.Builder builder = new JobInfo.Builder(0,serviceComponent);
        builder.setMinimumLatency(1*1000);    // wait at least
        builder.setOverrideDeadline(3*1000);  //delay time
        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);  // require unmetered network
        builder.setRequiresCharging(false);  // we don't care if the device is charging or not
        builder.setRequiresDeviceIdle(true); // device should be idle
        System.out.println("(scheduler Job");
    
        JobScheduler jobScheduler = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            jobScheduler = context.getSystemService(JobScheduler.class);
        }
        jobScheduler.schedule(builder.build());
       }
      }
    

    Then, make JobService class named as TestJobService.java

    import android.app.job.JobParameters;
    import android.app.job.JobService;
    import android.widget.Toast;
     
      /**
       * JobService to be scheduled by the JobScheduler.
       * start another service
       */ 
    public class TestJobService extends JobService {
    @Override
    public boolean onStartJob(JobParameters params) {
        Util.schedulerJob(getApplicationContext()); // reschedule the job
        Toast.makeText(this, "Bg Service", Toast.LENGTH_SHORT).show();
        return true;
    }
    
    @Override
    public boolean onStopJob(JobParameters params) {
        return true;
      }
     }
    

    After that BroadCast Receiver class named ServiceReceiver.java

    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    
     public class ServiceReceiver extends BroadcastReceiver {
     @Override
    public void onReceive(Context context, Intent intent) {
        Util.schedulerJob(context);
     }
    }
    

    Update Manifest file with service and receiver class code

    <receiver android:name=".ServiceReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
        <service
            android:name=".TestJobService"
            android:label="Word service"
            android:permission="android.permission.BIND_JOB_SERVICE" >
    
        </service>
    

    Left main_intent launcher to mainActivity.java file which is created by default, and changes in MainActivity.java file are

    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    
    public class MainActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Util.schedulerJob(getApplicationContext());
      }
     }
    

    WOOAAH!! Background Service starts without Foreground service

    [Edit]: You can use Work Manager for any type of background tasks in Android.

    0 讨论(0)
  • 2020-11-22 09:49

    I see a lot of responses that recommend just using a ForegroundService. In order to use a ForegroundService there has to be a notification associated with it. Users will see this notification. Depending on the situation, they may become annoyed with your app and uninstall it.

    The easiest solution is to use the new Architecture Component called WorkManager. You can check out the documentation here: https://developer.android.com/topic/libraries/architecture/workmanager/

    You just define your worker class that extends Worker.

    public class CompressWorker extends Worker {
    
        public CompressWorker(
            @NonNull Context context,
            @NonNull WorkerParameters params) {
            super(context, params);
        }
    
        @Override
        public Worker.Result doWork() {
    
            // Do the work here--in this case, compress the stored images.
            // In this example no parameters are passed; the task is
            // assumed to be "compress the whole library."
            myCompress();
    
            // Indicate success or failure with your return value:
            return Result.SUCCESS;
    
            // (Returning RETRY tells WorkManager to try this task again
            // later; FAILURE says not to try again.)
        }
    }
    

    Then you schedule when you want to run it.

        OneTimeWorkRequest compressionWork = 
            new OneTimeWorkRequest.Builder(CompressWorker.class)
                .build();
        WorkManager.getInstance().enqueue(compressionWork);
    

    Easy! There are a lot of ways you can configure workers. It supports recurring jobs and you can even do complex stuff like chaining if you need it. Hope this helps.

    0 讨论(0)
  • 2020-11-22 09:50

    If the service is running in a background thread by extending IntentService, you can replace IntentService with JobIntentService which is provided as part of Android Support Library

    The advantage of using JobIntentService is, it behaves as an IntentService on pre-O devices and on O and higher, it dispatches it as a job

    JobScheduler can also be used for periodic/on demand jobs. But, ensure to handle backward compatibility as JobScheduler API is available only from API 21

    0 讨论(0)
  • 2020-11-22 09:50

    Due to controversial votes on this answer (+4/-4 as of this edit), PLEASE LOOK AT THE OTHER ANSWERS FIRST AND USE THIS ONLY AS A LAST RESORT. I only used this once for a networking app that runs as root and I agree with the general opinion that this solution should not be used under normal circumstances.

    Original answer below:

    The other answers are all correct, but I'd like to point out that another way to get around this is to ask user to disable battery optimizations for your app (this isn't usually a good idea unless your app is system related). See this answer for how to request to opt out of battery optimizations without getting your app banned in Google Play.

    You should also check whether battery optimizations are turned off in your receiver to prevent crashes via:

    if (Build.VERSION.SDK_INT < 26 || getSystemService<PowerManager>()
            ?.isIgnoringBatteryOptimizations(packageName) != false) {
        startService(Intent(context, MyService::class.java))
    } // else calling startService will result in crash
    
    0 讨论(0)
  • 2020-11-22 09:51

    If you are running your code on 8.0 then application will crash. So start the service in the foreground. If below 8.0 use this :

    Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
    context.startService(serviceIntent);
    

    If above or 8.0 then use this :

    Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
    ContextCompat.startForegroundService(context, serviceIntent );
    
    0 讨论(0)
提交回复
热议问题