preface

== The main contents of this session include: ==

1, after Android8.0 IntentService start exception tracking

2. JobIntentService replaces IntentService

IntentService startup exception tracking after Android8.0

During startup optimizations in the project, bugly often reported the following problems when the Application started a third-party component via IntentService:

android.app.RemoteServiceException Context.startForegroundService() did not then call Service.startForeground()


# main(2)
android.app.RemoteServiceException
Context.startForegroundService() did not then call Service.startForeground()

1 android.app.ActivityThread$H.handleMessage(ActivityThread.java:2056)
2 android.os.Handler.dispatchMessage(Handler.java:106)
3 android.os.Looper.loop(Looper.java:192)
4 android.app.ActivityThread.main(ActivityThread.java:6959)
5 java.lang.reflect.Method.invoke(Native Method)
6 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:557)
7 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:875)
Copy the code
1. Service and IntentService usage scenarios and differences

Service usage scenarios:

A) Services run on the main Thread. If we need to perform time-consuming operations, we will do so on the Service Thread.

B. If a time-consuming operation cannot perform the completed task in the current Activity lifecycle, it needs to be performed in the Service, such as an asynchronous download.

C. It needs to run in the background for a long time. Tasks following the APP life cycle need to be executed in the Service, such as Socket long connections.

D. Service is the base class of all services. We usually implement services from this class. If we use this class, we need to manage the life cycle of the Service and stop the Service where appropriate.

Characteristics of IntentService

A, IntentService inheritance in Service, through the source code as you can see, is to increase the Handler on the basis of the Service, which, HandlerThread support;

B, you just need to rewrite onHandleIntent (Intentintent) asynchronous tasks, this method is the UI thread, can perform the time-consuming operation;

C. Once this method is executed, stopSelf() will be executed immediately to stop the service without manually stopping the service.

StartForegroundService ==Android 8.0 added the startForegroundService method to start the foreground service. The foreground service is a service with notification bar. If we start the service with startForegroundService, StartForeground () must be called within 5 seconds to display a notification box, otherwise == is returned

2, clearly calls startforeground why will quote Context. StartForegroundService () did not then call Service. Startforeground ()

Let’s start with the call to IntentService:

private void startInitService() { Intent intent = new Intent(this, InitIntentService.class); intent.setAction("com.pxwx.student.action"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(intent); } else { startService(intent); }}Copy the code

Handling in IntentService:

The startForeground method is called in the onCreate method

