BindService詳解

       接着上一篇,本文就解決《篇Binder詳解》末尾拋出的問題,也就是如下的問題:

        我們客戶端(即MainActivity)接受遠程對象是在自己重寫的ServiceConnection的onServiceConnected()方法中接收的,那麼系統是何時對ServiceConnection的onServiceConnect()方法進行回調的呢?這一切要從我們調用bindService()說起。

bindService()的發源地

      我們知道,在Activity要啓動Service有兩種方式,一種就是startService(),還有一種就是bindService(),兩者的使用場景不一樣,啓動的參數也不同,前者是當Service和當前組件在同一個進程時使用,後者是當Service和當前組件不在同一個進程時使用,而且bindService()還需要傳遞一個ServiceConnection實例。雖然我們在Activity中可以直接使用bindService(),但是bindService()並不是Activity的方法,而是Context的方法,我們都知道,四大組件的超類都是Context,只不過各組件的繼承鏈不一樣,比如Activity的繼承鏈是:Activity→ContextThemeWrapper→ContextWrapper→Context。此外Context中的bindService()只是一個abstract方法,真正的實現是在ContextImpl中,而且ContextWrap也把bindService()給包裝了一下,包裝的目的就是去調用ContextImpl中真正實現了的bindService(),所以我們在Activity中調用bindService()的時候最後實際上是調用了ContextImpl的bindService()。但是在Activity的繼承鏈中我們似乎並沒有發現ContextImpl的影子?確實,Activity並沒有直接繼承ContextImpl,ContextImpl是一個隱藏的類。那麼Activity是如何去使用到ContextImpl的bindService的()?我們可以看到在ContextWrap中有一個變量叫做mBase,此mBase就是一個Context實例,確切說是一個ContextImpl實例,而且ContextWrapper的bindService()就是一行代碼:mBase.bindService()來實現調用ContextImpl的bindService()的。那麼mBase他是在什麼時候被賦值的?那就是在Activity被創建時調用其attach()的時候傳遞過來的,當ActivityThread在創建Activity的時候就會調用ContextImpl的createActivityContext()方法new一個ContextImpl實例,並調用Activity的atatch()方法爲mBase賦值。說了這麼多,其實就是想說兩點,第一bindService()是ContextImpl中的方法,第二既然Activity沒有直接實現ContextImpl,那爲什麼我們可以直接調用bindService()。

bindService()所做的一切

       現在我們開始對bindService()進行分析,通過上面的介紹,那我們的分析自然是從ContextImpl的bindService()開始了,上代碼:

	@Override
	public boolean bindService(Intent service, ServiceConnection conn, int flags) {
		//這個方法僅僅是打印日誌用於警告,忽略
		warnIfCallingFromSystemProcess();
		//調用了bindServiceCommon()看下面
		return bindServiceCommon(service, conn, flags, Process.myUserHandle());
	}
	/**
	 *  啓動遠程Service
	 * @param service  Intent指定要啓動的Service
	 * @param conn      ServiceConnection,用於Service連接後就通過這個對象來回調其方法返回遠程的Binder對象
	 * @param flags
	 * @param user
	 * @return
	 */
	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) {
			//mPackageInfo是一個LoadApk實例,此方法用於返回一個IServiceConnection實例也就是InnerConnection類型的對象
			sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
					mMainThread.getHandler(), flags);
		} else {
			throw new RuntimeException("Not supported in system context");
		}
		validateServiceIntent(service);
		try {
			//獲取令牌,表示要bindService的身份,這裏就是用來表明Activity的身份
			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();
			//AMS調用bindService()
			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);
		}
	}

       可以看到在bindServiceCommon()方法中,會首先通過mPackageInfo.getServiceDispatcher()獲取一個IServiceConnection實例,這個mPackageInfo是一個LoadApk實例,我們去看看getServiceDispatcher()是個什麼方法:

   

    /**
     * 獲取一個IServiceConnection實例,
     * @param c
     * @param context
     * @param handler
     * @param flags
     * @return
     */
    public final IServiceConnection getServiceDispatcher(ServiceConnection c,
            Context context, Handler handler, int flags) {
        synchronized (mServices) {
            LoadedApk.ServiceDispatcher sd = null;
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
            if (map != null) {
                sd = map.get(c);
            }
            if (sd == null) {
            	//ServiceDispatcher是LoadedApk的內部類,在此方法中就會創建一個InnerConnection實例
                sd = new ServiceDispatcher(c, context, handler, flags);
                //如果本地還沒有此鏈接就創建新的連接並保存到數組
                if (map == null) {
                    map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                    mServices.put(context, map);
                }
                map.put(c, sd);
            } else {
                sd.validate(context, handler);
            }
            //返回InnerConnection實例mIServiceConnection
            return sd.getIServiceConnection();
        }
    }
       首先會從本地map中讀取是否已經存在ServiceDispatcher實例,如果沒有就會去創建一個,注意ServiceDispatcher是LoadedApk的內部類,而ServiceDispatcher裏還有一個內部類InnerConnection,這個InnerConnection是很重要的,它就是繼承了IServiceConnection.Stub的類。在ServiceDispatcher的構造方法中就會根據我們bindService()時傳入的ServiceConnection實例創建InnerConnection,最後在方法的結尾,調用ServiceDispatcher實例返回一個IServiceConnection實例。這裏再強調一下,IServiceConnection就相當於上一篇文章的CustomBinder,而InnerConnection相當於TestService的MyBinder!我們可以看到IServiceConnection的結構和CustomBinder的結構一模樣的。而且在IServiceConnection.Stub中有一個抽象的connected()方法,此方法就是在InnerConnection中實現的,而InnerConnection中的connected()方法最終會調用到我們在調用bindService()時傳入的ServiceConnection的onServiceConnected()方法,從而告知客戶端服務已連接,並且返回一個遠程對象。那麼InnerConnection的connected()方法何時被調用的呢?

       上面講了一大堆獲取了IServiceConnection的過程,我們回到ContextImpl的bindServiceCommon()方法中去,在獲取IServiceConnection實例後,就又通過跨進程通信讓ActivityManagerService調用bindService()方法,也就是通過下面一段代碼:

int res = ActivityManagerNative.getDefault().bindService(
					mMainThread.getApplicationThread(), getActivityToken(),
					service, service.resolveTypeIfNeeded(getContentResolver()),
					sd, flags, getOpPackageName(), user.getIdentifier());
      關於他是如何跨進程通信到ActivityManagerService的,請看另一篇文章http://blog.csdn.net/u012481172/article/details/49658633; 好,接下來進入到AMS的bindService(),看下AMS的bindService()方法如下:

 

   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) {
        	//這個mServices是一個ActivityService實例
            return mServices.bindServiceLocked(caller, token, service,
                    resolvedType, connection, flags, callingPackage, userId);
        }
    }

      忽略掉不重要的信息,直接看方法的最後一行,發現是調用了ActivityService的bindServiceLocked()方法,好我們去看看這個方法:
  int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags,
            String callingPackage, int userId) throws TransactionTooLargeException {
        //根據調用者來獲取正在運行的當前app的所有進程信息
        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
        //如果沒有找到進程記錄就說明進程跪了,拋出異常
        if (callerApp == null) {
            throw new SecurityException(
                    "Unable to find app for caller " + caller
                    + " (pid=" + Binder.getCallingPid()
                    + ") when binding service " + service);
        }

        ActivityRecord activity = null;
        if (token != null) {
        	//根據token獲取ActivityRecord,也就是獲取調用bindService()的那個Activity
        	//這一步就是從任務棧中去查找Activity
            activity = ActivityRecord.isInStackLocked(token);
            //如果沒有找到就會綁定失敗
            if (activity == null) {
                return 0;
            }
        }

        int clientLabel = 0;
        PendingIntent clientIntent = null;
        final boolean callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
        //這裏根據Intent獲取Service
        ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType, callingPackage,
                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
        ServiceRecord s = res.record;
        final long origId = Binder.clearCallingIdentity();
        try {
            if (unscheduleServiceRestartLocked(s, callerApp.info.uid, false)) {
            }

            if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                s.lastActivity = SystemClock.uptimeMillis();
            }

            mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
                    s.appInfo.uid, s.name, s.processName);

            AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
            //創建一個Connection記錄,Service的連接
            //ConnectionRecord的解釋是:Description of a single binding to a service
            ConnectionRecord c = new ConnectionRecord(b, activity,
                    connection, flags, clientLabel, clientIntent);

            IBinder binder = connection.asBinder();
            ArrayList<ConnectionRecord> clist = s.connections.get(binder);
            if (clist == null) {
                clist = new ArrayList<ConnectionRecord>();
                s.connections.put(binder, clist);
            }
            clist.add(c);
            b.connections.add(c);
            if (activity != null) {
                if (activity.connections == null) {
                    activity.connections = new HashSet<ConnectionRecord>();
                }
                //把ConnectionRecord保存到ActivityRecord中也就是雲行的Activity,
                activity.connections.add(c);
            }

            if (s.app != null && b.intent.received) {
                try {
                	//這裏開始就是調用了InnerConnection的connected()方法,這種調用是跨進程的,即它調用時會首先調用IServiceConnection.Stub.Proxy的connected()方法
                	//這個流程在上一篇文章已經講過了,不再贅述,總之,它最後就調用了我們的MainActivity中定義的MyServiceConnection的onServiceConnected()方法,這個b.intent.binder就是遠程的對象即IServiceConnection對象。
                    c.conn.connected(s.name, b.intent.binder);
                } catch (Exception e) {
                }
            } else if (!b.intent.requested) {
                requestServiceBindingLocked(s, b.intent, callerFg, false);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
        return 1;
    }
      其實這個方法很長,已經省了不少代碼了,可以看到它裏面調用了c.conn.connected(s.name,b.intent.binder),這就是回調了MainActivity中我們定義的MyServiceConnection的onServiceConnected()方法了。至此,整個binderService()的過程已經講完了,雖然分析得還有點粗糙,今後會慢慢研究慢慢體會了~~

     


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