android 远程服务传递自定义数据类型

在Android系统中,进程间传递的数据包括Java语言支持的基本数据类型和用户自定义的数据类型,为了使数据能够穿越进程边界,所有数据都必须是“可打包”。对于Java语言的基本数据类型,打包过程是自动完成的。但对于自定义的数据类型,用户需要实现Parcelable接口,使自定义的数据类型能够转换为系统级原语保存在Parcel对象中,穿越进程边界后可再转换为初始格式。

AIDL支持的数据类型如下表:

类型 说明 需要引入
基本数据类型 boolean、byte、short、int、
long、char、float、double
String java.lang.String
CharSequence java.lang.CharSequence
List 其中元素都必须是AIDL支持的数据类型
Map 其中ket和value都必须是AIDL支持的数据类型
其他AIDL接口 任何其他使用AIDL语言生成的接口类型
Parcelable对象 实现Parcelable接口的对象

下面以ParcelMathServiceDemo示例为参考,说明如何在远程服务中使用自定义类型。这个示例是RemoteMathServiceDemo示例的延续,查看我的RemoteMathServiceDemo示例文章 ,也定义了MathService服务,同样可以为远程调用者提供加法服务,而且同样也是没有启动界面。

不同之处在于MathService服务涉及到了自定义数据类型,在接受到输入参数后,将不再只向调用者返回long类型的数据,而是返回一个包含“加、减、乘、除”全部运算结果的对象。这个对象是一个自定义的类,为了能够使其他AIDL文件可以使用这个自定义的类,需要使用AIDL语言声明这个类。

首先建立AllResult.aidl文件

然后在AllResult.aidl文件中声明AllResult类,AllResult.aidl文件中的代码如下:

// AllResult.aidl
package com.example.remotemathservicedemo;

//在这里声明任何非默认类型
parcelable AllResult;

(这个简陋的aidl文件可不会自动生成对应的java接口文件哦)

然后在IMathService.aidl中为全运算增加新函数,返回类型就是在AllResult.aidl中定义的AllResult,代码如下:

// IMathService.aidl
package com.example.remotemathservicedemo;

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

interface IMathService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
     long Add(long a,long b);
     AllResult ComputeAll(long a,long b);
}

然后Build->Make Project 重新生成接口文件,使增加的新函数生效。这时候在新生成的IMathService.java中会提示错误,因为其中需要关于AllResult的信息都找不到。

接下来手动构造AllResult类,可以放在和自动生成的IMathService.java同目录下。下面先把AllResult.java的完整代码贴出来:

package com.example.remotemathservicedemo;

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

/**
 * Created by yinghao on 2016/5/7.
 */
public class AllResult implements Parcelable {

    public long addResult;
    public long subResult;
    public long mulResult;
    public double divResult;

    public AllResult(long addResult, long subResult, long mulResult, double divResult) {
        this.addResult = addResult;
        this.subResult = subResult;
        this.mulResult = mulResult;
        this.divResult = divResult;
    }

    //从Parcel对象得到数据,拆包函数
    public AllResult(Parcel parcel) {
        addResult = parcel.readLong();
        subResult = parcel.readLong();
        mulResult = parcel.readLong();
        divResult = parcel.readDouble();
    }

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

