死磕Android_Service綁定流程分析(二)

通過startService只能是把Service給啓動起來,但是我們無法與其建立聯繫.通過bindService方式啓動Service的話,不僅能啓動Service,還能與其建立連接,相互調用比較方便.今天我們來理一理bindService其中的原理.

建議先看一下如下兩篇文章,我按照順序來寫的,循序漸進.可能有些東西前面已經介紹了,後面就不再贅述,感謝理解.

1. 使用方式

簡單回顧一下使用方式,就是在Activity裏面調一下bindService方法,需要傳入一個ServiceConnection.

Intent intentService = new Intent(this,MyService.class);
bindService(intentService,mServiceConnection,BIND_AUTO_CREATE);

2. 源碼分析

和startService類似,bindService也是調用的ContextImpl裏面的方法.

@Override
public boolean bindService(Intent service, ServiceConnection conn,
        int flags) {
    ...
    return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), getUser());
}

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
        handler, UserHandle user) {
    
    IServiceConnection sd;
    
    //注意  ServiceConnection不能傳入null,否則直接拋個異常給你
    if (conn == null) {
        throw new IllegalArgumentException("connection is null");
    }
    if (mPackageInfo != null) {
        //會走到這裏來  生成一個IServiceConnection,它其實是一個ServiceDispatcher.InnerConnection,用來與Service建立連接的,與Service建立連接時可能會需要遠程調用,那麼ServiceConnection是不得行的.就需要ServiceDispatcher.InnerConnection來遠程調用,然後再通知ServiceConnection.
        sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
    } else {
        throw new RuntimeException("Not supported in system context");
    }
    
    validateServiceIntent(service);
    try {
        //獲取當前Activity的token
        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(this);
        
        //好巧  又是我們所熟悉的AMS bindService也需要AMS參與
        int res = ActivityManager.getService().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 e.rethrowFromSystemServer();
    }
}

bindService是ContextImpl裏面的方法,最終會調用到bindServiceCommon方法.bindServiceCommon方法首先將ServiceConnection轉換成了一個ServiceDispatcher.InnerConnection(也就是上面的IServiceConnection sd).我們知道,綁定Service可能是跨進程的,所以需要跨進程通信,這裏使用的是Binder方式.而這裏的IServiceConnection其實是一個ServiceDispatcher.InnerConnection,而這裏的ServiceDispatcher.InnerConnection是拿來跨進程通信的,因爲ServiceConnection不能直接進行跨進程通信,所以需要ServiceDispatcher.InnerConnection來充當Binder的角色.然後ServiceDispatcher是連接InnerConnection和ServiceConnection的,InnerConnection是ServiceDispatcher的一個內部類.

上面的mPackageInfo是LoadedApk,跟進去看看getServiceDispatcher方法

public final IServiceConnection getServiceDispatcher(ServiceConnection c,
        Context context, Handler handler, int flags) {
    synchronized (mServices) {
        //ServiceDispatcher是LoadedApk的內部類
        LoadedApk.ServiceDispatcher sd = null;
        
        //mServices是一個map,定義是private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
        = new ArrayMap<>();
        //它裏面存放的是當前Activity與ServiceConnection和ServiceDispatcher的映射關係
        //如果之前有創建好的ServiceDispatcher,那麼直接拿出來用,沒有則創建一個
        ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
        if (map != null) {
            sd = map.get(c);
        }
        if (sd == null) {
            //創建一個ServiceDispatcher
            sd = new ServiceDispatcher(c, context, handler, flags);
            if (map == null) {
                map = new ArrayMap<>();
                //放入mServices進行緩存起來
                mServices.put(context, map);
            }
            map.put(c, sd);
        } else {
            sd.validate(context, handler);
        }
        
        //獲取ServiceDispatcher中的InnerConnection對象
        return sd.getIServiceConnection();
    }
}

首先ServiceDispatcher是LoadedApk的內部類.系統將當前Activity與ServiceConnection和ServiceDispatcher的映射關係緩存起來了的,有需要的時候直接拿出來.當前第一次綁定的時候,肯定緩存起來沒有,所以需要創建一個ServiceDispatcher.創建ServiceDispatcher的時候就會創建InnerConnection. 相當於ServiceDispatcher內部有ServiceConnection和InnerConnection,那麼後面需要調用ServiceConnection裏面的方法就比較方便了.

接着我們繼續看AMS的綁定Service的過程,是調用的是AMS的bindService方法

public int bindService(IApplicationThread caller, IBinder token, Intent service,
        String resolvedType, IServiceConnection connection, int flags, String callingPackage,
        int userId) throws TransactionTooLargeException {
    return mServices.bindServiceLocked(caller, token, service,
            resolvedType, connection, flags, callingPackage, userId);
}

mServices是ActiveServices,在startService源碼分析中提到過,ActiveServices類是輔助AMS管理Service的,包括Service的啓動、綁定和停止等.

上面是調用了ActiveServices的bindServiceLocked方法,bindServiceLocked再調
用bringUpServiceLocked,bringUpServiceLocked又會調用realStartServiceLocked方法,
realStartServiceLocked方法的執行邏輯和"死磕Android_Service啓動流程分析"中的邏輯類似,最終都是通過
ApplicationThread來完成Service實例的創建並執行其onCreate方法,這裏不再重複講解了.

