AIDL Service实现跨进程通信

AIDL Service用于在不同进程间进行数据交换,即所谓的跨进程通信(interprocess communication,简称IPC)

与绑定本地Service不同的是,本地Service的onBind()方法会直接把IBinder对象本身传给客户端的ServiceConnection的onServiceConnected()方法的第二个参数。但远程Service的onBind()方法只是将IBinder对象的代理传给客户端的ServiceConnection的onServiceConnected()方法的第二个参数

1、创建AIDL接口文件

AIDL的语法与Java接口很相似,但是有几点要求:

(1) AIDL定义的接口文件必须以.aidl结尾
(2) AIDL接口中用到的数据类型,除了基本类型、String、List、Map、CharSequence之外,其他类型都需要导入包,即使在同一个包中也需要导入

新建客户端工程,创建IBook.aidl文件:

package com.example.testaidlservice;

interface IBook {
    String getName();
    int getPageCount();
}

定义好IBook.aidl接口文件后,ADT工具会自动在gen目录下生成一个IBook.java文件,在java文件里包含一个Stub内部类,该内部类实现了IBinder、IBook两个接口,因此可作为Service的onBind()方法的返回值,这个Stub类将会作为远程Service的回调类

2、定义远程Service类

新建类BookService.java继承Service类

public class BookService extends Service {

    private BookBinder bookBinder;
    private String bookName = "Android开发艺术探索";
    private int pageCount = 524;

    public class BookBinder extends Stub {
        @Override
        public String getName() throws RemoteException {
            // TODO Auto-generated method stub
            return bookName;
        }
        @Override
        public int getPageCount() throws RemoteException {
            // TODO Auto-generated method stub
            return pageCount;
        }
    }

    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
        bookBinder = new BookBinder();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return bookBinder;
    }

}

3、在AndroidManifest.xml中配置该Service

<service android:name="com.example.testaidlservice.BookService">
            <intent-filter >
                <action android:name="com.example.testaidlservice.BOOK_SERVICE"/>
            intent-filter>
        service>

配置好以后,就可以在客户端调用远程Service了,Service端目录结构:

AIDL Service实现跨进程通信_第1张图片

4、客户端访问AIDL Service

AIDL接口定义了两个进程之间的通信接口,所以客户端也需要用到AIDL接口。所以在创建客户端后,要将Service端的AIDL接口文件复制到客户端,复制后ADT工具会生成相应的接口文件

客户端绑定到远程Service需要两步:

(1)创建ServiceConnection对象
(2)调用Context的bindService()方法绑定远程Service

客户端界面布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.testaidlservice.MainActivity" >

    <Button 
        android:id="@+id/bind"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="绑定远程Service"/>

    <Button 
        android:id="@+id/get"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="获取Service数据"/>

    <TextView 
        android:id="@+id/bookName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <TextView 
        android:id="@+id/pageCount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

LinearLayout>

客户端代码:

public class MainActivity extends ActionBarActivity implements OnClickListener {

    IBook iBook;
    Button bind, get;
    TextView bookName, pageCount;

    ServiceConnection conn = new ServiceConnection() {
        public void onServiceConnected(ComponentName name, IBinder service) {
            //获取远程Service的代理对象
            iBook = IBook.Stub.asInterface(service); 
        };
        public void onServiceDisconnected(ComponentName name) {
            iBook = null;
        };
    };

    public void bind() {
        Intent intent = new Intent(this, BookService.class);
        bindService(intent, conn, BIND_AUTO_CREATE);
        Toast.makeText(this, "绑定Service成功", Toast.LENGTH_LONG).show();
    }

    public void getData() throws RemoteException {
        //通过获得的代理对象访问Service中的数据
        String name = iBook.getName();
        int count = iBook.getPageCount();
        bookName.setText("书名:" + name);
        pageCount.setText("页数:" + count);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bind = (Button)findViewById(R.id.bind);
        get = (Button)findViewById(R.id.get);
        bookName = (TextView)findViewById(R.id.bookName);
        pageCount = (TextView)findViewById(R.id.pageCount);
        bind.setOnClickListener(this);
        get.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch(v.getId()) {
            case R.id.bind:
                bind();
                break;
            case R.id.get:
                try {
                    getData();
                } catch (RemoteException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                break;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}

客户端目录结构:

AIDL Service实现跨进程通信_第2张图片

绑定远程Service后,在ServiceConnection的onServiceConnected()方法中可以获取远程Service的代理对象,通过返回的代理对象就可以访问远程Service中的数据

运行客户端程序,点击绑定远程Service,效果如下图所示:

AIDL Service实现跨进程通信_第3张图片

点击获取Service数据:

AIDL Service实现跨进程通信_第4张图片

传递自定义类型的AIDL Service

当传递的数据中包含自定义类型时,需要对自定义的类型进行处理,Android要求调用远程Service中的自定义类型都必须实现Parcelable接口。实现Parcelable接口时,不仅要实现接口的方法,而且在实现类中必须定义一个类型为Parcelable.Create类型、名称为CREATOR的静态常量,除此之外,所有用到的自定义类型必须用AIDL进行定义。

在本例中需要使用到Person和Book两个自定义类型,首先使用AIDL定义,新建Person.aidl文件,代码如下:

parcelable Person;

然后定义实现Parcelable接口的Person类,直接上代码:

public class Person implements Parcelable {

    int id;
    String name;

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public int describeContents() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // TODO Auto-generated method stub
        //将对象的信息写入到Parce中
        dest.writeInt(id);
        dest.writeString(name);
    }

    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
        public Person createFromParcel(Parcel source) {
            //从Parcel中读取数据,返回Person对象
            return new Person(source.readInt(), source.readString());
        };
        public Person[] newArray(int size) {
            return new Person[size];
        };
    };

}

定义好Person类以后,用同样的方法定义Book类,有了这两个类,就可以来定义AIDL通信接口了,新建IBook.aidl文件,代码如下:

package com.example.testaidlservice;

import com.example.testaidlservice.Person;
import com.example.testaidlservice.Book;

interface IBook {
    List getBooks(in Person owner);
}

AIDL接口在定义方法时,需要指定形参的传递模式,分为输入参数和输出参数,Java中一般都是采用传入参数的形式,所以指定Person类型为in模式

Book类和Person类跟IBook文件在同一个目录下,但也需要手动导入

接下来定义Service类

public class BookService extends Service {

