首页 > 代码库 > Service服务AIDL进程通信详细总结
Service服务AIDL进程通信详细总结
一.基础概念
(一)定义以及作用
AIDL,Android Interface Definition Language(安卓接口定义语言)。这里使用的接口定义语言aidl里面的语言其实并非是java语言,是跟C语言相近的一种语言。
我们要知道的一点是ContentProvider内容提供者,给我们提供的是数据,而Service服务中的AIDL提供给我们的是方法,这就是这两种进程间通信的作用的区别。
(二)创建AIDL服务步骤
建立AIDL服务要比建立普通的服务复杂一些,具体步骤如下:
1.在Eclipse Android工程的Java包目录中建立一个扩展名为aidl的文件。
该文件的语法类似于Java代码,但会稍有不同。详细介绍见实例的内容。
aidl文件的设计规范:
(1) 不能有访问权限
(2)同包也必须导入
(3)所有对象数据必须是简单的基本数据或实现序列化Parcelable的数据或对象
2.如果aidl文件的内容是正确的,ADT会自动生成一个Java接口文件(*.java)。
3.建立一个服务类(Service的子类)。
4.实现由aidl文件生成的Java接口。
5.在AndroidManifest.xml文件中配置AIDL服务,其实就是注册服务,但是如果是要别的程序可以访问就要设置入口为true,并且设置action。
例如:
<service android:name=".MathService" android:exported="true">
<intent-filter >
<action android:name="NumMath"/>
</intent-filter>
</service>
(三)AIDL服务接口方法的调用
1.如果是在别的程序中调用,要先复制aidl文件到你的工程中,并且包的目录要和之前aidl文件创建的包目录一样,这个也是比较坑爹的地方!
2.创建服务的连接对象,重写连接的回调方法
3.在连接服务成功的回调方法中获取aidl文件定义的接口对象
4.使用绑定服务的方式来启动服务,要传入action
5.通过接口对象执行接口里面的方法
AIDL通信可以传送数据类型
1:可以传递基本数据类型。
2:可以传递集合,注意加入是in 还是out 还是inout
3:可以传递实现Parcelable接口的对象。也需要加入in,out,inout
下面是程序开发示例,在Eclipse中开发演示!
二.基本数据调用AIDL方法来完成操作的示例
这里在一个程序中创建aidl文件并定义一个两数相加的方法,并创建Service来实现aidl文件定义的方法。接着新建一个程序,调用上一个程序中aidl定义的方法来完成加法操作。
(一)在第一个程序的设计(aidl服务的创建)
1.在src文件夹下创建包aidl,在创建文件IAdd.aidl
文件代码:
package aidl;
interface IAdd{
int add(int num1,int num2);
}
2.如果aidl文件的内容是正确的,ADT会自动生成一个Java接口文件(IAidl.java)。
接口文件的内容都是系统帮我们写好的。里面源码并不多,有兴趣可以看看,接口里面有一个静态的内部类Stub,以及静态内部类的静态方法asInterface都是下面用到的方法!
3.建立一个服务类(Service的子类),并实现aidl文件中的方法
代码:
package com.example.lesson18_aidl;
import aidl.IAdd;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class MathService extends Service {
//定义Sub对象,这个是AIDL文件设计后系统生成的对象,
//并且这个对象是继承了IBinder类,
private IAdd.Stub stub=new IAdd.Stub() {
//这是自己编写的AIDL文件里面定义的方法
@Override
public int add(int num1, int num2) throws RemoteException {
//定义返回值
return num1+num2;
}
};
/**
* 服务绑定时的回调方法
*/
@Override
public IBinder onBind(Intent arg0) {
//返回的stub对象也是一个IBinder类的对象,这个对象在服务绑定时另一端能收到
return stub;
}
}
4.在AndroidManifest.xml文件中配置AIDL服务.
<!--由于是用于被别人来访问的,出口要为true -->
<service android:name=".MathService" android:exported="true">
<intent-filter >
<action android:name="NumMath"/>
</intent-filter>
</service>
通上面四步,一个最简单的aidl服务已经创建完成了。第一个程序中的MainActivity中可以什么都不写!
(二)AIDL服务的调用
注意这里是在另一个新的程序中。
1.先复制aidl文件到你的工程中,并且包的目录要和之前aidl文件创建的包目录一样,这个也是比较坑爹的地方!
这里aidl文件的包名是一定要一样的,否则即使能运行,但是服务是不能正常启动的。
2.设计简单布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et1"
android:inputType="number"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et2"
android:inputType="number"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="BindService"
android:text="启动服务"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="add"
android:text="相加"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"
android:id="@+id/tv"
/>
</LinearLayout>
上面设计两个输入框,一个按钮用来启动服务,一个按钮用来实现运算,这里启动服务后可以一直进行运算!
3.java代码调用aidl服务:
package com.example.lesson18_aidl_test;
import aidl.IAdd;
import android.app.Activity;
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.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
/**
*调用其他工程的服务的示例
*
*/
public class MainActivity extends Activity {
//布局内的控件
EditText et1;
EditText et2;
TextView tv;
//接口对象
IAdd asInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et1=(EditText) findViewById(R.id.et1);
et2=(EditText) findViewById(R.id.et2);
tv=(TextView) findViewById(R.id.tv);
}
/**
* 启动服务
*/
public void BindService(View view){
//启动服务,来计算结果
Intent service=new Intent("NumMath");//通过Action来找到服务对象
int flags=BIND_AUTO_CREATE;
bindService(service, conn, flags);
}
/**
* 点击按钮实现计算
*/
public void add(View view){
if (!TextUtils.isEmpty(et1.getText().toString())&&!TextUtils.isEmpty(et2.getText().toString())) {
//获取输入框的值
int num1=Integer.parseInt(et1.getText().toString());
int num2=Integer.parseInt(et2.getText().toString());
try {
//通过服务对象来得到结果
if(asInterface!=null){
int sum=asInterface.add(num1, num2);
//把结果显示在文本中
tv.setText("相加后的和为:"+sum);
}else {
Toast.makeText(this,"服务没有启动!", Toast.LENGTH_SHORT).show();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
/**
* 创建连接服务的对象
*/
ServiceConnection conn=new ServiceConnection() {
//服务连接时的触发方法
@Override
public void onServiceConnected(ComponentName arg0, IBinder arg1) {
//asInterface是AIDL文件设计好系统生成的IAdd接口的Stub类的静态的方法,
//这个方法我们可以记住使用,也可以查看源码便于记忆
//生成的IAdd接口对象里面就有我们在AIDL定义的方法,可以直接调用
asInterface = IAdd.Stub.asInterface(arg1);
}
//服务断开时的回调方法,正常断开时不会回调的,只有异常情况下断开服务才回调这个方法,比如服务抛出异
@Override
public void onServiceDisconnected(ComponentName arg0) {
}
};
}
到这里aidl服务的简单创建和调用就已经完成了!
这里要先要启动一次程序一,再启动程序二,才能使用aidl服务的方法。
程序运行后,操作的结果图:
三.对象数据调用AIDL方法来完成操作的示例
对象必须要实现序列化Parcelable,并且传递时必须定义好流向!
同包也要导包!很多人不理解这句话用在哪里,详情请看示例~~
还是两个程序间通信,使用用户登录的示例演示。
(一)在第一个程序的设计(aidl服务的创建)
1.在src文件夹下创建包aidl,在创建文件User.java
文件代码:
package aidl;
import android.os.Parcel;
import android.os.Parcelable;
public class User implements Parcelable{
// Serializable 可以读写到本地,也可以通过网络传递,效率非常慢,会创建非常多的临时对象。
//Parcelable 不能读写到本地,只能在网络,Intent,AIDL 中传递,效率非常快。
//用户的基本数据
String username;
String password;
int age;
boolean isAnmin;
double money;
@Override
public int describeContents() {
// 描述的值,基本没有用!
return 0;
}
/**
* 数据写入
*/
@Override
public void writeToParcel(Parcel dest, int arg1) {
dest.writeString(username);
dest.writeString(password);
dest.writeInt(age);
dest.writeByte((byte) (isAnmin ? 1 : 0));
dest.writeDouble(money);
}
/**
* 数据读取的值,必须和数据写入是一致的
* 方法也是固定的记住就可以了
*/
protected void readFromParcel(Parcel in) {
username = in.readString();
password = in.readString();
age = in.readInt();
isAnmin = in.readByte() != 0;
money = in.readDouble();
}
/**
* 这个也是要重写的值,记住就可以了!
*/
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
User user = new User();
user.readFromParcel(in);
return user;
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
//设置get和set方法
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isAnmin() {
return isAnmin;
}
public void setAnmin(boolean isAnmin) {
this.isAnmin = isAnmin;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password
+ ", age=" + age + ", isAnmin=" + isAnmin + ", money="
+ money + "]";
}
}
2.在src文件夹aidl下,再创建文件User.aidl
文件代码:
package aidl;
//同名也要导包!说的就是这里~~这里倒入的是java文件的类,注意的是aidl调用的是aidl文件!
import aidl.User;
//声明对象已经序列化
parcelable User;
上面的文件只是定义了类,没有定义方法,但是也是必须要的!
3.在src文件夹aidl下,再创建文件Login.aidl
文件代码:
package aidl;
//同名也要导包,这里要注意
import aidl.User;
interface Login{
void check(inout User user);//对象必须指明流向in/out/inout
}
4.如果aidl文件的内容是正确的,ADT会自动生成一个Java接口文件(Login.java)。
5.建立一个服务类(Service的子类),并实现aidl文件中的方法
代码:
package com.example.lesson18_aidl_parcelable;
import aidl.Login;
import aidl.User;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.widget.Toast;
public class UserService extends Service {
//定义Sub对象,这个是AIDL文件设计后系统生成的对象,
//并且这个对象是继承了IBinder类,
private Login.Stub stub=new Login.Stub() {
//这是自己编写的AIDL文件里面定义的方法
@Override
public void check(User user) throws RemoteException {
// 由于这里是inout流向,所以用户数据数据的验证成功的同时也能拿到用户数据
if (user.getUsername().equals("liwen")&&user.getPassword().equals("123456")) {
//给User设置其他值
//这里只是简单模拟数据,应用中是用户输入数据后从数据库或本地文件读取用户的数据
user.setAge(18);
user.setAnmin(true);
user.setMoney(8000);
}
}
};
/**
* 服务绑定时的回调方法
*/
@Override
public IBinder onBind(Intent arg0) {
//返回的stub对象也是一个IBinder类的对象,这个对象在服务绑定时另一端能收到
return stub;
}
}
6.在AndroidManifest.xml文件中配置AIDL服务.
<!--由于是用于被别人来访问的,出口要为true -->
<service android:name=".UserService" android:exported="true">
<intent-filter >
<action android:name="userLogin"/>
</intent-filter>
</service>
通上面六步,一个复杂一点的aidl服务已经创建完成了。第一个程序中的MainActivity中可以什么都不写!
(二)AIDL服务的调用
注意这里是在另一个新的程序中。
1.先复制aidl包内的三个文件到你的工程中
并且包的目录要和之前aidl文件创建的包目录一样,这个也是比较坑爹的地方!
这里aidl文件的包名是一定要一样的,否则即使能运行,但是服务是不能正常启动的。
2.设计简单布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et1"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et2"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="BindService"
android:text="启动服务"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="login"
android:text="登录"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"
android:id="@+id/tv"
/>
</LinearLayout>
上面设计两个输入框,一个按钮用来启动服务,一个按钮用来实现登录验证,这里要先启动服务后进行登录!
3.java代码调用aidl服务:
package com.example.lesson18_aidl_test;
import aidl.Login;
import aidl.User;
import android.app.Activity;
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.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
/**
*调用其他工程的服务的示例
*
*/
public class LoginActivity extends Activity {
//布局内的控件
EditText et1;
EditText et2;
TextView tv;
//接口对象
Login asInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
et1=(EditText) findViewById(R.id.et1);
et2=(EditText) findViewById(R.id.et2);
tv=(TextView) findViewById(R.id.tv);
}
/**
* 启动服务
*/
public void BindService(View view){
//启动服务,来计算结果
Intent service=new Intent("userLogin");//通过Action来找到服务对象
int flags=BIND_AUTO_CREATE;
bindService(service, conn, flags);
}
/**
* 点击按钮实现计算
*/
public void login(View view){
if (!TextUtils.isEmpty(et1.getText().toString())&&!TextUtils.isEmpty(et2.getText().toString())) {
//获取输入框的值
String name=et1.getText().toString();
String password=et2.getText().toString();
if (!"liwen".equals(name)||!"123456".equals(password)) {
Toast.makeText(this, "用户名或密码错误!", Toast.LENGTH_SHORT).show();
return;
}
try {
//通过服务对象来得到结果
if(asInterface!=null){
User user=new User();
user.setUsername(name);
user.setPassword(password);
asInterface.check(user);
//把结果显示在文本中,可以看到输入两个值就可以拿到用户在服务另一端的其他数据值
tv.setText("相加后的和为:"+user.toString());
}else {
Toast.makeText(this,"服务没有启动!", Toast.LENGTH_SHORT).show();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
/**
* 创建连接服务的对象
*/
ServiceConnection conn=new ServiceConnection() {
//服务连接时的触发方法
@Override
public void onServiceConnected(ComponentName arg0, IBinder arg1) {
//asInterface是AIDL文件设计好系统生成的IAdd接口的Stub类的静态的方法,
//这个方法我们可以记住使用,也可以查看源码便于记忆
//生成的IAdd接口对象里面就有我们在AIDL定义的方法,可以直接调用
asInterface = Login.Stub.asInterface(arg1);
}
//服务断开时的回调方法,正常断开时不会回调的,只有异常情况下断开服务才回调这个方法,比如服务抛出异
@Override
public void onServiceDisconnected(ComponentName arg0) {
}
};
}
到这里aidl服务的复杂一点的创建和调用就已经完成了!
这里要先要启动一次程序一,再启动程序二,才能使用aidl服务的方法。
程序运行后的结果图,登录失败时:
用户名和密码登录成功时:
到这里AIDL服务的演示基本上就完成了,AIDL应用基本上也就上面两种。
Parcelable序列化小结:
1. 实现接口
2. 重写writeToParcel方法。序列化写入
3. 在创建静态Creator对象,泛型为本类对象的 CREATOR,名字不能改变,再重写createFromParcel,newArray。
4. createFromParcel中实现序列化读取读取和写入 必须顺序相同。
boolean使用int来代替,读写进行转换。
在AIDL中 序列化读取方法必须是 readFromParcel
AIdl中使用的对象必须事先 Parcelable,并且需要建立adil的文件来说明序列化对象,导包
基础数据类型,默认定向标记为 in 输入
对象必须指定它的定向标签:in out inout
in 是 从客户端流向 服务端,服务改变,不影响客户端
out 是从客户端流向服务端的对象属性为空,从服务端改变
inout 是从客户端流向服务端,在服务端改变,客户端同步改变。后流向客户端.
对于两种序列化,Serailizable和Parcelable它们的区别和联系:
联系:
它们都是可以让对象实现序列化,来实现数据传递,比如Intent数据传递的数据要么是基础数据,要么是实现了序列化的数据。
区别:
Serializable 可以读写到本地,也可以通过网络传递,效率非常慢,会创建非常多的临时对象。它的实现非常简单,不需要重写任何方法。
Parcelable 不能读写到本地,只能在网络,Intent,AIDL 中传递,效率非常快。它的实现麻烦,要重写方法:writeToParcel实现序列化写入,createFromParcel实现序列化读取,创建静态Creator对象。
关于这两种序列化的使用示例,之前也是有总结的,供大家参考:
Java序列化对象的存储和读取,ObjectOutputStream和它的方法writeObject来进行写入对象:
http://blog.csdn.net/wenzhi20102321/article/details/53148094
Android序列化的存储和读取,Parcelable和Serializable对比使用示例:
http://blog.csdn.net/wenzhi20102321/article/details/53148026
Service服务AIDL进程通信详细总结