Android进程间通信之----Aidl传递对象

转载请注明出处 CSDN废墟的树

前言

有关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。目录结构如下:

相关代码如下:

Students

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<Students> CREATOR = new Creator<Students>() {
        @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类型的值
    }

以上写数值的顺序是

  1. 写一个int类型的值
  2. 写一个String类型的值
  3. 写一个String类型的值
  4. 写一个int类型的值

那么readFromParcel方法里的读取顺序也应该是按照以上顺序读取,否则读取的数据会错乱。

  1. 读取一个int类型的值
  2. 读取一个String类型的值
  3. 读取一个String类型的值
  4. 读取一个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:

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

当然你可以配置属性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传递对象就实现了,需要注意点有如下:

  1. 传递的对象参数前面需要 in,out,inout三个当中的其中一个修饰符,否则会报错。
  2. 对象Students序列化重写writeToParcel和readFromParcel方法中写数据操作和读取操作顺序必须保持一致,否则会报错。
  3. 在使用到Students对象的地方都需要引入 import com.example.xjp.aidla.Students;包路径。否则也会报错找不到 Students类。
  4. 编译代码之前需要在 build.gradle配置文件中添加如下配置
sourceSets {
        main {
            java.srcDirs = [‘src/main/java‘, ‘src/main/aidl‘]
        }
    }
时间: 2024-08-04 19:25:32

Android进程间通信之----Aidl传递对象的相关文章

Android之Activity之间传递对象

在很多时候,我们需要在Activity之间传递对象,比如当你点击了某列表的item,需要传递给下一个Activity该对象,那我们需要该怎么做呢? Android支持两种传递对象的方式,一种是bundle.putSerializable方式,一种是bundle.putParcelable. 那么下面我们就用一个例子来实践Activity传递对象: 1.首先建立两个类,一个Teacher类表示老师,一个Student类表示学生.内容分别如下: <span style="font-size:1

Android开发——使用intent传递对象

intent传递对象有两种方法: 方式一:Serializable 方式 方式二:Parcelable方式 在这里不多介绍了,这一篇就是快速上手使用教程,至于详细原理介绍的,请看这一篇http://www.cnblogs.com/kexing/p/8270667.html 我们先在Android Studio下载一个插件android parcelable code generator,安装完毕重启Android Studio之后,我们创建一个java bean类,写上成员变量,直接快速生成ge

Android 如何进行页面传递对象

当我们从一个页面调到另一个页面的时候,需要把该页面的一些设定值也传递给下一个页面.当要传递的值很多时,我们可以传递一个对象. 页面1: Intent intent = new Intent(PageOneActivity.this, PageTwoActivity.class); SoftwareProlemInfo info = softwareProlemInfos.get(position); Bundle bundle = new Bundle(); bundle.putSerializ

android 中传递对象两种方法探索(Serializable,Parcelable)

相信大家在android开发的过程中总会遇到要在Activity中间传递数据的情况,当然,遇到需要在Intent中传递对象的情况也不可避免,所以我就so了一下相关的知识,在这里总结消化一下.就目前来说,我了解到的只有两种方式: 1.利用Bundle.putSerializable(Key,Object): 2.利用Bundle.putParcelable(Key, Object): 下面详细介绍两种方法的使用和区别: 首先第一点,这两种方法实现的前提都需要将传递的对象Object序列化,那么,问

初识Android进程间通信之----Binder机制

[转载请注明出处:http://blog.csdn.net/feiduclear_up/article/details/51405967 CSDN废墟的树] 前言 前面两篇博客分别介绍了Android进程间通信之AIDL的使用,以及使用AIDL传递复杂对象以及Bitmap对象.所谓AIDL:Android Interface Definition Language,是一种Android接口定义语言,用于编写Android进程间通信代码.也就是说AIDL只是一个实现进程间通信的一个工具,真正实现A

Activity传递对象的方法

Android中通过Intent传递对象类型的方法有两种,一种是Bundle.putSerializable(Key,Object),另一种是Bundle.putParcelable(Key,Object).传递这些对象要满足一定的条件,前者是实现Serializable接口,后一种是实现了Parcelable. 本文的例子是Parcelable接口的方法. Parcelable需要实现三个函数:writeToParcel.describeContents和CREATOR. wroteToPar

Android开发——进程间通信之AIDL(二)

0.  前言 不论是Android还是其他操作系统,都会有自己的IPC机制,所谓IPC(Inter-Process Communication)即进程间通信.首先线程和进程是很不同的概念,线程是CPU调用的最小单元,进程一般在PC和移动设备上指一个程序或者一个应用,一个进程可以包含多个线程. IPC方式有很多,在Android中常用的IPC方式包括Bundle.文件.Messenger.AIDL.ContentProvider和Socket等方式. Android开发--进程间通信之AIDL(一

Android进程间通信之使用AIDL

AIDL(Android Interface Definition Language ),可实现进程间的通信,并且允许多线程访问.(如果需要进程间通信,又不需要处理多线程访问,那么使用Messenger的方式更为合适),实现AIDL,需要以下几个步奏. 1.定义AIDL接口 AIDL接口使用后缀名为.aidl的文件来定义(例如创建一个IRemoteData.aidl),使用java语法来编写,在编译时,Android sdk工具会在/gen目录下生成一个java文件(IRemoteData.ja

Android中的Parcel机制 实现Bundle传递对象

Android中的Parcel机制    实现了Bundle传递对象    使用Bundle传递对象,首先要将其序列化,但是,在Android中要使用这种传递对象的方式需要用到Android Parcel机制,即,Android实现的轻量级的高效的对象序列化和反序列化机制. JAVA中的Serialize机制,译成串行化.序列化……,其作用是能将数据对象存入字节流当中,在需要时重新生成对象.主要应用是利用外部存储设备保存对象状态,以及通过网络传输对象等.        Android中的新的序列