Android系統開發之七:新增Android Native Service方法
阿新 • • 發佈:2019-02-06
一、 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)
所有相關檔案組織:
[email protected]:~/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://micklongen.blog.163.com/blog/static/1624491502010480218654/
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)
所有相關檔案組織:
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://micklongen.blog.163.com/blog/static/1624491502010480218654/