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.使用绑定服务的方式来启动服务,要传入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

人生不如意十之八-九,与其诅咒黑暗,倒不如在生命中点燃一盏灯

Service服务AIDL进程通信详细总结

相关文章:

你感兴趣的文章:

标签云: