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()的过程已经讲完了,虽然分析得还有点粗糙,今后会慢慢研究慢慢体会了~~

     


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
bindService是Android中一种组件之间进行通信的方式,它可以在两个组件之间建立一种长期的连接,使得它们可以进行交互。下面是bindService的流程详解: 1. 调用bindService方法:在客户端组件中调用bindService方法,该方法接受两个参数,第一个参数是Intent对象,用于指定要绑定的服务组件,第二个参数是ServiceConnection对象,用于指定连接建立和断开时的回调。 2. 系统查找并启动服务组件:系统会查找并启动指定的服务组件,如果服务还没有启动,则将先启动服务。 3. 创建服务组件:系统会创建服务组件,并在其中调用onCreate方法进行初始化。 4. 调用ServiceConnection的onServiceConnected方法:当服务组件创建完成后,系统会调用ServiceConnection的onServiceConnected方法,通知客户端组件连接已建立,并将IBinder对象传递给客户端组件。 5. 获取IBinder对象:客户端组件可以通过ServiceConnection的onServiceConnected方法中传递的IBinder对象与服务组件进行交互。 6. 交互:客户端组件可以通过IBinder对象调用服务组件中的方法,完成交互。 7. 断开连接:当客户端组件不再需要服务组件时,可以调用unbindService方法断开连接,系统会调用ServiceConnection的onServiceDisconnected方法通知客户端组件连接已断开。 8. 销毁服务组件:如果没有任何客户端组件绑定服务组件,系统会调用服务组件的onDestroy方法进行销毁。 总体来说,bindService的流程就是客户端组件通过Intent对象指定要绑定的服务组件,系统查找并启动服务组件,客户端组件通过IBinder对象与服务组件进行交互,当客户端组件不再需要服务组件时,调用unbindService方法断开连接,系统会调用服务组件的onDestroy方法进行销毁。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值