【起航计划 037】2015 起航计划 Android APIDemo的魔鬼步伐 36 App->Service->Remote Service Binding AIDL实现不同进程间调用服务接口 kill 进程
本例和下个例子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的实现步骤:
- 将. aidl定义包含着 src目录下,由于本例Service ,Client 都包含在ApiDemos中,.aidl已在src中定义了。
- 根据.aidl接口生成IBinder接口定义(编译时由Android SDK工具自动生成)。
- 实现ServiceConnection接口
- 调用Context.bindService 来绑定需调用的Service。
- 在ServiceConnection 的onServiceConnected方法中,根据传入的IBinder对象(被调用的Service),使用 YourInterfaceName.Stub.asInterface((IBinder)service)) 将 service转换为YourInterfaceName类型。
- 调用YourInterfaceName定义的方法,这里需要捕获DeadObjectException 异常,DeadObjectException会在链接断裂时抛出。
- 使用完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 进程的更多相关文章
- 【起航计划 002】2015 起航计划 Android APIDemo的魔鬼步伐 01
本文链接:[起航计划 002]2015 起航计划 Android APIDemo的魔鬼步伐 01 参考链接:http://blog.csdn.net/column/details/mapdigitap ...
- 【起航计划 031】2015 起航计划 Android APIDemo的魔鬼步伐 30 App->Preferences->Advanced preferences 自定义preference OnPreferenceChangeListener
前篇文章Android ApiDemo示例解析(31):App->Preferences->Launching preferences 中用到了Advanced preferences 中 ...
- 【起航计划 027】2015 起航计划 Android APIDemo的魔鬼步伐 26 App->Preferences->Preferences from XML 偏好设置界面
我们在前面的例子Android ApiDemo示例解析(9):App->Activity->Persistent State 介绍了可以使用Shared Preferences来存储一些状 ...
- 【起航计划 020】2015 起航计划 Android APIDemo的魔鬼步伐 19 App->Dialog Dialog样式
这个例子的主Activity定义在AlertDialogSamples.java 主要用来介绍类AlertDialog的用法,AlertDialog提供的功能是多样的: 显示消息给用户,并可提供一到三 ...
- 【起航计划 012】2015 起航计划 Android APIDemo的魔鬼步伐 11 App->Activity->Save & Restore State onSaveInstanceState onRestoreInstanceState
Save & Restore State与之前的例子Android ApiDemo示例解析(9):App->Activity->Persistent State 实现的UI类似,但 ...
- 【起航计划 034】2015 起航计划 Android APIDemo的魔鬼步伐 33 App->Service->Local Service Binding 绑定服务 ServiceConnection Binder
本例和下列Local Service Controller 的Activity代码都定义在LocalServiceActivities.Java 中,作为LocalServiceActivities ...
- 【起航计划 033】2015 起航计划 Android APIDemo的魔鬼步伐 32 App->Service->Foreground Service Controller service使用,共享service,前台服务,onStartCommand
Android系统也提供了一种称为“Service”的组件通常在后台运行.Activity 可以用来启动一个Service,Service启动后可以保持在后台一直运行,即使启动它的Activity退出 ...
- 【起航计划 009】2015 起航计划 Android APIDemo的魔鬼步伐 08 App->Activity->QuickContactsDemo 联系人 ResourceCursorAdapter使用 QuickContactBadge使用
QuickContactsDemo示例介绍了如何使用Content Provider来访问Android系统的Contacts 数据库. Content Provider为不同应用之间共享数据提供了统 ...
- 【起航计划 003】2015 起航计划 Android APIDemo的魔鬼步伐 02 SimpleAdapter,ListActivity,PackageManager参考
01 API Demos ApiDemos 详细介绍了Android平台主要的 API,android 5.0主要包括下图几个大类,涵盖了数百api示例:
随机推荐
- 19年PDYZ冬令营游记
我和卓越的那些事 ——2019年平度一中卓越计划冬令营 题前记: 正月十三那天,刚看完<流浪地球>,便接到了一个电话,老妈告诉我竟然一中组织了一个冬令营,并且起了一个很好的名字“卓越计 ...
- P2050 [NOI2012]美食节(费用流)
P2050 [NOI2012]美食节 P2053 [SCOI2007]修车的加强版 因为数据较大,一次性把所有边都加完会T 于是我们每次只连需要的边跑费用流 就是开始先连所有厨师做倒数第1道菜 跑费用 ...
- windows cmd下创建虚拟环境virtualenv
一:虚拟环境virtualenv 如果在一台电脑上, 想开发多个不同的项目, 需要用到同一个包的不同版本, 如果使用上面的命令, 在同一个目录下安装或者更新, 新版本会覆盖以前的版本, 其它的项目就无 ...
- 【算法笔记】B1009 说反话
1009 说反话 (20 分) 给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出. 输入格式: 测试输入包含一个测试用例,在一行内给出总长度不超过 80 的字符串.字符串由若干单词和若干空格 ...
- tensorflow-如何防止过拟合
回归:过拟合情况 / 分类过拟合 防止过拟合的方法有三种: 1 增加数据集 2 添加正则项 3 Dropout,意思就是训练的时候隐层神经元每次随机抽取部分参与训练.部分不参与 最后对之前普通神经网络 ...
- c语言实现队列的基本操作
话不多说,直接代码 #include"stdio.h" #include"stdlib.h" typedef struct QNode{ int date; s ...
- 超级详细全截图化VMware 安装ubantu
一,下载镜像 由于ubantu时国外源所以下载十分的缓慢 这里我用清华源下载:https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/18.10/ub ...
- 【Tensorflow】 Object_detection之配置Training Pipeline
参考:Configuring an object detection pipeline 1.config文件 配置好的config文件存放路径:object_detection/samples/con ...
- ife task0003学习笔记(二):JavaScript原型
function aaa(){} aaa.prototype.bbb=function(){} var obj1=new aaa() var obj2=new aaa() obj1和obj2都有一个属 ...
- C# 类型、对象、线程栈和托管堆在运行时的关系
我们将讨论类型.对象.线程栈和托管堆在运行时的相互关系,假定有以下两个类定义: internal class Employee { public int GetYearsEmplo ...