android: 使用AIDL實現進程間通信(附示例源碼下載)

關於AIDL的介紹及實現步驟等請參考:

http://www.cnblogs.com/hibraincol/archive/2011/09/06/2169325.html

本篇文章只是用一個實例來分析AIDL的實現。

本示例實現的是:AIDL客戶端通過AIDL接口獲取AIDL服務端中提供的webPage信息,下面詳述AIDL通信的實現步驟:

一、編寫服務端代碼

1. 首先編寫AndroidManifest.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.braincol.aidl.service"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />
 
    <application android:icon="@drawable/icon" android:label="@string/app_name">
 
    <service android:name="RemoteService">
        <intent-filter>
            <action android:name="com.braincol.aidl.remote.webpage"/>
        </intent-filter>
    </service>
 
    </application>
</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
2
3
4
package com.braincol.aidl.service;
 interface RemoteWebPage {
    String getCurrentPageUrl();    
}

可以看到內容很簡單,該文件中包含一個RemoteWebPage 接口,並且接口中只有getCurrentPageUrl()這麼一個方法,後面的客戶端將通過這裏提供的getCurrentPageUrl()方法獲取想要的信息。

3.生成RemoteWebPage.java文件

保存並編譯該工程(在eclipse中編譯)會看到 gen/ 目錄下的com.braincol.aidl.service包下出現了一個RemoteWebPage.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: F:\\workspace\\android\\AIDL-simple\\AIDLService\\src\\com\\braincol\\aidl\\service\\RemoteWebPage.aidl
 */
package com.braincol.aidl.service;
public interface RemoteWebPage extends android.os.IInterface
{
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.braincol.aidl.service.RemoteWebPage
    {
        private static final java.lang.String DESCRIPTOR = "com.braincol.aidl.service.RemoteWebPage";
        /** Construct the stub at attach it to the interface. */
        public Stub()
        {
            this.attachInterface(this, DESCRIPTOR);
        }
        /**
         * Cast an IBinder object into an com.braincol.aidl.service.RemoteWebPage interface,
         * generating a proxy if needed.
         */
        public static com.braincol.aidl.service.RemoteWebPage asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.braincol.aidl.service.RemoteWebPage))) {
                return ((com.braincol.aidl.service.RemoteWebPage)iin);
            }
            return new com.braincol.aidl.service.RemoteWebPage.Stub.Proxy(obj);
        }
        public android.os.IBinder asBinder()
        {
            return this;
        }
        @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
        {
            switch (code)
            {
            case INTERFACE_TRANSACTION:
            {
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_getCurrentPageUrl:
            {
                data.enforceInterface(DESCRIPTOR);
                java.lang.String _result = this.getCurrentPageUrl();
                reply.writeNoException();
                reply.writeString(_result);
                return true;
            }
            }
            return super.onTransact(code, data, reply, flags);
        }
        private static class Proxy implements com.braincol.aidl.service.RemoteWebPage
        {
            private android.os.IBinder mRemote;
            Proxy(android.os.IBinder remote)
            {
                mRemote = remote;
            }
            public android.os.IBinder asBinder()
            {
                return mRemote;
            }
            public java.lang.String getInterfaceDescriptor()
            {
                return DESCRIPTOR;
            }
            public java.lang.String getCurrentPageUrl() throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getCurrentPageUrl, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
        static final int TRANSACTION_getCurrentPageUrl = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }
    public java.lang.String getCurrentPageUrl() throws android.os.RemoteException;
}

這個文件是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的代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.braincol.aidl.service;
 
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
/**
 *
 * @author briancol
 * @description 提供service
 *
 */
public class RemoteService extends Service {
    private final static String TAG = "RemoteService";
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "OnBind");
        return new MyBinder();
    }
 
    private class MyBinder extends RemoteWebPage.Stub{
        @Override
        public String getCurrentPageUrl() throws RemoteException{
            return "http://www.cnblogs.com/hibraincol/";
        }
    }
}

這樣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文件,然後客戶端就可以通過該接口來訪問服務端提供的方法了。下面是客戶端的只要代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package com.braincol.aidl.client;
 
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import com.braincol.aidl.service.RemoteWebPage;
 
public class ClientActivity extends Activity implements OnClickListener {
    private final static String TAG="ClientActivity";
    TextView textView ;
    Button btn_bind ;
    Button btn_getAllInfo;
    String actionName = "com.braincol.aidl.remote.webpage";
    RemoteWebPage remoteWebPage=null;
    String allInfo = null;
    boolean isBinded=false;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        textView = (TextView) findViewById(R.id.textView);
        btn_bind = (Button) findViewById(R.id.btn_bind);
        btn_getAllInfo = (Button)findViewById(R.id.btn_allinfo);
 
        btn_getAllInfo.setEnabled(false);
 
        btn_bind.setOnClickListener(this);
        btn_getAllInfo.setOnClickListener(this);
    }
    @Override
    protected void onPause(){
        super.onPause();
        Log.d(TAG,"onPause");
        if(isBinded){
            Log.d(TAG,"unbind");
            unbindService(connection);   
        }
    }
    private class MyServiceConnection implements ServiceConnection{
 
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, "建立連接...");
            remoteWebPage = RemoteWebPage.Stub.asInterface(service);
            if(remoteWebPage==null){
                textView.setText("bind service failed!");   
                return;
            }
            try {
                isBinded=true;
                btn_bind.setText("斷開");
                textView.setText("已連接!");
                allInfo = remoteWebPage.getCurrentPageUrl();
                btn_getAllInfo.setEnabled(true);   
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
 
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "onServiceDisconnected...");
        }
 
    }
    MyServiceConnection connection = new MyServiceConnection();
 
    @Override
    public void onClick(View v) {
        if(v==this.btn_bind){
            if(!isBinded){
                Intent intent  = new Intent(actionName);
                bindService(intent, connection, Context.BIND_AUTO_CREATE);               
            }else{
                Log.i(TAG, "斷開連接...");
                unbindService(connection);
                btn_getAllInfo.setEnabled(false);   
                btn_bind.setText("連接");
                isBinded = false;
                textView.setText("已斷開連接!");
            }
        }else if(v==this.btn_getAllInfo){
            textView.setText(allInfo);
        }
 
    }
}

 

上面的代碼中類MyServiceConnection實現了ServiceConnection類,在MyServiceConnection類的onServiceConnected方法中通過RemoteWebPage.Stub.asInterface(service)獲取到遠端服務端的RemoteWebPage接口對象remoteWebPage,這樣就可以通過remoteWebPage.getCurrentPageUrl()獲取到服務端提供的相關的信息。客戶端通過bindService()方法綁定遠程服務端,通過unbindService()斷開連接。連接客戶端的相關的代碼爲:

1
2
Intent intent  = new Intent(actionName);
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.csdn.net/detail/Niosm/3593187

在附一個稍微複雜點的例子(通過IPC傳遞Parcelable對象):

http://download.csdn.net/detail/Niosm/3593376

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章