1. 程式人生 > >Android AIDL Service 跨進程傳遞復雜數據

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

構造函數 lar 建立 comm rtt enc mar 它的 created

黑夜

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

傳值方式

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<User> CREATOR = new Creator<User>() { @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屬性:

<?

xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.qiujuer.sample.service"> <application android:allowBackup="true" android:label="@string/app_name"> <service android:name=".IndependentService" android:enabled="true" android:exported="true" android:permission="1000" android:process=":AidlService" /> </application> </manifest>

在這裏我設置的是: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("\n");
        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<Account> CREATOR = new Creator<Account>() {
        @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文件來申明。

代碼

當然,源代碼肯定是會有的:
AidlService 源代碼

寫在最後

近期沒事兒搗鼓了一個APP[UPMiss]。一個簡單的生日,紀念日提醒軟件。歡迎大家嘗鮮。

{UPMiss} 思念你的夏天
下載地址:

  • 魅族
  • docid=7821362&from=web_alad_5&f=search_app_UPMiss@list_1_title@1@header_app_input">百度 新版還在審核中!

  • 豌豆莢 新版還在審核中!

========================================================
作者:qiujuer
博客:blog.csdn.net/qiujuer
站點:www.qiujuer.net
開源庫:github.com/qiujuer/Genius-Android
開源庫:github.com/qiujuer/Blink
轉載請註明出處:http://blog.csdn.net/qiujuer/article/details/46885987
—— 學之開源,用於開源;剛開始學習的人的心態。與君共勉。

========================================================

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