Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android進程間通信之----Aidl傳遞對象

Android進程間通信之----Aidl傳遞對象

編輯:關於Android編程

前言

有關Android進程間通信之Aidl編程的基本使用步驟已經在上一篇博客中有講解,Android studio 下的aidl編程實現Android的誇進程間通信。上一篇博客中只是演示了怎麼利用Aidl實現跨進程間傳遞Java基本類型,以及Aidl傳遞Bitamap對象。可能在一些場景下你需要跨進程傳遞一個對象,那麼Aidl是否能傳遞一個對象呢?答案是肯定的,網上也有很多相關的資料,之所以寫這篇博客:一是當作自己學習筆記咯,二是把自己遇到的問題分享出來。

Aidl傳遞對象簡介

由於Aidl只支持Java基本類型數據傳遞,因此是不能直接傳遞一個復雜類型對象的,所以為了解決這個問題,Android提供了一套機制—-將需要傳遞的對象序列化,然後在反序列化。

序列化:把Java對象轉換為字節序列的過程。 反序列化:把字節序列恢復為Java對象的過程。

Java有一套自己的序列化機制Serializable,不過Android也自己實現了一套自己的序列化機制Parcelable。有關Serializable和Parcelable的區別請自行網上搜一把。總之:Aidl可以實現跨進程傳遞序列化之後的對象,接下來詳細介紹實現的過程。

Aidl傳遞對象步驟

服務端Aidl

基本步驟和上一篇博客一樣,在main目錄下新建一個aidl目錄,然後新建一個aidl接口類IMyAidlInterface.aidl。現在假如我們需要傳遞的對象是一個Students對象,那麼在aidl目錄下新建Students類,並實例化該類。最後在同樣的目錄下新建Students的aidl文件Students.aidl。目錄結構如下:

這裡寫圖片描述

相關代碼如下:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxzdHJvbmc+U3R1ZGVudHM8L3N0cm9uZz48L3A+DQo8cHJlIGNsYXNzPQ=="brush:java;"> package com.example.xjp.aidla; import android.os.Parcel; import android.os.Parcelable; /** 1. Created by 850302 on 2016/4/29. */ public class Students implements Parcelable { private int id; private String name; private String className; private int age; protected Students(Parcel in) { id = in.readInt(); name = in.readString(); className = in.readString(); age = in.readInt(); } public Students(int id, String name, String className, int age) { this.id = id; this.name = name; this.className = className; this.age = age; } public Students(){} public static final Creator CREATOR = new Creator() { @Override public Students createFromParcel(Parcel in) { return new Students(in); } @Override public Students[] newArray(int size) { return new Students[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); dest.writeString(name); dest.writeString(className); dest.writeInt(age); } public void readFromParcel(Parcel source) { id = source.readInt(); name = source.readString(); className = source.readString(); age = source.readInt(); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }

Students類繼承了Parcelable接口類,並且實現了其中的方法。值得注意的是writeToParcel方法和readFromParcel方法裡面的寫和讀取順序是需要一一對應的,就比如writeToParcel方法裡寫的順序是

@Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);//1:寫一個int類型的值
        dest.writeString(name);//2:寫一個String類型的值
        dest.writeString(className);//3:寫一個String類型的值
        dest.writeInt(age);//4:寫一個int類型的值
    }

以上寫數值的順序是

寫一個int類型的值 寫一個String類型的值 寫一個String類型的值 寫一個int類型的值

那麼readFromParcel方法裡的讀取順序也應該是按照以上順序讀取,否則讀取的數據會錯亂。

讀取一個int類型的值 讀取一個String類型的值 讀取一個String類型的值 讀取一個int類型的值

代碼如下:

public void readFromParcel(Parcel source) {
        id = source.readInt();
        name = source.readString();
        className = source.readString();
        age = source.readInt();
    }

Students.aidl

package com.example.xjp.aidla;
parcelable Students;

該類很簡單,僅僅是申明了 Students為parcelable類型。**注意:**parcelable開頭是小寫p,不是Parcelable。別問我為什麼是這樣,我也不知道 ^_^,只能說這是游戲規則。

IMyAidlInterface.aidl

// IMyAidlInterface.aidl
package com.example.xjp.aidla;

import com.example.xjp.aidla.Students;
interface IMyAidlInterface {

    int add(int arg1, int arg2);

    String inStudentInfo(in Students student);

    String outStudentInfo(out Students student);

    String inOutStudentInfo(inout Students student);

}

以上方法的參數都是Students,但是每個參數前面都有一個修飾符:in,out,inout,且這些修飾符是必須的,否則會報錯。那麼他們代表什麼意思呢?