    private BookBinder bookBinder = new BookBinder();
    private static Map> data = new HashMap>();
    private static List list1 = new ArrayList();
    private static List list2 = new ArrayList();
    private static Person jack = new Person(1, "Jack");
    private static Person jet = new Person(2, "Jet");

    static {
        list1.add(new Book("疯狂Android讲义", 704));
        list1.add(new Book("Android开发艺术探索", 524));
        data.put(jack, list1);
        list2.add(new Book("疯狂Java讲义", 887));
        list2.add(new Book("Android经典项目案例开发实战宝典", 638));
        data.put(jet, list2);
    }

    public class BookBinder extends Stub {
        @Override
        public List getBooks(Person owner) throws RemoteException {
            // TODO Auto-generated method stub
            String name = owner.getName();
            if(name.equals("Jack")) {
                return data.get(jack);
            }else{
                return data.get(jet);
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return bookBinder;
    }

}

在代码中使用静态初始化块向一个map集合中添加了2条数据,每条数据的key为Person类型,值为List类型,List中包含Person类所拥有的Book信息

在AndroidManifest.xml中配置Service:

<service android:name="com.example.testaidlservice.BookService">
            <intent-filter >
                <action android:name="com.example.testaidlservice.BOOK_SERVICE"/>
            intent-filter>
        service>

这样,Service端所需要的类都已定义完毕,目录结构:

AIDL Service实现跨进程通信_第5张图片

接下来创建客户端,首先将Service端的AIDL通信接口文件拷贝到客户端,包括用到的Person.aidl、Book.aidl、Person.java、Book.java和IBook.aidl全部复制到客户端中,这样会在客户端的gen目录下生成IBook.java文件。

最后,在客户端中访问Service中的数据,客户端代码

布局文件:

"http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.testaidlservice.MainActivity" >

    

MainActivity代码:

public class MainActivity extends ActionBarActivity implements OnClickListener {

    IBook iBook;
    Button bind, getJack, getJet;
    TextView bookInfo;

    ServiceConnection conn = new ServiceConnection() {
        public void onServiceConnected(ComponentName name, IBinder service) {
            //获取远程Service的代理对象
            iBook = IBook.Stub.asInterface(service); 
        };
        public void onServiceDisconnected(ComponentName name) {
            iBook = null;
        };
    };

    public void bind() {
        Intent intent = new Intent();
        intent.setAction("com.example.testaidlservice.BOOK_SERVICE");
        bindService(intent, conn, BIND_AUTO_CREATE);
        Toast.makeText(this, "绑定Service成功", Toast.LENGTH_LONG).show();
    }

    public void getData(Person owner) throws RemoteException {
        //通过获得的代理对象访问Service中的数据
        List books = iBook.getBooks(owner);
        String name = owner.getName();
        String result = "";
        for(Book book:books) {
            result = result + "书名:" + book.getBookName() + ",页数:" +book.getPageCount() + "\n";
        }
        bookInfo.setText(name + "的Book信息:\n" + result);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bind = (Button)findViewById(R.id.bind);
        getJack = (Button)findViewById(R.id.getJack);
        getJet = (Button)findViewById(R.id.getJet);
        bookInfo = (TextView)findViewById(R.id.bookInfo);
        bind.setOnClickListener(this);
        getJack.setOnClickListener(this);
        getJet.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch(v.getId()) {
            case R.id.bind:
                bind();
                break;
            case R.id.getJack:
                try {
                    getData(new Person(1, "Jack"));
                } catch (RemoteException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                break;
            case R.id.getJet:
                try {
                    getData(new Person(1, "Jet"));
                } catch (RemoteException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                break;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}

客户端目录结构:

AIDL Service实现跨进程通信_第6张图片

运行程序,效果如下图所示:

绑定到远程Service:

AIDL Service实现跨进程通信_第7张图片

获取Person类的Book信息:

1、
AIDL Service实现跨进程通信_第8张图片

2、
AIDL Service实现跨进程通信_第9张图片

你可能感兴趣的:(Android)