Service框架---关于aidl的使用介绍

来源:互联网 发布:迅雷修改源码去广告 编辑:程序博客网 时间:2024/05/18 09:15


                     Service框架---关于aidl的使用介绍

1.2 应用框架解析

除了基本的组件外,为了维护整个UI系统的良好运行,在应用层,Android还设计了诸多的框架来进行相关方面的管理。本节主要介绍Service框架、Activity管理机制、Broadcast机制、对话框框架、标题栏框架、状态栏框架、通知机制和ActionBar框架等。

1.2.1 Service框架

在Android中,作为执行应用后台运算和框架层运算的基本组件,服务扮演着十分重要的角色。根据通信的方式和应用场景,服务有不同的类型。具体而言,从通信的方式来看,服务可分为两种类型,即本地服务和远程服务,其中远程服务根据通信的方式又可分为基于AIDL的服务和基于Message的服务两种。远程服务是Android中跨进程通信的主要形式之一。从应用的场景来看,服务可以分为应用服务和系统服务等。

1. 本地服务

如果服务没有跨进程调用的需求,应将服务设计为本地服务,这样一方面加快了通信的速度,另一方面也简化了设计。如下是实现一个本地服务的最基本形式的示例:
public class HelloService  extends  Service {
   @Override
   public IBinder onBind(Intent arg0) {
  return null;
 }
}

在本地服务中,必须实现onBind()方法,当然,如果不需要绑定服务(即调用bindService()方法),可以返回null。一个具有绑定服务的本地服务实现示例如下:
public class LocalService extends Service {
   public class LocalBinder extends Binder {
        LocalService getService() {          
          return LocalService.this;       
        }
    }
public IBinder onBind(Intent intent) {
        return mBinder;
    }
private final IBinder mBinder = new LocalBinder();
}

注意 在默认情况下,Service依然运行在主线程中,而非另开线程,如果希望有大运算量的后台计算,则在实现本地服务时,必须在Service中创建单独的线程来执行相应运算。

2. 基于AIDL的远程服务

基于AIDL的远程服务本质上沿袭了分布式计算的思想,当然在以往的嵌入式系统中,其框架相对简单,通常并不具备跨语言的能力。但Android实现了真正的分布式计算,能够支持跨语言的调用。实现基于AIDL的远程服务需要经过以下4个步骤:

(a)创建AIDL文件。

(b)将AIDL文件纳入编译系统。

(c)实现接口方法。

(d)绑定服务客户端。

在完成编码工作后,Android会在编译过程中自动为相应的AIDL文件生成对应的桩(Stub),简化了开发的难度。

下面分别介绍实现基于AIDL的远程服务的4个步骤。

(1)创建AIDL文件

AIDL文件的实现非常简单,其本身仅是一个文件名以“I”开头的接口文件。下面是ITestService的一个实现:
interface ITestService
{
    boolean getSthEnabled();
    void setSthEnabled(boolean on);
}

(2)将AIDL文件纳入编译系统

为了生成相应的桩,必须将AIDL文件纳入编译系统,其在frameworks/base/Android.mk中的实现如下:
...
LOCAL_SRC_FILES += \
...
core/java/android/os/ITestService.aidl \
...

在应用层,以IMediaPlaybackService为例,其在Android.mk中的实现通常如下:
...
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
 src/com/android/music/IMediaPlaybackService.aidl
...

在框架层和应用层将AIDL文件纳入编译系统,两者仅在写法习惯上不同而已。

(3)实现接口方法

实现接口方法只需继承相应接口的Stub子类即可,但必须实现接口所定义的所有方法,示例如下:
public class ITestServiceImpl extends ITestService.Stub{

public boolean getSthEnabled() {
   ...
}

public void setSthEnabled(boolean on){
  ...
}
}

为了便于客户端绑定,通常会将桩封装到到一个服务中,方法如下:
public class TestService extends Service{
 @Override
 public IBinder onBind(Intent arg0) {
 return new ITestServiceImpl(getApplicationContext());  
 }
}

(4)绑定服务客户端

为了与服务进行通信,必须在客户端绑定远程服务,在应用层的实现中,如果是跨进程调用的,必须将相应的ITestService文件复制到客户端所在的进程中。假设服务位于com.miaozl.test包中,在客户端实现进程调用时,其方法如下:
private ITestService mTest = null;
...
public void onCreate() {
Intent intent = new Intent();
intent.setComponent(
new ComponentName("com.miaozl.test","com. miaozl.test.service.TestService"));
bindService(intent, mTestConnection,Context.BIND_AUTO_CREATE);
}

如果是在本地进程中,则实现较简单,方法如下:
bindService(new Intent(this, TestService.class),
mTestConnection,BIND_AUTO_CREATE);

而mTestConnection的实现则不区分是本地应用调用还是跨进程调用,具体如下:
private ServiceConnection mTestConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName name, IBinder service) {
//绑定方法
   mTest= ITestService.Stub.asInterface(service);           
...
  }
   public void onServiceDisconnected(ComponentName name) {
         mTest = null;
        }
    };

