一、编写服务端代码
1. 首先编写AndroidManifest.xml文件:
| 01 | <?xml version="1.0" encoding="utf-8"?> | |
| 02 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | |
| 03 | package="com.braincol.aidl.service" | |
| 04 | android:versionCode="1" | |
| 05 | android:versionName="1.0"> | |
| 06 | <uses-sdk android:minSdkVersion="8" /> | |
| 07 |
| ||
| 08 | <application android:icon="@drawable/icon" android:label="@string/app_name"> | ||
| 09 |
| |
| 10 | <service android:name="RemoteService"> | |
| 11 | <intent-filter> | ||
| 12 | <action android:name="com.braincol.aidl.remote.webpage"/> | ||
| 13 | </intent-filter> | |
| 14 | </service> | |
| 15 |
| |
| 16 | </application> | |
| 17 | </manifest> |
可以看到服务端的包名为:com.braincol.aidl.service,且该服务端只需一个service组件提供AIDL服务,service组件的名称为RemoteService,这是待会要实现的Service子类。其中<action android:name="com.braincol.aidl.remote.webpage"/> ,指定了action名称为"com.braincol.aidl.remote.webpage", 客户端会通过该action的名称来找到并连接该服务端。
2. 创建RemoteWebPage.aidl文件
在包com.braincol.aidl.service下创建RemoteWebPage.aidl文件:
| 1 | package com.braincol.aidl.service; | |
| 2 | interface RemoteWebPage { | |
| 3 | String getCurrentPageUrl(); | |
| 4 | } | |
可以看到内容很简单,该文件中包含一个RemoteWebPage 接口,并且接口中只有getCurrentPageUrl()这么一个方法,后面的客户端将通过这里提供的getCurrentPageUrl()方法获取想要的信息。
3.生成RemoteWebPage.java文件
保存并编译该工程(在eclipse中编译)会看到 gen/ 目录下的com.braincol.aidl.service包下出现了一个RemoteWebPage.java文件:
| 01 | /* | |
| 02 | * This file is auto-generated. DO NOT MODIFY. | |
| 03 | * Original file: F:\\workspace\\android\\AIDL-simple\\AIDLService\\src\\com\\braincol\\aidl\\service\\RemoteWebPage.aidl | ||
| 04 | */ | ||
| 05 | package com.braincol.aidl.service; | |
| 06 | public interface RemoteWebPage extends android.os.IInterface | |
| 07 | { | |
| 08 | /** Local-side IPC implementation stub class. */ | |
| 09 | public static abstract class Stub extends android.os.Binder implements com.braincol.aidl.service.RemoteWebPage | |
| 10 | { | |
| 11 | private static final java.lang.String DESCRIPTOR = "com.braincol.aidl.service.RemoteWebPage"; | |
| 12 | /** Construct the stub at attach it to the interface. */ | |
| 13 | public Stub() | |
| 14 | { | |
| 15 | this.attachInterface(this, DESCRIPTOR); | |
| 16 | } | |
| 17 | /** | |
| 18 | * Cast an IBinder object into an com.braincol.aidl.service.RemoteWebPage interface, | |
| 19 | * generating a proxy if needed. | |
| 20 | */ | |
| 21 | public static com.braincol.aidl.service.RemoteWebPage asInterface(android.os.IBinder obj) | |
| 22 | { | |
| 23 | if ((obj==null)) { | |
| 24 | return null; | |
| 25 | } | |
| 26 | android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR); | |
| 27 | if (((iin!=null)&&(iin instanceof com.braincol.aidl.service.RemoteWebPage))) { | |
| 28 | return ((com.braincol.aidl.service.RemoteWebPage)iin); | |
| 29 | } | ||
| 30 | return new com.braincol.aidl.service.RemoteWebPage.Stub.Proxy(obj); | ||
| 31 | } | |
| 32 | public android.os.IBinder asBinder() | |
| 33 | { | |
| 34 | return this; | |
| 35 | } | |
| 36 | @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException | |
| 37 | { | |
| 38 | switch (code) | |
| 39 | { | |
| 40 | case INTERFACE_TRANSACTION: | |
| 41 | { | |
| 42 | reply.writeString(DESCRIPTOR); | |
| 43 | return true; | |
| 44 | } | |
| 45 | case TRANSACTION_getCurrentPageUrl: | |
| 46 | { | |
| 47 | data.enforceInterface(DESCRIPTOR); | |
| 48 | java.lang.String _result = this.getCurrentPageUrl(); | |
| 49 | reply.writeNoException(); | |
| 50 | reply.writeString(_result); | |
| 51 | return true; | |
| 52 | } | |
| 53 | } | |
| 54 | return super.onTransact(code, data, reply, flags); | |
| 55 | } | ||
| 56 | private static class Proxy implements com.braincol.aidl.service.RemoteWebPage | ||
| 57 | { | |
| 58 | private android.os.IBinder mRemote; | |
| 59 | Proxy(android.os.IBinder remote) | |
| 60 | { | |
| 61 | mRemote = remote; | |
| 62 | } | |
| 63 | public android.os.IBinder asBinder() | |
| 64 | { | |
| 65 | return mRemote; | |
| 66 | } | |
| 67 | public java.lang.String getInterfaceDescriptor() | |
| 68 | { | |
| 69 | return DESCRIPTOR; | |
| 70 | } | |
| 71 | public java.lang.String getCurrentPageUrl() throws android.os.RemoteException | |
| 72 | { | |
| 73 | android.os.Parcel _data = android.os.Parcel.obtain(); |
| 74 | android.os.Parcel _reply = android.os.Parcel.obtain(); |
| 75 | java.lang.String _result; | |
| 76 | try { | |
| 77 | _data.writeInterfaceToken(DESCRIPTOR); | ||
| 78 | mRemote.transact(Stub.TRANSACTION_getCurrentPageUrl, _data, _reply, 0); | ||
| 79 | _reply.readException(); | |
| 80 | _result = _reply.readString(); | |
| 81 | } | |
| 82 | finally { | |
| 83 | _reply.recycle(); | |
| 84 | _data.recycle(); | |
| 85 | } | |
| 86 | return _result; | |
| 87 | } | |
| 88 | } | |
| 89 | static final int TRANSACTION_getCurrentPageUrl = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); | |
| 90 | } | |
| 91 | public java.lang.String getCurrentPageUrl() throws android.os.RemoteException; | |
| 92 | } | |
这个文件是Android SDK工具根据RemoteWebPage.aidl自动生成的,不要尝试着去修改该文件(改了也白改)。可以看到RemoteWebPage接口内包含了一个名为Stub的抽象的内部类,该类声明了RemoteWebPage.aidl中描述的方法getCurrentPageUrl(),并且还定义了少量的辅助方法Stub还定义了少量的辅助方法,尤其是asInterface(),通过它或以获得IBinder(当applicationContext.bindService()成功调用时传递到客户端的onServiceConnected())并且返回用于调用IPC方法的接口实例,更多细节参见Calling an IPC Method。
4. 编写RemoteService.java
为了实现AIDL通信,必须在RemoteService类中实现RemoteWebPage.Stub接口,然后RemoteWebPage.Stbu内的相关方法,下面是RemoteService.java的代码:
| 01 | package com.braincol.aidl.service; | |
| 02 |
| |
| 03 | import android.app.Service; | |
| 04 | import android.content.Intent; | |
| 05 | import android.os.IBinder; | |
| 06 | import android.os.RemoteException; | |
| 07 | import android.util.Log; | |
| 08 | /** | |
| 09 | * | |
| 10 | * @author briancol | |
| 11 | * @description 提供service | |
| 12 | * | |
| 13 | */ | |
| 14 | public class RemoteService extends Service { | |
| 15 | private final static String TAG = "RemoteService"; | |
| 16 | @Override | |
| 17 | public IBinder onBind(Intent intent) { | |
| 18 | Log.i(TAG, "OnBind"); | |
| 19 | return new MyBinder(); | |
| 20 | } | |
| 21 |
| |
| 22 | private class MyBinder extends RemoteWebPage.Stub{ | |
| 23 | @Override | |
| 24 | public String getCurrentPageUrl() throws RemoteException{ | |
| 25 | return "http://www.cnblogs.com/hibraincol/"; | |
| 26 | } | |
| 27 | } | |
| 28 | } | |
这样MyBinder就是一个RemoteWebPage.Stub类得子类,这样就可以通过RemoteService向客户端暴露AIDL接口了(MyBinder )。现在,如果客户端(比如一个Activity)调用bindService()来连接该服务端(RemoteService) ,客户端的onServiceConnected()回调函数将会获得从服务端(RemoteService )的onBind()返回的MyBinder对象。
在这里总结下服务端的编写流程:
1. 创建.aidl文件:
该文件(YourInterface.aidl)定义了客户端可用的方法和数据的接口
2. 实现这个接口:
Android SDK将会根据你的.aidl文件产生AIDL接口。生成的接口包含一个名为Stub的抽象内部类,该类声明了所有.aidl中描述的方法,你必须在代码里继承该Stub类并且实现.aidl中定义的方法。
3.向客户端公开服务端的接口:
实现一个Service,并且在onBinder方法中返回第2步中实现的那个Stub类的子类(实现类)。
至此,服务端的代码就编写完成了。 下面开始编写客户端。
二、编写客户端代码
因为客户端和服务端不在同一个进程(应用程序)中,那么客户端也必须在src/目录下拥有和服务端同样的一份.aidl文件的拷贝(这样的同样是指:包名、类名、内容完全一样,可以把客户端的.aidl文件理解为代理),这样客户端将会通过这个RemoteWebPage.aidl文件在gen/目下生成和服务端一样的RemoteWebPage.java文件,然后客户端就可以通过该接口来访问服务端提供的方法了。下面是客户端的只要代码:
| 01 | package com.braincol.aidl.client; | |
| 02 |
| |
| 03 | import android.app.Activity; | |
| 04 | import android.content.ComponentName; | |
| 05 | import android.content.Context; | |
| 06 | import android.content.Intent; | |
| 07 | import android.content.ServiceConnection; | |
| 08 | import android.os.Bundle; | |
| 09 | import android.os.IBinder; | |
| 10 | import android.os.RemoteException; | |
| 11 | import android.util.Log; | |
| 12 | import android.view.View; | |
| 13 | import android.view.View.OnClickListener; | |
| 14 | import android.widget.Button; | |
| 15 | import android.widget.TextView; | |
| 16 | import com.braincol.aidl.service.RemoteWebPage; | |
| 17 |
| ||
| 18 | public class ClientActivity extends Activity implements OnClickListener { | ||
| 19 | private final static String TAG="ClientActivity"; | |
| 20 | TextView textView ; | |
| 21 | Button btn_bind ; | |
| 22 | Button btn_getAllInfo; | |
| 23 | String actionName = "com.braincol.aidl.remote.webpage"; | |
| 24 | RemoteWebPage remoteWebPage=null; | |
| 25 | String allInfo = null; | |
| 26 | boolean isBinded=false; | |
| 27 |
| |
| 28 | @Override | |
| 29 | public void onCreate(Bundle savedInstanceState) { | |
| 30 | super.onCreate(savedInstanceState); | |
| 31 | setContentView(R.layout.main); | |
| 32 | textView = (TextView) findViewById(R.id.textView); | |
| 33 | btn_bind = (Button) findViewById(R.id.btn_bind); | |
| 34 | btn_getAllInfo = (Button)findViewById(R.id.btn_allinfo); | |
| 35 |
| |
| 36 | btn_getAllInfo.setEnabled(false); | |
| 37 |
| |
| 38 | btn_bind.setOnClickListener(this); | |
| 39 | btn_getAllInfo.setOnClickListener(this); | |
| 40 | } | |
| 41 | @Override | |
| 42 | protected void onPause(){ | |
| 43 | super.onPause(); | |
| 44 | Log.d(TAG,"onPause"); | |
| 45 | if(isBinded){ | |
| 46 | Log.d(TAG,"unbind"); | |
| 47 | unbindService(connection); | |
| 48 | } | |
| 49 | } | |
| 50 | private class MyServiceConnection implements ServiceConnection{ | |
| 51 |
| |
| 52 | @Override | |
| 53 | public void onServiceConnected(ComponentName name, IBinder service) { | |
| 54 | Log.i(TAG, "建立连接..."); | |
| 55 | remoteWebPage = RemoteWebPage.Stub.asInterface(service); | |
| 56 | if(remoteWebPage==null){ | |
| 57 | textView.setText("bind service failed!"); | |
| 58 | return; | |
| 59 | } | |
| 60 | try { | |
| 61 | isBinded=true; | |
| 62 | btn_bind.setText("断开"); | |
| 63 | textView.setText("已连接!"); | |
| 64 | allInfo = remoteWebPage.getCurrentPageUrl(); | |
| 65 | btn_getAllInfo.setEnabled(true); | |
| 66 | } catch (RemoteException e) { | |
| 67 | e.printStackTrace(); | |
| 68 | } | |
| 69 | } | |
| 70 |
| |
| 71 | @Override | |
| 72 | public void onServiceDisconnected(ComponentName name) { | |
| 73 | Log.i(TAG, "onServiceDisconnected..."); | |
| 74 | } | |
| 75 |
| |
| 76 | } | |
| 77 | MyServiceConnection connection = new MyServiceConnection(); | |
| 78 |
| |
| 79 | @Override | |
| 80 | public void onClick(View v) { | |
| 81 | if(v==this.btn_bind){ | |
| 82 | if(!isBinded){ | |
| 83 | Intent intent = new Intent(actionName); | |
| 84 | bindService(intent, connection, Context.BIND_AUTO_CREATE); | |
| 85 | }else{ | |
| 86 | Log.i(TAG, "断开连接..."); | |
| 87 | unbindService(connection); | |
| 88 | btn_getAllInfo.setEnabled(false); | |
| 89 | btn_bind.setText("连接"); | |
| 90 | isBinded = false; | |
| 91 | textView.setText("已断开连接!"); | |
| 92 | } | |
| 93 | }else if(v==this.btn_getAllInfo){ | |
| 94 | textView.setText(allInfo); | |
| 95 | } | |
| 96 |
| |
| 97 | } | |
| 98 | } | |
上面的代码中类MyServiceConnection实现了ServiceConnection类,在MyServiceConnection类的onServiceConnected方法中通过RemoteWebPage.Stub.asInterface(service)获取到远端服务端的RemoteWebPage接口对象remoteWebPage,这样就可以通过remoteWebPage.getCurrentPageUrl()获取到服务端提供的相关的信息。客户端通过bindService()方法绑定远程服务端,通过unbindService()断开连接。连接客户端的相关的代码为:
| 1 | Intent intent = new Intent(actionName); | |
| 2 | bindService(intent, connection, Context.BIND_AUTO_CREATE); | |
客户端就是通过actionName(com.braincol.aidl.remote.webpage)来找到服务端。
下面总结下客户端的编写流程:
1. 在 src/ 目录下包含.adil文件。
2. 声明一个IBinder接口(通过.aidl文件生成的)的实例。
3. 实现ServiceConnection.
4. 调用Context.bindService()绑定你的ServiceConnection实现类的对象(也就是远程服务端)。
5. 在onServiceConnected()方法中会接收到IBinder对象(也就是服务端),调用YourInterfaceName.Stub.asInterface((IBinder)service)将返回值转换为YourInterface类型。
6. 调用接口中定义的方法,并且应该总是捕获连接被打断时抛出的DeadObjectException异常,这是远端方法可能会抛出唯一异常。
7. 调用Context.unbindService()方法断开连接。
下面给出本示例源码的下载地址:
http://download.youkuaiyun.com/detail/Niosm/3593187
在附一个稍微复杂点的例子(通过IPC传递Parcelable对象):
http://download.youkuaiyun.com/detail/Niosm/3593376
摘自:http://www.cnblogs.com/hibraincol/archive/2011/09/11/2173780.html
本文详细介绍了如何使用AIDL(Android Interface Definition Language)实现服务端与客户端之间的通信。包括创建服务端代码,如AndroidManifest.xml、RemoteWebPage.aidl文件、RemoteService.java;以及客户端代码,涉及创建RemoteWebPage接口实例、实现ServiceConnection、绑定服务端等关键步骤。通过实例代码解释了服务端如何通过继承Stub类实现接口方法,以及客户端如何通过bindService方法与服务端进行交互。最后提供了代码示例和链接,以便读者深入理解并实践AIDL通信机制。

被折叠的 条评论
为什么被折叠?



