您当前的位置: 首页 >  ar

ZhangJiQun&MXP

暂无认证

  • 0浏览

    0关注

    1187博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

android 加入AIDL进行底层通讯,Android接口定义语言aidl通信简单理解, 简单客户端和服务端demo,ipc,Serializable和Parcelable区别

ZhangJiQun&MXP 发布时间:2019-08-05 15:06:32 ,浏览量:0

目录

android 加入AIDL进行底层通讯

Android接口定义语言aidl通信简单理解

Serializable和Parcelable区别:

 

简单客户端和服务端demo:

android 加入AIDL进行底层通讯

 

直接将aidl文件复制到main目录中,在sync和make project

生成的文件在 android 目录中,在java中在邮特殊符号的文件夹中能看到;

java文件将目录下的包直接复制到项目的java 目录中;进行调用;

 

AIDL重要的是通讯必须是报名是相同的;

 

Android接口定义语言aidl通信简单理解

 

​AIDL:Android Interface Definition Language,即Android接口定义语言。

Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。

为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC(Remote Procedure Call Protocol)--远程过程调用协议)的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。我们知道4个Android应用程序组件中的3个(Activity、BroadcastReceiver和ContentProvider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。

 

ipc:IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信

Serializable和Parcelable区别:

Serializable(可串行化的)是Java中的序列化接口,其使用起来简单但是开销很大,在序列化和反序列化过程中需要大量的I/O操作。

而Parcelable(打包的)是Android中的序列化方式,因此更适合用在Android平台上,它的缺点就是使用起来稍微麻烦点,但是它的效率很高。

 

两种AIDL文件:在我的理解里,所有的AIDL文件大致可以分为两类。一类是用来定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型的。一类是用来定义方法接口,以供系统使用来完成跨进程通信的。可以看到,两类文件都是在“定义”些什么,而不涉及具体的实现,这就是为什么它叫做“Android接口定义语言”。  注:所有的非默认支持数据类型必须通过第一类AIDL文件定义才能被使用。  

AIDL 实例 下面以实例代码演示一个 AIDL 的编写。

1.创建 AIDL ①创建要操作的实体类,实现 Parcelable 接口,以便序列化/反序列化

package net.sxkeji.shixinandroiddemo2.bean;

import android.os.Parcel; import android.os.Parcelable;

public class Person implements Parcelable {     private String mName;

    public Person(String name) {         mName = name;     }

    protected Person(Parcel in) {         mName = in.readString();     }

    public static final Creator CREATOR = new Creator() {         @Override         public Person createFromParcel(Parcel in) {             return new Person(in);         }

        @Override         public Person[] newArray(int size) {             return new Person[size];         }     };

    @Override     public int describeContents() {         return 0;     }

    @Override     public void writeToParcel(Parcel dest, int flags) {         dest.writeString(mName);     }

    @Override     public String toString() {         return "Person{" +                 "mName='" + mName + '\'' +                 '}';     } } 实现 Parcelable 接口是为了后序跨进程通信时使用。

关于 Parcelable 可以看我的这篇文章 Android 进阶6:两种序列化方式 Serializable 和 Parcelable。

注意 实体类所在的包名。

②新建 aidl 文件夹,在其中创建接口 aidl 文件以及实体类的映射 aidl 文件

在 main 文件夹下新建 aidl 文件夹,使用的包名要和 java 文件夹的包名一致:

先创建实体类的映射 aidl 文件,Person.aidl:

 

// Person.aidl package net.sxkeji.shixinandroiddemo2.bean;

//还要和声明的实体类在一个包里 parcelable Person;

在其中声明映射的实体类名称与类型

注意,这个 Person.aidl 的包名要和实体类包名一致。

然后创建接口 aidl 文件,IMyAidl.aidl:

// IMyAidl.aidl package net.sxkeji.shixinandroiddemo2;

// Declare any non-default types here with import statements import net.sxkeji.shixinandroiddemo2.bean.Person;

interface IMyAidl {     /**      * 除了基本数据类型,其他类型的参数都需要标上方向类型:in(输入), out(输出), inout(输入输出)      */     void addPerson(in Person person);

    List getPersonList(); } 在接口 aidl 文件中定义将来要在跨进程进行的操作,上面的接口中定义了两个操作:

addPerson: 添加 Person getPersonList:获取 Person 列表 需要注意的是:

非基本类型的数据需要导入,比如上面的 Person,需要导入它的全路径。  这里的 Person 我理解的是 Person.aidl,然后通过 Person.aidl 又找到真正的实体 Person 类。 方法参数中,除了基本数据类型,其他类型的参数都需要标上方向类型  in(输入), out(输出), inout(输入输出)③Make Project ,生成 Binder 的 Java 文件

AIDL 真正的强大之处就在这里,通过简单的定义 aidl 接口,然后编译,就会为我们生成复杂的 Java 文件。

点击 Build -> Make Project,然后等待构建完成。

然后就会在 build/generated/source/aidl/你的 flavor/ 下生成一个 Java 文件:

现在我们有了跨进程 Client 和 Server 的通信媒介,接着就可以编写客户端和服务端代码了。

我们先跑通整个过程,这个文件的内容下篇文章介绍。

2.编写服务端代码 创建 Service,在其中创建上面生成的 Binder 对象实例,实现接口定义的方法;然后在 onBind() 中返回

创建将来要运行在另一个进程的 Service,在其中实现了 AIDL 接口中定义的方法:

public class MyAidlService extends Service {     private final String TAG = this.getClass().getSimpleName();

    private ArrayList mPersons;

    /**      * 创建生成的本地 Binder 对象,实现 AIDL 制定的方法      */     private IBinder mIBinder = new IMyAidl.Stub() {

        @Override         public void addPerson(Person person) throws RemoteException {             mPersons.add(person);         }

        @Override         public List getPersonList() throws RemoteException {             return mPersons;         }     };

    /**      * 客户端与服务端绑定时的回调,返回 mIBinder 后客户端就可以通过它远程调用服务端的方法,即实现了通讯      * @param intent      * @return      */     @Nullable     @Override     public IBinder onBind(Intent intent) {         mPersons = new ArrayList();         LogUtils.d(TAG, "MyAidlService onBind");         return mIBinder;     } } 上面的代码中,创建的对象是一个 IMyAidl.Stub() ,它是一个 Binder,具体为什么是它我们下篇文章介绍。

别忘记在 Manifest 文件中声明:

服务端实现了接口,在 onBind() 中返回这个 Binder,客户端拿到就可以操作数据了。

3.编写客户端代码 这里我们以一个 Activity 为客户端。

①实现 ServiceConnection 接口,在其中拿到 AIDL 类

private IMyAidl mAidl;

private ServiceConnection mConnection = new ServiceConnection() {     @Override     public void onServiceConnected(ComponentName name, IBinder service) {         //连接后拿到 Binder,转换成 AIDL,在不同进程会返回个代理         mAidl = IMyAidl.Stub.asInterface(service);     }

    @Override     public void onServiceDisconnected(ComponentName name) {         mAidl = null;     } }; 在 Activity 中创建一个服务连接对象,在其中调用 IMyAidl.Stub.asInterface() 方法将 Binder 转为 AIDL 类。

②接着绑定服务

Intent intent1 = new Intent(getApplicationContext(), MyAidlService.class); bindService(intent1, mConnection, BIND_AUTO_CREATE); 要执行 IPC,必须使用 bindService() 将应用绑定到服务上。

注意:

5.0 以后要求显式调用 Service,所以我们无法通过 action 或者 filter 的形式调用 Service,具体内容可以看这篇文章 Android 进阶:Service 的一些细节。

③拿到 AIDL 类后,就可以调用 AIDL 类中定义好的操作,进行跨进程请求

@OnClick(R.id.btn_add_person) public void addPerson() {     Random random = new Random();     Person person = new Person("shixin" + random.nextInt(10));

    try {         mAidl.addPerson(person);         List personList = mAidl.getPersonList();         mTvResult.setText(personList.toString());     } catch (RemoteException e) {         e.printStackTrace();     } }

总结 这篇文章介绍了 AIDL 的简单编写流程,其中也踩过一些坑,比如文件所在包的路径不统一,绑定服务收不到回调等问题。

到最后虽然跨进程通信成功,但是我们还是有很多疑问的,比如:

AIDL 生成的文件内容? 什么是 Binder? 为什么要这么写? 知其然还要知其所以然,这一切都要从 Binder 讲起,且听下一回合介绍。

代码地址 Thanks 《Android 开发艺术探索》  https://developer.android.com/guide/components/aidl.html  http://www.jianshu.com/p/b9b15252b3d6  http://rainbow702.iteye.com/blog/1149790

  简单客户端和服务端demo:

编写服务端代码 通过上面几步,我们已经完成了AIDL及其相关文件的全部内容,那么我们究竟应该如何利用这些东西来进行跨进程通信呢?其实,在我们写完AIDL文件并 clean 或者 rebuild 项目之后,编译器会根据AIDL文件为我们生成一个与AIDL文件同名的 .java 文件,这个 .java 文件才是与我们的跨进程通信密切相关的东西。事实上,基本的操作流程就是:在服务端实现AIDL中定义的方法接口的具体逻辑,然后在客户端调用这些方法接口,从而达到跨进程通信的目的。

接下来我直接贴上我写的服务端代码:

/**  * 服务端的AIDLService.java  *

 * Created by lypeer on 2016/7/17.  */ public class AIDLService extends Service {

    public final String TAG = this.getClass().getSimpleName();

    //包含Book对象的list     private List mBooks = new ArrayList();

    //由AIDL文件生成的BookManager     private final BookManager.Stub mBookManager = new BookManager.Stub() {         @Override         public List getBooks() throws RemoteException {             synchronized (this) {                 Log.e(TAG, "invoking getBooks() method , now the list is : " + mBooks.toString());                 if (mBooks != null) {                     return mBooks;                 }                 return new ArrayList();             }         }

        @Override         public void addBook(Book book) throws RemoteException {             synchronized (this) {                 if (mBooks == null) {                     mBooks = new ArrayList();                 }                 if (book == null) {                     Log.e(TAG, "Book is null in In");                     book = new Book();                 }                 //尝试修改book的参数,主要是为了观察其到客户端的反馈                 book.setPrice(2333);                 if (!mBooks.contains(book)) {                     mBooks.add(book);                 }                 //打印mBooks列表,观察客户端传过来的值                 Log.e(TAG, "invoking addBooks() method , now the list is : " + mBooks.toString());             }         }     };

    @Override     public void onCreate() {         super.onCreate();         Book book = new Book();         book.setName("Android开发艺术探索");         book.setPrice(28);         mBooks.add(book);        }

    @Nullable     @Override     public IBinder onBind(Intent intent) {         Log.e(getClass().getSimpleName(), String.format("on bind,intent = %s", intent.toString()));         return mBookManager;     } } 整体的代码结构很清晰,大致可以分为三块:第一块是初始化。在 onCreate() 方法里面我进行了一些数据的初始化操作。第二块是重写 BookManager.Stub 中的方法。在这里面提供AIDL里面定义的方法接口的具体实现逻辑。第三块是重写 onBind() 方法。在里面返回写好的 BookManager.Stub 。

接下来在 Manefest 文件里面注册这个我们写好的 Service ,这个不写的话我们前面做的工作都是无用功:

                                        到这里我们的服务端代码就编写完毕了,如果你对里面的一些地方感觉有些陌生或者根本不知所云的话,说明你对 Service 相关的知识已经有些遗忘了,建议再去看看这两篇博文:Android中的Service:默默的奉献者 (1),Android中的Service:Binder,Messenger,AIDL(2) 。

4.5,编写客户端代码 前面说过,在客户端我们要完成的工作主要是调用服务端的方法,但是在那之前,我们首先要连接上服务端,完整的客户端代码是这样的:

/**  * 客户端的AIDLActivity.java  * 由于测试机的无用debug信息太多,故log都是用的e  *

 * Created by lypeer on 2016/7/17.  */ public class AIDLActivity extends AppCompatActivity {

    //由AIDL文件生成的Java类     private BookManager mBookManager = null;

    //标志当前与服务端连接状况的布尔值,false为未连接,true为连接中     private boolean mBound = false;

    //包含Book对象的list     private List mBooks;

    @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_aidl);     }

    /**      * 按钮的点击事件,点击之后调用服务端的addBookIn方法      *      * @param view      */     public void addBook(View view) {         //如果与服务端的连接处于未连接状态,则尝试连接         if (!mBound) {             attemptToBindService();             Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();             return;         }         if (mBookManager == null) return;

        Book book = new Book();         book.setName("APP研发录In");         book.setPrice(30);         try {             mBookManager.addBook(book);             Log.e(getLocalClassName(), book.toString());         } catch (RemoteException e) {             e.printStackTrace();         }     }

    /**      * 尝试与服务端建立连接      */     private void attemptToBindService() {         Intent intent = new Intent();         intent.setAction("com.lypeer.aidl");         intent.setPackage("com.lypeer.ipcserver");         bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);     }

    @Override     protected void onStart() {         super.onStart();         if (!mBound) {             attemptToBindService();         }     }

    @Override     protected void onStop() {         super.onStop();         if (mBound) {             unbindService(mServiceConnection);             mBound = false;         }     }

    private ServiceConnection mServiceConnection = new ServiceConnection() {         @Override         public void onServiceConnected(ComponentName name, IBinder service) {             Log.e(getLocalClassName(), "service connected");             mBookManager = BookManager.Stub.asInterface(service);             mBound = true;

            if (mBookManager != null) {                 try {                     mBooks = mBookManager.getBooks();                     Log.e(getLocalClassName(), mBooks.toString());                 } catch (RemoteException e) {                     e.printStackTrace();                 }             }         }

        @Override         public void onServiceDisconnected(ComponentName name) {             Log.e(getLocalClassName(), "service disconnected");             mBound = false;         }     }; } 同样很清晰,首先建立连接,然后在 ServiceConnection 里面获取 BookManager 对象,接着通过它来调用服务端的方法。

4.6,开始通信吧! 通过上面的步骤,我们已经完成了所有的前期工作,接下来就可以通过AIDL来进行跨进程通信了!将两个app同时运行在同一台手机上,然后调用客户端的 addBook() 方法,我们会看到服务端的 logcat 信息是这样的:

//服务端的 log 信息,我把无用的信息头去掉了,然后给它编了个号 1,on bind,intent = Intent { act=com.lypeer.aidl pkg=com.lypeer.ipcserver } 2,invoking getBooks() method , now the list is : [name : Android开发艺术探索 , price : 28] 3,invoking addBooks() method , now the list is : [name : Android开发艺术探索 , price : 28, name : APP研发录In , price : 2333] 客户端的信息是这样的:

//客户端的 log 信息 1,service connected 2,[name : Android开发艺术探索 , price : 28] 3,name : APP研发录In , price : 30  

关注
打赏
1665659684
查看更多评论
立即登录/注册

微信扫码登录

0.0405s