源碼探索系列7---四大金剛之Service

今天我們來看下這安卓的四大組件的另外一個Service,按套路應該先列點我們在探索過程需要注意的問題,不過現在一時沒想到有什麼,讓我們邊看說解釋,看下有什麼需要注意的

起航——開啓服務

API : 23

我們啓動服務一般有兩種調用StartServiceBindService,這裏我們先從StartService開始。
首先去到ContextWrapper裏面的startService函數

@Override
public ComponentName startService(Intent service) {
    return mBase.startService(service);
}

這個我們在前一篇也分析廣播時候也遇到過類似情況,這個mBase的具體實現是ContextImpl,我們繼續看下

@Override
public ComponentName startService(Intent service) {
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, mUser);
}

private ComponentName startServiceCommon(Intent service, UserHandle user) {
    try {
        validateServiceIntent(service);
        service.prepareToLeaveProcess();
        ComponentName cn = ActivityManagerNative.getDefault().startService(
            mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                        getContentResolver()), getOpPackageName(), user.getIdentifier());

           ...
        return cn;
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
}

好吧,看到這個AMN,最近我們都看到他,已經在熟悉不過了,他的實現是AMS,我們就跑去哪裏再看下那個startService裏面做了什麼吧

    @Override
public ComponentName startService(IApplicationThread caller, Intent service,
        String resolvedType, String callingPackage, int userId)
        throws TransactionTooLargeException {
    enforceNotIsolatedCaller("startService");

      ...

    synchronized(this) {
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        ComponentName res = mServices.startServiceLocked(caller, service,
                resolvedType, callingPid, callingUid, callingPackage, userId);
        Binder.restoreCallingIdentity(origId);
        return res;
    }
}

他在底層去調用了ActivityService.startServiceLocked(),感覺這個命名格式也很有熟悉感啊,來我們繼續看下他做了什麼

 ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
        int callingPid, int callingUid, String callingPackage, int userId)
        throws TransactionTooLargeException {

    ServiceLookupResult res =
        retrieveServiceLocked(service, resolvedType, callingPackage,
                callingPid, callingUid, userId, true, callerFg);

    ServiceRecord r = res.record;

     ...
    return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}

經過一對的處理,他最後調用了startServiceInnerLocked();

 ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
        boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
     ...
    String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false);
    if (error != null) {
        return new ComponentName("!!", error);
    }

    ...
    return r.name;
}

繼續看下里面的內容

private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
        boolean whileRestarting) throws TransactionTooLargeException {

       ...
       app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
       realStartServiceLocked(r, app, execInFg);
       ...

}

哈,realStartServiceLocked這名字看起來,前面都是騙人的啓動過程,現在纔開始幹真活的樣子

private final void realStartServiceLocked(ServiceRecord r,
       ProcessRecord app, boolean execInFg) throws RemoteException {

        ... 
   try {
       ...

       mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
       app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
       app.thread.scheduleCreateService(r, r.serviceInfo,
               mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
               app.repProcState);
       r.postNotification();
       created = true;
   } catch (DeadObjectException e) {
       ...
   }

   requestServiceBindingsLocked(r, execInFg);

   updateServiceClientActivitiesLocked(app, null, true);

   // If the service is in the started state, and there are no
   // pending arguments, then fake up one so its onStartCommand() will
   // be called.
   if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
       r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
               null, null));
   }

   sendServiceArgsLocked(r, execInFg, true);

   ...
}

好了,截取掉一部分不相干的消息,我們現在看下他實際做了什麼,我們看到了他是去調用app.thread.scheduleCreateService()函數去執行service的,這個我們前篇文章有看到過這個app.thread,最終幹活的是ActivityThread裏的一個ApplicationThread。讓我們去看下他實際幹了什麼。
另外結尾也有註釋,如果service已啓動且沒Pending arguments,那是去調用了他onStartCommand()
好了,還是就像去看下主線任務吧

public final void scheduleCreateService(IBinder token,
            ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
        updateProcessState(processState, false);
        CreateServiceData s = new CreateServiceData();
        s.token = token;
        s.info = info;
        s.compatInfo = compatInfo;

        sendMessage(H.CREATE_SERVICE, s);
    }

我們的”H“顯示似乎又要登場了,靠他來發送一個創建服務的消息來幹活。

private void sendMessage(int what, Object obj) {
    sendMessage(what, obj, 0, 0, false);
}

