Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android AIDL Service 跨進程傳遞復雜數據

Android AIDL Service 跨進程傳遞復雜數據

編輯:關於Android編程

黑夜

黑夜給了我黑色的眼睛,我卻用它尋找光明~

傳值方式

AIDL是允許跨進程傳遞值的,一般來說有三種方式:
- 廣播;這種算是比較常見的一種方式了,傳遞小數據不錯
- 文件;這個是保存到文件中,然後讀取,傳遞大數據不錯
- Service Bind模式;這個算是居中的一種方式,不過效率要高的多,唯一麻煩的是編寫代碼較為麻煩。特別是復雜類型數據傳遞麻煩。
其是,還有一些其他的辦法進行數據傳遞,另外傳遞也並不是只可以使用一種,可以采用幾種結合的方式進行。
今天要說的就是Service Bind進行復雜數據的傳遞。


傳遞類型

在AIDL中之所以使用Bind進行傳遞會比較麻煩是因為:其在跨進程的情況下只允許傳遞如下類型數據:
- String
- CharSequence
- android.os.Parcelable
- java.util.List
- java.util.Map

雖然可以使用 List與Map但是其類型一樣不能使用復雜類型;當然上面 int、long、bool就沒有單獨寫出來了。
如過要進行復雜數據傳遞,如傳遞User類的實例,此時就要使用Parcelable來輔助完成。

簡單流程

Created with Raphaël 2.1.0A進程數據User在A進程把User打包為Parcelable在B進程把Parcelable解包為UserB進程數據User

其調用方式依然為:綁定服務->得到目標服務的Binder->調用對應方法
跨進程傳遞數據麻煩就在於打包/解包Parcelable的操作。

目標:開啟一個獨立進程的服務,在主進程中綁定目標服務,調用服務的方法。
實現:將需要進行復雜傳遞的數據類,繼承Parcelable,並實現其中的序列化與反序列化方法。

傳遞類

傳遞類包括兩個:User.java、User.aidl
其中java類是具體的實現,aidl文件僅僅只是用於對java文件的聲明。告知進程其可以看作Parcelable處理。

User.Java

package net.qiujuer.sample.service.bean;

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

import java.util.UUID;

/**
 * Created by qiujuer on 15/7/15.
 */
public class User implements Parcelable {
    private UUID id;
    private int age;
    private String name;

    public User(int age, String name) {
        this.age = age;
        this.name = name;
        this.id = UUID.randomUUID();
    }

    protected User(Parcel in) {
        // Id
        long m = in.readLong();
        long l = in.readLong();
        id = new UUID(m, l);

        age = in.readInt();
        name = in.readString();
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // ID
        long m = id.getMostSignificantBits();
        long l = id.getLeastSignificantBits();
        dest.writeLong(m);
        dest.writeLong(l);

        dest.writeInt(age);
        dest.writeString(name);
    }

    @Override
    public String toString() {
        return Id: + id.toString() +  Age: + age +  Name: + name;
    }
}

在類中,包含三個屬性,兩個基本類型,一個UUID,對於基本類型可以直接序列化,而UUID則不能,此時兩個方案,一種是把UUID看作String進行處理,當解包時則把String轉換為UUID即可。
第二種則是得到其中關鍵的數據,分別是兩個long值,然後對long值進行傳遞,並反序列化。

在類中,我們實現了Parcelable接口,則需要完成兩個方法與一個靜態值操作。
- describeContents 為描述方法,通常返回0
- writeToParcel 具體的寫入操作,在這裡對需要傳輸的數據進行寫入,請一定需要注意的是寫入順序則決定了讀取順序
- CREATOR 此靜態屬性,則是為了反編譯時使用,在Parcelable進行反編譯時將會調用該屬性,所以名稱寫法基本是固定不變的。
- User 在該類的構造函數中,我們對其進行了反序列化讀取數據操作。

User.aidl

// User.aidl
package net.qiujuer.sample.service.bean;

parcelable User;

在該文件中,只需要兩行代碼就OK,一個指定包名,一個為parcelable申明。

# Service類
這個部分主要包含兩個文件,一個AIDL申明文件

IServiceAidlInterface.aidl

// IServiceAidlInterface.aidl
package net.qiujuer.sample.service;
import net.qiujuer.sample.service.bean.User;


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

interface IServiceAidlInterface {
    void addAge();
    void setName(String name);
    User getUser();
}

在該文件中,定義了一個接口,其中有一個方法getUser(),該方法返回服務中的User類的實例;當然需要使用該類所以需要加上import net.qiujuer.sample.service.bean.User;

