Android Service實現詳細過程

來源:互聯網
上載者:User

原文地址::http://blog.csdn.net/andy_android/article/details/7301814

 

 

一、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) 在開機過程中,就可以啟動核心服務(例如漢字IME服務等),讓眾多應用程式來共用之。

(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 |

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.