private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {

    Message msg = Message.obtain();
    msg.what = what;
    msg.obj = obj;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    if (async) {
        msg.setAsynchronous(true);
    }
    mH.sendMessage(msg);
}

確實是去調用我們的H朋友去發送消息去了,我們看下消息裏面調用的handleCreateService((CreateServiceData)msg.obj)具體做了什麼

private void handleCreateService(CreateServiceData data) { 

    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;

    java.lang.ClassLoader cl = packageInfo.getClassLoader();
    service = (Service) cl.loadClass(data.info.name).newInstance();

    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
    context.setOuterContext(service);

    Application app = packageInfo.makeApplication(false, mInstrumentation);
    service.attach(context, this, data.info.name, data.token, app,
             ActivityManagerNative.getDefault());
    service.onCreate();
    mServices.put(data.token, service);

    ActivityManagerNative.getDefault().serviceDoneExecuting(
               data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);

}

爲何方便看,精簡了下代碼,從上面的代碼可以看到,到這裏用ClassLoader去啓動了服務,配置了下Context,這個就是我們然後調用了服務的onCreate方法。好了,這樣我們的啓動過程基本就到這裏了。

另外,我們知道onCreate函數執行完後,就是到了onStartCommand()啦,不過這個過程好像沒看到啊?跑去哪裏了呢?

這是因爲還有一部分代碼沒說,重新回到我們的 真 · 幹活的realStartServiceLocked函數的地步,有這麼一個函數我留着 sendServiceArgsLocked(r, execInFg, true); ,他的作用就是發送參數,讓我們看下這部分內容

 private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
        boolean oomAdjusted) throws TransactionTooLargeException {
   ...
   r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
   ...  
}

只留下最重要的一句話,就是調度發送service的參數

public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
        int flags ,Intent args) {
        ServiceArgsData s = new ServiceArgsData();
        s.token = token;
        s.taskRemoved = taskRemoved;
        s.startId = startId;
        s.flags = flags;
        s.args = args;

        sendMessage(H.SERVICE_ARGS, s);
    }

這個複雜發送了一個消息回去,代碼在下面

private void handleServiceArgs(ServiceArgsData data) {
    Service s = mServices.get(data.token);
    if (s != null) {

        if (data.args != null) {
            data.args.setExtrasClassLoader(s.getClassLoader());
            data.args.prepareToEnterProcess();
        }
        int res;
        if (!data.taskRemoved) {
            res = s.onStartCommand(data.args, data.flags, data.startId);
        } else {
            s.onTaskRemoved(data.args);
            res = Service.START_TASK_REMOVED_COMPLETE;
        }
        ...
   }
}

好了,這樣我們的onStartCommannd也就集齊了。
整個流程算跑完一半了。

前進— — 停止服務

在開始看過程前,我們來猜下他打開的過程,應該最後也是跑回到H裏面執行一個什麼STOP_SERVICE消息。帶着這個猜想,我們偷下懶,直接從我們的H先生哪裏看下消息列表,看下有沒我們想要的

 case CREATE_SERVICE:
     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");
     handleCreateService((CreateServiceData)msg.obj);
     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     break;
 case BIND_SERVICE:
     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
     handleBindService((BindServiceData)msg.obj);
     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     break;
 case UNBIND_SERVICE:
     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind");
     handleUnbindService((BindServiceData)msg.obj);
     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     break;
 case SERVICE_ARGS:
     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStart");
     handleServiceArgs((ServiceArgsData)msg.obj);
     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     break;
 case STOP_SERVICE:
     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop");
     handleStopService((IBinder)msg.obj);
     maybeSnapshot();
     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     break;

哈哈,真的中了,我們看到還有BIND_SERVICEUNBIND_SERVICE,全部在這裏,那麼中間過程我們就可以猜想啦

private void handleStopService(IBinder token) {
    Service s = mServices.remove(token);
    if (s != null) {
        try { 
            s.onDestroy();
            Context context = s.getBaseContext();
            if (context instanceof ContextImpl) {
                final String who = s.getClassName();
                ((ContextImpl) context).scheduleFinalCleanup(who, "Service");
            }
            QueuedWork.waitToFinish();
            ActivityManagerNative.getDefault().serviceDoneExecuting(
                        token, SERVICE_DONE_EXECUTING_STOP, 0, 0); 
        } catch (Exception e) {
          ...
        }     
   }
} 

大躍進— — 綁定服務

現在我們直接來看下綁定服務是怎麼回事

