以上手工编写Binder服务端和客户端的过程存在两个重要问题。
第一,客户端如何获得服务端的Binder对象引用。
第二,客户端和服务端必须事先约定好两件事情。
服务端函数的参数在包裹中的顺序。
服务端不同函数的int型标识。
关于第一个问题,请思考为什么要用Binder。答案很简单,即为了提供一个全局服务,所谓的"全局",是指系统中的任何应用程序都可以访问。很明显,这是一个操作系统应该提供的最基本的功能之一,Android的工程师自然也是这么认为的,因此,他们提供了一个更傻瓜的解决方法,那就是Service。它是Android应用程序四个基本程序片段(Component)之一,四个基本片段包括Activity、Service、Content Provier、Receiver。
无论是否使用Service类,都必须要解决以上两个重要问题。因此,下面先介绍如何解决第一个问题。事实上,对于有创造力的程序员来讲,可以完全不使用Service类,而仅仅基于Binder类编写服务程序,但只是一部分。具体来讲,可以仅使用Binder类扩展系统服务,而对于客户端服务则必须基于Service类来编写。所谓的系统服务是指可以使用getSystemService()方法获取的服务,所谓的客户端服务是指应用程序提供的自定义服务。
Service和Binder的关系。
那么,Service类是如何解决本节开头所提出的两个重要问题的呢?
首先,AmS提供了startService()函数用于启动客户服务,而对于客户端来讲,可以使用以下两个函数来和一个服务建立连接,其原型在android.app. ContextImpl类中。
@Override
public ComponentName startService(Intent service) {
try {
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()));
if (cn != null && cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
+ " without permission " + cn.getClassName());
}
return cn;
} catch (RemoteException e) {
return null;
}
}
该函数用于启动intent指定的服务,而启动后,客户端暂时还没有服务端的Binder引用,因此,暂时还不能调用任何服务功能。
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
IServiceConnection sd;
if (mPackageInfo != null) {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
mMainThread.getHandler(), flags);
} else {
throw new RuntimeException("Not supported in system context");
}
try {
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(),
service, service.resolveTypeIfNeeded(getContentResolver()),
sd, flags);
if (res < 0) {
throw new SecurityException(
"Not allowed to bind to service " + service);
}
return res != 0;
} catch (RemoteException e) {
return false;
}
}
该函数用于绑定一个服务,这就是第一个重要问题的关键所在。其中第二个参数是一个interface类,该interface的定义如以下代码所示:
/**
* Interface for monitoring the state of an application service. See
* {@link android.app.Service} and
* {@link Context#bindService Context.bindService()} for more information.
* <p>Like many callbacks from the system, the methods on this class are called
* from the main thread of your process.
*/
public interface ServiceConnection {
/**
* Called when a connection to the Service has been established, with
* the {@link android.os.IBinder} of the communication channel to the
* Service.
*
* @param name The concrete component name of the service that has
* been connected.
*
* @param service The IBinder of the Service's communication channel,
* which you can now make calls on.
*/
public void onServiceConnected(ComponentName name, IBinder service);
/**
* Called when a connection to the Service has been lost. This typically
* happens when the process hosting the service has crashed or been killed.
* This does <em>not</em> remove the ServiceConnection itself -- this
* binding to the service will remain active, and you will receive a call
* to {@link #onServiceConnected} when the Service is next running.
*
* @param name The concrete component name of the service whose
* connection has been lost.
*/
public void onServiceDisconnected(ComponentName name);
}
请注意该interface中的onServiceConnected()方法的第二个变量Service。当客户端请求AmS启动某个Service后,该Service如果正常启动,那么AmS就会远程调用ActivityThread类中的ApplicationThread对象,调用的参数中会包含Service的Binder引用,然后在ApplicationThread中会回调bindService中的conn接口。因此,在客户端中,可以在onServiceConnected()方法中将其参数Service保存为一个全局变量,从而在客户端的任何地方都可以随时调用该远程服务。这就解决了第一个重要问题,即客户端如何获取远程服务的Binder引用。