Android Studio Service AIDL 詳解

quanke發表於2016-07-30

公司產品之前IM這塊存在很多問題,訊息到達率低,加上協議上有些問題,丟訊息頻繁,所以需要重構IM,AIDL不能解決以上問題。好吧!那AIDL可以解決什麼問題?什麼是AIDL?

什麼是AIDL?

AIDL是 Android Interface definition language的縮寫,它是一種Android內部程式通訊介面的描述語言,通過它我們可以定義程式間的通訊介面

AIDL可以解決什麼問題?

  • 可以實現多個應用程式共享同一個Service的功能,比如:IM服務可以提供給多個APP使用,先在推送基本都是採取這種方案
  • 可以跨程式呼叫服務裡的方法

Android Studio AIDL 實戰

大部分文章介紹都是在eclipse下介紹的,現在 Android Studio 作為開發工具比較普及了,所以我在Android Studio 下介紹(其實區別不大)。

言歸正傳,我們需要使用Android Studio實現一個遠端Service,並且建立AIDL進行通訊。

搭建了簡單的Service框架

1.繼承Service

package name.quanke.aidldemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

/**
 *
 * Created by http://quanke.name on 16/7/23.
 */
public class PushService extends Service {
    public PushService() {

    }

    @Override
    public IBinder onBind(Intent intent) {
        return new LibHandler();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

}

2.在AndroidManifest.xml裡註冊

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="name.quanke.aidldemo">

    <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:name=".App"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <service
                android:name=".PushService"
                android:enabled="true"
                android:process=":push"
                android:exported="true">
        </service>
    </application>

</manifest>

建立AIDL

1.建立AIDL資料夾

1.建立AIDL資料夾

1.建立AIDL資料夾

2.建立AIDL檔案

2.建立AIDL檔案

2.建立AIDL檔案

3.編寫AIDL檔案
// IHandler.aidl
package name.quanke.aidldemo;

// Declare any non-default types here with import statements

interface IHandler {
    void connect();
}
4.AIDL檔案 生成介面

4.AIDL檔案 生成介面

4.AIDL檔案 生成介面

生成後的樣子

4.AIDL檔案 生成後的樣子

4.AIDL檔案 生成後的樣子

5.編寫客戶端 ServiceConnection
package name.quanke.aidldemo;

import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

/**
 * Created by quanke on 16/7/23.
 */
public class PushManager {

    private static final String TAG = "PushManager.class";
    private IHandler iHandler;

    private static PushManager ourInstance = new PushManager();

    public static PushManager getInstance() {
        return ourInstance;
    }

    private PushManager() {

    }

    public void init(Application app){

        Intent binderIntent = new Intent(app,PushService.class);
        app.bindService(binderIntent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    public void connect(){
        try {
            //通過AIDL遠端呼叫
            Log.d(TAG,"++start Remote++");
            iHandler.connect();
        } catch (RemoteException e) {
            e.printStackTrace();
        }

    }

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iHandler = IHandler.Stub.asInterface(service);
            //連線成功調動
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //斷開連線呼叫
        }
    };
}
6.編寫服務端實現connect方法
package name.quanke.aidldemo;

import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

/**
 *
 * Created by quanke on 16/7/23.
 */
public class LibHandler extends IHandler.Stub{

    @Override
    public void connect() throws RemoteException {
        Log.d("","connect()");
    }

    @Override
    public IBinder asBinder() {
        return null;
    }
}

以上實現了簡單的連線,接下來我們實現傳遞自定義型別

傳遞自定義的型別

AIDL預設支援的型別包括Java基本型別(int、long、boolean等),和(String、List、Map、CharSequence),如果要傳遞自定義的型別需要實現android.os.Parcelable介面。自己寫了一個實體類public class Message implements Parcelable。

package name.quanke.aidldemo.model;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * 
 * Created by quanke on 16/7/23.
 */
public class Message implements Parcelable {
    private long id;
    private String content;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "Message{" +
                "id=" + id +
                ", content='" + content + '\'' +
                '}';
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(this.id);
        dest.writeString(this.content);
    }

    public Message() {
    }

    protected Message(Parcel in) {
        this.id = in.readLong();
        this.content = in.readString();
    }

    public static final Creator<Message> CREATOR = new Creator<Message>() {
        @Override
        public Message createFromParcel(Parcel source) {
            return new Message(source);
        }

        @Override
        public Message[] newArray(int size) {
            return new Message[size];
        }
    };
}

修改IHandler

// IHandler.aidl
package name.quanke.aidldemo;

// Declare any non-default types here with import statements

import name.quanke.aidldemo.model.Message;

interface IHandler {
    void connect();

    void sendMessage(Message message);
}

編譯專案,報錯

/Users/quanke/Dev/android/src/AIDLDemo/app/src/main/aidl/name/quanke/aidldemo/IHandler.aidl
Error:(6) couldn't find import for class name.quanke.aidldemo.model.Message
Error:Execution failed for task ':app:compileDebugAidl'.
> java.lang.RuntimeException: com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/Users/quanke/Dev/android/tools/android-sdks/build-tools/23.0.3/aidl'' finished with non-zero exit value 1
Information:BUILD FAILED

因為自定義型別不僅要定義實現android.os.Parcelable介面的類,還得為該實現類定義一個aidl檔案,如下:

自定義類aidl檔案

自定義類aidl檔案

// IHandler.aidl
package name.quanke.aidldemo.model;

// Declare any non-default types here with import statements

import name.quanke.aidldemo.model.Message;

parcelable Message ;

切記 自定型別aidl檔名字、路徑需要和自定義類名字、路徑保持一致,

編譯專案,還是報錯

parameter 1: 'Message message' can be an out parameter, so you must declare it as in, out or inout.

AIDL不是 Java。它是真的很接近,但它不是 Java。

Java 引數沒有方向的概念,AIDL 引數有方向,引數可以從客戶端傳到服務端,再返回來。

如果sendMessage方法的message引數是純粹的輸入引數—這意味著是從客戶端到伺服器的資料,你需要在AIDL宣告:

void sendMessage(in Message message);

如果sendMessage方法的message引數是純粹的輸出-這意味著它的資料是通過從伺服器到客戶端,使用:

void sendMessage(out Message message);

如果sendMessage方法的message引數是輸入也是輸出-客戶端的值在服務可能會修改,使用:

void sendMessage(inout Message message);

總結

Android Studio Service AIDL 詳解 就到這裡,現在應該可以使用AIDL實現想要的功能了,實現簡答的AIDL很簡單,主要是在自定義型別的時候,有幾個坑注意就好。

原始碼地址: https://github.com/quanke/AIDLDemo.git

有什麼問題歡迎留言。

如果喜歡請關注我,讚我,來撫平我虛榮的心

相關文章