注意 绑定服务是以异步的方式进行的,对于必须为同步的场景,是无法实现绑定服务的。

3. 基于Messenger的远程服务

基于Messenger的远程服务同样是跨进程的,其本质是将本地服务和Messenger结合,以便实现进程间的通信。基于Messenger实现远程服务的示例如下:
public class AlertService extends Service {
final Messenger mMessenger = new Messenger(new ServiceHandler ());
private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            processMessage(msg);
        }
    }
   void processMessage(Message msg) {
      ...
   }
    @Override
    public IBinder onBind(Intent intent) {
      return mMessenger.getBinder();
    }
}

通过Messenger,服务的调用者可以很方便地发送Message,示例如下:
Messenger mService = null;
...
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
   Message msg = Message.obtain(null,
MessengerService.MSG_SET_VALUE, this.hashCode(), 0);
    mService.send(msg);
}
}

4. 系统服务

系统服务主要由3部分构成: *Service.java 、I*.aidl、*Manager.java。另外还需要在SystemServer.java增加框架层封装,在ContextImpl.java增加应用层接口。

图1-7所示为以AlarmManagerService为例对系统服务进行的描述。

从图1-7所示中可以看出,为了实现一个系统服务,有5部分的内容需要关注。
 接口文件;
 客户端文件;
 桩文件(系统自动实现);
 服务端文件;
 系统调用接口。


 

下面对除桩文件以外的其他文件进行较详细的介绍。

(1)接口文件

通常情况下,接口文件中仅用到了基本的数据类型,如果需要用到复杂的数据类型,则需对数据进行序列化,具体实现可以参见5.1.2 节。如下是IAlarmManager.aidl文件的具体实现:
interface IAlarmManager {
    void set(int type, long triggerAtTime, in PendingIntent operation);
    void setRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation);
    void setInexactRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation);
    void setTime(long millis);
    void setTimeZone(String zone);
    void remove(in PendingIntent operation);
}

在AIDL文件中,通常定义了客户端需要调用的接口中的方法。

(2)客户端文件

在Android系统中,客户端文件对系统服务的调用非常简单,其调用方法类似于普通方法调用,只是需要捕获RemoteException异常。下面是AlarmManager的部分实现:
public class AlarmManager
{
   private final IAlarmManager mService;
    AlarmManager(IAlarmManager service) {
        mService = service;
    }
    public void set(int type, long triggerAtTime, PendingIntent operation) {
        try {
            mService.set(type, triggerAtTime, operation);
        } catch (RemoteException ex) {
        }
    }
...
}

(3)服务端文件

服务端的实现是实现系统服务的最重要工作,下面是AlarmManagerService的部分实现:
class AlarmManagerService extends IAlarmManager.Stub {
public void set(int type, long triggerAtTime, PendingIntent operation) {
        setRepeating(type, triggerAtTime, 0, operation);
    }
...
}

(4)系统调用接口

为了方便应用层进行调用,需要在ContextImpl.java中实现统一的接口封装,并在Context.java中定义如下接口:
public static final String ALARM_SERVICE = "alarm";

在ContextImpl.java中实现统一接口封装的代码如下:
class ContextImpl extends Context {
private static AlarmManager sAlarmManager;
  ...
public Object getSystemService(String name) {
...
if (ALARM_SERVICE.equals(name)) {
        return getAlarmManager();
}
...
}
private AlarmManager getAlarmManager() {
  synchronized (sSync) {
    if (sAlarmManager == null) {
     IBinder b = ServiceManager.getService(ALARM_SERVICE);
     IAlarmManager service = IAlarmManager.Stub.asInterface(b);
     sAlarmManager = new AlarmManager(service);
   }
 }
 return sAlarmManager;
}
}

完成以上工作后,如果需要闹钟服务,可按照下面的方式执行调用:
AlarmManager alarmManager =
 (AlarmManager) getSystemService(Context.ALARM_SERVICE);

注意 框架层的变动会导致SDK的变动,故需要根据设计者的需求选择@hide或make update-api来更新current.xml。

5. 服务配置

为了对服务进行编译,必须在AndroidManifest.xml中对服务进行配置,其方法如下:
<service android:name=".service.EmailBroadcastProcessorService" />

对于远程服务,如果希望服务可以被其他进程调用,则必须开放相应的权限,开放权限的方法如下:
<service android:name="com.android.server.LoadAverageService"
android:exported="true" />

如果服务具有一些敏感的信息,需要对其进行权限配置,这在对安全要求较高的应用中十分重要。权限配置方法如下:
<service android:name=
"com.android.internal.os.storage.ExternalStorageFormatter"
android:permission="android.permission.MASTER_CLEAR"
android:exported="true" />

如果希望服务运行在单独的进程中,则可应用如下方法进行配置:
<service android:name="android.os.MessengerService"
android:process=":messengerService" />  //设置进程名