本例和下个例子Remote Service Controller 涉及到的文件有RemoteService.java ,IRemoteService.aidl, IRemoteServiceCallback.aidl 及ISecondary.aidl。

Android Interface Definition Language(AIDL)和其它一些支持远程方法调用RMI的系统的IDL类似,它定义了Service和Client 之间的使用接口约定,这种远程调用一般需要通过进程间通信机制(IPC)来实现。在Android系统,一个进程(Process)通常不能直接访问其它 进程的内存空间,Android系统支持使用AIDL来实现使用不同进程间调用服务接口。

在设计AIDL接口之前,要意识到使用AIDL接口是通过直接函数调用的方法来进行的,但这种远程调用所发生的线程Thread随调用者是否和Service提供者属于同一进程Process的不同而不同:

  • 如果调用者与Service属于同一个进程(可以称为Local Process),那么AIDL Call将使用与调用者同一线程执行。因此如果你的应用只使用Local Process来访问AIDL Call,那么根本就无必要使用AIDL接口,使用Binder即可,参见Android ApiDemo示例解析(39):App->Service->Local Service Binding
  • 如 果使用Remote Process方式来调用AIDL ,Android将会使用由本进程管理的线程池(Thread pool)来分发函数调用。因此你的Service需要能够处理多进程触发的AIDL Call,换句话来说,AIDL接口的实现必须是Thread-safe的。
  • 关键字oneway 可以修改远程调用的的行为,当使用oneway关键字时,remote call调用后立即返回,有点类似异步调用。

定义AIDL 接口的步骤如下:

AIDL接口定义使用Java Interface语法定义在 .aidl文件中,然后必须同时放在Service和Client 的 src目录下。 当使用Eclipse 编译时,Android SDK工具会根据 .aidl的接口定义自动生成对应的IBinder接口定义 (定义在gen目录下) Service必须实现由这个IBinder接口定义。 Client然后可以通过绑定Service来访问这些方法。

1. 创建. aidl 文件

AIDL接口定义使用和Java Interface定义同样的语法,每个.aidl文件只能定义一个调用接口,而且只能定义接口方法,不能含有静态变量定义。AIDL缺省支持 int ,long, char, boolean, String, CharSequence, List ,Map 变量类型,也可以引用其它 .aidl中定义的类型。

下面是IRemoteService.aidl 的定义:

package com.example.android.apis.app;
import com.example.android.apis.app.IRemoteServiceCallback;
/**
* Example of defining an interface for calling on to a remote service
* (running in another process).
*/
interface IRemoteService {
/**
* Often you want to allow a service to call back to its clients.
* This shows how to do so, by registering a callback interface with
* the service.
*/
void registerCallback(IRemoteServiceCallback cb);
/**
* Remove a previously registered callback interface.
*/
void unregisterCallback(IRemoteServiceCallback cb);<br />
}

编译时,Android SDK 工具自动在gen目录下生成对应的 IRemoteService.java。

2. 实现这个AIDL接口

编译时,Android SDK 工具自动在gen目录下生成对应的 IRemoteService.java,这个文件中会定义一个子类Stub (IRemoteService.Stub),Stub定义了由.aidl 定义的抽象方法实现。除此之外,Stub还定义了几个Help 方法,比如asInterface() 参数为IBinder对象(通常由Client中的onServiceConnected()传入),返回一个Stub实例供Client使用。

下面是IRemoteService.Stub 在 RemoteService中的实现:

    /**
* The IRemoteInterface is defined through IDL
*/
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public void registerCallback(IRemoteServiceCallback cb) {
if (cb != null) mCallbacks.register(cb);
}
public void unregisterCallback(IRemoteServiceCallback cb) {
if (cb != null) mCallbacks.unregister(cb);
}
};

3. Expose the interface to Clients

在定义.aidl 和实现AIDL接口定义后,就需要将这个接口提供给Client使用。方法是派生Service并提供onBind方法返回上面实现的Stub的一个实例。

public class RemoteService extends Service {
...
@Override
public IBinder onBind(Intent intent) {
// Select the interface to return. If your service only implements
// a single interface, you can just return it here without checking
// the Intent.
if (IRemoteService.class.getName().equals(intent.getAction())) {
return mBinder;
}
if (ISecondary.class.getName().equals(intent.getAction())) {
return mSecondaryBinder;
}
return null;
}

RemoteService 中定义了两个.aidl 接口可供Client使用IRemoteService.aidl和ISecondary.aidl。

有了AIDL定义并在Service中定义了可供Client使用的AIDL实现。下面再来看看Client的实现步骤:

