Service官方教程(11)Bound Service示例之2-AIDL 定义跨进程接口并通信
Android Interface Definition Language (AIDL)
AIDL (Android Interface Definition Language) is similar to other IDLs you might have worked with. It allows you to define the programming interface that both the client and service agree upon in order to communicate with each other using interprocess communication (IPC). On Android, one process cannot normally access the memory of another process. So to talk, they need to decompose their objects into primitives that the operating system can understand, and marshall the objects across that boundary for you. The code to do that marshalling is tedious to write, so Android handles it for you with AIDL.
Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.
注意:只在你的服务需要跨进程通信并且处理多线程时,才用的到AIDL,跨进程单线程时Messenger就可以搞定。
Before you begin designing your AIDL interface, be aware that calls to an AIDL interface are direct function calls. You should not make assumptions about the thread in which the call occurs. What happens is different depending on whether the call is from a thread in the local process or a remote process. Specifically:
- Calls made from the local process are executed in the same thread that is making the call. If this is your main UI thread, that thread continues to execute in the AIDL interface. If it is another thread, that is the one that executes your code in the service. Thus, if only local threads are accessing the service, you can completely control which threads are executing in it (but if that is the case, then you shouldn't be using AIDL at all, but should instead create the interface by implementing a Binder).
- Calls from a remote process are dispatched from a thread pool the platform maintains inside of your own process. You must be prepared for incoming calls from unknown threads, with multiple calls happening at the same time. In other words, an implementation of an AIDL interface must be completely thread-safe.
- The
oneway
keyword modifies the behavior of remote calls. When used, a remote call does not block; it simply sends the transaction data and immediately returns. The implementation of the interface eventually receives this as a regular call from theBinder
thread pool as a normal remote call. Ifoneway
is used with a local call, there is no impact and the call is still synchronous.
2.Defining an AIDL Interface
2.1 简介
You must define your AIDL interface in an .aidl
file using the Java programming language syntax, then save it in the source code (in the src/
directory) of both the application hosting the service and any other application that binds to the service.
.aidl 文件内定义在服务端、客户端使用的接口,这个文件在两边都要存在。
When you build each application that contains the .aidl
file, the Android SDK tools generate an IBinder
interface based on the .aidl
file and save it in the project's gen/
directory. The service must implement the IBinder
interface as appropriate. The client applications can then bind to the service and call methods from the IBinder
to perform IPC.
Android SDK 工具会根据.aidl文件生成相应接口,在 gen/目录下。
To create a bounded service using AIDL, follow these steps: AIDL步骤
- Create the .aidl file
This file defines the programming interface with method signatures.
- Implement the interface
The Android SDK tools generate an interface in the Java programming language, based on your
.aidl
file. This interface has an inner abstract class namedStub
that extendsBinder
and implements methods from your AIDL interface. You must extend theStub
class and implement the methods. - Expose the interface to clients
Implement a
Service
and overrideonBind()
to return your implementation of theStub
class.
Caution: Any changes that you make to your AIDL interface after your first release must remain backward compatible in order to avoid breaking other applications that use your service. That is, because your .aidl
file must be copied to other applications in order for them to access your service's interface, you must maintain support for the original interface.
在.aidl文件中定义接口时要注意兼容问题,因为它可能被拷贝到多个其它应用的代码中。
2.2 Create the .aidl file
AIDL uses a simple syntax that lets you declare an interface with one or more methods that can take parameters and return values. The parameters and return values can be of any type, even other AIDL-generated interfaces.
注意使用as时,要在使用.aidl接口的包下点生成aidl文件。不然生成到根包下,容易找不到定义。
.aidl中声明的参数和返回值可以是任意类型,甚至可以是在其它.aidl的定义的类型。
You must construct the .aidl
file using the Java programming language. Each .aidl
file must define a single interface and requires only the interface declaration and method signatures.
By default, AIDL supports the following data types:
AIDL支持的数据类型如下:
- All primitive types in the Java programming language (such as
int
,long
,char
,boolean
, and so on) String
CharSequence
List
All elements in the
List
must be one of the supported data types in this list or one of the other AIDL-generated interfaces or parcelables you've declared. AList
may optionally be used as a "generic" class (for example,List<String>
). The actual concrete class that the other side receives is always anArrayList
, although the method is generated to use theList
interface.注意,这里是List接口,其中的元素要实现parcelable,通常使用的是ArrayList。
Map
All elements in the
Map
must be one of the supported data types in this list or one of the other AIDL-generated interfaces or parcelables you've declared. Generic maps, (such as those of the formMap<String,Integer>
are not supported. The actual concrete class that the other side receives is always aHashMap
, although the method is generated to use theMap
interface.Map内的元素也要实现Parcelable,但是
Map<String,Integer>
不支持。
You must include an import
statement for each additional type not listed above, even if they are defined in the same package as your interface.
When defining your service interface, be aware that:
以下几点也要注意:
- Methods can take zero or more parameters, and return a value or void.
- All non-primitive parameters require a directional tag indicating which way the data goes. Either
in
,out
, orinout
(see the example below).Primitives are
in
by default, and cannot be otherwise.非基本数据类型要用,in,out,inout标示出数据的流动方向。基本类型默认用in标示。
Caution: You should limit the direction to what is truly needed, because marshalling parameters is expensive.
- All code comments included in the
.aidl
file are included in the generatedIBinder
interface (except for comments before the import and package statements).在package com.xxxx.xx和import以后的注释都被原样拷贝到生成的IBinder接口中。
- Only methods are supported; you cannot expose static fields in AIDL.
Here is an example .aidl
file:
// IRemoteService.aidl
// package前的注释不拷贝。
package com.example.android; // Declare any non-default types here with import statements /** Example service interface */
interface IRemoteService {
/** Request the process ID of this service, to do evil things with it. */
int getPid(); /** Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
Simply save your .aidl
file in your project's src/
directory and when you build your application, the SDK tools generate the IBinder
interface file in your project's gen/
directory. The generated file name matches the .aidl
file name, but with a .java
extension (for example, IRemoteService.aidl
results in IRemoteService.java
).
IRemoteService.aidl最终在gen/下生成IRemoteService.java
If you use Android Studio, the incremental build generates the binder class almost immediately. If you do not use Android Studio, then the Gradle tool generates the binder class next time you build your application—you should build your project with gradle assembleDebug
(or gradle assembleRelease
) as soon as you're finished writing the .aidl
file, so that your code can link against the generated class.
2.3 Implement the interface
When you build your application, the Android SDK tools generate a .java
interface file named after your .aidl
file. The generated interface includes a subclass named Stub
that is an abstract implementation of its parent interface (for example, YourInterface.Stub
) and declares all the methods from the .aidl
file.
Note: Stub
also defines a few helper methods, most notably asInterface()
, which takes an IBinder
(usually the one passed to a client's onServiceConnected()
callback method) and returns an instance of the stub interface. See the section Calling an IPC Method for more details on how to make this cast.
To implement the interface generated from the .aidl
, extend the generated Binder
interface (for example, YourInterface.Stub
) and implement the methods inherited from the .aidl
file.
Here is an example implementation of an interface called IRemoteService
(defined by the IRemoteService.aidl
example, above) using an anonymous instance:
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
Now the mBinder
is an instance of the Stub
class (a Binder
), which defines the RPC interface for the service. In the next step, this instance is exposed to clients so they can interact with the service.
There are a few rules you should be aware of when implementing your AIDL interface:
实现AIDL接口的注意事项:
- Incoming calls are not guaranteed to be executed on the main thread, so you need to think about multithreading from the start and properly build your service to be thread-safe.
- By default, RPC calls are synchronous. If you know that the service takes more than a few milliseconds to complete a request, you should not call it from the activity's main thread, because it might hang the application (Android might display an "Application is Not Responding" dialog)—you should usually call them from a separate thread in the client.
远程调用通常是同步的,不要在main线程进行远程调用。要启新线程。
- No exceptions that you throw are sent back to the caller.
2.4 Expose the interface to clients
Once you've implemented the interface for your service, you need to expose it to clients so they can bind to it. To expose the interface for your service, extend Service
and implement onBind()
to return an instance of your class that implements the generated Stub
(as discussed in the previous section). Here's an example service that exposes the IRemoteService
example interface to clients.
public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
} @Override
public IBinder onBind(Intent intent) {
// Return the interface
return mBinder;
} private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
}
Now, when a client (such as an activity) calls bindService()
to connect to this service, the client's onServiceConnected()
callback receives the mBinder
instance returned by the service's onBind()
method.
The client must also have access to the interface class, so if the client and service are in separate applications, then the client's application must have a copy of the .aidl
file in its src/
directory (which generates the android.os.Binder
interface—providing the client access to the AIDL methods).
如果客户端和服务器不在同一个应用内,那么要把.aidl文件拷贝到客户端代码中。
When the client receives the IBinder
in the onServiceConnected()
callback, it must call YourServiceInterface.Stub.asInterface(service)
to cast the returned parameter to YourServiceInterface
type. For example:
IRemoteService mIRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Following the example above for an AIDL interface,
// this gets an instance of the IRemoteInterface, which we can use to call on the service
mIRemoteService = IRemoteService.Stub.asInterface(service);
} // Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected");
mIRemoteService = null;
}
};
For more sample code, see the RemoteService.java
class in ApiDemos.
3.Passing Objects over IPC 远程通信传递对象
If you have a class that you would like to send from one process to another through an IPC interface, you can do that. However, you must ensure that the code for your class is available to the other side of the IPC channel and your class must support the Parcelable
interface. Supporting the Parcelable
interface is important because it allows the Android system to decompose objects into primitives that can be marshalled across processes.
To create a class that supports the Parcelable
protocol, you must do the following:
想要通过IPC在不同进程间传递的对象必需是Parcelable,实现它的步骤如下:
- Make your class implement the
Parcelable
interface. - Implement
writeToParcel
, which takes the current state of the object and writes it to aParcel
. - Add a static field called
CREATOR
to your class which is an object implementing theParcelable.Creator
interface. - Finally, create an
.aidl
file that declares your parcelable class (as shown for theRect.aidl
file, below).If you are using a custom build process, do not add the
.aidl
file to your build. Similar to a header file in the C language, this.aidl
file isn't compiled..aidl文件在客户端就像c的头文件一样。
AIDL uses these methods and fields in the code it generates to marshall and unmarshall your objects.
For example, here is a Rect.aidl
file to create a Rect
class that's parcelable:
package android.graphics; // Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;
And here is an example of how the Rect
class implements the Parcelable
protocol.
import android.os.Parcel;
import android.os.Parcelable; public final class Rect implements Parcelable {
public int left;
public int top;
public int right;
public int bottom; public static final Parcelable.Creator<Rect> CREATOR = new
Parcelable.Creator<Rect>() {
public Rect createFromParcel(Parcel in) {
return new Rect(in);
} public Rect[] newArray(int size) {
return new Rect[size];
}
}; public Rect() {
} private Rect(Parcel in) {
readFromParcel(in);
} public void writeToParcel(Parcel out) {
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
} public void readFromParcel(Parcel in) {
left = in.readInt();
top = in.readInt();
right = in.readInt();
bottom = in.readInt();
}
}
The marshalling in the Rect
class is pretty simple. Take a look at the other methods on Parcel
to see the other kinds of values you can write to a Parcel.
Warning: Don't forget the security implications of receiving data from other processes. In this case, the Rect
reads four numbers from the Parcel
, but it is up to you to ensure that these are within the acceptable range of values for whatever the caller is trying to do. See Security and Permissions for more information about how to keep your application secure from malware.
4.Calling an IPC Method 调用远程方法
Here are the steps a calling class must take to call a remote interface defined with AIDL:
- Include the
.aidl
file in the projectsrc/
directory. - Declare an instance of the
IBinder
interface (generated based on the AIDL). - Implement
ServiceConnection
. - Call
Context.bindService()
, passing in yourServiceConnection
implementation. - In your implementation of
onServiceConnected()
, you will receive anIBinder
instance (calledservice
). CallYourInterfaceName.Stub.asInterface((IBinder)service)
to cast the returned parameter to YourInterface type. - Call the methods that you defined on your interface. You should always trap
DeadObjectException
exceptions, which are thrown when the connection has broken; this will be the only exception thrown by remote methods. - To disconnect, call
Context.unbindService()
with the instance of your interface.
A few comments on calling an IPC service:
- Objects are reference counted across processes.
进程间通过引用计数持有对象?
- You can send anonymous objects as method arguments.
可以传递匿名对象当方法参数。
For more information about binding to a service, read the Bound Services document.
Here is some sample code demonstrating calling an AIDL-created service, taken from the Remote Service sample in the ApiDemos project.
public static class Binding extends Activity {
/** The primary interface we will be calling on the service. */
IRemoteService mService = null;
/** Another interface we use on the service. */
ISecondary mSecondaryService = null; Button mKillButton;
TextView mCallbackText; private boolean mIsBound; /**
* Standard initialization of this activity. Set up the UI, then wait
* for the user to poke it before doing anything.
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); setContentView(R.layout.remote_service_binding); // Watch for button clicks.
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(mUnbindListener);
mKillButton = (Button)findViewById(R.id.kill);
mKillButton.setOnClickListener(mKillListener);
mKillButton.setEnabled(false); mCallbackText = (TextView)findViewById(R.id.callback);
mCallbackText.setText("Not attached.");
} /**
* 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();
}
}; /**
* Class for interacting with the secondary interface of the service.
*/
private ServiceConnection mSecondaryConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// Connecting to a secondary interface is the same as any
// other interface.
mSecondaryService = ISecondary.Stub.asInterface(service);
mKillButton.setEnabled(true);
} public void onServiceDisconnected(ComponentName className) {
mSecondaryService = null;
mKillButton.setEnabled(false);
}
}; private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
// Establish a couple connections with the service, binding
// by interface names. This allows other applications to be
// installed that replace the remote service by implementing
// the same interface.
Intent intent = new Intent(Binding.this, RemoteService.class);
intent.setAction(IRemoteService.class.getName());
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
intent.setAction(ISecondary.class.getName());
bindService(intent, mSecondaryConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
mCallbackText.setText("Binding.");
}
}; private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
if (mIsBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
mService.unregisterCallback(mCallback);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
} // Detach our existing connection.
unbindService(mConnection);
unbindService(mSecondaryConnection);
mKillButton.setEnabled(false);
mIsBound = false;
mCallbackText.setText("Unbinding.");
}
}
}; 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();
}
}
}
}; // ----------------------------------------------------------------------
// Code showing how to deal with callbacks.
// ---------------------------------------------------------------------- /**
* This implementation is used to receive callbacks from the remote
* service.
*/
private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
/**
* This is called by the remote service regularly to tell us about
* new values. Note that IPC calls are dispatched through a thread
* pool running in each process, so the code executing here will
* NOT be running in our main thread like most other things -- so,
* to update the UI, we need to use a Handler to hop over there.
*/
public void valueChanged(int value) {
mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, ));
}
}; private static final int BUMP_MSG = ; private Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case BUMP_MSG:
mCallbackText.setText("Received from service: " + msg.arg1);
break;
default:
super.handleMessage(msg);
}
} };
}
Service官方教程(11)Bound Service示例之2-AIDL 定义跨进程接口并通信的更多相关文章
- Service官方教程(7)Bound Service示例之1-同进程下直接继承Service
Extending the Binder class If your service is used only by the local application and does not need t ...
- Service官方教程(10)Bound Service的生命周期函数
Managing the Lifecycle of a Bound Service When a service is unbound from all clients, the Android sy ...
- Service官方教程(8)Bound Service示例之2-跨进程使用Messenger
Compared to AIDL When you need to perform IPC, using a Messenger for your interface is simpler than ...
- Service官方教程(6)Bound Services主要用来实现通信服务,以及3种实现通信的方案简介。
1.Bound Services A bound service is the server in a client-server interface. A bound service allows ...
- Service官方教程(3)Bound Services
Bound Services 1.In this document The Basics Creating a Bound Service Extending the Binder class Usi ...
- node-webkit教程(11)Platform Service之shell
node-webkit教程(11)Platform Service之shell 文/玄魂 目录 node-webkit教程(10)Platform Service之shell 前言 11.1 She ...
- Service官方教程(2)*IntentService与Service示例、onStartCommand()3个返回值的含义。
1.Creating a Started Service A started service is one that another component starts by calling start ...
- Service官方教程(1)Started与Bound的区别、要实现的函数、声明service
Services 简介和分类 A Service is an application component that can perform long-running operations in the ...
- Service官方教程(9)绑定服务时的注意事项
Binding to a Service Application components (clients) can bind to a service by calling bindService() ...
随机推荐
- Linux 的 Socket IO 模型
前言 之前有看到用很幽默的方式讲解Windows的socket IO模型,借用这个故事,讲解下linux的socket IO模型: 老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系. 他 ...
- spring 学习资料
spring 官方教程 1.3.1 http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/index.html ...
- vue 获取当前时间 格式YYYY-MM-DD
函数封装: /** * 获取当前时间 * 格式YYYY-MM-DD */ Vue.prototype.getNowFormatDate = function() { var date = new Da ...
- Jenkins系列之-—03 修改Jenkins用户的密码
一.Jenkins修改用户密码 Jenkins用户的数据存放在JENKINS_HOME/users目录. 1. 打开忘记密码的用户文件夹,里面就一个文件config.xml.打开并找到<pass ...
- WebApi-路由机制 Visual Studio 2015中的常用调试技巧分享
WebApi-路由机制 一.WebApi路由机制是什么? 路由机制通俗点来说:其实就是WebApi框架将用户在浏览器中输入的Url地址和路由表中的路由进行匹配,并根据最终匹配的路由去寻找并匹配相应 ...
- 一起talk C栗子吧(第一百二十一回:C语言实例--线程知识体系图)
各位看官们.大家好,上一回中咱们说的线程属性的样例.这一回咱们说的样例是:线程知识体系图.闲话休提.言归正转. 让我们一起talk C栗子吧! 我们在前面的章回中介绍了与线程相关的知识,在今天的章回中 ...
- Linux(Ubuntu14.04)下Google Chrome / Chromium标题栏乱码问题
注:我使用的Linux发行版是Ubuntu 14.04,不同Linux发行版可能会有不同. 最近在使用Chromium的时候tab的标题栏中文显示乱码,在地址栏输入中文是同样时乱码,就像下图: 看起来 ...
- HBase 数据迁移
最近两年负责 HBase,经常被问到一些问题, 本着吸引一些粉丝.普及一点HBase 知识.服务一点阅读人群的目的,就先从 HBase 日常使用写起,后续逐渐深入数据设计.集群规划.性能调优.内核源码 ...
- C项目实践--图书管理系统(4)
前面已经把图书管理系统的所有功能模块都已实现完毕了,下面通过运行来分析该系统的操作流程并检验是否符合逻辑设计要求. 3.系统操作过程 F5 运行 1.登录系统 系统运行之后,提示输入用户名和密码,系统 ...
- Delphi全角转半角
function ToDBC( input :String):WideString;varc:WideString;i:Integer;beginc := input;for i:=1 to Leng ...