Android service 实现过程

一、 Android Service 介绍

Android  Service 分为两种: Android Service  Native Service 

Android Service :又称为 Java Service ,是实现在框架层( framework )里的 Server  Android Service  Java 编写。

Native Service :又称为 System Service ,是实现在 Runtime 层里的 Server 

 MediaPlayer 为例,从下图我们可以得出两种服务的关系:

 

 

接下来要讨论的 Service  Native Service ,与应用程序设计上所讨论的 Service  android.app.Service )不同。

 

二、为什么要写底层的核心服务呢?

 1  因为底层核心服务是 Android 框架里最接近 Linux/Driver 的部分。为了充分发挥硬件设备的差异化特性,核心服务是让上层 Java 应用程序来使用 Driver/HW Device 特色的重要管道。

 2  在开机过程中,就可以启动核心服务 ( 例如汉字输入法服务等 ) ,让众多应用程序来共享之。

 3  由于共享,所以能有效降低 Java 应用程序的大小 (Size) 

 

三、如何实现一个核心服务呢 ?

要点如下:

 1 )核心服务通常在独立的进程 (Process) 里执行。

 2 )必须提供 IBinder 接口,让应用程序可以进行跨进程的绑定 (Binding) 和呼叫。

 3 )因为共享,所以必须确保多线裎安全 (Thread-safe) 

 4 )以 C++ 类别定义,诞生其对象,透过 SM 之协助,将该对象参考值传给 IServiceManager::addService() 函数,就加入到 Binder Driver 里了。

 5 )应用程序可透过 SM 之协助而远距绑定该核心服务,此时 SM 会回传 IBinder 接口给应用程序。

 6 )应用程序可透过 IBinder::transact() 函数来与核心服务互传数据。


四、 Server 实现实践

下面以一个小例子来说明具体实现一个 Server 的步骤。此实例功能为简单的整数加法 (Add) 运算,我们将其命名AddService 

Step-1 :以 C++ 撰写 AddService 类别,其完整程序代码为:

AddService.h 文件:

 

#ifndef ANDROID_GUILH_ADD_SERVICE_H
#define ANDROID_GUILH_ADD_SERVICE_H
#include <utils.h>
#include <utils/RefBase.h>
#include <utils/IInterface.h>
#include <utils/Parcel.h>
namespace android
{
       class AddService : public BBinder
       {
              mutable Mutex mLock;
              int32_t mNextConnId;
 
              public:
              static int instantiate();
              AddService();
              virtual ~AddService();
              virtual status_t onTransact(uint32_t, const Parcel&, Parcel*, uint32_t);
       };
}
#endif
AddService.cpp 文件:
 
#include "AddService.h"
#include <utils/IServiceManager.h>
#include <utils/IPCThreadState.h>
 
namespace android {
       static struct sigaction oldact;
       static pthread_key_t sigbuskey;
       int AddService::instantiate() {
              LOGE("AddService instantiate");
 
              int r = defaultServiceManager()->addService(
                            String16("guilh.add"), new AddService());
              LOGE("AddService r = %d/n", r);
              return r;
       }
       AddService::AddService()
       { LOGV("AddService created");
              mNextConnId = 1;
              pthread_key_create(&sigbuskey, NULL);
       }
       AddService::~AddService()
       { pthread_key_delete(sigbuskey);
              LOGV("AddService destroyed");
       }
       status_t AddService::onTransact(
                     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){
              switch(code) {
                     case 0: {
                                   pid_t pid = data.readInt32();
                                   int num = data.readInt32();
                                   num = num + 1000;
                                   reply->writeInt32(num);
                                   return NO_ERROR;
                            } break;
                     default:
                            return BBinder::onTransact(code, data, reply, flags);
              }
       }
};
 

 

Android.mk 文件:

 

 

LOCAL_PATH:= $(call my-dir)
 
include $(CLEAR_VARS)
 
LOCAL_SRC_FILES:= AddService.cpp
 
#LOCAL_C_INCLUDES:= $(JNI_H_INCLUDE)   
 
LOCAL_SHARED_LIBRARIES:= libutils
 
LOCAL_MODULE:= libAddService
 
LOCAL_PRELINK_MODULE:= false
include $(BUILD_SHARED_LIBRARY)

 

 


 

Step-2 :以 C++ 撰写一个可独立执行的 addserver.cpp 程序,它的用途是:诞生一个 AddService 类别之对象,然后将该对象参考存入 Binder Driver 里。其内容为:

addserver.cpp 文件:

 

#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
#include <utils/IPCThreadState.h>
#include <utils/ProcessState.h>
#include <utils/IServiceManager.h>
#include <utils/Log.h>
#include <private/android_filesystem_config.h>
#include "../libaddservice/AddService.h"
 
using namespace android;
 
int main(int argc, char** argv)
{
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
LOGI("ServiceManager: %p", sm.get());
AddService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
 

 

Android.mk 文件:

 

LOCAL_PATH:= $(call my-dir)
 
include $(CLEAR_VARS)
 
LOCAL_SRC_FILES:= addserver.cpp
 
LOCAL_SHARED_LIBRARIES:= libutils libAddService
 
LOCAL_MODULE:= addservice
 
include $(BUILD_EXECUTABLE)
 

 

Step-3 :编译上述两个文件分别产出了 libAdd.so 类别库和 addserver 可执行程序。接着将 libAdd.so 拷贝到 Android 仿真器的 /system/lib/ 里;也把 addserver 拷贝到 /system/bin/ 里。

 

Step-4 :执行 addserver 。其中的指令: AddServer::instantiate() 就执行到 AddServer 类别的 instantiate() 函数,其内容为:

 

int AddService::instantiate() {
LOGE("AddService instantiate");
int r = defaultServiceManager()->addService(
String16("guilh.add"), new AddService());
LOGE("AddService r = %d/n", r);
return r;
}
 

其先执行到 new AddServer() ,就诞生一个 AddServer 类别之对象;

接着,呼叫 defaultServiceManager() 函数取得 SM  IServiceManager 接口;

再呼叫 IServiceManager::addServer() 将该对象参考存入 Binder Driver 里。

 

Step-5 :这样就成功地将 AddService 服务加入到 Binder Driver 里了。现在就可以写个 Add 类来使用 AddService 核心服务了。以 C++ 撰写 Add 类别,其完整程序代码为:

Add.h 文件:

 

#ifndef ANDROID _ADD_H
#define ANDROID _ADD_H
namespace android {
       class Add {
              public:
                     int setN(int n);
              private:
                     static const void getAddService();
       };
}; //namespace
#endif // ANDROID _ADD_H
 

 

Add.cpp 文件:

 

#include <utils/IServiceManager.h>
#include <utils/IPCThreadState.h>
#include "Add.h"
 
namespace android {
       sp<IBinder> binder;
       int Add::setN(int n){
              getAddService();
              Parcel data, reply;
              data.writeInt32(getpid());
              data.writeInt32(n);
 
              LOGE("BpAddService::create remote()->transact()/n");
              binder->transact(0, data, &reply);
              int i = reply.readInt32();
              return i;
       }
       const void Add::getAddService(){
              sp<IServiceManager> sm = defaultServiceManager();
              binder = sm->getService(String16("guilh.add"));
              LOGE("Add::getAddService %p/n",sm.get());
              if (binder == 0) {
                     LOGW("AddService not published, waiting...");
                     return;
              }
       }
 
};
 

 

Android.mk 文件:

 

LOCAL_PATH:= $(call my-dir)
 
include $(CLEAR_VARS)
 
LOCAL_SRC_FILES:=Add.cpp
 
LOCAL_SHARED_LIBRARIES := libutils libAddService
 
LOCAL_MODULE := libAdd
 
LOCAL_PRELINK_MODULE:= false
 
include $(BUILD_SHARED_LIBRARY)
 
 

Step-6 :下面写个 JNI Native 类别来使用 Add 类别之对象。透过 JNI Native 函数,就可以与 Java 层的 Service 服务衔接起来。

首选使用 javah 命令生成相应头文件。

com_hello_Service_MySer.h 文件:

 

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_hello_Service_MySer */
 
#ifndef _Included_com_hello_Service_MySer
#define _Included_com_hello_Service_MySer
#ifdef __cplusplus
extern "C" {
#endif
/*
  * Class:     com_hello_Service_MySer
  * Method:    intFromJNI
  * Signature: ()I
  */
JNIEXPORT jint JNICALL Java_com_hello_Service_MySer_intFromJNI
  (JNIEnv *, jobject);
 
#ifdef __cplusplus
}
#endif
#endif
然后实现相应函数。
com_hello_Service_MySer.cpp 文件:
#include <jni.h>
#include <JNIHelp.h>
#include "../libadd/Add.h"
#include "com_hello_Service_MySer.h"
 
JNIEXPORT jint JNICALL Java_com_hello_Service_MySer_intFromJNI(JNIEnv * env, jobject thiz)
{
    android::Add myadd;
       int r = myadd.setN(5);
       return r;
}
 
 

Android.mk 文件:

 

LOCAL_PATH:= $(call my-dir)
 
include $(CLEAR_VARS)
 
LOCAL_SRC_FILES:=com_hello_Service_MySer.cpp
 
LOCAL_C_INCLUDES:= $(JNI_H_INCLUDE)   
 
LOCAL_SHARED_LIBRARIES := libutils libAdd
 
LOCAL_MODULE := libJniAdd
 
LOCAL_PRELINK_MODULE:= false
 
include $(BUILD_SHARED_LIBRARY)
 

所有相关文件组织:

a@ubuntu:~/work/android/source_android/development/my_module$ tree service

service

|-- addserver

|   |-- Android.mk

|   `-- addserver.cpp

|-- jni

|   |-- Android.mk

|   |-- com_hello_Service_MySer.cpp

|   `-- com_hello_Service_MySer.h

|-- libadd

|   |-- Add.cpp

|   |-- Add.h

|   `-- Android.mk

`-- libaddservice

    |-- AddService.cpp

    |-- AddService.h

    `-- Android.mk

 

4 directories, 11 files

 Eclipse 中创建一个工程使用以上的 Add 类,即可使用我们的 AddService 了。

MySer.java 文件:

 

package com.hello.Service;
 
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
 
public class MySer extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        int i = intFromJNI();
        TextView  tv = new TextView(this);
        tv.setText( String.valueOf(i) );
        setContentView(tv);
    }
   
    public native int intFromJNI();
   
static {
       
        System.loadLibrary("JniAdd");
    }
}
 

 

五、Service 编译问题

Android 所用的 Toolchain (即交叉编译工具链)可从下面的网址下载:

http://android.kernel.org/pub/android-toolchain-20081019.tar.bz2 。如果下载了完整的 Android 项目的源代码,则可以在“<your_android>/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin ”目录下找到交叉编译工具,比如 Android 所用的 arm-eabi-gcc-4.2.1 。我们可以直接使用源码包来进行编译。

Android 并没有采用 glibc 作为 C 库,而是采用了 Google 自己开发的 Bionic Libc ,它的官方 Toolchain 也是基于Bionic Libc 而并非 glibc 的。这使得使用或移植其他 Toolchain 来用于 Android 要比较麻烦:在 Google 公布用于Android 的官方 Toolchain 之前,多数的 Android 爱好者使用的 Toolchain 是在http://www.codesourcery.com/gnu_toolchains/arm/download.html 下载的一个通用的 Toolchain ,它用来编译和移植Android  Linux 内核是可行的,因为内核并不需要 C 库,但是开发 Android 的应用程序时,直接采用或者移植其他的Toolchain 都比较麻烦,其他 Toolchain 编译的应用程序只能采用静态编译的方式才能运行于 Android 模拟器中,这显然是实际开发中所不能接受的方式。目前尚没有看到说明成功移植其他交叉编译器来编译 Android 应用程序的资料。

 glibc 相比, Bionic Libc 有如下一些特点:

l         采用 BSD License ,而不是 glibc  GPL License 

l         大小只有大约 200k ,比 glibc 差不多小一半,且比 glibc 更快;

l         实现了一个更小、更快的 pthread 

l         提供了一些 Android 所需要的重要函数,如” getprop   LOGI ”等;

l         不完全支持 POSIX 标准,比如 C++ exceptions  wide chars 等;

l         不提供 libthread_db  libm 的实现

  另外, Android 中所用的其他一些二进制工具也比较特殊:

加载动态库时使用的是 /system/bin/linker 而不是常用的 /lib/ld.so;

prelink 工具不是常用的 prelink 而是 apriori ,其源代码位于“ <your_android>/build/tools/apriori 

strip 工具也没有采用常用的 strip ,而是“ <your_android>/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin ”目录下的arm-eabi-strip ,而是位于 <your_android>/out/host/linux-x86/bin/  soslim 工具。

 

下面就具体说一下如何编译我们刚才创建的 Service 程序。

1.  $(YOUR_ANDROID)/development 目录下创建 my_module 目录,然后将我们的 server 文件夹拷贝到此目录下,其中$(YOUR_ANDROID)  Android 源代码所在的目录。

  # mkdir $(YOUR_ANDROID)/development/ my_module

2. Android.mk 这是 Android Makefile 的标准命名,不要更改。 Android.mk 文件的格式和内容可以参考其他已有的Android.mk 文件的写法,针对 Add 程序的 Android.mk 文件内容如下:

 

LOCAL_PATH:= $(call my-dir)
 
include $(CLEAR_VARS)
 
LOCAL_SRC_FILES:=Add.cpp
 
LOCAL_SHARED_LIBRARIES := libutils libAddService
 
LOCAL_MODULE := libAdd
 
LOCAL_PRELINK_MODULE:= false
 
include $(BUILD_SHARED_LIBRARY)
 

注意上面 LOCAL_SRC_FILES 用来指定源文件;, LOCAL_MODULE 指定要编译的模块的名字,下一步骤编译时就要用到; include $(BUILD_SHARED_LIBRARY) 表示要编译成动态库,如果想编译成一个可执行文件则可用BUILD_EXECUTABLE ,这些可以在 $(YOUR_ANDROID)/build/core/config.mk 查到。

 

3. 回到 Android 源代码顶层目录进行编译:

# cd $(YOUR_ANDROID) && make libAdd

注意 make libAdd 中的目标名 libAdd 就是上面 Android.mk 文件中由 LOCAL_MODULE 指定的模块名。

4 .编译后的可执行文件存放在通过” adb push ”将它传送到模拟器上,再通过” adb shell ”登录到模拟器终端,就可以执行了。

 

六、出现问题及及解决办法:

 

 1 )提示缺 bison ,安装 bison  sudo apt-get install bison

 2 )出现 frameworks/policies/base/PolicyConfig.mk:22: *** No module defined for the given PRODUCT_POLICY (android.policy_phone).  Stop. 错误。

解决办法:

 build/tools/findleaves.sh 中的第 89 行,

这一句 find "${@:0:$nargs}" $findargs -type f -name "$filename" -print |

改为 find "${@:1:$nargs-1}" $findargs -type f -name "$filename" -print |

转载:http://blog.chinaunix.net/u/22630/article_105768.html

猜你喜欢

转载自huaonline.iteye.com/blog/1670522