  1. 将. aidl定义包含着 src目录下,由于本例Service ,Client 都包含在ApiDemos中,.aidl已在src中定义了。
  2. 根据.aidl接口生成IBinder接口定义(编译时由Android SDK工具自动生成)。
  3. 实现ServiceConnection接口
  4. 调用Context.bindService 来绑定需调用的Service。
  5. 在ServiceConnection 的onServiceConnected方法中,根据传入的IBinder对象(被调用的Service),使用 YourInterfaceName.Stub.asInterface((IBinder)service)) 将 service转换为YourInterfaceName类型。
  6. 调用YourInterfaceName定义的方法,这里需要捕获DeadObjectException 异常,DeadObjectException会在链接断裂时抛出。
  7. 使用完Service,使用Context.unbindService断开与Service之间的绑定。

Remote Service Binding 例子中 Service 端实现了两个Service:

  • IRemoteService :提供registerCallback,unregisterCallback用来在RemoteCallbackList 中注册或注销一个Client的Callback。
  • ISecondary: 实际Client会调用的服务,getPid返回当前进程Process ID。basicTypes 介绍了一般参数类型用法,本例中Client为使用。
        /**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = IRemoteService.Stub.asInterface(service);
mKillButton.setEnabled(true);
mCallbackText.setText("Attached."); // We want to monitor the service for as long as we are
// connected to it.
try {
mService.registerCallback(mCallback);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
} // As part of the sample, tell the user what happened.
Toast.makeText(Binding.this, R.string.remote_service_connected,
Toast.LENGTH_SHORT).show();
} public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mKillButton.setEnabled(false);
mCallbackText.setText("Disconnected."); // As part of the sample, tell the user what happened.
Toast.makeText(Binding.this, R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};

在RemoteService 的主线程中定义了一个mHandler,它每隔1秒钟将value值加1,并通过IRemoteServiceCallback 将这个新值发给注册过的Client。

                    // Up it goes.
int value = ++mValue; // Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
mCallbacks.getBroadcastItem(i).valueChanged(value);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast(); // Repeat every 1 second.
sendMessageDelayed(obtainMessage(REPORT_MSG), 1*1000);

当运行这个示例时,如果没有事先运行Remote Service Controller 的start Service ,这个值会从1开始,每隔1秒钟加1.

再来看看Client如何绑定Service的。

bindService(new Intent(IRemoteService.class.getName()),
mConnection, Context.BIND_AUTO_CREATE);
bindService(new Intent(ISecondary.class.getName()),
mSecondaryConnection, Context.BIND_AUTO_CREATE);

注意这里的Context.BIND_AUTO_CREATE,这意味这如果在绑定的过程中,如果Service由于某种原因被Destroy 了,Android还会自动重新启动被绑定的Service。你可以点击Kill Process 杀死Service看看结果 :-)。

        private OnClickListener mKillListener = new OnClickListener() {
public void onClick(View v) {
// To kill the process hosting our service, we need to know its
// PID. Conveniently our service has a call that will return
// to us that information.
if (mSecondaryService != null) {
try {
int pid = mSecondaryService.getPid();
// Note that, though this API allows us to request to
// kill any process based on its PID, the kernel will
// still impose standard restrictions on which PIDs you
// are actually able to kill. Typically this means only
// the process running your application and any additional
// processes created by that app as shown here; packages
// sharing a common UID will also be able to kill each
// other's processes.
Process.killProcess(pid);
mCallbackText.setText("Killed service process.");
} catch (RemoteException ex) {
// Recover gracefully from the process hosting the
// server dying.
// Just for purposes of the sample, put up a notification.
Toast.makeText(Binding.this,
R.string.remote_call_failed,
Toast.LENGTH_SHORT).show();
}
}
}
};

在RemoteService.Binding 中也定义了一个mHandler,是用来显示从Service发回的当前的Value值,Servcie发回Client的接口 IRemoteServiceCallback 同样也需要对应的AIDL定义。使用mHandler是因为valueChanged 需要更新UI。Android 系统Handler用法简介

本例按unBind Service 会解除与Service之间的绑定,同样如果没有事先运行Remote Service Controller 的start Service,这个操作会导致Destory Service,因为”Bound” Service 在没有与之绑定的Service是自动退出,下次绑定时,Value又会从1开始计数。

此外本例中用到了Android.os.RemoteCallbackList 这个类是为了方便管理Remote调用时向Client发送Callback而提供的,具体不详述了,可以参见http://developer.android.com/reference/android/os/RemoteCallbackList.html

【起航计划 037】2015 起航计划 Android APIDemo的魔鬼步伐 36 App->Service->Remote Service Binding AIDL实现不同进程间调用服务接口 kill 进程的更多相关文章

  1. 【起航计划 002】2015 起航计划 Android APIDemo的魔鬼步伐 01

    本文链接:[起航计划 002]2015 起航计划 Android APIDemo的魔鬼步伐 01 参考链接:http://blog.csdn.net/column/details/mapdigitap ...

  2. 【起航计划 031】2015 起航计划 Android APIDemo的魔鬼步伐 30 App->Preferences->Advanced preferences 自定义preference OnPreferenceChangeListener

    前篇文章Android ApiDemo示例解析(31):App->Preferences->Launching preferences 中用到了Advanced preferences 中 ...

  3. 【起航计划 027】2015 起航计划 Android APIDemo的魔鬼步伐 26 App->Preferences->Preferences from XML 偏好设置界面

    我们在前面的例子Android ApiDemo示例解析(9):App->Activity->Persistent State 介绍了可以使用Shared Preferences来存储一些状 ...

  4. 【起航计划 020】2015 起航计划 Android APIDemo的魔鬼步伐 19 App->Dialog Dialog样式

    这个例子的主Activity定义在AlertDialogSamples.java 主要用来介绍类AlertDialog的用法,AlertDialog提供的功能是多样的: 显示消息给用户,并可提供一到三 ...

  5. 【起航计划 012】2015 起航计划 Android APIDemo的魔鬼步伐 11 App->Activity->Save & Restore State onSaveInstanceState onRestoreInstanceState

    Save & Restore State与之前的例子Android ApiDemo示例解析(9):App->Activity->Persistent State 实现的UI类似,但 ...

  6. 【起航计划 034】2015 起航计划 Android APIDemo的魔鬼步伐 33 App->Service->Local Service Binding 绑定服务 ServiceConnection Binder

    本例和下列Local Service Controller 的Activity代码都定义在LocalServiceActivities.Java 中,作为LocalServiceActivities ...

  7. 【起航计划 033】2015 起航计划 Android APIDemo的魔鬼步伐 32 App->Service->Foreground Service Controller service使用,共享service,前台服务,onStartCommand

    Android系统也提供了一种称为“Service”的组件通常在后台运行.Activity 可以用来启动一个Service,Service启动后可以保持在后台一直运行,即使启动它的Activity退出 ...

  8. 【起航计划 009】2015 起航计划 Android APIDemo的魔鬼步伐 08 App->Activity->QuickContactsDemo 联系人 ResourceCursorAdapter使用 QuickContactBadge使用

    QuickContactsDemo示例介绍了如何使用Content Provider来访问Android系统的Contacts 数据库. Content Provider为不同应用之间共享数据提供了统 ...

  9. 【起航计划 003】2015 起航计划 Android APIDemo的魔鬼步伐 02 SimpleAdapter,ListActivity,PackageManager参考

    01 API Demos ApiDemos 详细介绍了Android平台主要的 API,android 5.0主要包括下图几个大类,涵盖了数百api示例:

随机推荐

  1. python pika简单实现RabbitMQ通信

    Windows上安装及启动RabbitMQ https://blog.csdn.net/hzw19920329/article/details/53156015 安装python pika库 pip ...

  2. 记一个SpringBoot中属性注入失败的问题Consider defining a bean of type ''' in your configuration

    今天遇到的一个问题: 代码检查了好几次,都没有错误,但是启动时就会报错Consider defining a bean of type ''' in your configuration. 启动类在c ...

  3. tornado 09 cookie和session

    tornado 09 cookie和session 一.cookie #有什么办法能够让浏览器记住登录信息,下次再打开的时候可以自动登录?网站是如何记录登录信息的? class SetCookieHa ...

  4. Kibana6.x.x源码分析--如何自定义savedObjectType对象

    上篇说到了如何使用kibana自带的savedObjectType对象,现在我们来自定义一个自己的savedObjectType. 下面的截图是我自己模仿写的保存对象,以及如何在kibana插件中注册 ...

  5. 【算法笔记】B1003 我要通过!

    1003 我要通过! (20 分) “答案正确”是自动判题系统给出的最令人欢喜的回复.本题属于 PAT 的“答案正确”大派送 —— 只要读入的字符串满足下列条件,系统就输出“答案正确”,否则输出“答案 ...

  6. 在URL地址栏中显示ico

             <!-- 在URL地址栏中显示ico -->         <link Rel="SHORTCUT ICON" href="imag ...

  7. AJAX概述和简单使用

    一.ajax概述: asynchronous javascript and xml ,用于异步的向服务器发出请求,接收数据的 一种技术. 在整个过程中:页面无刷新,不打断用户的操作: 按需要获取数据, ...

  8. I Hate It(线段树区间最值,单点更新)-------------蓝桥备战系列

    很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少.  这让很多学生很反感.  不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问.当然,老 ...

  9. my31_MGR单写模式压测以及对比普通从库记录

    场景MGR单写模式三节点,db46写节点,db47/db48为读节点工具sysbencn.压测15个小时,db46上18线程纯写,12线程oltp混合测试,db48上12线程select在压测2个小时 ...

  10. groovy——运行方式、基本语法、引入方式、metaClass

    jvm运行groovy类有两种方式: 1.使用groovyc编译所有的*.groovy为java的*.class文件,把这些*.class文件放在java类路径中,通过java类加载器来加载这些类. ...