C++层Service的创建与使用

来源:互联网 发布:卫计委大数据 编辑:程序博客网 时间:2024/05/21 10:10

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 += -DDEBUGendifLOCAL_PRELINK_MODULE := false#Binder ProxyLOCAL_MODULE := libCalcServiceLOCAL_MODULE_TAGS := optionalLOCAL_SRC_FILES := CalcService.cpp LOCAL_SHARED_LIBRARIES := libbinder libutilsinclude $(BUILD_SHARED_LIBRARY)include $(CLEAR_VARS)# calcLOCAL_MODULE := calcLOCAL_MODULE_TAGS := optionalLOCAL_SRC_FILES := Calc.cpp LOCAL_PRELINK_MODULE := falseLOCAL_SHARED_LIBRARIES := libbinder libutils libCalcServiceinclude $(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()    {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubint 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 := CalcServiceTestLOCAL_MODULE_TAGS := optionalLOCAL_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.flagsinclude $(BUILD_PACKAGE)include $(call all-makefiles-under, $(LOCAL_PATH))

在CalcServiceTest.apk生成后通过app luancher就可以运行这个apk,通过logcat或者实际运行效果,我们可以看见apk与c++ service实现了正常的通信。