    //顾名思义 wiiteToParcel  打包函数
    //将AllResult类内部的数据按照特定顺序写入Parcel对象
    //写入顺序必须与构造函数读取顺序一致
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(addResult);
        dest.writeLong(subResult);
        dest.writeLong(mulResult);
        dest.writeDouble(divResult);
    }

    //实现静态公共字段Creator,用来使用Parcel对象构造AllResult对象
    public static final Parcelable.Creator<AllResult> CREATOR = new Creator<AllResult>() {
        @Override
        public AllResult createFromParcel(Parcel parcel) {
            return new AllResult(parcel);
        }

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

AllResult继承于Parcelable,其中的数据就是全运算的运算结果。

AllResult类除了基本的构造函数以外,还需要以Parcel对象为输入的构造函数,并且要重载打包函数writeToParcel()。 把这个类写完后,你就会发现IMathService.java中的错误提示消失了。

到这里,关于自定义数据类型的工作就完成了,然后在MathService.java文件中,增加用来进行全运算的ComputAll()函数,并将运算结果保存在AllResult对象中。MathService.java中的完整代码如下:

package com.example.remotemathservicedemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.widget.Toast;

/**
 * Created by yinghao on 2016/5/7.
 */
public class MathService extends Service {
    /*
           1. 建立 IMathService.Stub的实例mBinder并实现AIDL文件定义的远程服务接口
           2. 在onBind()方法中将mBinder返回给远程调用者
     */

    private final IMathService.Stub mBinder = new IMathService.Stub(){
        @Override
        public long Add(long a, long b) throws RemoteException {
            return a + b;
        }

        @Override
        public AllResult ComputeAll(long a, long b) throws RemoteException {
            long addResult = a + b;
            long subResult = a - b;
            long mulResult = a * b;
            double divResult = a / b;
            AllResult allResult = new AllResult(addResult, subResult, mulResult, divResult);
            return allResult;
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(MathService.this, "远程绑定:MathService", Toast.LENGTH_SHORT).show();
        return mBinder;
    }

    //Return true if you would like to have the service‘s onRebind method later called when new clients bind to it.
    @Override
    public boolean onUnbind(Intent intent) {
        Toast.makeText(MathService.this, "取消远程绑定", Toast.LENGTH_SHORT).show();
        return false;
    }
}

到这里,服务端的工作就全部完成了,当然如果你没看过我的上篇关于远程服务传递基本数据类型的话,那还缺少一步,注册Service。

然后就是调用者如何去绑定和调用服务,首先需要将服务端的Module中的两个aidl文件以及对应的java接口文件和自己构造的AllResult.java类全部拷贝到调用端Module中。(关于原因可在上篇文章中查看)

然后将原来的“加法运算”功能改为“全运算”功能,关于remoteMathCallerDemo中MainActivity.java中的完整代码如下:

package com.example.remotemathcallerdemo;

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.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.example.remotemathservicedemo.AllResult;
import com.example.remotemathservicedemo.IMathService;

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private Button bind;
    private Button unbind;
    private Button add;

    private boolean isBound = false;
    private IMathService mathService;
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mathService = IMathService.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mathService = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textView);
        bind = (Button) findViewById(R.id.bind);
        unbind = (Button) findViewById(R.id.unbind);
        add = (Button) findViewById(R.id.add);

        bind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!isBound) {
                    final Intent serviceIntent = new Intent();
                    serviceIntent.setAction("com.example.remote.MathService");
                    bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
                    isBound = true;
                }
            }
        });

        unbind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isBound) {
                    unbindService(mConnection);
                    isBound = false;
                    mathService = null;
                }
            }
        });

        add.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mathService == null) {
                    textView.setText("未绑定远程服务");
                    return;
                }
                long a = Math.round(Math.random() * 100);
                long b = Math.round(Math.random() * 100);
                AllResult result = null;
                try {
                    result = mathService.ComputeAll(a, b);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                String msg = "";
                if (result != null) {
                    msg += String.valueOf(a) + "+" + String.valueOf(b) + "=" +
                            String.valueOf(result.addResult) + "\n";
                    msg += String.valueOf(a) + "-" + String.valueOf(b) + "=" +
                            String.valueOf(result.subResult) + "\n";
                    msg += String.valueOf(a) + "*" + String.valueOf(b) + "=" +
                            String.valueOf(result.mulResult) + "\n";
                    msg += String.valueOf(a) + "/" + String.valueOf(b) + "=" +
                            String.valueOf(result.divResult) + "\n";
                }
                textView.setText(msg);
            }
        });
    }
}

运行效果如下:

对于远程服务的一些基础知识总结以及远程服务传递基本数据类型的Demo在Android Service 远程服务(点击查看),欢迎大家指出差错或交流。

时间: 2024-10-14 17:00:46

android 远程服务传递自定义数据类型的相关文章

Activity传递参数——传递自定义数据类型

一.新建一个空的工程 二.在主界面中添加一个按钮 三.新建一个空的activity,并命名为TheAty 四.新建一个user类 //注意这里要实现Serializable,不然在传递参数时会出错 public class User implements Serializable{ private String name; private int age; public String getName() { return name; } public void setName(String na

android Service Activity交互之传递复杂数据类型的远程服务