但是需要注意的是在realStartServiceLocked方法裏面,當我們是bindService綁定Service的時候,需要關注一個東西

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

    boolean created = false;
    
    //啓動Service
    app.thread.scheduleCreateService(r, r.serviceInfo,
            mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
            app.repProcState);
    created = true;
    
    //深入
    requestServiceBindingsLocked(r, execInFg);
    ......
}

private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)
        throws TransactionTooLargeException {
    //深入
    requestServiceBindingLocked(r, ibr, execInFg, false);
}

private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
        boolean execInFg, boolean rebind) throws TransactionTooLargeException {
    
    //遠程調用,執行ActivityThread中的scheduleBindService方法
    r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
            r.app.repProcState);
    return true;
}

在realStartServiceLocked方法裏面調用了requestServiceBindingsLocked方法,requestServiceBindingsLocked方法調用了另一個requestServiceBindingLocked方法,然後遠程調用ActivityThread的scheduleBindService方法.scheduleBindService方法從名字看,終於要開始執行綁定了

public final void scheduleBindService(IBinder token, Intent intent,
        boolean rebind, int processState) {
    updateProcessState(processState, false);
    BindServiceData s = new BindServiceData();
    //注意  這裏將Activity的token傳過來了
    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);
}

然後又來到了我們熟悉的H這個Handler,在H中BIND_SERVICE消息調用的是ActivityThread中的handleBindService()這個方法

private void handleBindService(BindServiceData data) {
    //在handleCreateService方法裏面將token和Service映射存入了mServices裏面
    //mServices定義是final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
    //所以這裏可以根據token取出Service
    Service s = mServices.get(data.token);
    if (s != null) {
        data.intent.setExtrasClassLoader(s.getClassLoader());
        data.intent.prepareToEnterProcess();
        
        if (!data.rebind) {
            //如果不是重新綁定
            
            //注意,這裏調用了我們熟悉的Service的onBind方法,,,,就是在這裏調用的哦,,,所以這裏是UI線程哈,記住了
            IBinder binder = s.onBind(data.intent);
            //調用AMS的publishService方法
            ActivityManager.getService().publishService(
                    data.token, data.intent, binder);
        } else {
            s.onRebind(data.intent);
            ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        }
    }
}

在handleCreateService方法裏面將token和Service映射存入了mServices裏面,mServices定義是final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();.所以那裏可以根據token取出Service,然後緊接着調用了我們熟悉的Service的onBind方法,這裏可以得出onBind方法是在UI線程執行的.還可以得出,重新綁定Service時,onBind方法只會調用一次,除非Service之前被終止了.onBind方法執行之後,說明Service已經成功連接了.

然後又來到了AMS,這次是AMS的publishService方法.

public void publishService(IBinder token, Intent intent, IBinder service) {
    mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}

//ActiveServices.java => publishServiceLocked
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
    try {
        if (r != null) {
            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, false);
                }
            }

            serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
        }
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
}

c.conn的類型是ServiceDispatcher.InnerConnection,service就是Service
的onBind方法返回的Binder對象.來看一下ServiceDispatcher.InnerConnection的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, boolean dead)
            throws RemoteException {
        LoadedApk.ServiceDispatcher sd = mDispatcher.get();
        if (sd != null) {
            sd.connected(name, service, dead);
        }
    }
}

InnerConnection的構造方法裏面就傳入了ServiceDispatcher,所以可以很輕鬆拿到ServiceDispatcher,拿到ServiceDispatcher調用其connected方法

public void connected(ComponentName name, IBinder service, boolean dead) {
    //mActivityThread其實就是ActivityThread中名爲H的Handler
    if (mActivityThread != null) {
        //主線程中的Handler調用post  說明RunConnection是運行在主線程中
        mActivityThread.post(new RunConnection(name, service, 0, dead));
    } else {
        doConnected(name, service, dead);
    }
}

private final class RunConnection implements Runnable {
    RunConnection(ComponentName name, IBinder service, int command, boolean dead) {
        mName = name;
        mService = service;
        mCommand = command;
        mDead = dead;
    }

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

    final ComponentName mName;
    final IBinder mService;
    final int mCommand;
    final boolean mDead;
}

//ServiceDispatcher => doConnected
public void doConnected(ComponentName name, IBinder service, boolean dead) {
    ...
    //mConnection是ServiceDispatcher中的ServiceConnection,初始化ServiceDispatcher的時候就初始化了ServiceConnection
    mConnection.onServiceConnected(name, service);
    ...
}

通過ActivityThread中的Handler#post執行一個doConnected方法,而doConnected方法裏面就是通過我們熟悉的ServiceConnection對象進行了onServiceConnected方法的回調.

可以看到ServiceDispatcher做了一個轉接.當Service連接上之後,通過InnerConnection去遠程調用ServiceDispatcher中的ServiceConnection中的onServiceConnected方法,完成綁定成功消息的通知.讓客戶端知道Service已綁定.

bindService也就分析完成了,其他比如
Service的停止過程和解除綁定的過程,系統的執行過程是類似的,這裏留給大家自己去分析咯.

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