xamarin学习笔记A19(安卓AIDL)
来源:互联网 发布:随身wifi网络链接异常 编辑:程序博客网 时间:2024/06/05 13:33
(每次学习一点xamarin就做个学习笔记和视频来加深记忆巩固知识)
如有不正确的地方,请帮我指正。
AIDL简介
AIDL(Android Interface Definition Language)翻译为安卓接口定义语言,用于IPC(Inter-Process Communication)进程间通信。
在xamarin学习笔记A08(安卓广播)中使用了Broadcast进行了跨进程通信,但是广播接收器是不允许开启线程的,所以在onReceive()方法中不能进行耗时操作,否则会报错,所以广播一般是用来打开其它组件,例如启动一个Service或者在状态栏创建一个Notification等。
在xamarin学习笔记A11(安卓ContentProvider)中使用ContentProvider实现了跨进程通信,但它主要用于持久化数据的共享。
而AIDL配合Service一起可以让两个进程持续的通信,AIDL效率也比上面的高。
AIDL个人认为
个人认为AIDL语言只是用来帮助生成跨进程通信的代码的,帮助程序员少写代码,帮助程序员生成正确的代码。没有AIDL也可以手写,不过太麻烦。
通信过程
安卓中大部分跨进程通信都是基于Binder机制。Binder翻译出来是“连接器”的意思。由于进程之间不能直接通信,所以需要“连接器”来帮助。
通过下面这张图来简单了解下跨进程通信过程。
如上图所示,例如有一个A19B程序,它提供BookService,允许传入一个bookId来返回给别人一个Book对象。还有一个A19程序,它想访问这个Service,那么大概的流程是这样的:
A. 首先A19B这个程序安装到安卓中后,会在ServiceManage中注册BookService服务。
B. A19这个程序的MainActivity通过this.BindService()来得到IBinder对象,即得到了连接BookService的“连接器”。BindService方法会触发很多内部复杂的操作,比如Binder程序把A19程序传递过来的请求的服务名去SerivceManage中查询对应的服务,然后创建一个指向该服务的“连接器”对象IBinder以供A19使用。
C. 有了“连接器”对象IBinder,就可以通过此连接器来访问远程BookService服务了,可以调用GetBook()方法来得到Book对象了。
那么具体该如何做呢?
1.首先两个程序都新建一个一样的Book类且必须实现了IParcelabler接口。
public class Book : Java.Lang.Object, IParcelable { public int BookId { get; set; } public string BookName { get; set; } public double BookPrice { get; set; } public Book() { } //打包数据 public void WriteToParcel(Parcel dest, [GeneratedEnum] ParcelableWriteFlags flags) { dest.WriteInt(BookId); dest.WriteString(BookName); dest.WriteDouble(BookPrice); } public int DescribeContents() { return 0; //不用管,填0即可 } private static readonly MyParcelableCreator<Book> _creator = new MyParcelableCreator<Book>(GetBook); //解包数据 private static Book GetBook(Parcel parcel) { Book book = new Book(); book.BookId = parcel.ReadInt(); book.BookName = parcel.ReadString(); book.BookPrice = parcel.ReadDouble(); return book; } [ExportField("CREATOR")] public static MyParcelableCreator<Book> GetCreator() { return _creator; }}
2.其次两边都新建一个Book.aidl文件,内容如下
package A19B;parcelable Book;
这个aidl文件定义了这是个parcelable(可序列化)的Book,它属于A19B这个包。
3.然后两边都新建一个IBook.aidl文件,内容如下
package A19B;import A19B.Book;interface IBook{ Book GetBook(in int bookId);}
此aild文件定义了一个IBook接口,里面有一个GetBook()方法,因为它使用了Book类,所以得import A19B.Book;。
4.设置这个两个aidl文件的生成操作,如下图
编译项目后会在“obj\Debug\aidl”目录下生成Book.cs,IBook.cs跨进程通信代码。(个人认为生成的Book.cs没啥用,我也没用到)
Book.cs代码如下
// This file is automatically generated and not supposed to be modified.using System;using Boolean = System.Boolean;using String = System.String;using List = Android.Runtime.JavaList;using Map = Android.Runtime.JavaDictionary;namespace A19B{}
IBook.cs代码如下
namespace A19B{ public interface IBook : global::Android.OS.IInterface { global::A19B.Book GetBook(int bookId); } public abstract class IBookStub : global::Android.OS.Binder, global::Android.OS.IInterface, A19B.IBook { const string descriptor = "A19B.IBook"; public IBookStub() { this.AttachInterface(this, descriptor); } public static A19B.IBook AsInterface(global::Android.OS.IBinder obj) { if (obj == null) return null; var iin = (global::Android.OS.IInterface)obj.QueryLocalInterface(descriptor); if (iin != null && iin is A19B.IBook) return (A19B.IBook)iin; return new Proxy(obj); } public global::Android.OS.IBinder AsBinder() { return this; } protected override bool OnTransact(int code, global::Android.OS.Parcel data, global::Android.OS.Parcel reply, int flags) { switch (code) { case global::Android.OS.BinderConsts.InterfaceTransaction: reply.WriteString(descriptor); return true; case TransactionGetBook: { data.EnforceInterface(descriptor); int arg0 = default(int); arg0 = data.ReadInt(); var result = this.GetBook(arg0); reply.WriteNoException(); if (result != null) { reply.WriteInt(1); result.WriteToParcel(reply, global::Android.OS.ParcelableWriteFlags.ReturnValue); } else reply.WriteInt(0); return true; } } return base.OnTransact(code, data, reply, flags); } public class Proxy : Java.Lang.Object, A19B.IBook { global::Android.OS.IBinder remote; public Proxy(global::Android.OS.IBinder remote) { this.remote = remote; } public global::Android.OS.IBinder AsBinder() { return remote; } public string GetInterfaceDescriptor() { return descriptor; } public global::A19B.Book GetBook(int bookId) { global::Android.OS.Parcel __data = global::Android.OS.Parcel.Obtain(); global::Android.OS.Parcel __reply = global::Android.OS.Parcel.Obtain(); global::A19B.Book __result = default(global::A19B.Book); try { __data.WriteInterfaceToken(descriptor); __data.WriteInt(bookId); remote.Transact(IBookStub.TransactionGetBook, __data, __reply, 0); __reply.ReadException(); __result = __reply.ReadInt() != 0 ? (global::A19B.Book)global::Android.OS.Bundle.Creator.CreateFromParcel(__reply) : null; } finally { __reply.Recycle(); __data.Recycle(); } return __result; } } internal const int TransactionGetBook = global::Android.OS.Binder.InterfaceConsts.FirstCallTransaction + 0; public abstract global::A19B.Book GetBook(int bookId); }}
生成的IBookStub这个名字中的Stub很形象,Stub翻译为“存根”,例如,平时我们去寄快递时双方会各留一张存根。
其中特别要注意是的这地方生成的是错的
global::Android.OS.Bundle.Creator.CreateFromParcel(__reply)
应改为
global::A19B.Book.GetCreator().CreateFromParcel(__reply)
我用是VS2017 15.4.0版本,可能在下个新版本会解决掉这个BUG吧。
不过没关系,把这个文件的代码全部复制到剪切版,然后自已新建一个IBook.cs文件,还不行,得先清理解决方案,不然前面自动生成的IBook.cs文件还在,导致不能新建,同时还得把两个aidl文件的生成操作改为“无”。
新建后粘贴代码,修改那句错了的地方,并把这个IBook.cs文件复制到A19项目里,不需做任何修改,命名空间都不要改。如果没有这个BUG,则不需要这么麻烦,两个项目都会根据aidl文件自动生成跨进程通信代码。
这样就完成了跨进程通信代码的生成这一步了。
5.在A19B程序中新建一个BookService服务
[Service(Exported =true, Enabled =true)] [IntentFilter(new string[] {"com.A19B.BookService"})] public class BookService : Service { public override IBinder OnBind(Intent intent) { return new BookBinder(); } private class BookBinder : IBookStub { public override Book GetBook(int bookId) { if (bookId == 1) { Book book = new Book(); book.BookId = 1; book.BookName = "C#高级编程"; book.BookPrice = 88.88; return book; } else return null; } }}
这里定义了一个BookBinder类来继承生成的抽象类IBookStub,实现GetBook就可以了。然后在OnBind()方法中实例化此类的对象返回给外部使用。
6.在A19项目的MainActivity中访问BookService,上主要代码
public class MainActivity : AppCompatActivity{ private TextView _textView; private BookServiceConnection conn; //省略其它代码 private void BindRemoteService()//绑定远程服务 { Intent intent = new Intent("com.A19B.BookService"); if (intent != null) { if (conn == null) { conn = new BookServiceConnection(); } this.BindService(intent, conn, Bind.AutoCreate); } } private void InvokeRemoteService()//调用远程服务 { if (conn != null) { Book book = conn.IBook.GetBook(1); if(book!=null) _textView.Text = string.Format("BookId={0} BookName={1} BookPrice={2}", book.BookId, book.BookName, book.BookPrice); } } } public class BookServiceConnection : Java.Lang.Object, IServiceConnection { public IBook IBook { get; private set; } public void OnServiceConnected(ComponentName name, IBinder service) { IBook = IBookStub.AsInterface(service); } }}
需要定义一个BookServiceConnection类并实现IServiceConnection接口,因为在调用this.BindService()方法时需要一个实现了此接口的对象,成功绑定远程服务后会回调OnServiceConnected方法把“连接器”对象IBinder传过来。
因为“连接器”对象实现了IBook接口,所以可以通过AsInterface转换,然后存到public IBook IBook变量中以供使用。
最后通过Book book = conn.IBook.GetBook(1);调用了远程服务获得了Book对象。表面上看像是直接调用了BookSerive服务,实际内部是通过Binder驱动来调用的。
需要注意
在Android5.0及以上系统中需要显式声明Intent才能启动Service。
//从隐式声明的Intent中创建一个显式声明的Intent(在Android5.0及以上系统中需要显式声明Intent才能启动Service) private Intent CreateExplicitFromImplicitIntent(Context context, Intent implicitIntent) { PackageManager pm = context.PackageManager; //查出所有的能匹配这个隐式意图的服务列表 IList<ResolveInfo> resolveInfo = pm.QueryIntentServices(implicitIntent, 0); if (resolveInfo == null || resolveInfo.Count != 1) { return null; } ResolveInfo serviceInfo = resolveInfo[0]; string packageName = serviceInfo.ServiceInfo.PackageName;//取出包名 string className = serviceInfo.ServiceInfo.Name;//取出服务名 ComponentName component = new ComponentName(packageName, className);//用包名和服务名来创建一个ComponentName对象 //拿隐式意图对象implicitIntent作为构造参数,来创建一个新的显示的意图 Intent explicitIntent = new Intent(implicitIntent); explicitIntent.SetComponent(component);//设置显示意图的组件名对象 return explicitIntent; }private void BindRemoteService()//绑定远程服务 { Intent intent = new Intent("com.A19B.BookService"); if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)//(在Android5.0及以上系统中需要显式声明Intent才能启动Service) { intent = CreateExplicitFromImplicitIntent(this, intent); } if (_bookServiceConnection == null) { _bookServiceConnection = new BookServiceConnection(); } this.BindService(intent, _bookServiceConnection, Bind.AutoCreate); Toast.MakeText(this,"绑定远程服务", ToastLength.Short).Show(); }
代码和视频在我上传的CSDN资源中http://download.csdn.net/download/junshangshui/10121804
- xamarin学习笔记A19(安卓AIDL)
- xamarin学习笔记A03(安卓Activiy)
- xamarin学习笔记A07(安卓Fragment)
- xamarin学习笔记A08(安卓广播)
- xamarin学习笔记A10(安卓SQLite)
- xamarin学习笔记A11(安卓ContentProvider)
- xamarin学习笔记A12(安卓Notification)
- xamarin学习笔记A18(安卓Service)
- xamarin学习笔记A02(安卓项目组成)
- xamarin学习笔记A04(安卓基本布局)
- xamarin学习笔记A05(安卓RecycleView简单显示)
- xamarin学习笔记A06(安卓RecycleView点击事件)
- xamarin学习笔记A09(安卓数据简单存储)
- xamarin学习笔记A13(安卓Handler异步消息处理)
- xamarin学习笔记A14(安卓AsyncTask和RunOnUiThread)
- xamarin学习笔记A15(安卓OkHttp3和HttpURLConnection) 上
- xamarin学习笔记A16(安卓OkHttp3和HttpURLConnection)下
- xamarin学习笔记A17(安卓Parcel和IParcelable)
- java 反射 访问构造方法
- Maven学习总结(一)——Maven入门
- java并发-线程安全与共享资源(4)
- swift 接入 swjsdk
- TC SRM568,奇妙的题目
- xamarin学习笔记A19(安卓AIDL)
- 使用python爬虫(一)
- Spring配置文件<context:property-placeholder>标签使用漫谈
- 将项目导入到git中
- 【异常】Hadoop分布式集群搭建之zookeeper故障
- Setting property 'source' to '' did not find a matching property.
- HTML:HTML基础
- 实体店也可以代办?外卖市场还需严加监管
- C++中的::运算符