远程服务往往不只是传递java基本数据类型.这时需要注意android的一些限制和规定: android支持String和CharSequence 如果需要在aidl中使用其他aidl接口类型,需要import,即使是在相同包结构下: android允许传递实现Parcelable接口的类,需要import: android支持集合接口类型List和Map,但是有一些限制,元素必须是基本型或者上述三种情况,不需要import集合接口类,但是需要对元素涉及到的类型import: 非基本数据类型,也

[Android Pro] AIDL进程间传递自定义类型参数

1.创建.aidl 文件 AIDL 语法简单,用来声明接口,其中的方法接收参数和返回值,但是参数和返回值的类型是有约束的,且有些类型是需要 import,另外一些则无需这样做. AIDL 支持的数据类型划分为四类,第一类是 Java 编程语言中的基本类型,第二类包括 String.List.Map 和 CharSequence,第三类是其他 AIDL 生成的 interface,第四类是实现了 Parcelable protocol 的自定义类. 其中,除了第一类外,其他三类在使用时均需要特别小

Android中View自定义XML属性详解以及R.attr与R.styleable的区别

为View添加自定义XML属性 Android中的各种Widget都提供了很多XML属性,我们可以利用这些XML属性在layout文件中为Widget的属性赋值. 如下所示: <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" /> 我们可以通过TextView所提供

结合手机上网流量业务来说明Hadoop中的自定义数据类型(序列化、反序列化机制)

大家都知道,Hadoop中为Key的数据类型必须实现WritableComparable接口,而Value的数据类型只需要实现Writable接口即可:能做Key的一定可以做Value,能做Value的未必能做Key.但是具体应该怎么应用呢?--本篇文章将结合手机上网流量业务进行分析. 先介绍一下业务场景:统计每个用户的上行流量和,下行流量和,以及总流量和. 本次描述所用数据: 日志格式描述: 日志flowdata.txt中的具体数据: 接下来贴出详细代码,代码中含有详细注释,从代码中可以看出,

Android UI之自定义——类似iOS的Tabbar

Android UI之自定义--类似iOS的Tabbar Tabbar最早出现在iOS,iOS中的TabBarController实现了这个功能,开发起来相当简单.现在的APP,大多数都会使用Tabbar来作为应用的功能导航,界面简单清晰.那么Android常见的实现是通过RadioGroup来实现,今天将带来自定义实现,补充RadioGroup实现的不足. 先看看常见的软件中的使用: 这个是高铁管家APP,大家应该非常熟悉.这个APP的首页底部就是一个类似iOS的Tabbar.这里就不多举例子

android事件传递机制详解

本篇内容将结合Android源码来分析Android的事件传递机制.众所周知,点按.滑动.触摸构成了Android等智能设备的基本操作,几乎所有的应用都通过对触摸屏的操作来进行应用程序的使用.那么,在Android中,触摸事件是如何响应及传递的呢,通过本篇内容你将有一个初步的了解. 实验环境 OS X 10.9 Eclipse(ADT) Android源码版本:API Level 19(Android 4.4) Android事件构成 在Android中,事件主要包括点按.长按.拖拽.滑动等,点

AIDL使用中报错找不到自定义数据类型的解决办法

在研究Android多进程编程的时候,照书敲了一个AIDL的例子.其中,用Android Studio自动生成了AIDL文件,会放进aidl文件夹,其中使用到了一个自定义数据类型,于是在aidl文件夹下会有3个文件,如下图所示. IBookManager中定义服务器提供的接口,其中用到了自定义数据类型Book,除了要用java实现Book类(必须implement parcelable)外,还要添加同名的Book.aidl文件,里面有两句代码. package *.aidl;parcelable

Android事件传递机制(转)

Android事件构成 在Android中,事件主要包括点按.长按.拖拽.滑动等,点按又包括单击和双击,另外还包括单指操作和多指操作.所有这些都构成了Android中的事件响应.总的来说,所有的事件都由如下三个部分作为基础: 按下(ACTION_DOWN) 移动(ACTION_MOVE) 抬起(ACTION_UP) 所有的操作事件首先必须执行的是按下操作(ACTIONDOWN),之后所有的操作都是以按下操作作为前提,当按下操作完成后,接下来可能是一段移动(ACTIONMOVE)然后抬起(ACTI