@Override
public boolean bindService(Intent service, ServiceConnection conn,
        int flags) {
    return mBase.bindService(service, conn, flags);
}

老套路,我們繼續看下ContextImpl的內容

@Override
public boolean bindService(Intent service, ServiceConnection conn,
        int flags) {
    warnIfCallingFromSystemProcess();
    return bindServiceCommon(service, conn, flags, Process.myUserHandle());
}

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
        UserHandle user) {
    IServiceConnection sd;
    if (conn == null) {
        throw new IllegalArgumentException("connection is null");
    }
    if (mPackageInfo != null) {
        sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
                mMainThread.getHandler(), flags);
    } else {
        throw new RuntimeException("Not supported in system context");
    }
    validateServiceIntent(service);
    try {
        IBinder token = getActivityToken();
        if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
                && mPackageInfo.getApplicationInfo().targetSdkVersion
                < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            flags |= BIND_WAIVE_PRIORITY;
        }
        service.prepareToLeaveProcess();
        int res = ActivityManagerNative.getDefault().bindService(
            mMainThread.getApplicationThread(), getActivityToken(), service,
            service.resolveTypeIfNeeded(getContentResolver()),
            sd, flags, getOpPackageName(), user.getIdentifier());
        if (res < 0) {
            throw new SecurityException(
                    "Not allowed to bind to service " + service);
        }
        return res != 0;
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
}

我們這裏看到,最重要的一句還是上面第十行的位置,跑去調用AMS的bindService了。
另外這裏有點要補充的內容就是,我們的這個綁定可能是跨進程的情況啊,所以我們看到上面,他把我們的
ServiceConnection被再次打包了一下,弄成了IServiceConnection,顯然這個是一個Binder,和我們的廣播類似啊,被打包成了IIntentReceiver,從而達到能夠跨進程的效果。這個IServiceConnection也是在LoadedApk裏面啊,保存着一個map,存儲兩者的關係,這裏就不細細去看了,繼續看我們的主線吧。

public int bindService(IApplicationThread caller, IBinder token, Intent service,
        String resolvedType, IServiceConnection connection, int flags, String callingPackage,
        int userId) throws TransactionTooLargeException {
    enforceNotIsolatedCaller("bindService");

    // Refuse possible leaked file descriptors
    if (service != null && service.hasFileDescriptors() == true) {
        throw new IllegalArgumentException("File descriptors passed in Intent");
    }

    if (callingPackage == null) {
        throw new IllegalArgumentException("callingPackage cannot be null");
    }

    synchronized(this) {
        return mServices.bindServiceLocked(caller, token, service,
                resolvedType, connection, flags, callingPackage, userId);
    }
}

我們看到結尾這裏,繼續調用了ActiveServices的bindServiceLocked()。想說的是,這個ActivityService是負責給AMS打工,管理Service的啓動,綁定,解綁和停止等工作的,終於想說這個AMS裏面有點像組合模型的地方,把一些業務分割出去了。應該在分割出去點的,雖然很多都是判斷和處理語句,太龐大了,整個AMS類

我們繼續看下那個bindServiceLock,他和我們啓動任務的時候類似,去調用了bringUpServiceLocked,調用完後就去realStartServiceLocked,後面流程類似,就不重複,唯一看完全部,不一樣的地方當然是我們是綁定啊,要返回連接的啊!
RealStartServiceLock裏面,他繼續去執行了函數結尾處的下面的函數

requestServiceBindingsLocked(r, execInFg);
updateServiceClientActivitiesLocked(app, null, true);

private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
        boolean execInFg, boolean rebind) throws TransactionTooLargeException {

      ...

    if ((!i.requested || rebind) && i.apps.size() > 0) {

        bumpServiceExecutingLocked(r, execInFg, "bind");
        r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
        r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                r.app.repProcState);
        if (!rebind) {
            i.requested = true;
        }
        i.hasBound = true;
        i.doRebind = false;

    }
    ...
    return true;
}

這樣我們就看到他去調用多了ApplicationThread裏面的scheduleBindService去綁定多服務!

public final void scheduleBindService(IBinder token, Intent intent,
            boolean rebind, int processState) {
        updateProcessState(processState, false);
        BindServiceData s = new BindServiceData();
        s.token = token;
        s.intent = intent;
        s.rebind = rebind;

        if (DEBUG_SERVICE)
            Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
                    + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
        sendMessage(H.BIND_SERVICE, s);
    }

通過這裏去綁定我們的服務

private void handleBindService(BindServiceData data) {
    Service s = mServices.get(data.token);

    if (s != null) {            
         data.intent.setExtrasClassLoader(s.getClassLoader());
         data.intent.prepareToEnterProcess();
         try {
             if (!data.rebind) {
                 IBinder binder = s.onBind(data.intent);
                 ActivityManagerNative.getDefault().publishService(
                         data.token, data.intent, binder);
             } else {
                 s.onRebind(data.intent);
                 ActivityManagerNative.getDefault().serviceDoneExecuting(
                         data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
             }
             ensureJitEnabled();
         } catch (RemoteException ex) {
         }         

    }
}

在這裏我們看到,對於多次綁定,他調用了我們服務的onRebind函數!而對於第一次綁定,我們是調用了我們的服務的onBind函數,這和我們以前的開發經驗也很對,不過好像缺少了跑到回調去的步驟啊,
看下去,嗯,應該是用AMS的publishService去做的,讓我們探索下去吧!。

public void publishService(IBinder token, Intent intent, IBinder service) {
    // Refuse possible leaked file descriptors
    if (intent != null && intent.hasFileDescriptors() == true) {
        throw new IllegalArgumentException("File descriptors passed in Intent");
    }

    synchronized(this) {
        if (!(token instanceof ServiceRecord)) {
            throw new IllegalArgumentException("Invalid service token");
        }
        mServices.publishServiceLocked((ServiceRecord)token, intent, service);
    }
}

你看,我們的小助手mService又出現了,真的是管理Service的好助手啊

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
    final long origId = Binder.clearCallingIdentity();   
        if (r != null) {
            Intent.FilterComparison filter
                    = new Intent.FilterComparison(intent);
            IntentBindRecord b = r.bindings.get(filter);
            if (b != null && !b.received) { 
                for (int conni=r.connections.size()-1; conni>=0; conni--) {
                    ArrayList<ConnectionRecord> clist = 
                    r.connections.valueAt(conni);
                    for (int i=0; i<clist.size(); i++) {
                        ConnectionRecord c = clist.get(i);                            
                        ...                                                                                     
                        c.conn.connected(r.name, service);                             
                    }
                }
            }
           serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
       }  
 }

 看到這裏,需要解釋下,還記得前面說,我們的ServiceConnection是被打包起來,弄成了IServiceConnection,纔來達到跨進程的效果的,這裏的c.conn就是原來那個iServiceConnection。
 很顯然他的這個connected方法就是回調,像前面說的廣播一樣,再在裏面去執行我們的方法的回調。

private static class InnerConnection extends IServiceConnection.Stub {
        final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

        InnerConnection(LoadedApk.ServiceDispatcher sd) {
            mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
        }

        public void connected(ComponentName name, IBinder service) throws RemoteException {
            LoadedApk.ServiceDispatcher sd = mDispatcher.get();
            if (sd != null) {
                sd.connected(name, service);
            }
        }
    }

這個我們看到跑到了sd.connected();不小心看成了連接sb.

public void connected(ComponentName name, IBinder service) {
        if (mActivityThread != null) {
            mActivityThread.post(new RunConnection(name, service, 0));
        } else {
            doConnected(name, service);
        }
    }

好了,這裏有看到一些熟悉的內容啦!!!
這個坑爹的mActivyThread!!實際上就是那個“H”先生!!,這裏就不解釋了!通過他來運行回主線程!既然是調用它的post方法去執行,繼續看下那個RunConnection裏面的內容吧

public void run() {
  if (mCommand == 0) {
           doConnected(mName, mService);
       } else if (mCommand == 1) {
           doDeath(mName, mService);
       }
}

好了,我們去看下doConencted的內容把

public void doConnected(ComponentName name, IBinder service) {
           ...
         if (service != null) {
            mConnection.onServiceConnected(name, service);
        }
}

到這裏我們就回調到我們綁定的時候的的回調接口去。
好了,貼了這麼多代碼,終於進入尾聲了,我們的整個綁定過程!
 
 


後記

偷下懶,別的過程就先不寫了。其餘的過程基本最後也是跑回到那個H先生那裏去執行下的,沒什麼大的問題的。
另外,說到Service,都會想到他的朋友IntentService,爲何它可以執行一些耗時的操作呢?
這部分沒寫下來。

這樣就剩下最後一個金剛沒寫他的故事啦,今晚抽空寫下,就算對四大金剛有一個進一步的瞭解了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章