in:參數由客戶端設置,或者理解成客戶端傳入參數值。 out:參數由服務端設置,或者理解成由服務端返回值。 inout:客戶端輸入端都可以設置,或者理解成可以雙向通信。

值得注意的是:由於IMyAidlInterface接口類中使用到了Students類,所以你得主動import引入該類

import com.example.xjp.aidla.Students;

否則會報錯,即使是Students類和IMyAidlInterface在同一個報名下也得引入。

服務端MyService

package com.example.xjp.aidl;

import android.app.ActivityManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import com.example.xjp.aidla.IMyAidlInterface;
import com.example.xjp.aidla.Students;

/**
 * Created by 850302 on 2016/4/28.
 */
public class MyServer extends Service {

    IMyAidlInterface.Stub mStub = new IMyAidlInterface.Stub() {
        @Override
        public int add(int arg1, int arg2) throws RemoteException {
            return arg1 + arg2;
        }

        @Override
        public String inStudentInfo(Students student) throws RemoteException {
            String msg = "table1" + "\n" + "----------------------------------------------" + "\n" + "|" +
                    " id " + "|" + " " +
                    "age " +
                    "|" + " name " + "|" + " className " + "|" + "\n" +
                    "----------------------------------------------" + "\n" + "|  " + student.getId() + " " +
                    "|  " + student
                    .getAge() + "  |  " + student.getName() + "   |     " + student.getClassName() + "   | " +
                    "\n" + "----------------------------------------------";
            return msg;
        }

        @Override
        public String outStudentInfo(Students student) throws RemoteException {
//            student.setClassName("090412");
//            student.setName("Tom2");

//            String msg = "Id = " + student.getId() + " age = " + student.getAge() + " ClassName = " +
//                    student.getClassName() + " Name = " + student.getName();
            String msg = "table2" + "\n" + "----------------------------------------------" + "\n" + "|" +
                    " id " + "|" + " " +
                    "age " +
                    "|" + " name " + "|" + " className " + "|" + "\n" +
                    "----------------------------------------------" + "\n" + "|  " + student.getId() + " " +
                    "|  " + student
                    .getAge() + "  |  " + student.getName() + "   |     " + student.getClassName() + "   | " +
                    "\n" + "----------------------------------------------";
            return msg;
        }

        public String inOutStudentInfo(Students student) throws RemoteException {

            String msg = "table3" + "\n" + "----------------------------------------------" + "\n" + "|" +
                    " id " + "|" + " " +
                    "age " +
                    "|" + " name " + "|" + " className " + "|" + "\n" +
                    "----------------------------------------------" + "\n" + "|  " + student.getId() + " " +
                    "|  " + student
                    .getAge() + "  |  " + student.getName() + "   |     " + student.getClassName() + "   | " +
                    "\n" + "----------------------------------------------";

            return msg;
        }

    };

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("xjp", "the remote Process Name is ==>" + getCurProcessName(this));
    }


    @Override
    public IBinder onBind(Intent intent) {
        Log.e("xjp", "the remote onBind......");
        return mStub;
    }


    @Override
    public void onRebind(Intent intent) {
        Log.e("xjp", "the remote onRebind......");
        super.onRebind(intent);
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.e("xjp", "the remote onUnbind......");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("xjp", "the remote onDestroy......");
    }

    /**
     * get current process name
     *
     * @param context
     * @return
     */
    private String getCurProcessName(Context context) {
        int pid = android.os.Process.myPid();
        ActivityManager mActivityManager = (ActivityManager) context
                .getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningAppProcessInfo appProcess : mActivityManager
                .getRunningAppProcesses()) {
            if (appProcess.pid == pid) {
                return appProcess.processName;
            }
        }
        return null;
    }
}

服務端的工作是根據客戶端傳遞過來的Students信息來生成對應的Student的一張信息表。

在AndroidManifest.xml文件裡配置Service:

<code class="hljs xml">  <service android:name="com.example.xjp.aidl.MyServer">
        <intent-filter>
           <action android:name="com.xjp.myService"></action>
        </intent-filter>
  </service></code>

當然你可以配置屬性android:process,也可以不配置。不配置也就是默認配置時,該Service的進程名就是當前應用的包名,如果配置的話就會覆蓋默認進程名。

OK,到此服務端所有的工作都准備好了,此時你去編譯整個工程,發現編譯出錯,錯誤提示:找不到 符號類Students。

這裡寫圖片描述

臥槽,懵圈了,該定義的都定義了,該import的也引入了,怎麼會報錯了?不知道Eclipse中是否有這個問題,反正Android Studio是有的,然後就網上搜吧,這方面的資料不多,有給出如下解答可以解決問題的。配置當前應用的 build.gradle文件,在該文件中添加如下配置即可。

