首页 > 代码库 > 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

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

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