為 Android 添加 Java 層服務
1. 為什麼要寫底層核心服務呢?
因為底層核心服務是 Android框架裏最接近 Linux/Driver的部分。為了充分發揮硬件設備的差異化特性,核心服務是讓上層 Java應用程序來使用 Driver/HW Device 特色的重要管道。例如 Media、 Telephone等底層硬件。
在開機過程中,就可以啟動核心服務(漢字輸入法服務等),讓眾多應用程序來共同使用。
由於共用,所以能有效降低 Java應用程序的大小( Size)。
2. 核心服務與 Java 層的 Service有何區別和關係?
Android具有兩層服務
--Java層 SDK-based Service
--C++層的 Code Service
3. 編寫自己的核心服務( C++ 層)
1). 要點
核心服務通常在獨立的進程( Process )裏執行。
必須提供 IBinder 接口,讓應用程序可以進行跨進程的綁定( Binding )和調用。
因為共用,所以必須確保多線程安全( Thread-safe )。
使用 C++ 來實現,並調用 IServiceManager::addService() 函數添加到係統的 Binder Driver 裏。
上層應用程序通過 ServiceManager 獲取該服務。
上層應用程序通過 IBinder::transact() 函數來與核心服進行數據交互。
2). 添加服務
下麵詳細介紹如何添加一個底層服務到係統中,假設服務名為 AddService ,其用途是對傳入的參數加上 1000 ,並返回結果。
服務實現
進入 android 源碼 的目錄 frameworks/base ,在該目錄下建立自己的目錄,假設為 addservice ,再在這個目錄中建立兩個子目錄 addserver 和 libaddservice , addserver 用於存放服務的啟動文件,其最終的生成為可執行文件,在係統啟動的時候運行, libaddservice 用於存放服務的實現文件,最終會生成動態鏈接庫,有 addserver 調用。
首先,服務的實現文件包括兩個文件, AddService.h 和 AddService.cpp ,
以下是 AddService.h :
#ifndef ANDROID_GUILH_ADD_SERVICE_H
#define ANDROID_GUILH_ADD_SERVICE_H
#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <utils/threads.h>
namespace android {
class AddService : public BBinder{// 從 BBinder 派生,實現本地接口
public:
static int instantiate();
AddService();
virtual ~AddService();
virtual status_t onTransact(uint32_t, const Parcel&, Parcel*, uint32_t);
};
}; //namespace
#endif
然後是服務的實現文件 AddService.cpp :
#include "AddService.h"
#include <binder/IServiceManager.h>
#include <binder/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());// 這裏主要是把 //AddSerice 這個服務添加到 Binder Driver 中服務名為 guilh.add
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");
}
// 這個是服務具體的本地實現,功能實現都應該放在這裏麵,通過傳入執行代碼( code ) // 的不同來執行不同的操作,上層隱射為不同的 api 。
status_t AddService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){
switch(code) {
case 0: {// 根據 code 的不同執行不同的操作
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);
}
}}; //namespace
以下是編譯服務的 Android.mk ,和上麵的 cpp 放在一起。
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= /
AddService.cpp
LOCAL_C_INCLUDES := /
$(JNI_H_INCLUDE)
LOCAL_SHARED_LIBRARIES := /
libcutils /
libutils /
libbinder /
libandroid_runtime
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := libAdd
include $(BUILD_SHARED_LIBRARY) #這一行表示編譯為動態庫
在命令行中退出到 android/目錄級 加載編譯環境 . build/envsetup.sh
然後 lunch。
然後在 cd /android/frameworks/base/addservice/ libaddservice/ 目錄 輸入 mm
之後在 out 目錄產出 libAdd.so 文件。
在此 完成核心服務第一步。
服務進程實現
進入到 cd /android/frameworks/base/addservice/addserver/ 目錄
增加一個文件 addserver.cpp ,文件內容如下:
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>
#include <private/android_filesystem_config.h>
#include "../libaddservice/AddService.h"
//#include <libadd/AddService.h>
using namespace android;
int main(int argc, char** argv)
{
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();//取得 ServiceManager
LOGI("ServiceManager: %p", sm.get());
AddService::instantiate();//把自己添加到 ServiceManager中
ProcessState::self()->startThreadPool();//啟動緩衝池
IPCThreadState::self()->joinThreadPool();//這裏是把服務添加到 Binder閉合循環進程中
}
以上為底層服務的標準操作。
下麵是這個服務 makefile:
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= /
addserver.cpp
LOCAL_SHARED_LIBRARIES := /
libAdd /
libutils /
libbinder
LOCAL_MODULE:= addserver
include $(BUILD_EXECUTABLE)//編譯為可執行文件
退出後當前目錄執行 mm即可在 out目錄的 system/bin下產出 addserver可執行文件。
實現服務進程開機自動運行
進入到 /android/system/core/rootdir/目錄中有個 init.rc文件
vi init.rc
在 service中添加
service addservice /system/bin/addserver //將 /system/bin/addserver作為一個服務啟動,服務的名稱為 addservice(這個不重要)。
最後退出到 android/目錄下執行全編譯:
輸入 . build/envsetup.sh
Lunch
Make
完成之後
Emulator打開模擬器
打開另一個 shell終端 輸入 adb shell 進入模擬器模式 如果 adbshell係統提示沒有發現該命令 就在 android/out/host/linux-x86/bin/中輸入 ./adb shell
在輸入 ps 查看進程 找到是否有 addserver進程
如果有就成功一半。
測試我們的服務
隨便在 android/packages/apps 中 建立一個簡單的應用程序,
這裏可以直接在 eclipse 中建立好工程 拷貝到 android/packages/apps 中,然後為應用添加一個 Android.mk 文件,可以從其他應用中拷貝來修改。
在應用程序中測試服務的代碼:
private void test(){
try{
IBinder binder = ServiceManager.getService("guilh.add");// 取得服務
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
if(binder == null)
Log.d(TAG,"failed to get service");
data.writeInt(Process.myPid());// 固定操作
data.writeInt(100);// 傳入參數
binder.transact(0, data, reply, 0);// 執行遠程調用
Log.d(TAG,"result="+reply.readInt());// 驗證結果
}catch(Exception e){
Log.d(TAG,e.toString());
}
Java層服務顧名思義即為從 Java層提供的服務,它與 C++層的服務不同之處在於其服務進程的運行由係統( ServiceManager)統一維護。在文件 frameworks /base /services /java /com /android /server /SystemServer.java 中我們可以看到以下代碼:
AlarmManagerService alarm = new AlarmManagerService (context );
ServiceManager .addService (Context .ALARM_SERVICE , alarm );
這個操作在係統啟動時完成,由 ServiceManager 負責創建服務進程並運行之。所以我們要做的事就是實現一個 java 服務,並將其添加到這裏並由係統運行起來,以下是具體實現步驟:
實現自己的 java層 service
1. 在目錄
frameworks/base/core/java/android/os中增加自己的 AIDL文件用來申明服務:
BelyService.aidl:
package android.os;
interface IBelyService {
/**
*
*/
int calculateSqu(int value);
}
2. 在目錄
frameworks/base/services/java/com/android/server增加 service的實現文件:
BelyService.java:
package com.android.server;
import android.content.Context;
import android.os.IBelyService;
public class BelyService extends IBelyService.Stub {
public BelyService(Context context){
super();
}
public int calculateSqu(int val){
return val*val;
}
}
3. 將 java服務添加到 ServiceManager中:
BelyService bs = new BelyService(context);
ServiceManager.addService("BelyService", bs);
自此,重新編譯 Android並運行,我們所創建的服務即可訪問,下麵是演示如何調用:
同樣在 package/apps下任意創建一個應用,調用服務的方法如下:
import android.os.ServiceManager;
import android.os.IBelyService;
IBelyService bs = IBelyService.Stub.asInterface(ServiceManager.getService("BelyService"));
int ret = bs.calculateSqu(9);
上麵我們使用的是 Android內部的方法來獲取服務,當然也可以使用公開的 API: context.getSystemService來獲取。
最後更新:2017-04-02 22:16:35