一個service是一個用用組件它能夠在系統的後台中長期駐留並且不向外提供使用者介面。一個應用程式組件可以開啟一個服務,即便是使用者切換到另一個應用程式了,服務仍將繼續。另外,一個組件可以綁定到一個服務上並且能夠進行處理序間通訊。例如,一個服務可以處理網路傳輸,播放音樂,進行檔案的輸入輸出,並且和服務提供者進行互動,而所有的這一切將是在後台中啟動並執行。
為了支援特殊的硬體或者特別需要提供API或者服務的情況下,需要編寫的檔案。
但是必須需要知道擴充系統服務和其它的framework層的特徵是不提倡的。
設計的時候需要考慮的問題:
在實現一個系統服務的時候需要回答關於線程需要,API,以及硬體介面的問題。完成一個系統service並不是一件容易的事情,但是如果能明白下面的問題也許會對你有所協助:
1 這個server出現的頻率如何?如果服務只是偶然使用的,回敘和系統服務的內容一起運行會更好,儘管最常見的方式還是給他分配單獨的線程。一個典型的不頻繁使用的server是耳機的檢測,它只需要在耳機的串連和移除的情況下運行。
2 硬體如何訪問?標準的硬體能夠通過裝置檔案訪問或者檔案檢測,但是最好的解決辦法五一是實現HAL庫。HAL能夠更容易的將你的服務應用於其他的硬體,並且可以在需要的時候能夠運行一些功能。
3 API?在很多情況下,可以將你的服務作為一個intent receiver,不過你無疑要使用aidl。
4 擴充framework?如果你想讓你的新介面可見,你將不得不更新你的API描述並且建立你自己的SDK。(這很容易實現,通過“make update-api”,接著使用“make sdk”)然而,如果你只想讓你自己的特定服務使用你的特徵,你應該使用javadoc@hide選項。
5 還有沒有其他方式?添加你自己的服務到新發布的framework。為了最小化檔案的添加,臨時的改變已經存在的服務而不是添加你自己的服務。但是切記,如果是一些功能性的改變,可能會導致第三方的應用不能夠工作。再次強調,如果你的服務只是一些特定的應用程式訪問並且不是對所有人都有用的,可以考慮添加到應用程式中。
程式碼範例
下面是一個專有的服務的例子,它能夠讓你明白如何設定一個值。簡單來講,這個代碼僅僅是添加到framework。對於產品的實現,代碼應該放在產品的目錄中。
執行個體化Interface
這個例子使用了aidl,所以第一步是加入介面定義檔案:
frameworks/base/core/java/android/os/IEneaService.aidl
package android.os;
interface IEneaService{
/**
*{@hide}
*/
void setValue(int val);
}
這個介面檔案需要添加到build 系統中:
frameworks/base/Android.mk
添加下面的內容到檔案的165行(the end of the list of SRC_FILES)
core/java/android/os/IEneaService.aidl \
實現服務
這個服務將產生一個背景工作執行緒,這個線程將完成所有的工作,作為系統服務進程的一部分。既然服務是由系統服務產生的,他將需要被放置在系統服務能夠找到它的地方。
frameworks/base/services/java/com/android/server/EneaService.java
package com.android.server;
import android.comtent.Context;
import android.os.Hander;
import android.os.IEneaService;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.util.Log;
public class EneaService extends IEneaService.Stub{
private static final String TAG = "EneaService";
private EneaWorkerThread mWorker;
private EneaWorkerHandler mHandler;
private Context mContext;
public EneaService(Context context){
super();
mContext = context;
mWorker = new EneaWorkerThread("EneaServiceWorker");
mWorker.start();
Log.i(TAG,"Spawned worker thread" );
}
public void setValue(int val)
{
Log.i(TAG,“setValue” + val);
Message msg = Message.obtain();
msg.what = EneaWorkerHandler.MESSAGE_SET;
msg.arg1 = val;
mHandler.sendMessage(msg);
}
private class EneaWorkerThread extends Thread{
public EneaWorkerThread(String name){
super(name);
}
public void run(){
Looper.prepare();
mHandler = new EneaWorkerHandler();
Looper.loop();
}
private class EneaWorkerHandler extends Handler{
private static final int MESSAGE_SET = 0;
@Override
//此處需要特彆強調的是如果我們在程式中添加了“@Override”編譯器可以給你驗證@Override下面的方法名是否是你父類中所有的,如果沒有則報錯
//例如我在下面的函數中就寫錯了函數的名字在實際的編輯中就出現了報錯的情況,所以如果要重寫父類中的方法可以添加此選項。
public void handlerMessage(Message msg){
try{
if (msg.what == MESSAGE_SET){
Log.i(TAG."set message received:" + msg.arg1);
}
}catch(Exception e){
//Log,don't crash!
Log.e(TAG,"Exception in EneaWorkerHandler.handlerMessage:",e);
}
}
}
}
此時並沒有真正的完成service的編寫過程。我們還需要完成的任務是將Service註冊進SystemServer.java。
我們需要在合適的位置添加如下代碼
try{
Slog.i(TAG,"Test Service");
ServiceManager.addService("Test", new EneaService(context));
}catch (Throwable e){
Slog.e(TAG,"Failure starting TestService Service",e);
}
在完成這些操作後編譯仍會出現問題,此時需要運行,make update-api命令,此時改變的檔案是current.txt改變的內容如下:
+ public abstract interface IEneaService implements android.os.IInterface {
+ }
+
+ public static abstract class IEneaService.Stub extends android.os.Binder implements android.os.IEneaService {
+ ctor public IEneaService.Stub();
+ method public android.os.IBinder asBinder();
+ method public static android.os.IEneaService asInterface(android.os.IBinder);
+ method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
+ }
+
你可能想輸出的log資訊線程id或者名字,來明確哪個線程正在執行中。
現在,所有我們需要做的只是在輸入make來完成建立工作,並且啟動模擬器。使用logcat,你將通過列印資訊知道線程已經被建立。
測試程式
你很有可能也想測試你的服務。因此,你可以建立一個“Hello World”的Activity.把工程放在/vendor/enea目錄。
在你的測試專案的主Activity類中,添加以下代碼到oncreate()
IEneaService em =
IEneaService.Stub.asInterface(serviceManager.getService("EneaService"));
try{
em.setValue(7);//Any value would do, really.
}catch(Exception e){
e.printStackTrace();
}
你也需要匯入android.os.IEneaService 並且當然需要在Android.mk來增加。
啟動你的測試程式並且查看log。你應該現在能看到“set value”資訊。