sourceSets {
        main {
//            manifest.srcFile 'src/main/AndroidManifest.xml'
            java.srcDirs = ['src/main/java', 'src/main/aidl']
//            resources.srcDirs = ['src/main/java', 'src/main/aidl']
//            aidl.srcDirs = ['src/main/aidl']
//            res.srcDirs = ['src/main/res']
//            assets.srcDirs = ['src/main/assets']
        }
    }

注:我的AS是1.3的,所以只配置了一行 java.srcDirs = [‘src/main/java’, ‘src/main/aidl’] 就解決問題了。這一行的意思是指定源文件的路徑,把aidl包含進去了。

此時你再去編譯就OK啦!

客戶端aidl

客戶端aidl就簡單啦,直接把剛才的服務端的整個aidl目錄拷貝到客戶端即可。這裡重復利用服務端的aidl代碼,無需重寫。

客戶端

package com.example.xjp.myaidldemocustomer;

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.widget.ImageView;
import android.widget.TextView;

import com.example.xjp.aidla.IMyAidlInterface;
import com.example.xjp.aidla.Students;

public class MainActivity extends Activity {

    private IMyAidlInterface mStub;
    private TextView txt;
    private ImageView img;
    private View unBindService;
    private boolean isBind;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mStub = IMyAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e("xjp", "the onServiceDisconnected");
            mStub = null;
        }
    };

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

        txt = (TextView) findViewById(R.id.text);
    }

    public void bindService(View v) {
        isBind = true;
        if (unBindService != null) unBindService.setEnabled(true);
        Intent intent = new Intent();
        intent.setAction("com.xjp.myService");
        intent.setComponent(new ComponentName("com.example.xjp.aidl", "com.example.xjp.aidl.MyServer"));
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    public void getTableInfo(View v) {
        if (mStub == null) {
            Log.e("xjp", "the mStub is null");
        } else {
            try {
                String info1 = mStub.inStudentInfo(new Students(1, "Jim", "090415", 18));
                String info2 = mStub.outStudentInfo(new Students(2, "Lida", "090416", 17));
                String info3 = mStub.inOutStudentInfo(new Students(3, "Tom", "090417", 16));

                txt.setText(info1 + "\n" + "===========line=========" + "\n" + info2 + "\n" +
                        "===========line=========" + "\n" + info3);
            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }
    }

    public void unbindService(View v) {
        if (isBind) {
            isBind = false;
            unbindService(serviceConnection);
            unBindService = v;
            v.setEnabled(false);
        }

    }

    @Override
    protected void onDestroy() {
        if (isBind) unbindService(serviceConnection);
        super.onDestroy();
    }

}

客戶端代碼也很簡單,實現ServiceConnection 連接服務的回調得到遠程 binder,之後實現綁定服務方法,解綁服務方法,遠程調用方法等。

同樣客戶端也需要配置 build.gradle文件,配置和服務端一樣:

 sourceSets {
        main {
            java.srcDirs = ['src/main/java', 'src/main/aidl']
        }
    }

自此,整個Aidl傳遞對象的步驟基本完成。運行結果如下:

這裡寫圖片描述

由以上三張表格的輸出信息可以看出來:

String inStudentInfo(in Students student);//對應table1信息

String outStudentInfo(out Students student);//對應table2信息

String inOutStudentInfo(inout Students student);//對應table3信息

table1:表格信息是由客戶端傳入過去,且顯示的信息也是客戶端設置的信息,由此也證明in修飾符表示值由客戶端設置。
table2:表格信息為空的,此時雖然客戶端傳入了Students參數,但是不生效,從此也證明了out修飾符表示由服務端設置值。
table3:表格信息和客戶端傳入的有變化,id=3,name=Tom沒有改變,age=16變成22,className=“090417”變成了“090411”。這些改變都是在服務端修改的,也側面說明了inout修飾符表示客戶端和服務端都可以設置值。

總結

Aidl傳遞對象就實現了,需要注意點有如下:

傳遞的對象參數前面需要 in,out,inout三個當中的其中一個修飾符,否則會報錯。 對象Students序列化重寫writeToParcel和readFromParcel方法中寫數據操作和讀取操作順序必須保持一致,否則會報錯。 在使用到Students對象的地方都需要引入 import com.example.xjp.aidla.Students;包路徑。否則也會報錯找不到 Students類。 編譯代碼之前需要在 build.gradle配置文件中添加如下配置
sourceSets {
        main {
            java.srcDirs = ['src/main/java', 'src/main/aidl']
        }
    }
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved