Android Servie AIDL 复杂数据

上一章节中我们传递的是 int 类型的参数,然后服务端返回一个 String 类型的参数,很简单,但是实际开发中,传递的往往是复杂的数据类型

本章节我们就来学习如何使用 AIDL Service 向服务端传递复杂数据类型的数据

Parcelable 接口

Parcelable 和 Serializable 一样,都是用于序列化的,只是 Parcelable 更加轻量级,速度更快

但是写起来就有点麻烦了,复杂无比,好在有工具 Android Parcelable Code Generator 来完成序列化

  1. 首先实现 writeToParcel() 和 readFromPacel() 方法

    写入方法将对象写入到包裹 (parcel) 中

    读取方法则从包裹中读取对象

    注意: 写入属性顺序需与读取顺序相同

  2. 接着在该类中添加一个名为 CREATOR 的 static final 属性,该属性需要实现 android.os.Parcelable.Creator 接口

  3. 重写接口中的两个方法

    1. createFromParcel(Parcel source)

      方法实现从 source 创建出 JavaBean 实例的功能

    2. newArray(int size)

      创建一个类型为 T,长度为 size 的数组,只有一个简单的 return new T[size];

  4. 最后创建 describeContents() 直接返回 0 即可

注意

非原始类型 中除了 String 和 CharSequence 以外,其余均需要一个 方向指示符

方向指示符包括 in 、 out 、和 inout

方向符 说明
in 表示由客户端设置
out 表示由服务端设置
inout 表示客户端和服务端都设置了该值

范例

自定义两种对象类型 Person 与 Salary

  1. Person 作为调用远程的 Service 的参数
  2. Salary 作为返回值

先来看看最后的效果图


创建服务端

  1. 创建一个 空的 Android 项目 cn.twle.android.MixedServer

  2. 创建 AIDL 目录,在 app 目录上点右键,选择 NEW -> Folder -> AIDL Folder 然后点 Finish

  3. 在 app/aidl 目录下新建包 cn.twle.android.aidl

  4. 在 app/aidl 目录下的包 cn.twle.android.aidl 上点右键,然后选择 New->AIDL->AIDL File 分别创建两个文件 Person.aidl 和 Salary.aidl

    它们需要实现 Parcelable 接口

    Person.aidl

    parcelable Person;
    

    Salary.aidl

    parcelable Salary;
    
  5. 在 app/aidl 目录下的包 cn.twle.android.aidl 上点右键,然后选择 New->AIDL->AIDL File 创建文件 ISalary.aidl 在里面写一个简单的获取工资信息的方法

    package cn.twle.android.aidl;
    
    import cn.twle.android.aidl.Salary;  
    import cn.twle.android.aidl.Person;  
    interface ISalary  
    {  
        //定义一个Person对象作为传入参数  
        //接口中定义方法时,需要制定新参的传递模式,这里是传入,所以前面有一个in
    
        Salary getMsg(in Person owner);  
    }
    
  6. 然后点击 Build->Make Project 先生成我们需要的 ISalary.java 这时候会报错,提示 Person.java 和 Salary.java 不存在,没关系

  7. 在 app/java 目录下新建包 cn.twle.android.aidl

  8. 在 app/java 的包 cn.twle.android.aidl 创建文件 Person.java 和 Salary.java ,需要实现 Parcelable 接口,重写对应的方法

    因为我们后面是根据 Person 对象来获取 Map 集合中的数据,所以 Person.java 中我们重写了 hashcode 和 equals 的方法;而 Salary 类则不需要

    Person.java

    package cn.twle.android.aidl;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    public class Person implements Parcelable{
    
        private Integer id;
        private String name;
    
        public Person() {}
    
        public Person(Integer id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        //实现 Parcelable 必须实现的方法
        @Override
        public int describeContents() {
            return 0;
        }
    
        //写入数据到Parcel中的方法
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            //把对象所包含的数据写入到parcel中
            dest.writeInt(id);
            dest.writeString(name);
        }
    
        //必须提供一个名为CREATOR的static final属性 该属性需要实现
        //android.os.Parcelable.Creator<T> 接口
        public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
            //从Parcel中读取数据,返回Person对象
            @Override
            public Person createFromParcel(Parcel source) {
                return new Person(source.readInt(),source.readString());
            }
            @Override
            public Person[] newArray(int size) {
                return new Person[size];
            }
        };
    
        //因为我们集合取出元素的时候是根据Person对象来取得,所以比较麻烦,
        //需要我们重写hashCode()和equals()方法
        @Override
        public int hashCode()
        {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }
        @Override
        public boolean equals(Object obj)
        {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Person other = (Person) obj;
            if (name == null)
            {
                if (other.name != null)
                    return false;
            }
            else if (!name.equals(other.name))
                return false;
            return true;
        }
    }
    

    Salary.java

    package cn.twle.android.aidl;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    public class Salary implements Parcelable {
    
        private String type;
        private Integer salary;
    
        public Salary() {
        }
    
        public Salary(String type, Integer salary) {
            this.type = type;
            this.salary = salary;
        }
    
        public String getType() {
            return type;
        }
    
        public Integer getSalary() {
            return salary;
        }
    
        public void setType(String type) {
            this.type = type;
        }
    
        public void setSalary(Integer salary) {
            this.salary = salary;
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(type);
            dest.writeInt(salary);
        }
    
        public static final Parcelable.Creator<Salary> CREATOR = new Parcelable.Creator<Salary>() {
            //从Parcel中读取数据,返回Person对象
            @Override
            public Salary createFromParcel(Parcel source) {
                return new Salary(source.readString(), source.readInt());
            }
    
            @Override
            public Salary[] newArray(int size) {
                return new Salary[size];
            }
        };
    
        public String toString() {
            return "工作:" + type + "    薪水: " + salary;
        }
    }
    
  9. 然后点击 Build-> rebuild project 重新编译,这时候是成功的

  10. 在 MainActivity.java 目录下新建文件 MsAidlService.java

    定义一个 SalaryBinder 类继承 Stub,从而实现 ISalary 和 IBinder 接口;定义一个存储信息的 Map 集合

    重载 onBind() 方法,返回 SalaryBinder 类的对象实例

    package cn.twle.android.mixedserver;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import cn.twle.android.aidl.ISalary.Stub;
    import cn.twle.android.aidl.Person;
    import cn.twle.android.aidl.Salary;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.RemoteException;
    
    public class MsAidlService extends Service {
    
        private SalaryBinder salaryBinder;
        private static Map<Person,Salary> ss = new HashMap<Person, Salary>();
        //初始化Map集合,这里在静态代码块中进行初始化,当然你可也以在构造方法中完成初始化
        static
        {
            ss.put(new Person(1, "jav"), new Salary("码农", 2000));
            ss.put(new Person(2, "gen"), new Salary("歌手", 20000));
            ss.put(new Person(3, "xm"), new Salary("学生", 20));
            ss.put(new Person(4, "mrwang"), new Salary("老师", 2000));
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            salaryBinder = new SalaryBinder();
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return salaryBinder;
        }
    
        //同样是继承Stub,即同时实现ISalary接口和IBinder接口
        public class SalaryBinder extends Stub
        {
    
            @Override
            public Salary getMsg(Person owner) throws RemoteException {
                return ss.get(owner);
            }
    
        }
    
        @Override
        public void onDestroy() {
            System.out.println("服务结束!");
            super.onDestroy();
        }
    
    }
    
  11. 修改 AndroidManifest.xml 注册 Service

    <service android:name=".MsAidlService">  
        <intent-filter>    
            <action android:name="cn.twle.android.aidl.MsAidlService" />  
            <category android:name="android.intent.category.DEFAULT" />  
        </intent-filter>    
    </service>
    

先运行一下

客户端

  1. 创建一个 空的 Android 项目 cn.twle.android.MixedClient

  2. 重复上面的 [2-9] 步骤

  3. 先 build 一下

  4. 修改 activity_main.xml

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="请输入要查询的人的姓名" />
    
        <EditText
            android:id="@+id/ipt_search"
            android:inputType="text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <Button 
            android:id="@+id/btn_query"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="查询"/>
    
        <TextView 
            android:id="@+id/txt_show"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    
    </LinearLayout>
    
  5. 修改 MainActivity.java 定义一个 ServciceConnection 对象, 重写对应方法,和前面的普通数据的类似

    package cn.twle.android.mixedclient;
    
    import cn.twle.android.aidl.ISalary;  
    import cn.twle.android.aidl.Person;  
    import cn.twle.android.aidl.Salary;
    
    import android.app.Activity;  
    import android.app.Service;  
    import android.content.ComponentName;  
    import android.content.Intent;  
    import android.content.ServiceConnection;  
    import android.os.Bundle;  
    import android.os.IBinder;  
    import android.os.RemoteException;  
    import android.view.View;  
    import android.view.View.OnClickListener;  
    import android.widget.Button;  
    import android.widget.EditText;  
    import android.widget.TextView;
    
    public class MainActivity extends Activity {
    
        private ISalary salaryService;  
        private Button btn_query;  
        private EditText ipt_search;  
        private TextView txt_show;  
        private ServiceConnection conn = new ServiceConnection() {
    
            @Override  
            public void onServiceDisconnected(ComponentName name) {  
                salaryService = null;  
            }
    
            @Override  
            public void onServiceConnected(ComponentName name, IBinder service) {  
                //返回的是代理对象,要调用这个方法哦!  
                salaryService = ISalary.Stub.asInterface(service);  
            }  
        };
    
        @Override  
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.activity_main);
    
            btn_query  = (Button)   findViewById(R.id.btn_query);  
            ipt_search = (EditText) findViewById(R.id.ipt_search);  
            txt_show   = (TextView) findViewById(R.id.txt_show);
    
            Intent it = new Intent("cn.twle.android.aidl.MsAidlService");  
            it.setPackage("cn.twle.android.mixedserver");  
            bindService(it, conn, Service.BIND_AUTO_CREATE);
    
            btn_query.setOnClickListener(new OnClickListener() {           
                @Override  
                public void onClick(View v) {  
                    try  
                    {  
                        String name = ipt_search.getText().toString();  
                        Salary salary = salaryService.getMsg(new Person(1,name));  
                        txt_show.setText(name + salary.toString());  
                    }catch(RemoteException e){e.printStackTrace();}  
                }  
            });
    
        }  
        @Override  
        protected void onDestroy() {  
            super.onDestroy();  
            this.unbindService(conn);  
        }
    
    }
    

Android 基础教程

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

♥   简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.