Related recommendations:

  • The Android source code to read | Handler
  • The Android source code to read | SparseArray
  • Android custom View | distortion effect

Thanks

Detailed analysis of IntentService operation mechanism

Class notes

IntentService is a base class for {@link Service}s that handle asynchronous
requests (expressed as {@link Intent}s) on demand.  Clients send requests
through {@link android.content.Context#startService(Intent)} calls; the
service is started as needed, handles each Intent in turn using a worker
thread, and stops itself when it runs out of work.
Copy the code

IntentService: IntentService: IntentService: IntentService: IntentService: IntentService: IntentService: IntentService: IntentService: IntentService: IntentService: IntentService: IntentService: Android. It uses a worker thread for each Intent and automatically ends the lifecycle after all work is done.

 * <p>This "work queue processor" pattern is commonly used to offload tasks
 * from an application's main thread.  The IntentService class exists to
 * simplify this pattern and take care of the mechanics.  To use it, extend
 * IntentService and implement {@link #onHandleIntent(Intent)}.  IntentService
 * will receive the Intents, launch a worker thread, and stop the service as
 * appropriate.
Copy the code

Worker threads use the mechanism of queues, which usually accept tasks from the main thread. IntentService simplifies this handling mechanism. To use it, you need to implement onHandleIntent(Intent), which when it receives an Intent, starts a worker thread to handle the Intent and stops the service when it’s finished.

 * <p>All requests are handled on a single worker thread -- they may take as
* long as necessary (and will not block the application's main loop), but
* only one request will be processed at a time.
Copy the code

All requests are processed on the same worker thread, which may take a long time (but does not block the UI thread), but only one request is being processed at any one time.

Initialize HandlerThread & Handler

public void onCreate(a) {
   super.onCreate();
   HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
   thread.start();
   mServiceLooper = thread.getLooper();
   mServiceHandler = new ServiceHandler(mServiceLooper);
}
Copy the code

IntentService is a child thread-based Handler Handler, so we have a custom Handler class for ServiceHandler:

private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
        super(looper);
    }
    @Override
    public void handleMessage(Message msg) { onHandleIntent((Intent)msg.obj); stopSelf(msg.arg1); }}Copy the code

As you can see above, in the handlerMessage, the callback: onHandlerIntent, this method needs to be implemented externally. Of course, this method obviously calls back in the worker thread, and after the callback, it automatically calls stopSelf to stop the service.

    protected abstract void onHandleIntent(@Nullable Intent intent);
Copy the code

setIntentRedelivery

Sets whether the intent is resend, describing whether to resend unfinished intents killed by the system after the onStartCommand call of the service.

public void setIntentRedelivery(boolean enabled) {
    mRedelivery = enabled;
}
Copy the code
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
Copy the code

True returns START_REDELIVER_INTENT: Indicates that after the system kills an Intent, it is restarted and sent to an unfinished Intent. If both completed, the latest Intent is sent to restart the service. If the value is set to false, START_NOT_STICKY is returned.

Of course, onStartCommand works on native systems, but on native ROMs, it probably doesn’t work.

A question

Inside the handlerMessage method of ServiceHandler, stopSelf is called after the message is processed. The service is terminated when stopSelf() is called on the previous message. How does it guarantee that it will continue to process subsequent messages?

Let’s repeat the above query in code if there is a Service: TestIntentService

public class TestIntentService extends IntentService { private final static String TAG = "TestIntentService"; public final static String Action_Sleep = "Action_Sleep"; public final static String Action_Soon = "Action_Soon"; @Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) { LogUtils.i(TAG,"onStartCommand");  return super.onStartCommand(intent, flags, startId); } @Override protected void onHandleIntent(@Nullable Intent intent) { LogUtils.i(TAG, intent==null ? "null intent" : "action:" + intent.getAction()); if (intent! =null && intent.getAction()! =null) { switch (intent.getAction()) { case Action_Sleep: try { Thread.sleep(5*1000); } catch (InterruptedException e) { e.printStackTrace(); } break; case Action_Soon: break; } } } @Override public void onDestroy() { LogUtils.i(TAG,"onDestroy"); super.onDestroy(); }}Copy the code

Defines two actions, one that simulates blocking.

findViewById(R.id.btn_1).setOnClickListener(v -> {
    Intent intent = new Intent(this, TestIntentService.class);
    intent.setAction(TestIntentService.Action_Sleep);
    startService(intent);
});
findViewById(R.id.btn_2).setOnClickListener(v -> {
    Intent intent = new Intent(this, TestIntentService.class);
    intent.setAction(TestIntentService.Action_Soon);
    startService(intent);
});
Copy the code

Then, click bTN_2 immediately after clicking bTN_1, and log as follows:

15:09:23. 985, 8941-8941 / com. Chestnut. UI I/TestIntentService: OnStartCommand 15:09:23. 987, 8941-8959 / com. Chestnut. UI I/TestIntentService: Action: Action_Sleep 15:09:24. 848, 8941-8941 / com. Chestnut. UI I/TestIntentService: OnStartCommand 15:09:28. 989, 8941-8959 / com. Chestnut. UI I/TestIntentService: Action, done 15:09:28.990 8941-8959/com.chestnut. UI I/TestIntentService: Action: Action_Soon 15:09:28. 990, 8941-8959 / com. Chestnut. UI I/TestIntentService: Action, done 15:09:28.991 8941-8941/com.chestnut. UI I/TestIntentService: onDestroyCopy the code

When a second Intent request is received, it is not immediately processed. The service is called stopSelf after the previous Intent request is completed. To end the service, but why? We analyze it from the source code:

See first stopSelf ()

StopSelf () is located in service.java:

public final void stopSelf(int startId) {
    if (mActivityManager == null) {
        return;
    }
    try {
        mActivityManager.stopServiceToken(
                new ComponentName(this, mClassName), mToken, startId);
    } catch (RemoteException ex) {
    }
}
Copy the code

Find:mActivityManager.stopServiceToken:

@Override public boolean stopServiceToken(ComponentName className, IBinder token, int startId) { synchronized(this) { return mServices.stopServiceTokenLocked(className, token, startId); }}Copy the code

MServices ismServices = new ActiveServices(this).mServices.stopServiceTokenLockedAs follows:

boolean stopServiceTokenLocked(ComponentName className, IBinder token, int startId) { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "stopServiceToken: " + className + " " + token + " startId=" + startId); / / to find out the corresponding Service recorder ServiceRecord r = findServiceLocked (className, token, UserHandle getCallingUserId ()); if (r ! = null) { if (startId >= 0) { // Asked to only stop if done with all work. Note that // to avoid leaks, we will take this as dropping all // start items up to and including this one. ServiceRecord.StartItem si = r.findDeliveredStart(startId, false, false); if (si ! = null) { while (r.deliveredStarts.size() > 0) { ServiceRecord.StartItem cur = r.deliveredStarts.remove(0); cur.removeUriPermissionsLocked(); if (cur == si) { break; } // If the intent is not the same as the last intent, the service is not stopped. if (r.getLastStartId() ! = startId) { return false; } if (r.deliveredStarts.size() > 0) { Slog.w(TAG, "stopServiceToken startId " + startId + " is last, but have " + r.deliveredStarts.size() + " remaining args"); } } synchronized (r.stats.getBatteryStats()) { r.stats.stopRunningLocked(); } r.startRequested = false; if (r.tracker ! = null) { r.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(), SystemClock.uptimeMillis()); } r.callStart = false; final long origId = Binder.clearCallingIdentity(); bringDownServiceIfNeededLocked(r, false, false); Binder.restoreCallingIdentity(origId); return true; } return false; }Copy the code

The main purpose of this code is to remove the StartItem node from the deliveredStarts list of ServiceRecord until the StartItem node corresponding to the startId parameter is deleted, if the ServiceRecor has not yet arrived If the stopSelf() operation does not need to terminate the service, return false. Only is to remove the startItem node is the last startItem node, will call bringDownServiceIfNeededLocked () to the end of the service. This is why IntentService’s ServiceHandler can safely call stopSelf() after processing the message.

A ServiceRecord is a record of information about a running service. Servicerecord. StartItem, on the other hand, generates an Item object every time the startService method is called:

ActivityManagerService.java

public ComponentName startService(IApplicationThread caller, Intent service, String resolvedType, int userId) { . . . . . . . . . . . . ComponentName res = mServices.startServiceLocked(caller, service, resolvedType, callingPid, callingUid, userId); ............}Copy the code

ActiveServices.java

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, Int callingUid, int userId) {...... // Get ServiceRecord r = res.record; . . . . . . r.startRequested = true; r.delayedStop = false; // Add (new ServiceRecord.StartItem(r, false, r.MakenextStartid (), service, neededGrants)); . . . . . . . . . . . . return startServiceInnerLocked(smap, service, r, callerFg, addToStarting); }Copy the code

The makeNextStartId is the ID of startId in the service callback method. The makeNextStartId is the ID of startId.


Code word is not easy, convenient if the quality of three even, or pay attention to my public number technology sauce, focus on Android technology, not regularly push fresh articles, if you have a good article to share with you, welcome to pay attention to contribute!