一个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注册进SystemService.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”信息。