Android AIDL用法介绍
来源:互联网 发布:关于程序员的电影 编辑:程序博客网 时间:2024/05/17 22:37
Android AIDL用法介绍
一、简介
服务端
服务端首先要创建一个Service来监听客户端连接请求,然后创建一个aidl文件,将接口暴露给客户端,最后在Service中实现这个aidl接口客户端
先绑定服务端的Service,将服务端返回的Binder对象转成aidl接口对应的类型,然后就可以调用aidl接口了AIDL接口
并不是所有的数据类型在aidl文件中都可以使用,那aidl文件支持哪些数据类型?(a) 基本数据类型(int、long、char、boolean、double等)
(b) String和CharSequence
(c) List:只支持ArrayList,里面每个元素都必须被AIDL支持
(d) Map:只支持HashMap,key和value都必须被AIDL支持
(e) Parcelable:所有序列化的对象
(f) AIDL:所有AIDL接口本身也可以在AIDL文件中使用
(g) AIDL除了基本数据类型,其他类型参数需要标上in、out或inout,in为输入型参数,out为输出型参数
(h) AIDL接口中只支持方法,不支持使用静态常量
二、示例
场景是客户端调用服务端aidl接口getMovieList获取影片列表,客户端与服务端位于不同进程(或应用),下面通过例子详细了解下aidl跨进程通讯的基本用法
首先创建Movie.java、Movie.aidl和IMovieMgr.aidl文件,IMovieMgr中声明getMovieList接口,代码如下
// Movie.javapublic class Movie implements Parcelable{ public String name; public Movie(String name) { this.name = name; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); } @Override public int describeContents() { return 0; } public static final Parcelable.Creator<Movie> CREATOR = new Parcelable.Creator<Movie>() { @Override public Movie createFromParcel(Parcel source) { return new Movie(source); } @Override public Movie[] newArray(int size) { return new Movie[size]; } }; private Movie(Parcel in) { this.name = in.readString(); }}// Movie.aidlpackage com.rico.aidl;parcelable Movie;// IMovieMgr.aidlpackage com.rico.aidl;// Declare any non-default types here with import statementsimport com.rico.aidl.Movie;interface IMovieMgr { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); List<Movie> getMovieList(); void addMovie(in Movie movie);}
编译后系统自动生成IMovieMgr.java,通过对IMovieMgr.java的源码分析,可以进一步了解Binder的工作机制
接着定义服务端MovieMgrService.java,这里使用CopyOnWriteArrayList存储影片,原因是CopyOnWriteArrayList支持并发读写,aidl方法是在服务端的binder线程池中执行,当多个客户端同时连接服务端,存在多个线程同时访问资源的情形,所以我们需要在aidl方法中处理线程同步,这里直接使用CopyOnWriteArrayList来自动进行线程同步,代码如下
// AndroidManifest.xml<service android:name=".MovieMgrService" android:process=":remote"></service>// MovieMgrService.javapublic class MovieMgrService extends Service{ CopyOnWriteArrayList<Movie> mMoveList = new CopyOnWriteArrayList<>(); private Binder mBinder = new IMovieMgr.Stub() { @Override public List<Movie> getMovieList() throws RemoteException { System.out.println("movices return mMovieList"); return mMoveList; } @Override public void addMovie(Movie movie) throws RemoteException { mMoveList.add(movie); } @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { } }; @Override public void onCreate() { super.onCreate(); mMoveList.add(new Movie("钢铁侠")); mMoveList.add(new Movie("速度激情8")); System.out.println("movices onCreate"); } @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; }}
最后在客户端调用aidl接口
public class MovieMgrActivity extends Activity{ ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IMovieMgr movieMgr = IMovieMgr.Stub.asInterface(service); try { List<Movie> list = movieMgr.getMovieList(); for (Movie movie : list) { System.out.println(movie.name); } } catch (RemoteException e) { } } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent i = new Intent(this, MovieMgrService.class); bindService(i, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { super.onDestroy(); unbindService(mConnection); }}
运行一下:
07-24 00:08:50.872 21145-21145/com.rico.aidl I/System.out: 钢铁侠07-24 00:08:50.872 21145-21145/com.rico.aidl I/System.out: 速度激情8
到这里,基本用法介绍完毕,但是aidl的难点还没有涉及,下面继续分析~
服务端还提供了addMovie添加影片的接口,我们尝试在客户端添加一部影片,看看能否添加成功?只需要修改onServiceConnected接口
@Override public void onServiceConnected(ComponentName name, IBinder service) { IMovieMgr movieMgr = IMovieMgr.Stub.asInterface(service); try { System.out.println("添加一部影片:七月安生"); movieMgr.addMovie(new Movie("七月安生")); System.out.println("影片列表:"); List<Movie> list = movieMgr.getMovieList(); for (Movie movie : list) { System.out.println(movie.name); } } catch (RemoteException e) { } }
运行如下:
07-24 07:27:02.207 29092-29092/com.rico.aidl I/System.out: 添加一部影片:七月安生07-24 07:27:02.207 29092-29092/com.rico.aidl I/System.out: 影片列表:07-24 07:27:02.208 29092-29092/com.rico.aidl I/System.out: 钢铁侠07-24 07:27:02.208 29092-29092/com.rico.aidl I/System.out: 速度激情807-24 07:27:02.208 29092-29092/com.rico.aidl I/System.out: 七月安生
考虑一种情况,能不能在有影片的时候,自动通知客户端?这是典型的观察者模式,接下来实现这个需求
首先定义IOnNewMovieAddedListener.aidl监听器,针对注册了新影片提醒功能的客户端,服务端才会主动提醒
package com.rico.aidl;// Declare any non-default types here with import statementsimport com.rico.aidl.Movie;interface IOnNewMovieAddedListener { void onNewMovieAdded(in Movie movie);}
接着修改客户端代码,注册监听器IOnNewMovieAddedListener,改动如下:
public class MovieMgrActivity extends Activity{ IMovieMgr movieMgr; ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { movieMgr = IMovieMgr.Stub.asInterface(service); try { List<Movie> list = movieMgr.getMovieList(); for (Movie movie : list) { System.out.println(movie.name); } movieMgr.registerListener(mListener); } catch (RemoteException e) { } } @Override public void onServiceDisconnected(ComponentName name) { } }; IOnNewMovieAddedListener mListener = new IOnNewMovieAddedListener.Stub() { @Override public void onNewMovieAdded(Movie movie) throws RemoteException { System.out.println("新电影【"+movie.name+"】上线了"); } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent i = new Intent(this, MovieMgrService.class); bindService(i, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { unbindService(mConnection); try { if (mListener != null) { movieMgr.unregisterListener(mListener); } } catch (RemoteException e) { } super.onDestroy(); }}
最后修改服务端代码,服务端每个5s自动生成一部新电影,并通知注册过提醒功能的客户端:
public class MovieMgrService extends Service{ CopyOnWriteArrayList<Movie> mMoveList = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<IOnNewMovieAddedListener> mListenerList = new CopyOnWriteArrayList<>(); private Binder mBinder = new IMovieMgr.Stub() { @Override public List<Movie> getMovieList() throws RemoteException { return mMoveList; } @Override public void addMovie(Movie movie) throws RemoteException { mMoveList.add(movie); } @Override public void registerListener(IOnNewMovieAddedListener listener) throws RemoteException { if (!mListenerList.contains(listener)) { mListenerList.add(listener); } } @Override public void unregisterListener(IOnNewMovieAddedListener listener) throws RemoteException { if (mListenerList.contains(listener)) { mListenerList.remove(listener); } } }; private void onNewMovieAdded(Movie movie) throws RemoteException { System.out.println("mListenerList size = "+mListenerList.size()); mMoveList.add(movie); for (IOnNewMovieAddedListener listener:mListenerList) { try { listener.onNewMovieAdded(movie); } catch (RemoteException e) { e.printStackTrace(); } } } @Override public void onCreate() { super.onCreate(); mMoveList.add(new Movie("钢铁侠")); mMoveList.add(new Movie("速度激情8")); new Thread(new Runnable() { @Override public void run() { try { int id = 0; while (!isDestory) { Thread.sleep(5000); onNewMovieAdded(new Movie("New Movie "+id++)); } } catch (Exception e) { e.printStackTrace(); } } }).start(); } boolean isDestory = false; @Override public void onDestroy() { super.onDestroy(); isDestory = true; } @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; }}
运行一下:
07-24 08:22:22.931 27228-27228/com.rico.aidl I/System.out: 钢铁侠07-24 08:22:22.931 27228-27228/com.rico.aidl I/System.out: 速度激情807-24 08:22:27.930 27228-27240/com.rico.aidl I/System.out: 新电影【New Movie 0】上线了07-24 08:22:32.932 27228-27241/com.rico.aidl I/System.out: 新电影【New Movie 1】上线了07-24 08:22:37.935 27228-27240/com.rico.aidl I/System.out: 新电影【New Movie 2】上线了07-24 08:22:42.939 27228-27241/com.rico.aidl I/System.out: 新电影【New Movie 3】上线了
到这里还没完,当我们按返回键关闭页面的时候,理论上在onDestory中调用movieMgr.unregisterListener解除绑定,可是日志显示:
07-24 08:37:29.020 9341-9353/com.rico.aidl:remote I/System.out: not found, can't unregister
为什么解绑失败了?
在解绑过程中服务端无法找到之前注册的listener,在客户端我们注册和解绑明明用的是同一个listener,为什么服务端无法正常解绑?这种注册解绑的方式是对的,但在多进程中却不适用了,原因是Binder会把客户端传递过来的重新转换成新的对象,服务端接收到的对象已经不是客户端传递的对象了,对象跨进程传输的本质是反序列化的过程,这也是对象为什么需要实现Parcelable接口的原因。
解决方法:使用RemoteCallbackList
RemoteCallbackList是系统专门提供用于删除跨进程listener的接口,虽然跨进程传输客户端的同一个对象会在服务端产生不同对象,但这些新的对象有一个共同点就是他们底层的Binder对象是同一个,我们只要遍历服务端所有的listener,找出那个和解注册listener具有相同binder对象的服务端的listener并删除即可,这些事情RemoteCallbackList都替我们完成了,并且RemoteCallbackList内部还实现了线程同步的功能。
修改服务端代码:
// MovieMgrService.java RemoteCallbackList<IOnNewMovieAddedListener> mListenerList = new RemoteCallbackList<>(); private Binder mBinder = new IMovieMgr.Stub() { @Override public List<Movie> getMovieList() throws RemoteException { return mMoveList; } @Override public void addMovie(Movie movie) throws RemoteException { mMoveList.add(movie); } @Override public void registerListener(IOnNewMovieAddedListener listener) throws RemoteException { mListenerList.register(listener); } @Override public void unregisterListener(IOnNewMovieAddedListener listener) throws RemoteException { mListenerList.unregister(listener); } }; private void onNewMovieAdded(Movie movie) throws RemoteException { mMoveList.add(movie); int num = mListenerList.beginBroadcast(); for (int i=0; i<num; i++) { IOnNewMovieAddedListener l = mListenerList.getBroadcastItem(i); if (l != null) { l.onNewMovieAdded(movie); } } mListenerList.finishBroadcast(); }
aidl的使用介绍完了,最后在强调一下,客户端调用远程的aidl接口,由于被调用的方法运行在服务端binder线程池中,同时客户端被挂起,如果服务端执行了耗时操作,客户端会出现ANR问题,所以不要在UI线程中访问aidl接口~
深入理解aidl,可以阅读之前的博客
Android Binder通讯机制和Android多进程模式
参考【Android开发艺术探索】
- Android AIDL用法介绍
- Android AIDL介绍
- Android--AIDL基础介绍
- Android aidl命令用法
- android aidl 用法实现
- Android AIDL用法解析
- android aidl oneway用法
- Android AIDL基础用法
- android的AIDL简单介绍
- android studio aidl用法详解
- Android AIDL(Android Interface Definition Language)介绍
- Android AIDL(Android Interface Definition Language)介绍
- Android AIDL(Android Interface Definition Language)介绍
- Android AIDL(Android Interface Definition Language)介绍
- android之Service介绍之二 AIDL
- Android的AIDL文件使用入门介绍
- android之Service介绍之二 AIDL
- android之Service介绍之二 AIDL
- SEO(搜索引擎优化)友情链接策略
- pl/sql监听配置
- Python_2
- 关于spring动态切换数据源
- C/C++结构体总结
- Android AIDL用法介绍
- Pick公式
- c/c++里的 堆区 栈区 静态区 文字常量区 程序代码区
- axis2调用 .net的webservice asmx
- tcp与udp的区别
- JAVA中使用内部类的原因
- java各常用容器总结
- 文件的读取操作(2)
- HTML标签--行级标签