Android的service可以分为c++层面的和Java层面的。这是一个例子,介绍了如何在c++层面创建service,并且如何在java应用程序中使用这个service. 这个例子很简单,c++层的service就是提供了一个相加和一个相减的功能。java应用里面就是添加了几个控件来调用c++ service的加减功能实现两个数的相加和相减,并且显示出来。
首先来看看c++层的service是如何创建的。先创建一个ICalcService.h的文件,里面定义了一个接口,包含了一个相加的sum函数接口和一个相减的minus的函数接口。
#ifndef _ICALC_SERVICE_H
#define _ICALC_SERVICE_H
#include <binder/IInterface.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <cutils/properties.h>
#include <utils/Log.h>
namespace android {
/**********************************************/
/*! @class ICalcService
@brief Calc Service Proxy Interface class
***********************************************/
class ICalcService : public IInterface {
public:
DECLARE_META_INTERFACE(CalcService);
virtual int32_t sum(int32_t x, int32_t y) = 0;
virtual int32_t minus(int32_t x, int32_t y) = 0;
protected:
enum{
CALC_SUM = IBinder::FIRST_CALL_TRANSACTION,
CALC_MINUS
};
};
}
#endif
再定义一个CalcService.h的头文件
#ifndef _CALC_SERVICE_H
#define _CALC_SERVICE_H
#include <utils/Log.h>
#include "ICalcService.h"
namespace android {
/**********************************************/
/*! @class ICalcService
@brief Calc Service Proxy Interface class
***********************************************/
class BnCalcService : public BnInterface<ICalcService> {
public:
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
};
class CalcService : public BnCalcService {
public:
CalcService();
~CalcService();
virtual int32_t sum(int32_t x, int32_t y);
virtual int32_t minus(int32_t x, int32_t y);
static void instantiate();
};
}
#endif
再定义一个CalcService.cpp的实现文件
#define TAG "CalcService"
#include "CalcService.h"
#include <utils/Log.h>
namespace android {
class BpCalcService : public BpInterface<ICalcService> {
public:
BpCalcService(const sp<IBinder>& impl) : BpInterface<ICalcService>(impl)
{
}
virtual int32_t sum(int32_t x, int32_t y)
{
LOGD("BpCalcService sum.");
Parcel data, reply;
data.writeInterfaceToken(ICalcService::getInterfaceDescriptor());
data.writeInt32(x);
data.writeInt32(y);
remote()->transact(CALC_SUM,data,&reply);
int32_t sumxy = reply.readInt32();
LOGD("sumxy=%d",sumxy);
return sumxy;
}
virtual int32_t minus(int32_t x, int32_t y)
{
LOGD("BpCalcService sum.");
Parcel data, reply;
data.writeInterfaceToken(ICalcService::getInterfaceDescriptor());
data.writeInt32(x);
data.writeInt32(y);
remote()->transact(CALC_MINUS,data,&reply);
int32_t mxy = reply.readInt32();
LOGD("minuxsy=%d",mxy);
return mxy;
}
};
IMPLEMENT_META_INTERFACE(CalcService,"com.test.ICalcService");
status_t BnCalcService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
LOGD("onTransact received request.");
reply->writeInt32(0);
switch(code)
{
case CALC_SUM:
{
LOGD("calc sum");
CHECK_INTERFACE(ICalcService,data,reply);
int32_t x = data.readInt32();
int32_t y = data.readInt32();
int32_t sumxy = sum(x,y);
reply->writeInt32(sumxy);
return NO_ERROR;
}
case CALC_MINUS:
{
LOGD("calc minus");
CHECK_INTERFACE(ICalcService,data,reply);
int32_t x = data.readInt32();
int32_t y = data.readInt32();
int32_t minusxy = minus(x,y);
reply->writeInt32(minusxy);
return NO_ERROR;
}
default:{
return BBinder::onTransact(code,data,reply,flags);
}
}
}
CalcService::CalcService()
{
LOGD("constructor of CalcService");
}
CalcService::~CalcService()
{
LOGD("destructor of CalcService");
}
int32_t CalcService::sum(int32_t x, int32_t y)
{
return (x+y);
}
int32_t CalcService::minus(int32_t x, int32_t y)
{
return (x-y);
}
void CalcService::instantiate()
{
LOGD("CalcService instantiate.");
LOGD("CalcService:ServiceManager: start\n");
defaultServiceManager()->addService(String16("calcservice"),new android::CalcService());
}
}
从上面可以看见CalcService从BnCalcService继承而来,而BnCalcService从BnInterface继承而来,我们还能看见一个BpCalcService的类,他继承于BpInterface。实际上BnCalcService也就是CalcService是service功能的本地实现,是真正做事情的地方,它是单独的一个进程。而BpCalcService是service的一个代理,对使用这个service的应用而言,应用只与BpService也就是代理打交道。应用并不与BnCalcService往来。在BpCalcService的函数内都使用了remote()->transact,这实际上是通过IPC与BnCalcService通信,BnCalcService的onTransact响应。
instantiate函数内通过defaultServiceManager向service manager注册这个service,其名字就是calcservice,那么应用就可以使用这个名字获取这个service.
另外还需要创建一个可执行程序通过init.rc来运行这个service. 创建一个叫Calc.cpp的文件
#include "ICalcService.h"
#include "CalcService.h"
using namespace android;
int main(int argc, char **argv)
{
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
LOGD("CalcService: %p",sm.get());
android::CalcService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
然后要添加Android.mk文件编译
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
ifeq ($(TARGET_BUILD_TYPE),debug)
LOCAL_CFLAGS += -DDEBUG
endif
LOCAL_PRELINK_MODULE := false
#Binder Proxy
LOCAL_MODULE := libCalcService
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := CalcService.cpp
LOCAL_SHARED_LIBRARIES := libbinder libutils
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
# calc
LOCAL_MODULE := calc
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := Calc.cpp
LOCAL_PRELINK_MODULE := false
LOCAL_SHARED_LIBRARIES := libbinder libutils libCalcService
include $(BUILD_EXECUTABLE)
CalcService.cpp生成的是一个库libCalcService,Calc.cpp生成的是一个可执行文件。最后添加这两行到init.rc启动这个服务
service calcservice /system/bin/calc
class services
通过以上的代码我们已经在c++层面创建了一个service,并且系统启动后这个service 就运行起来了。那么如何在java应用程序中使用这个service呢?
下面就通过一个例子来说说,创建一个CalcServiceTest的android工程,修改main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="30dip" >
<EditText
android:id="@+id/sxText"
android:layout_width="200dip"
android:layout_height="wrap_content"
android:numeric="integer"
/>
<TextView
android:layout_width="20dip"
android:layout_height="wrap_content"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:text="@string/sum"
/>
<EditText
android:id="@+id/syText"
android:layout_width="200dip"
android:layout_height="wrap_content"
android:numeric="integer"
/>
<Button
android:id="@+id/sumBtn"
android:layout_width="100dip"
android:layout_height="wrap_content"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:text="@string/equal"
/>
<EditText
android:id="@+id/sumText"
android:layout_width="200dip"
android:layout_height="wrap_content"
android:numeric="integer"
/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="30dip" >
<EditText
android:id="@+id/mxText"
android:layout_width="200dip"
android:layout_height="wrap_content"
android:numeric="integer"
/>
<TextView
android:layout_width="20dip"
android:layout_height="wrap_content"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:text="@string/minus"
/>
<EditText
android:id="@+id/myText"
android:layout_width="200dip"
android:layout_height="wrap_content"
android:numeric="integer"
/>
<Button
android:id="@+id/minusBtn"
android:layout_width="100dip"
android:layout_height="wrap_content"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:text="@string/equal"
/>
<EditText
android:id="@+id/minusText"
android:layout_width="200dip"
android:layout_height="wrap_content"
android:numeric="integer"
/>
</LinearLayout>
</LinearLayout>
这个xml定义了一个layout,第一行控件是用来输入x,y两个计算数,点击等号的控件后,把相加的结果显示出来;第二行的控件是计算相减并且显示出来。
然后strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, CalcServiceTest!</string>
<string name="app_name">CalcServiceTest</string>
<string name="sum">+</string>
<string name="minus">-</string>
<string name="equal">=</string>
</resources>
定义一个叫ICalcService.aidl的文件,这个文件很重要,通过它才能与service通信,这个接口有两个函数,对应了CalcService内的两个函数
package com.test;
interface ICalcService
{
int sum(int x, int y);
int minus(int x, int y);
}
如果是eclipse会自动生成一个叫ICalcService.java的文件,其中你可以见这么行代码
private static final java.lang.String DESCRIPTOR = "com.test.ICalcService"
注意CalcService.cpp内的
IMPLEMENT_META_INTERFACE(CalcService,"com.test.ICalcService");两者的名字必须一致,否则无法实现正常的通信。
修改CalcServiceTest.java
package com.test; import android.app.Activity; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import com.test.ICalcService; public class CalcServiceTest extends Activity { /** Called when the activity is first created. */ private EditText et1,et2,et3; private Button sumBtn, minusBtn; private String TAG = "CalcServiceTest"; private ICalcService mService; @Override public void onCreate(Bundle savedInstanceState) { Log.d(TAG,"onCreate"); super.onCreate(savedInstanceState); setContentView(R.layout.main); getCalcService(); ((Button)findViewById(R.id.sumBtn)).setOnClickListener(listener); ((Button)findViewById(R.id.minusBtn)).setOnClickListener(listener); } private void getCalcService() { IBinder calcServiceBinder; Log.d(TAG,"getCalcService"); calcServiceBinder = (IBinder)ServiceManager.getService("calcservice"); if(calcServiceBinder != null) mService = ICalcService.Stub.asInterface(calcServiceBinder); else Log.d(TAG,"calcServiceBinder is null."); } private OnClickListener listener = new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub int x,y,z=0; switch(v.getId()) { case R.id.sumBtn: { et1 = (EditText)findViewById(R.id.sxText); et2 = (EditText)findViewById(R.id.syText); et3 = (EditText)findViewById(R.id.sumText); x = Integer.parseInt(et1.getText().toString()); y = Integer.parseInt(et2.getText().toString()); //z = x + y; if(mService != null) { try { z = mService.sum(x,y); Log.d(TAG,"sum="+String.valueOf(z)); } catch(RemoteException e) { e.printStackTrace(); } } else { Log.d(TAG,"mService is null."); } et3.setText(String.valueOf(z)); break; } case R.id.minusBtn: { et1 = (EditText)findViewById(R.id.mxText); et2 = (EditText)findViewById(R.id.myText); et3 = (EditText)findViewById(R.id.minusText); x = Integer.parseInt(et1.getText().toString()); y = Integer.parseInt(et2.getText().toString()); //z = x - y; if(mService != null) { try { z = mService.minus(x, y); Log.d(TAG,"minus="+String.valueOf(z)); } catch(RemoteException e) { e.printStackTrace(); } } else { Log.d(TAG,"mService is null."); } et3.setText(String.valueOf(z)); break; } default:Log.d(TAG, "No mapping id."); } } }; }
仔细看getCalcService函数,其表明了如何将c++层的服务获取并且转换为java可以识别的aidl接口。通过转换后就可以简便地调用了。在listener中就调用了calcservice的sum和minus功能实现加减。创建Android.mk编译这个apk
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_PACKAGE_NAME := CalcServiceTest LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under,src) LOCAL_STATIC_JAVA_LIBRARIES := LOCAL_JAVA_LIBRARIES := LOCAL_PROGUARD_FLAG_FILES := proguard.flags include $(BUILD_PACKAGE) include $(call all-makefiles-under, $(LOCAL_PATH))
在CalcServiceTest.apk生成后通过app luancher就可以运行这个apk,通过logcat或者实际运行效果,我们可以看见apk与c++ service实现了正常的通信。