一.基础概念(一)定义以及作用
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.使用绑定服务的方式来启动服务,要传入action5.通过接口对象执行接口里面的方法
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
人生不如意十之八-九,与其诅咒黑暗,倒不如在生命中点燃一盏灯