写本文主要是为了深入了解进程间的Binder通讯,在《Android 入门 -应用启动分析》一文中提到,启动一个应用是当前应用进程通过Binder向ActivityManagerService服务请求操作,从而完成进程到最终的Activity的启动,严格说来,那篇文章不能算入门文章,因为写这篇文章时,我甚至连服务是什么都不了解。本文将通过一个实例来简单说明如何在Android中使用Service。
1. UpperService服务实例
如果要创建一个新的Service,需要以下步骤:
- 新建aidl文件,会生成一个基于IBinder服务存根的抽象类;
- 写服务的实现类,基于Service,并实现onBind函数和存根抽象类;
- 在AndroidManifset.xml中注册该服务;
本例将实现一个转换为大写的服务程序,并在客户端来调用它。
1.1 创建aidl文件
在创建aidl文件之前,假定你已经创建了一个Android ApplicationProject的工程,包名为:com.example.upperservice。新建一个文本文件,贴入以下内容:
package com.example.upperservice;
interface IUpperService {
String upper(in String toName);
}
将该文件保存到com.example.upperservice包下,取名为IUpperService.aidl。注意,文件名必须是IUpperService,与interface名称保持一致,可见,aidl文件其实是一个接口定义文件,语法和javainterface基本一致,所以,是很容易编写的。
IUpperServer接口中定义了一个upper方法,此方法完成将toName转换为大写的功能。
文件保存后,buildproject,将在gen目录自动生成IUpperService.java文件。下面是IUpperService.java的代码:
package com.example.upperservice;
public interface IUpperService extends android.os.IInterface {
/** 本地(服务端) IPC 实现存根,本类是抽象类,需要实现upper抽象方法,当然,你的aidl接口文件定义了多少个操作,就要实现多少个. */
public static abstract class Stub extends android.os.Binder implements com.example.upperservice.IUpperService {
private static final java.lang.String DESCRIPTOR = "com.example.upperservice.IUpperService";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR); //将存根连接到interface
}
/**
* 生成一个本地的调用代理,如果将IUpperService打包给第三方,可以调用此函数,直接call服务.
*/
public static com.example.upperservice.IUpperService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.upperservice.IUpperService))) {
return ((com.example.upperservice.IUpperService) iin);
}
return new com.example.upperservice.IUpperService.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_upper: {//服务端处理upper调用
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.upper(_arg0);//转至upper抽象方法,要在Service类中实现此方法,完成真正的upper处理
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
// 本地代理类,私有的,asInerface就是返回的这个类实例,供外部调用。我们也可以直接把这段代码拷贝到客户端,也可以直接调用远程的服务。
private static class Proxy implements com.example.upperservice.IUpperService {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.lang.String upper(java.lang.String toName) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(toName);
mRemote.transact(Stub.TRANSACTION_upper, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_upper = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
//引出的抽象方法,派生类实现
public java.lang.String upper(java.lang.String toName) throws android.os.RemoteException;
}
上面的代码做了注解,这个自动生成的代码,其实是生成了客户调用和客户实现之间的代码,用这个文件既可以写本地服务的实现,也可以写客户端的调用代码。
1.2 创建Service类
新建一个com.example.upperservice.UpperService.java,代码如下:
public class UpperService extends Service {
public static final String TAG = "UPPER_SERVICE";
final IUpperService.Stub binder = new IUpperService.Stub() {
@Override
public String upper(String toName) throws RemoteException {
return toName == null ? null : toName.toUpperCase();
}
};
@Override
public IBinder onBind(Intent arg0) {
Log.i(TAG, "onBind");
return binder;
}
}
上面的代码很简单,先是生成一个基于IUpperService.Stub的实例,并实现其抽象方法upper,然后,实现Service.onBind函数,返回此binder。服务端代码就完成了。
1.3 注册Service
在AndroidManifset.xml中注册此服务。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.upperservice"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.upperservice.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".UpperService" >
<intent-filter>
<action android:name="com.example.upperservice.UpperService" />
</intent-filter>
</service>
</application>
</manifest>
见service的定义段。
到此为止,服务端全部搞定。写一个Android服务真的是非常简单。
2 客户端调用实例
上面的IUpperService.java代码的注解中,已经说明了客户端如何调用。客户端调用服务端的步骤如下:
- 创建一个服务调用代理Proxy。
- 创建基于ServiceConnection的连接;
- 绑定服务,并在ServiceConnection.onServiceConnected,获取IBinder的服务调用接口,实例化Proxy;
- 调用Proxy中的方法调用远程服务;
2.1 创建服务调用代理
创建一个UpperServiceProxy类,代码如下:
public class UpperServiceProxy {
static final int TRANSACTION_upper = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
private static final java.lang.String DESCRIPTOR = "com.example.upperservice.IUpperService";
android.os.IBinder remote;
public UpperServiceProxy(android.os.IBinder remote) {
this.remote = remote;
}
public String upper(String v) throws RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(v);
remote.transact(TRANSACTION_upper, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
上面的代码,其实是拷贝IUpperService.java中的Proxy的部分代码,因为我在另一应用中调用,不想麻烦引出IUpperService.java类,这样写我们更能知道调用的细节。upper其实就是一个transact的Binder调用,只不过在调用之前按Binder的规范序列化数据,调用之后,再按Binder规范读取处理的返回数据。
2.2 创建服务连接
本实例是放在MainActivity中的一个变量,代码如下:UpperServiceProxy proxy;
ServiceConnection con = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
proxy = new UpperServiceProxy(service);
}
};
当服务连接之后,创建proxy变量,此后,可以通过proxy调用服务。
2.3 绑定服务
在MainActivity.onCreate中,加入如个代码:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
bindService(new Intent("com.example.upperservice.UpperService"), con, Context.BIND_AUTO_CREATE);
}
通过bindService方法绑定服务,需要指定服务的action名称,当服务绑定完成后,会触发调用con.onServiceConnected函数,从而设置proxy。
2.4 调用服务
调用服务就很简单了,直接调用proxy中的方法就行了。如:
Button button = (Button) rootView.findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
if(proxy!=null) System.out.println(proxy.upper("Hello World!"));
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
3. 升级UpperService为系统服务
Android系统中有许多的系统服务,如:ActivityManagerSerivce,PowerManagerService等,这些会在系统启动时就加载,并且调用方式有小小差别。现在说明如何建立一个UpperService的系统服务。3.1 IUpperService.aidl
将此文件放入到andorid-source/frameworks/base/core/java/android/os/目录下,如:package android.os;
interface IUpperService {
String upper(in String toName);
}
4.2 修改frameworks/base/Android.mk
在此文件下加入代码如下:# FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk
LOCAL_SRC_FILES := $(call find-other-java-files,$(FRAMEWORKS_BASE_SUBDIRS))
# EventLogTags files.
LOCAL_SRC_FILES += \
core/java/android/content/EventLogTags.logtags \
core/java/android/speech/tts/EventLogTags.logtags \
core/java/android/webkit/EventLogTags.logtags \
## READ ME: ########################################################
##
## When updating this list of aidl files, consider if that aidl is
## part of the SDK API. If it is, also add it to the list below that
## is preprocessed and distributed with the SDK. This list should
## not contain any aidl files for parcelables, but the one below should
## if you intend for 3rd parties to be able to send those objects
## across process boundaries.
##
## READ ME: ########################################################
LOCAL_SRC_FILES += \
core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl \
core/java/android/accounts/IAccountManager.aidl \
...
LOCAL_SRC_FILES += core/java/android/os/IUpperService.aidl
# FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
LOCAL_SRC_FILES += core/java/android/os/IUpperService.aidl为新添加的代码
3.3 编写服务类
framework\base\services\java\com\android\server\UpperService.java,代码如下:package com.android.server;
import android.content.Context;
public class UpperService extends IUpperService.Stub {
private final Context mContext;
public UpperService(Context context){
mContext = context;
}
@Override
public String upper(String toName) {
return toName == null ? null : toName.toUpperCase();
}
public void systemReady() {
}
}
3.4 服务注册
将服务注册到SystemServer中,这样可以在系统启动时启动服务了。通过修改SystemServer.java实现。frameworks/base/services/java/com/android/server/SystemServer.java
class ServerThread {
...
public void initAndLoop() {
...
try{
// 添加代码开始
Slog.i(TAG, "UPPER SERVICE");
ServiceManager.addService("upper",new com.android.server.UpperService(context));
// 添加代码结束
Slog.i(TAG, "Display Manager");
display = new DisplayManagerService(context, wmHandler);
ServiceManager.addService(Context.DISPLAY_SERVICE, display, true);
...
} catch (RuntimeException e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service", e);
}
...
}
...
}
3.5 编译
# cd /Volumes/android/source
# . build/envsetup.sh
# lunch
# make update-api
# make -j16
# make -j16
make update-api是由于系统的API没有更新,需要先更新,才能make。
3.6 客户端调用
系统服务都是通过ServiceManager.getService()来获取IBinder的,但在Android应用中,不知道有什么办法访问到ServiceManager. 我又不想再写一个类放到Android源码中引出,再重新编译,所以,只好采取反射的方法来获取IBinder。编写调用代理类如下:public class UpperServiceProxy {
static final int TRANSACTION_upper = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
private static final java.lang.String DESCRIPTOR = "android.os.IUpperService";
android.os.IBinder remote;
public UpperServiceProxy() {
try {
// 采用反射的方法调用ServiceManager.getService("upper"),获取IBinder接口
Method method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);
this.remote = (android.os.IBinder) method.invoke(null, "upper");
} catch (Throwable e) {
e.printStackTrace();
}
}
public String upper(String v) throws RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(v);
remote.transact(TRANSACTION_upper, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
从上面的代码可以看出,Android无论是系统级服务还是用户级服务,采用的机制都是一样的,只不过系统级服务可以在启动时加载,调用时直接使用ServiceManager.getService()来获取IBinder接口,而用户级服务则需要先绑定服务接口。