public class InitIntentService extends IntentService { public InitIntentService() { super("InitIntentService"); } @RequiresApi(api = Build.VERSION_CODES.O) private Notification getNotification() { NotificationChannel channel = new NotificationChannel("init", "", NotificationManager.IMPORTANCE_LOW); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (manager ! = null) { manager.createNotificationChannel(channel); } return new Notification.Builder(this, "init") .setContentTitle("") .setContentText("") .setAutoCancel(true) .setSmallIcon(com.pxwx.student.core.R.mipmap.small_icon) .build(); } @Override public void onCreate() { super.onCreate(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //notification ID must not be 0 startForeground(1,getNotification()); } } @Override protected void onHandleIntent(@Nullable Intent intent) { initThirdSDK(); }Copy the code

OnCreate method clearly call startforeground why will quote Context. StartForegroundService () did not then call Service. Startforeground ()?

Analysis:

OnCreate (); onStart(); onCreate (); onStart(); So startforeground is also executed in the onStart() method

@Override public void onStart(@Nullable Intent intent, int startId) { super.onStart(intent, startId); If service A is started by startForegroundService, the onCreate method of service A will not be called. Only onStart method is called if (build.version.sdk_int >= build.version_codes.o) {startForeground(1,getNotification()); }}Copy the code

2. However, the problem still exists, but the occurrence of the problem is reduced. Then I tried to use the starttForeground method in onHandleIntent again

@Override protected void onHandleIntent(@Nullable Intent intent) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {  startForeground(1,getNotification()); } initThirdSDK(); }Copy the code

3. However, the problem still exists, but it also reduces the occurrence of the problem, can not completely eliminate the occurrence of the problem.

IntentService If onhandleIntent starts for the first time and does not finish processing the intent, start the Service again. Instead, the request is placed in the request queue, waiting for the first intent to finish processing the second one. In this case, only one thread is running

IntentService processes tasks in order of request, i.e. one after another

3. IntentService source code analysis

IntentService; IntentService;

public abstract class IntentService extends Service {
    ...
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}
Copy the code

IntentService (IntentService) ¶ IntentService (IntentService) ¶ IntentService (IntentService) ¶ IntentService (IntentService) ¶ IntentService (IntentService) ¶ IntentService (IntentService) ¶ IntentService (IntentService) ¶ IntentService (IntentService) ¶ IntentService (IntentService) ¶

Second, look at the fields declared in IntentService

Private volatile Looper mServiceLooper; private volatile Looper mServiceLooper; // The Hander object associated with the child thread Looper private Volatile ServiceHandler mServiceHandler; // An identifier associated with the child thread HandlerThread private String mName; // Set the flag bit of the Service. Set the return value of onStartCommand based on its value. /** * You should not override this method for your IntentService. Instead, * override {@link #onHandleIntent}, which the system calls when the IntentService * receives a start request. * @see android.app.Service#onStartCommand */ @Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; }Copy the code

==mRedelivery is a flag bit parameter that handles the return value of onStartCommand. Note that the return value of onStartCommand is of several types defined in Service: ==

public static final int START_CONTINUATION_MASK = 0xf;
public static final int START_STICKY_COMPATIBILITY = 0;
public static final int START_STICKY = 1;
public static final int START_NOT_STICKY = 2;
public static final int START_REDELIVER_INTENT = 3;
Copy the code

According to these types of comments, it can be translated and explained:

START_STICKY_COMPATIBILITY: Compatibility mode. If the Service is killed after it is created, the onStartCommand method cannot be executed (it may or may not be executed).

START_STICKY: If the Service process is killed, retain the start state of the Service but retain the intent object delivered. The onStartCommand(Intent,int,int) method is called after the Service is created. If no start command is passed to the Service during that time, the Intent parameter is null.

START_NOT_STICKY: If a Service is killed by the system after it is started (returned from onStartCommand), no Service will be created until the next call to context.startService (). The onStartCommand method invocation with an empty Intent parameter is not accepted because an empty Intent cannot create a Service.

START_REDELIVER_INTENT: When a Service is killed after it starts, the system retransmits the latest Intent to the onStartCommand method to rebuild the Service.

MRedelivery controls the return value of onStartCommand:

If true, START_REDELIVER_INTENT is returned, indicating that if the Service is killed by the system, it can rebuild and retransmit the latest incoming Intent.

If false, START_NOT_STICKY means that if the Service is killed by the system, the Servcie will not be rebuilt unless context.startService () is called again.

When the Service is destroyed, the message queue of the child thread is stopped.

4. NtentService also has a setter method that sets mRedelivery

4. Summary and analysis:

IntentService = IntentService = IntentService Lead to android. App. RemoteServiceException Context. StartForegroundService () did not then call Service. StartForeground () : The cause of the exception: ==

1. The default flag bit parameter of the onStartCommand return value is START_NOT_STICKY

2. If a Service is killed by the system after it is started (returned from onStartCommand), the Service will not be created until the next call to context.startService (). The onStartCommand method invocation with an empty Intent parameter is not accepted because an empty Intent cannot create a Service.

3, lead to the Context. StartForegroundService () did not then call Service. StartForeground () could have been killed by the system, StartForeground method in onCreate, onStart, and onHandleIntent methods that do not execute IntentService

JobIntentService replaces IntentService

Based on the above analysis, the above abnormal situation can not be completely solved, how to solve it?

IntentService: IntentService: IntentService: IntentService: IntentService

* <p class="note"><b>Note:</b> IntentService is subject to all the * <a Href = "/ preview/features/background. The HTML" > background execution limits < / a > * imposed with Android 8.0 (API level 26). In most cases, you are better off * using {@link android.support.v4.app.JobIntentService}, Which uses jobs * instead of services when running on Android 8.0 or higher. * </p>Copy the code

Translation:

IntentService is subject to all background execution restrictions of Android 8.0 (API level 26). In most cases, run on Android 8.0 or higher. You’d better use the Android support. The v4. App. JobIntentService rather than services.

1. Introduction to JobIntentService

JobIntentService is a new class added to Android 8.0. It is also derived from Service.

Helper for processing work that has been enqueued for a job/service. When running on Android O or later, the work will be dispatched as a job via JobScheduler.enqueue. When running on older versions of the platform, it will use Context.startService.

Translation:

JobIntentService is used to execute tasks added to the queue. On Android 8.0 and later, JobIntentService tasks are dispatched to JobScheduler.enqueue. On Android 8.0 and later, tasks are still executed using Context.startService.

2. Use JobIntentService

1, in the Manifest Permission:

<uses-permission android:name="android.permission.WAKE_LOCK" />
Copy the code

2. In Manifest, Service:

<service android:name=".InitIntentService" android:permission="android.permission.BIND_JOB_SERVICE" />
Copy the code

JobIntentService (JobIntentService);

public class InitIntentService extends JobIntentService { public static final int JOB_ID = 1; public static void enqueueWork(Context context, Intent work) { enqueueWork(context, InitIntentService.class, JOB_ID, work); } @override protected void onHandleWork(@override protected void onHandleWork) {Copy the code

4. Call the start implementation of JobIntentService

InitIntentService.enqueueWork(context, new Intent());
Copy the code

The startService() method is not required. The startService() method is not required to start the JobIntentService.


Follow my technical official account