AIDL用法详解
Posted sunnyPP123
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AIDL用法详解相关的知识,希望对你有一定的参考价值。
文章目录
AIDL用法详解
如何编写AIDL文件
AIDL 是android Interface Definition Language (Android 接口定义语言)的缩写。只看这个定义其实也并不明白,其实它就是一个Android IPC通信的一个代码规范。并不是一个新的语言啥的。
分别介绍 在同一APP和不同APP通信的情况。
1.在同一APP内
1.1 编写AIDL文件
AS 提供右键提示创建AIDL的功能。
在main目录上右键 ,AIDL文件和Src目录统计
2. 重构构建下工程
在build目录下就会生成 对应的.java 文件
关于里面的内容,后面会分析
3. 编写Service,具体实现要暴露的接口
4. 客户端与服务端绑定
5. 执行结果
6. 注意事项
-
创建的service要在AndroidManifest.xml注册
<service android:name=".binder.MyAIDLService" android:enabled="true" android:exported="true" android:process=":aidl"/>
如果要让Service运行在不同的进程,添加process属性 android:process=“:hello” 表示当前进程名是包名+aidl 即 com.example.androidlearn:aidl。
2.不通APP内
先说在前面 : 我们需要保证,在客户端和服务端中都有我们需要用到的所有.aidl 文件和其中涉及到的 .java 文件,因此不管在哪一端写的这些东西,写完之后我们都要把这些文件复制到另一端去。
服务端创建形式和同一app内相同,不同的地方如下:
1. 客户端的创建方式。
让客户端和服务端的AIDL保持一致,重构之后,也会生成对应的java文件,这样是为了让客户端可以引用到服务端aidl文件的方法。
2. 绑定服务端service
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidlclient_main);
findViewById(R.id.aidl_client_btn).setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
Intent intent = new Intent();
ComponentName componentName = new ComponentName("com.example.aidl_servier", "com.example.aidl_servier.MyAIDLService");
intent.setComponent(componentName);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
);
由于服务端与客户端在不通的应用,要想绑定服务,使用intent的setComponment方法
3.先启动服务端,再启动客户端
自己误区的地方:在同一工程不同应用,通过setComponent可以启动工程的其他应用。以为其他Service不需要先运行,setComponent也能这样启动呢,其实对于Service不是这样。如果是这样,我不经过你允许就调起后台全部服务,岂不是崩溃了。
所以必须先启动服务端。
数据类型
支持的数据类型
- Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。
- String 类型。
- CharSequence类型。
- List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(下文关于这个会有详解)。List可以使用泛型。
- Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。
AIDL默认支持的数据类型都是可序列化的。
为什么AIDL的传递的数据都要支持序列化?
由于不同的进程有着不同的内存区域,并且它们只能访问自己的那一块内存区域,目标进程根本不能访问源进程的内存,所以我们必须将要传输的数据转化为能够在内存之间流通的形式。这个转化的过程就叫做序列化与反序列化。
简单来说是这样的:比如现在我们要将一个对象的数据从客户端传到服务端去,我们就可以在客户端对这个对象进行序列化的操作,将其中包含的数据转化为序列化流,然后将这个序列化流传输到服务端的内存中去,再在服务端对这个数据流进行反序列化的操作,从而还原其中包含的数据——通过这种方式,我们就达到了在一个进程中访问另一个进程的数据的目的。
自定义数据类型
如果想要自定义数据类型,进行AIDL通信,就需要对类型进行序列化,而通常,在我们通过AIDL进行跨进程通信的时候,选择的序列化方式是实现 Parcelable 接口。
默认实现Parcelable 接口代码如下:
public class Student implements Parcelable
protected Student(Parcel in)
public static final Creator<Student> CREATOR = new Creator<Student>()
@Override
public Student createFromParcel(Parcel in)
return new Student(in);
@Override
public Student[] newArray(int size)
return new Student[size];
;
@Override
public int describeContents()
return 0;
@Override
public void writeToParcel(Parcel dest, int flags)
但是请注意,这里有一个坑:上述默认生成的序列化类的对象只支持为 in 的定向 tag
为什么呢?因为默认生成的类里面只有 writeToParcel() 方法,而如果要支持为 out 或者 inout 的定向 tag 的话,还需要实现 readFromParcel() 方法——而这个方法其实并没有在 Parcelable 接口里面,所以需要我们自己写。后面数据流向有解释。
为Student类添加姓名和年龄信息。
完整的Student类兑现的代码是这样的:
package com.example.aidl_servier;
import android.os.Parcel;
import android.os.Parcelable;
public class Student implements Parcelable
private String name;
private int age;
public Student(String name, int age)
this.name = name;
this.age = age;
public String getName()
return name;
public void setName(String name)
this.name = name;
public int getAge()
return age;
public void setAge(int age)
this.age = age;
protected Student(Parcel in)
// 从序列化里解析成员变量
name = in.readString();
age = in.readInt();
@Override
public String toString()
return "Student" +
"name='" + name + '\\'' +
", age=" + age +
'';
public static final Creator<Student> CREATOR = new Creator<Student>()
@Override
public Student createFromParcel(Parcel in)
return new Student(in);
@Override
public Student[] newArray(int size)
return new Student[size];
;
@Override
public int describeContents()
return 0;
@Override
public void writeToParcel(Parcel dest, int flags)
//将成员变量写入到序列化对象里
dest.writeString(name);
dest.writeInt(age);
public void readFromParcel(Parcel parcel)
this.name = parcel.readString();
this.age = parcel.readInt();
Parcelable序列化都是标准样式,实际上就做了两件事:
1、将Student数据分别写入到序列化对象Parcel里
2、从序列化对象Parcel里构建出Student对象
至此,关于AIDL中非默认支持数据类型的序列化操作就完成了。
使用自定义数据类型
对于同一app下 AIDL使用自定义数据类型通信就不展示了。
主要针对不同app,这个看懂,同一app更不在话下。
服务端
1. 创建序列化数据类型
上一节形式创建Student序列化类。
2.创建非默认类型AIDL文件
同样右键的方式创建AIDL文件,
// Student.aidl
package com.example.aidl_servier;
// Declare any non-default types here with import statements
interface Student
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
将以上内容删除,改造成如下内容:
// Student.aidl
package com.example.aidl_servier;
// Declare any non-default types here with import statements
parcelable Student;
注意: 一定要确保自定义序列化类型数据和对应的aidl文件的包类名一致
解决类重复的问题
先定义了Student.java,当再定义Student.aidl 时,若两者处于同一包下,那么将无法创建Student.aidl文件。
分几种方法解决:
- 先定义Student.aidl,最后定义Student.java
- 先定义Student.java 在与Student.aidl不同的包名下,然后再定义Student.aidl,最后将Student.aidl 移动至与Student.java 同一包名下。
- 先不命名Student,比方命名hhh.java,等Student.aidl定义好了 再把hhh改为Student。
3.被其他aidl文件作为参数使用
创建IMyserver.aidl文件 使用自定义数据类型
// IMyServer.aidl
package com.example.aidl_servier;
import com.example.aidl_servier.Student; //----------------(1)
interface IMyServer
void getStudentInfo(int age, in Student student);//------------(2)
- 与平时一致,引入一个新的类型,要将其类名import 出来。(一定要导入)
- getStudentInfo(xx)有个形参类型为:Student student,并且前边还有个"in" 标记(这个后续说)
4.服务端Service
与之前创建Service逻辑相同
public class MyAIDLService extends Service
private static final String TAG = "MyAIDLService";
IMyServer.Stub myBinder = new IMyServer.Stub()
@Override
public void getStudentInfo(int age, Student student) throws RemoteException
Log.d(TAG, student.getName() + " in server");
;
public MyAIDLService()
@Override
public IBinder onBind(Intent intent)
// TODO: Return the communication channel to the service.
return myBinder;
客户端
和之前说的相同,把所有的.aidl文件和涉及到的.java 文件从服务端复制到客户端去。都要保证这些文件的包类名和服务端的包类名保证相同。否则会会报 Symbol not found 错误。AIDL文件是整个复制过去的,所以aidl对应的包名是相同的。对于Student.java 客户端和服务端的包名是不同的,我们可以在客户端新建包名(和服务端相同),然后把服务端的Student.java赋值过去。
为什么这样做呢?
客户端的IMServer.aidl是从服务端复制过去的
构建之后,生成的.IMyServer.java如下
导入的是 com.example.aidl_servier.Student student
如果客户端没有定义Student,肯定会 Symbol not found 错误。所以涉及到.java文件也要复制过去。要和生成的.java文件引入保持一致,也就是包名相同嘛。
客户端业务和之前相同
package com.example.aidl_client;
import androidx.appcompat.app.AppCompatActivity;
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.util.Log;
import android.view.View;
import com.example.aidl_servier.IMyServer;
import com.example.aidl_servier.Student;
public class AIDLClientMainActivity extends AppCompatActivity
private static final String TAG = "AIDLClientMainActivity";
ServiceConnection serviceConnection = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName name, IBinder service)
Log.d(TAG, "onServiceConnected: ");
IMyServer iMyServer = IMyServer.Stub.asInterface(service);
try
// iMyServer.say("hello server");
iMyServer.getStudentInfo(2,new Student("xiaoHong",18));
Log.d(TAG, "onServiceConnected: 成功通信");
catch (RemoteException e)
e.printStackTrace();
@Override
public void onServiceDisconnected(ComponentName name)
;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidlclient_main);
findViewById(R.id.aidl_client_btn).setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
Intent intent = new Intent();
ComponentName componentName = new ComponentName("com.example.aidl_servier", "com.example.aidl_servier.MyAIDLService");
intent.setComponent(componentName);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
);
运行结果如下
数据流向
回顾一下常用的方法调用方式:
public void getStudentInfo(int age, Student student)
student.setName("modify");
形参为:int 类型;Student类型;
在同一进程里,当调用该方法时,传入Student引用,方法里对Student成员变量进行了更改,方法调用结束后,调用者持有的Student引用所指向的对象其内容已经更改了。而对于int 类型,方法里却无法更改。
上述涉及到了经典问题:传值与传址。
而对于不同的进程,当客户端调用getStudentInfo(xx)方法时,虽然看起来是直接调用服务端的方法,实际上是底层中转了数据,因此当初传入Student,返回来的已经不是同一个Student引用。
因此,AIDL 规定了数据流方向。
之前又说到 参数类型前面有in关键字。这个是定向tag,表示了在跨进程通信中数据的流向,数据流向是针对在客户端中的那个传入方法的对象而言的,有三种类型
- in
其中 in 表示数据只能由客户端流向服务端,表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动; - out
out 表示数据只能由服务端流向客户端,表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动。 - inout
inout 则表示数据可在服务端与客户端之间双向流通,nout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动
另外,Java 中的基本类型和 String ,CharSequence 的定向 tag 默认是 in,不用标注 。还有,请注意,请不要滥用定向 tag ,而是要根据需要选取合适的——要是不管三七二十一,全都一上来就用 inout ,等工程大了系统的开销就会大很多——因为排列整理参数的开销是很昂贵的。
为测试它们的差异,分别写三个方法:
package com.fish.myapplication;
import package com.example.aidl_servier.Student;
interface IMyServer
void getStudentInfo(int age, in Student student);
void getStudentInfo2(int age, out Student student);
void getStudentInfo3(int age, inout Student student);
基本数据类型如 int、String 默认是数据流类型是: in,不用刻意标注。
服务端实现方法:
IMyServer.Stub myBinder = new IMyServer.Stub()
@Override
public void say(String word) throws RemoteException
Log.d(TAG, "say: "+word);
@Override
public void getStudentInfo(int age, Student student) throws RemoteException
Log.d(TAG, "student name:" + student.getName() + " in server in getStudentInfoIn");
student.setName("change name getStudentInfoIn");
@Override
public void getStudentInfo2(int age, Student student) throws RemoteException
Log.d(TAG, "student name:" + student.getName() + " in server in getStudentInfoOut");
student.setName("change name getStudentInfoOut");
@Override
public void getStudentInfo3(int age, Student student) throws RemoteException
Log.d(TAG, "student name:" + student.getName() + " in server in getStudentInfoInout");
student.setName("change name getStudentInfoInout");
;
将Student name 打印出来,并更改name 内容。
客户端调用服务端方法:
ServiceConnection serviceConnection = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName name, IBinder service)
Log.d(TAG, "onServiceConnected: ");
IMyServer iMyServer = IMyServer.Stub.asInterface(service);
try
// iMyServer.say("hello server");
Student student = new Student("xiaoming", 18);
Log.d(TAG, "student name:" + student
初涉IPC,了解AIDL的工作原理及用法
今天来讲讲AIDL。这个神奇的AIDL,也是近期在学习的,看了某课大神的解说写下的blog,希望结合自己的看法给各位同价通俗易懂的解说
官方文档:http://developer.android.com/guide/components/aidl.html
一.What is AIDL?(什么是AIDL)
AIDL:Android Interface Definition Language (Android接口定义语言)
首先,我们要知道。进程1和进程2,我们要怎样让他通讯?
在Android系统的虚拟机中,每个app执行都是在一个独立的沙箱里面的,这种话。一个应用崩溃也不会影响到其它应用,这样我们就提出了一个问题。跨进程如怎样进行通讯?怎样传递数据?事实上两个进程是不能直接通讯的。他须要Android系统底层的帮助来进行通讯!那就是我们每个人都知道的四大组件
我们首先还是先进Google的API看看
他大概的意思是他同意你去定义一个自己的标准。使用的是IPC(进程间通讯)进制,跨进程通讯,有兴趣的能够去翻译一下,只是值得注意的是他的第二段标记
这里提到了两个东西
- Binder
- Messenger
我们继续往下看话就知道
- Binder
- Messenger
翻译:假设您不须要执行并发IPC在不同的应用程序中 你就用Binder ,或者假设你想执行IPC,但不须要处理多线程,实现你的接口Messenger。不管怎样,确保你了解实现AIDL之前绑定服务。
所以我们就能理清晰AIDL的概念了
- AIDL //IPC 多应用 多线程
二.Defining an AIDL Interface(语法)
文档中强调。你必须定义一个.aidl的文件,那我们怎样去创建尼?
1. Create the .aidl file(创建一个.aidl文件)
官方提供的语法
// IRemoteService.aidl
package 包名(com.lgl.android);
// Declare any non-default types here with import statements
/** Example service interface 接口*/
interface IRemoteService {
/** Request the process ID of this service, to do evil things with it. 方法*/
int getPid();
/** Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
我们来做一个简单的演示
我们要使用的编译工具在我们的SDK/buidl-tools/android版本号/aidl.bat,只是实际开发中也不须要手动编译,我们新建一个项目ForAIDL,这里我们使用的开发工具是Android Studio,事实上Eclipse可能更加让人熟悉,只是用AS也是大势所趋了。并且AS的文件夹结构我也非常喜欢
我们直接java-new - folder - AIDL folder
然后你就会发现多了一个aidl的文件夹
我们继续新建一个包名,新建一个文件文件new - AIDL - file
大致的内容
// IMyAidlInterface.aidl
package com.lgl.foraidl;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
AS默认是不会去又一次构建Gradle,我们点击一下构建button
然后我们就能够在MainActivity中调用了
三.AIDL Client And Service(client和服务端)
如今就好玩了。我们先来理理思路,通常是这种,我们一个软件有某个功能。也就是服务端,然后client通过AIDL去訪问
这里能够看到服务端仅仅是处理一些操作,我们client去请求,这种话,那我创建一个AIDLService,相同的。我们须要去创建AIDL文件。命名-ServiceAidlInterface
ServiceAidlInterface
// ServiceAidlInterface.aidl
package com.lgl.aidlservice;
// Declare any non-default types here with import statements
interface ServiceAidlInterface {
/*
*计算两数之和
*/
int add(int num1,int num2);
}
如今我们处理的就不是默认的东西了,AIDL的原理就是你自己定义语言接口,对的,我们也来
2. Implement the interface(实现一个AIDL)
依据我们的Google文档,第一步Create the .aidl file已经完毕了,如今就来进行第二步了,我们这里须要使用到Service,看文档
这种话我们新建一个IRemoteService继承service,我们实现计算的逻辑处理
package com.lgl.aidlservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
/**
*
* 监听服务
* Created by lgl on 16/3/13.
*/
public class IRemoteService extends Service{
/**
* 当client绑定到该服务的时候启动onBind
* @param intent
* @return
*/
@Override
public IBinder onBind(Intent intent) {
//绑定之后就计算
return iBinder;
}
/**
* 開始处理结果
*/
private IBinder iBinder = new ServiceAidlInterface.Stub(){
@Override
public int add(int num1, int num2) throws RemoteException {
Log.i("AIDL","收到了请求,参數1"+num1+"参數2"+num2);
return num1+num2;
}
};
}
3. Expose the interface to clients(client的实现)
OK,写完了服务端就能够写client了,我们直接new一个Module-AIDLClients
我們先按剛才的逻辑,把界面写了
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="15dp">
<TextView
android:text="AIDL"
android:textSize="50sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:hint="请输入数字1"
android:id="@+id/et_num1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_marginBottom="15dp"
android:layout_marginTop="15dp"
android:textSize="20sp"
android:text="+"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:hint="请输入数字2"
android:id="@+id/et_num2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_marginBottom="15dp"
android:layout_marginTop="15dp"
android:textSize="20sp"
android:text="="
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_result"
android:text="结果:"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:textColor="#fff"
android:background="@android:color/holo_blue_light"
android:layout_marginTop="30dp"
android:id="@+id/btn_ok"
android:text="计算"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
我们要想client调用服务端的内容。那么就一定要定义标准的语言。所以client的aidl和服务端必须一致
记住,每次不通过的时候先编译一遍
MainActivity(client)
package com.lgl.aidlclients;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.lgl.aidlservice.ServiceAidlInterface;
/**
* AIDL client
* @author lgl
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
//输入
private EditText et_num1,et_num2;
//结果
private TextView tv_result;
//AIDL远程訪问
private Button btn_ok;
private ServiceAidlInterface aidl;
private ServiceConnection conn = new ServiceConnection() {
//连接上
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//拿到远程服务
aidl = ServiceAidlInterface.Stub.asInterface(iBinder);
}
//断开时
@Override
public void onServiceDisconnected(ComponentName componentName) {
//回收
aidl = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
bindServices();
}
private void initView() {
et_num1 = (EditText) findViewById(R.id.et_num1);
et_num2 = (EditText) findViewById(R.id.et_num2);
tv_result = (TextView) findViewById(R.id.tv_result);
btn_ok = (Button) findViewById(R.id.btn_ok);
btn_ok.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_ok:
int num1 = Integer.parseInt(et_num1.getText().toString());
int num2 = Integer.parseInt(et_num2.getText().toString());
try {
//结果
int res = aidl.add(num1,num2);
tv_result.setText("结果:"+res);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
/**
* 开启服务
*/
private void bindServices() {
//获取服务端
Intent intent = new Intent();
//5.0之后的改变
intent.setComponent(new ComponentName("com.lgl.aidlservice","com.lgl.aidlservice.IRemoteService"));
//绑定服务
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
/**
* 销毁服务
*/
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
这里逻辑也是十分的清晰。你创建了之后启动服务。我调用方法来计算,我们来演示一下,我们先启动服务端,再启动client
记得注冊
<service android:name=".IRemoteService"
android:process=":remote"
android:exported="true" >
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="com.lgl.aidlservice.IRemoteService" />
</intent-filter>
</service>
写到这里才看到玉刚也写了相关的信息,这边android:exported=”true”为权限问题。详细能够看下:
android跨进程通信(IPC):使用AIDL
只是这终究也仅仅是一些小菜,我们项目中也不可能用到这么low的方法。毕竟AIDL还是非常强大的,既然这样,那我们来玩玩高级一点的
四.AIDL数据传递
我们不能传递非常大的数据这是总所周知的,那AIDL默认支持什么类型的尼?我们可不能够自己定义尼?当然能够
1.默认数据类型
我们能够看一下官方文档
- 1.基本数据类型
- 2.String
- 3.CharSequence
- 4.List
- 5.Map
- 6.Parcelable(序列化)
我们新建一个aidl文件,他都会自己生成一个方法
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
意指的就是基础类型
然而在我们的实际測试中,你会发现他是不支持short的,我们做这种小測试
void basicTypes(byte b,short s, int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
然后编译
/Users/lgl/Documents/Android/ASCode/AIDLService/app/src/main/aidl/com/lgl/aidlservice/IMyAidlInterface.aidl:11 parameter s (2) unknown type short
他报的错误就是不支持short
再比方说,你想写一个List< String>list的数据。你必须指定他是in还是out类型的
in List< String>list
2.自己定义类型
这里我们先在服务端新建一个aidl文件-IMyAidlInterface,然后写入我们的方法
//我们返回的数据是一个List集合
List<Person>add(in Person person);
这里报错是毋庸置疑的,由于他不识别我们的Person,可是就算我们新建一个Person类也是没用的。由于aidl不支持这个类型,上面也说了,他仅仅支持基本类型和其它有限的几种数据类型。这种话。我们就须要自己定义了,怎么来呢?让他implements Parcelable就能够
package com.lgl.aidlservice;
import android.os.Parcel;
import android.os.Parcelable;
/**
* 序列化实体类
* Created by lgl on 16/3/15.
*/
public class Person implements Parcelable{
private String name;
private int age;
//构造方法
public Person(String name,int age){
this.name =name;
this.age = age;
}
protected Person(Parcel in) {
name = in.readString();
age = in.readInt();
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(name);
parcel.writeInt(age);
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
}
这里值得注意的就是读写部分了,你是怎么写的就得怎么读。顺序错了就没用了。那我们编译一下,你会发现他还是提示错误,依旧是位置的List数据类型
/Users/lgl/Documents/Android/ASCode/AIDLService/app/src/main/aidl/com/lgl/aidlservice/IMyAidlInterface.aidl:9 unknown return type List<Person>
这里就须要我们手动导入这个Person了不然人家也不认识啊。那要怎么做?还是须要创建一个Person的AIDL文件。内容十分的简单。说明文件
// IMyAidlInterface.aidl
package com.lgl.aidlservice;
parcelable Person;
能够看到,里面就一句话,好的,如今编译通过了,那我们又一次来改动一下IRemoteService了,这次就不是返回计算数据的和了
package com.lgl.aidlservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import java.util.ArrayList;
import java.util.List;
/**
*
* 监听服务
* Created by lgl on 16/3/13.
*/
public class IRemoteService extends Service{
private ArrayList<Person>persons;
/**
* 当client绑定到该服务的时候启动onBind
* @param intent
* @return
*/
@Override
public IBinder onBind(Intent intent) {
persons = new ArrayList<Person>();
//绑定之后就计算
return iBinder;
}
/**
* 開始处理结果
*/
// private IBinder iBinder = new ServiceAidlInterface.Stub(){
//
// @Override
// public int add(int num1, int num2) throws RemoteException {
// Log.i("AIDL","收到了请求,参數1"+num1+"参數2"+num2);
// return num1+num2;
// }
// };
/**
* 序列化
*/
private IBinder iBinder = new IMyAidlInterface.Stub(){
@Override
public List<Person> add(Person person) throws RemoteException {
//每添加一个人都能得到返回
persons.add(person);
return persons;
}
};
}
好的,服务端部分写完了,刚開始可能是有点混乱的。只是当你确实已经理解之后你会发现,也就这么点东西的逻辑,那我们继续来写client,看他是怎么进行序列化的处理的。
还是一样,我们把Person.aidl和IMyAidlInterface.aidl复制过去。顺便把Person类拷贝过来。这里要注意的事就是包名要一致
然后我们就开启服务了
package com.lgl.aidlclients;
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.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.lgl.aidlservice.IMyAidlInterface;
import com.lgl.aidlservice.Person;
import com.lgl.aidlservice.ServiceAidlInterface;
import java.util.ArrayList;
/**
* AIDL client
* @author lgl
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
//输入
private EditText et_num1,et_num2;
//结果
private TextView tv_result;
//AIDL远程訪问
private Button btn_ok;
private ServiceAidlInterface aidl;
private IMyAidlInterface imy;
private ServiceConnection conn = new ServiceConnection() {
//连接上
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//拿到远程服务
aidl = ServiceAidlInterface.Stub.asInterface(iBinder);
imy = IMyAidlInterface.Stub.asInterface(iBinder);
}
//断开时
@Override
public void onServiceDisconnected(ComponentName componentName) {
//回收
aidl = null;
imy = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindServices();
initView();
}
private void initView() {
et_num1 = (EditText) findViewById(R.id.et_num1);
et_num2 = (EditText) findViewById(R.id.et_num2);
tv_result = (TextView) findViewById(R.id.tv_result);
btn_ok = (Button) findViewById(R.id.btn_ok);
btn_ok.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_ok:
// int n1 = Integer.parseInt(et_num1.getText().toString());
// int n2 = Integer.parseInt(et_num2.getText().toString());
//
// try {
// //结果
// int res = aidl.add(n1,n2);
// tv_result.setText("结果:"+res);
// } catch (RemoteException e) {
// e.printStackTrace();
// tv_result.setText("Error");
// }
try{
ArrayList<Person>persons = (ArrayList<Person>) imy.add(new Person("name",21));
Log.i("Person",persons.toString());
}catch (RemoteException e){
e.printStackTrace();
}
break;
}
}
/**
* 开启服务
*/
private void bindServices() {
//获取服务端
Intent intent = new Intent();
//5.0之后的改变
intent.setComponent(new ComponentName("com.lgl.aidlservice","com.lgl.aidlservice.IRemoteService"));
//绑定服务
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
/**
* 销毁服务
*/
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
这种话,仅仅要我们点击了计算button,Log打印一条消息,那事实是如此吗?我们执行一下
不会发现,我们每一次加入都是多添加了一行,相当于我们远程加入了一行
五.AIDL的工作原理
我们每次编译AIDL的文件的时候,都会编译成一个JAVA文件,详细是什么,我们一起研究研究。借用大神的一张图,你如今可能看不懂,等讲完原理你就懂了
编译好的JAVA文件在
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/lgl/Documents/Android/ASCode/AIDLService/aidlclients/src/main/aidl/com/lgl/aidlservice/IMyAidlInterface.aidl
*/
package com.lgl.aidlservice;
//他是继承系统的接口。也就是说他本身就是一个接口
public interface IMyAidlInterface extends android.os.IInterface
{
/** 存根。并且实现了自己的接口*/
public static abstract class Stub extends android.os.Binder implements com.lgl.aidlservice.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.lgl.aidlservice.IMyAidlInterface";
/**自己的构造方法*/
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.lgl.aidlservice.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.lgl.aidlservice.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.lgl.aidlservice.IMyAidlInterface))) {
return ((com.lgl.aidlservice.IMyAidlInterface)iin);
}
return new com.lgl.aidlservice.IMyAidlInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
//返回的this是Stub
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add:
{
data.enforceInterface(DESCRIPTOR);
com.lgl.aidlservice.Person _arg0;
if ((0!=data.readInt())) {
//假设拿到传过来数值封装成一个data
_arg0 = com.lgl.aidlservice.Person.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
java.util.List<com.lgl.aidlservice.Person> _result = this.add(_arg0);
reply.writeNoException();
//最后通过这个又写回去了,这样就完毕了整个的通讯
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
//代理,他是Stud的内部类
private static class Proxy implements com.lgl.aidlservice.IMyAidlInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
//我们返回的数据是一个List集合
@Override public java.util.List<com.lgl.aidlservice.Person> add(com.lgl.aidlservice.Person person) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.lgl.aidlservice.Person> _result;
try {
//写入了类名
_data.writeInterfaceToken(DESCRIPTOR);
if ((person!=null)) {
//写了一个1,同一时候add数据,当你拿到了远程服务的时候,事实上你拿到的仅仅是远程服务的代理
_data.writeInt(1);
person.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
//传入到onTransact
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.lgl.aidlservice.Person.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
//我们返回的数据是一个List集合
//我们创建AIDL命名的这种方法
public java.util.List<com.lgl.aidlservice.Person> add(com.lgl.aidlservice.Person person) throws android.os.RemoteException;
}
Ok,AIDL这边算是讲完了,假设有兴趣的话。能够再去深入研究一下。我这边也仅仅是学习了大神的视频,然后做的一个小总结和小笔记
Demo下载:http://download.csdn.net/detail/qq_26787115/9462515