IndependentService.java

package net.qiujuer.sample.service;

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

import net.qiujuer.sample.service.bean.User;

public class IndependentService extends Service {
    private ServiceBinder mBinder;

    public IndependentService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        if (mBinder == null)
            mBinder = new ServiceBinder();
        return mBinder;
    }

    class ServiceBinder extends IServiceAidlInterface.Stub {
        private User user;

        public ServiceBinder() {
            user = new User(21, XiaoMing);
        }

        @Override
        public void addAge() throws RemoteException {
            user.setAge(user.getAge() + 1);
        }

        @Override
        public void setName(String name) throws RemoteException {
            user.setName(name);
        }

        @Override
        public User getUser() throws RemoteException {
            return user;
        }
    }

}

該類為具體的服務實現,在該服務中實現了服務接口,並進行了簡單實現。

文件梳理與配置

文件結構

這裡寫圖片描述

獨立進程配置

由於我們需要讓服務為獨立進程,所以需要在AndroidManifest文件中Service申明的地方加上process屬性:




    
        
    

在這裡我設置的是:android:process=”:AidlService”

使用

在APP Model中,我建立了一個MainActivity,並在其中調用服務的方法。

核心代碼

public class MainActivity extends AppCompatActivity {
    private final String TAG = this.getClass().getSimpleName();
    private TextView mText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mText = (TextView) findViewById(R.id.txt_str);

        bindService();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unBindService();
    }

    private IServiceAidlInterface mService = null;

    private ServiceConnection mConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mService = IServiceAidlInterface.Stub.asInterface(iBinder);
            if (mService != null)
                run();
            else
                showText(Bind Error.);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mService = null;
        }
    };

    private void bindService() {
        // UnBind
        unBindService();

        Intent intent = new Intent(this, IndependentService.class);
        bindService(intent, mConn, Context.BIND_AUTO_CREATE);
    }

    private void unBindService() {
        // Service
        IServiceAidlInterface service = mService;
        mService = null;
        if (service != null) {
            unbindService(mConn);
        }
    }

    private void run() {
        User user = null;
        try {
            user = mService.getUser();
            showText(user.toString());

            mService.addAge();
            user = mService.getUser();
            showText(user.toString());

            mService.setName(FangFang);
            user = mService.getUser();
            showText(user.toString());

        } catch (RemoteException e) {
            e.printStackTrace();
            showText(e.toString());
        }
    }

    private void showText(String str) {
        Log.d(TAG, str);
        mText.append(
);
        mText.append(str);
    }
}

打印日志

MainActivity﹕ Id:cdfb5bb4-7674-4a8a-b754-92256dfea8f4 Age:21 Name:XiaoMing
MainActivity﹕ Id:cdfb5bb4-7674-4a8a-b754-92256dfea8f4 Age:22 Name:XiaoMing
MainActivity﹕ Id:cdfb5bb4-7674-4a8a-b754-92256dfea8f4 Age:22 Name:FangFang

進程

這裡寫圖片描述

可以看出,服務的進程的確是獨立於主進程的。

引申

在這裡,我們是傳遞的一個User,假如傳遞的User中又包含一個Account類呢?Account中又包含其他的類呢?這個該如何辦?

有兩種辦法:
第一種:使用上面UUID傳遞類似的方式,得到其中的核心數據,然後傳遞,在解包時進行還原。
第二種:將類也實現Parcelable接口,這樣就能完美的解決了。

在這裡簡單寫一下第二種的代碼:
Account.java

public class Account implements Parcelable {
    private String name;

    protected Account(Parcel in) {
        name = in.readString();
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public Account createFromParcel(Parcel in) {
            return new Account(in);
        }

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
    }
}

User.java
這裡只寫改動部分;其實這些改動的東西,完全可以借助編譯工具自動生成,你只需要寫好屬性,然後繼承接口;然後讓編譯工具幫助你完成接口對應方法就OK。

public class User implements Parcelable {
    private Account account;

    protected User(Parcel in) {
        ...
        account = in.readParcelable(Account.class.getClassLoader());
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        ...
        dest.writeParcelable(account, flags);
    }
}

另外需要注意的是,在aidl中申明類,僅僅只需要申明aidl接口中需要傳遞的類就OK,在這裡直接傳遞的類只有User,所以只需要寫一個User.aidl文件就OK,就算User中包含了Account類,但不需要寫Account.aidl文件來申明。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved