BindService详解[通俗易懂]

BindService详解[通俗易懂]Service启动得两种方式分别为startService()、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()是个什么方法:

   

此代码由Java架构师必看网-架构君整理
/** * 获取一个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()方法如下:

 

此代码由Java架构师必看网-架构君整理
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()的过程已经讲完了,虽然分析得还有点粗糙,今后会慢慢研究慢慢体会了~~

     

架构君码字不易,如需转载,请注明出处:https://javajgs.com/archives/222653
0
 

发表评论