Android开发进阶 从小工到专家(txt+pdf+epub+mobi电子书下载)


发布时间:2020-06-23 11:31:51

点击下载

作者:何红辉

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Android开发进阶 从小工到专家

Android开发进阶 从小工到专家试读:

前言

为什么写这本书

写这本书的念头由来已久了。也许是从我打算写《Android源码设计模式解析与实战》那时起就萌生了这个念头,因为设计模式属于仅次于架构之下的局部战术,阅读这类书籍能够让具备一定工作经验的开发人员提升自己的设计能力,构建更灵活的软件。但是,对于初、中级工程师而言,最重要的还是在于基础知识以及知识广度的掌握上。因此,在《Android源码设计模式解析与实战》交稿之后,我就立即开始了本书的写作之旅。

从单位的面试经历和与开发群中网友的交流中,我发现很多有一定工作经验的开发人员对于Android的基础知识都还只停留在“会用”的阶段,而对于其基本原理一概不知,以至于工作多年之后依旧停留在很表面的层次。这样的知识结构的程序员往往是一旦开发的系统出现问题或者需要优化时就不能应对了。因此,仔细阅读一本深入讲述Android核心开发知识点的书是很有必要的。

目前,图书市场上关于Android的入门书籍大多是覆盖整个Android开发知识体系,这类书籍的特点是讲解的知识面多,也正是这个原因使得这类书籍缺乏深度,往往只是点到即止。例如,关于网络请求的技术,通常只讲解如何发送一个GET请求,但是,对于HTTP原理不会涉及,这使得很多读者在定制一些请求时根本无从下手,如上传图片、参数格式为Json等。

另一个问题就是,很多开发人员即使从业多年,可能都不知道什么是单元测试,不知道重构、面向对象基本原则,这使得他们的代码耦合度可能很高,难以测试和维护,这样带来的后果就是质量没法保证,随着时间的推移系统逐渐“腐化”。因此,读一本讲述设计软件的书也是必要的。

本书的目的就是解决上述两个问题,首先对Android开发的核心知识点进行深入讲解,然后介绍单元测试、代码规范、版本控制、重构、架构等重要知识点,使得读者在深入技术的同时开阔眼界,能够以更专业的方式设计应用软件,帮助读者完成从只会实现功能的“码农”到软件工程师、设计师的过渡。本书的特色

本书主要分为3部分,第一部分是前6章,在第一部分中深入讲解了Android开发过程中的核心知识点,包括View与动画、多线程、网络、数据库、性能优化,使得读者深入了解开发中最为重要的知识;第二部分是第7~11章,涵盖的内容包括代码规范、单元测试、版本控制、OOP与模式、重构等内容,从代码规范化、专业化的角度着手,开阔读者的眼界,使读者具备构建低耦合、灵活性强的应用软件的基本能力;最后一部分是第12章,在第12章中通过一个完整的示例,演示了如何把一个充满问题的应用软件逐步演化为低耦合、清晰、可测试的实现过程,在其中展示了常见的重构手法、测试手段,使读者从真实的示例中汲取知识与经验,提升技术与设计能力,绕过编程中的诸多陷阱。

当然,书中的知识点很多都只是做了部分讲解,起到一个抛砖引玉的作用,因此,如果需要更深入地了解各领域的知识,希望读者阅读其他专业书籍。面向的读者

本书面向的读者为初、中、高级Android工程师。本书的定位是学习Android开发的第二本书,因此,阅读的前提是读者需要有一定的Android开发知识。在阅读完本书之后,读者还可以选择《Android群英传》《Android开发艺术探索》《Android源码设计模式解析与实战》等书进行更深入地学习,从更深、更高的层次提升自己,完成从“码农”到专家的蜕变。如何阅读本书

本书从整体结构上分为3部分,分别为Android核心开发知识、规范化与专业化开发基本知识、实战示例。初、中级工程师建议阅读全书,高级工程师可以选择自己感兴趣的部分进行阅读。实战示例部分需要第二部分的知识,因此,在阅读最后一章时,如果你学习了第二部分的知识,那么理解效果会更好。判定你是否需要阅读某个章节的标准是,当你看到标题时是否对这个知识点了然于心,如果答案是否定的,那么阅读该章节还是很有必要的。当然,通读全书自然是最好的选择。“纸上得来终觉浅,绝知此事要躬行”,这放到任何一本书中都适用。因此,阅读本书时建议重新完成书中的示例,然后进行思考,从中体会为什么要这样做,这样做得到的好处是什么。读书、实践、思考结合起来,才会让你在技术道路上跑得更快、更远!读者反馈

最后需要说明的是,任何一本书籍都难免会有一些错误的地方,因此,我很乐意听到读者关于本书的意见或建议,希望与大家共同进步。读者可以通过发邮件(邮箱地址:simplecoder.h@gmail.com)的方式进行反馈,在这里致以诚挚的谢意。编辑联系邮箱:zhangtao@ptpress.com.cn。代码下载

本书中的示例代码都托管在Github,地址为https://github.com/bboyfeiyu/android_jtm_sourcecode ,读者可以通过Git进行下载。另外,本书的勘误地址为: https://github.com/bboyfeiyu/android-jtm-issues。如果读者发现了书中的错误,也可以提交到该项目中。何红辉于北京致谢

在本书的出版过程中得到了很多朋友的帮助,首先要感谢佳星、毕老师、凯子、小雨、文辉等好友的审稿,他们的付出使书中的大部分文字错误都在早期被修正。最重要的是要感谢张涛编辑的信任,在上一本书《Android源码设计模式解析与实战》的写作过程中,张涛编辑给了我很大的自由空间,对于我的各种问题也是耐心解答,也正是这些原因使我毫不犹豫地再度与张涛编辑合作。最后要感谢我的家人,在我写作的时候给我建议、校稿,在整个过程中给予我很大的支持。何红辉于北京第1章Android的构成基石——四大组件

由于本书的目标读者是有一定Android基础的开发人员,因此,本章不再介绍Android系统的架构、历史等知识,而是直接切入主题,从讲解Android的四大组件开始,然后一步一步深入学习开发中的重要知识点,使得我们能够从基本原理层面掌握Android开发基础知识。

Android中最重要的是四大组件,即Activity、Service、ContentProvider和Broadcast。这4个组件分工明确,共同构成了可重用、灵活、低耦合的Android系统。Activity负责UI元素的加载与页面之间的跳转,代表了一个页面单元;Service负责与UI无关的工作,如在后台执行耗时操作等;ContentProvider负责存储、共享数据,使得数据可以在多个应用之间共享;Broadcast则是在各个组件、应用之间进行通信,简化了Android开发中的通信问题。

下面就来简单学习一下这四大开发组件。1.1 Activity

Activity在应用中的表现就是一个用户界面,它会加载指定的布局文件来显示各种UI元素,例如TextView、Button、ImageView、ListView等,并且为这些UI元素设置事件处理函数,使得用户可以与这些UI进行交互。同时,Activity还可以在不同的Activity之间跳转,将不同的页面串连在一起,共同完成特定的操作流程。每个应用都是由一个或者多个Activity组成,它是Android应用程序中不可缺少的部分。

应用启动时会加载一个默认的Activity,这个Activity在AndroidManifest.xml中会被设置为如下intent-filter:

每个Activity都有生命周期,在不同的阶段会回调不同的生命周期函数,Activity的生命周期函数有如下几个。1.onCreate()

相信这是开发者见过次数最多的函数,我们在创建继承自Activity的类时都会默认生成这个函数。它会在Activity第一次被创建时调用,通常会在这个函数中完成Activity的初始化操作,如设置布局、初始化视图、绑定事件等。2.onStart()

这个函数在Activity的onCreate函数调用之后被调用,此时的Activity还处在不可见状态,它的下一个状态就是Activity变得可见的时候,也就是这个函数在Activity可见之前被调用。3.onResume()

这个函数在Activity变为可见时被调用,执行完onResume之后,Activity就会请求AMS渲染它所管理的视图。此时的Activity一定位于返回栈的栈顶,并且处于运行状态。4.onPause()

这个函数在系统准备去启动或者恢复另一个Activity时调用,也就是在Activity即将从可见状态变为不可见时。我们通常会在这个函数中将一些消耗CPU的资源释放掉,以及保存一些关键数据。5.onStop()

这个函数在Activity完全不可见时调用。它和onPause()函数的主要区别在于,如果新启动的Activity是一个对话框式的Activity,那么onPause()函数会得到执行,而onStop() 函数并不会执行。6.onDestroy()

这个函数在Activity被销毁之前调用,之后Activity的状态将变为销毁状态。7.onRestart()

这个函数在Activity由停止状态重新变为运行状态之前调用,也就是Activity被重新启动了。

从onCreate()函数到onDestroy()函数运行的时期就是一个Activity的完整生命周期。一般情况下。我们会在一个Activity的onCreate()函数中完成各种初始化操作,而在onDestroy()函数中完成释放内存的操作。然而并不是各个时期Activity都是可见的,只有onResume()函数和onStop()函数之间的Activity是可见的,在Activity可见期内,用户可以与Activity进行交互,完成所需的功能。

为了帮助读者能够更好地理解,Android 官方提供了一个Activity生命周期的示意图,如图1-1所示。▲图1-1 Activity的生命周期1.1.1 Activity的构成

Activity的构成并不是一个Activity对象再加上一个布局文件那么简单,在Activity和开发人员设置的视图之间还隔着两层。实际上视图会被设置给一个Window类,这个Window中含有一个DecorView,这个DecorView才是整个窗口的顶级视图。开发人员设置的布局会被设置到这个DecorView的mContentParent布局中。也就是说Android中实际上内置了一些系统布局文件xml,我们在xml中定义的视图最终会被设置到这些系统布局的特定节点之下,这样就形成了整个DecorView。结构如图1-2所示。

从图1-2中可以看到,我们的Activity之下有一个PhoneWindow,这个PhoneWindow是Window的实现类,然后Window之下包含一个DecorView,DecorView实际上是页面的顶级视图,它从一些系统布局中加载,并且在运行时将开发人员设置给Activity的布局资源添加到系统布局的mContentParent中。这样一来,用户界面就被添加到系统布局中了,而系统布局会为我们设置好标题栏区域等。▲图1-2 Activity结构

下面就是一个名为screen_title的系统布局xml文件:

上述xml文件中包含了actionbar和标题栏区域,下面就是开发人员设置给Activity的布局区域,这个区域被添加到名为content的布局中,而这整个screen_title.xml又是DecorView的子视图,因此,最终用户界面会显示为标题栏、开发人员设置的界面。例如我们的Activity布局代码如下:

该布局的根视图为RelativeLayout,其中只有一个居中的TextView。运行后的界面如图1-3所示。▲图1-3 用户界面

jtm_chap01显示的区域就是id为title的TextView,而Hello World就是content布局下的一个子视图。当Activity的onResume函数被调用之后,用户界面就显示在我们面前了。1.1.2 Activity的4种启动模式

每个应用程序都是由一个或者多个Activity组成,因此,Android内部使用通过回退栈来管理Activity实例。栈是一种后进先出的集合,对于Android来说,当前显示的Activity就在栈顶,当用户点击后退键或者点击应用上的返回按钮,系统就会将栈顶的Activity出栈,此时原来栈顶下的Activity就会变为栈顶显示到设备上。

然而事情可能并不是那么简单,在一些特殊情况下我们可能需要对Activity实例做一些特殊的处理,例如,为了避免重复创建Activity,我们要求一个Activity只有一个实例。好在Android系统为我们提供了这些功能,也就是我们本节要说的Activity的4个启动模式。用户可以在AndroidManifext.xml注册Activity时设置它的启动模式,例如:

Activity的启动模式有4个,分别为standard、singleTop、singleTask、singleInstance,下面我们逐个介绍它们。1.standard(标准启动模式)

这是Activity的标准启动模式,也是Activity的默认启动模式。在这种模式下启动的Activity可以被多次实例化,即在同一个任务栈中可以存在多个Activity实例,每个实例都会处理一个Intent对象。如果ActivityA的启动模式为standard,并且已经有一个ActivityA被启动,在该ActivityA中调用startActivity时会启动一个新的ActivityA实例。栈的变化如图1-4所示。▲图1-4 栈中有多个ActivityA实例

如果ActivityA是一个非常耗资源的类,那么将会使它所依附的应用消耗更多的系统资源。2.singleTop

如果一个以singleTop模式启动的Activity的实例已经存在于任务栈的栈顶,那么再启动这个Activity时,不会创建新的实例,而是重用位于栈顶的那个实例,并且会调用该实例的onNewIntent()函数将Intent对象传递到这个实例中。例如,ActivityA的启动模式为singleTop,并且ActivityA的一个实例已经存在于栈顶中。那么再调用startActivity启动另一个ActivityA时,不会再次创建ActivityA的实例,而是重用原来的实例,并且调用原来实例的onNewIntent()函数。此时任务桟中还是这一个ActivityA的实例。栈内变化如图1-5所示。▲图1-5 栈顶的ActivityA被重用

如果以singleTop模式启动的Activity的一个实例已经存在于任务桟中,但是不在桟顶,那么它的行为和standard模式相同也会创建一个新的实例。栈内变化如图1-6所示。▲图1-6 不在栈顶,重新创建一个ActivityA3.singleTask

singleTask模式是常用的启动模式,如果一个Activity设置了该启动模式,那么在一个任务栈中只能有一个该Activity的实例。如果任务栈中还没有该Activity,会新创建一个实例并放在栈顶。但是,如果已经存在Activity,系统会销毁处在该 Activity上的所有Activity,最终让该 Activity实例处于栈顶。最终让该 Activity实例处于栈顶,同时回调该Activity的onNewIntent()函数。栈内变化如图1-7所示。▲图1-7 处在ActivityA上的Activity被销毁4.singleInstance

设置了singleInstance模式的Activity会在一个独立的任务中开启,并且这个新的任务中有且只有这一个实例,也就是说被该实例启动的其他Activity会自动运行于另一个任务中。当再次启动该Activity实例时,会重用已存在的任务和实例。并且会调用该实例的onNewIntent()函数,将Intent实例传递到该实例中。

和singleTask不同的是,同一时刻在系统中只会存在一个这样的Activity实例,而singleTask模式的Activity是可以有多个实例的,只要这些Activity在不同的任务栈中即可,例如,应用A启动了一个启动模式为singleTask的ActivityA,应用B又通过Intent想要启动一个ActivityA,此时由于应用A和应用B都有自己的任务栈,因此,在这两个任务栈中分别都有一个ActivityA示例。而singleInstance能够保证Activity在系统中只有一个实例,不管多少应用要启动该Activity,这个Activity有且只有一个,如图1-8所示。▲图1-8 singleInstance的Activity独占一个任务栈1.1.3 FragmentActivity与Fragment

为了更好地运用越来越大的屏幕控件,Android在3.0版本引入了Fragment,它可以像Activity一样包含布局。不同的是Fragment是被嵌套在Activity中使用,它作为一个更大粒度的UI单元。如果需要兼容低于Android 3.0的系统,那么开发人员需要引用android-support-v4的jar包才能使用Fragment功能。

假如有这样的场景:我们的新闻应用含有两个Activity,第一个Activity是显示新闻标题、概要信息的列表,当用户点击这些标题时进入该新闻的详情页面进行阅读。

这是一个再普通不过的场景,但是这样做真的合适吗?我们是否能够简化用户的操作?

答案是:必须的!

Fragment就是为了应对这种情况而出现的,我们可以使用两个Fragment,Fragment1包含了一个新闻标题的列表,每行显示一个新闻的标题;Fragment2则展示这条新闻的详细内容。如果现在程序运行在竖屏模式的平板电脑或手机上,Fragment 1可能嵌入在一个Activity中,而Fragment 2可能嵌入在另一个Activity中,如图1-9所示。▲图1-9 每个Activity包含一个Fragment

而如果现在程序运行在横屏模式的平板电脑上,两个Fragment就可以嵌入在同一个Activity中,如图1-10所示。▲图1-10 横屏模式下包含两个Fragment的Activity

就目前开发来说,使用Fragment已经成为流行的开发方式,尽管在它的support v4中存在各种各样的Bug,以至于Square这样的公司举起了声讨Fragment的大旗,但是也不能阻止Fragment“驰骋”在大屏幕手机盛行的时代。1.2 Service与AIDL

Service是Android中实现程序后台运行的解决方案,它非常适合用于去执行那些不需要和用户交互而且还要求长期运行的任务。但不要被“后台”二字所迷惑,Service默认并不会运行在子线程中,它也不运行在一个独立的进程中,它同样执行在UI线程中,因此,不要在Service中执行耗时的操作,除非你在Service中创建了子线程来完成耗时操作。

Service的运行不依赖于任何用户界面,即使程序被切换到后台或者用户打开了另外一个应用程序,Service仍然能够保持正常运行,这也正是Service的使用场景。当某个应用程序进程被杀掉时,所有依赖于该进程的Service也会停止运行。1.2.1 普通Service

Service的生命周期相对Activity来说简单得多,只有3个,分别为onCreate、onStartCommand和onDestory。一旦在项目的任何位置调用了Context 的startService()函数,相应的服务就会启动起来,首次创建时会调用onCreate函数,然后回调onStartCommand()函数。服务启动了之后会一直保持运行状态,直到stopService()或stopSelf()函数被调用。虽然每调用一次startService()函数,onStartCommand()就会执行一次,但实际上每个服务都只会存在一个实例。所以不管你调用了多少次startService()函数, 只需调用一个stopService()或stopSelf()函数,服务就会被停止。

通常的Service大致如下:public class MyService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { doMyJob(intent); return super.onStartCommand(intent, flags, startId); } private void doMyJob(Intent intent){ // 从Intent中获取数据 // 执行相关操作 new Thread(){ @Override public void run() { // 耗时操作 } }.start(); } @Nullable @Override public IBinder onBind(Intent intent) { return null; }}

与Activity一样,Service也需要在AndroidManifest.xml中进行注册,示例如下:

上述示例表示注册一个在应用包service目录下的MyService服务,注册之后,当用户调用startService(new Intent(mContext,MyService.class)) 时会调用onStartCommand函数,我们在该函数中调用doMyJob,而在doMyJob中我们创建了一个线程来执行耗时操作,以避免阻塞UI线程。当我们的Service完成使命时,需要调用stopService来停止该服务。1.2.2 IntentService

完成一个简单的后台任务需要这么麻烦,Android显然早就“洞察”了这一点。因此,提供了一个IntentService来完成这样的操作,IntentService将用户的请求执行在一个子线程中,用户只需要覆写onHandleIntent函数,并且在该函数中完成自己的耗时操作即可。需要注意的是,在任务执行完毕之后IntentService会调用stopSelf自我销毁,因此,它适用于完成一些短期的耗时任务。示例如下: public class MyIntentService extends IntentService { MyIntentService(){ super(MyIntentService.class.getName()); } @Override protected void onHandleIntent(Intent intent) { // 这里执行耗时操作 }}1.2.3 运行在前台的Service

Service默认是运行在后台的,因此,它的优先级相对比较低,当系统出现内存不足的情况时,它就有可能会被回收掉。如果希望Service可以一直保持运行状态,而不会由于系统内存不足被回收,可以将Service运行在前台。前台服务不仅不会被系统无情地回收,它还会在通知栏显示一条消息,下拉状态栏后可以看到更加详细的信息。例如,墨迹天气在前台运行了一个Service,并且在Service中定时更新通知栏上的天气信息,如图1-11所示。▲图1-11 墨迹天气界面

下面我们就来实现一个类似于如图1-11所示的效果,首先我们定义一个服务,代码如下:public class WeatherService extends Service { private static final int NOTIFY_ID = 123; @Override public void onCreate() { super.onCreate(); showNotification(); } /** * 在通知栏显示天气信息 */ private void showNotification() { NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this) .setSmallIcon(R.drawable.weather) .setContentTitle(getText(R.string.the_day)) .setContentText(getText(R.string.weather)); // 创建通知被点击时触发的Intent Intent resultIntent = new Intent(this, MainActivity.class); // 创建任务栈Builder TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); stackBuilder.addParentStack(MainActivity.class); stackBuilder.addNextIntent(resultIntent); PendingIntent resultPendingIntent = stackBuilder.getPendingIntent( 0, PendingIntent.FLAG_UPDATE_CURRENT); mBuilder.setContentIntent(resultPendingIntent); NotificationManager mNotifyMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // 构建通知 final Notification notification = mBuilder.build() ; // 显示通知 mNotifyMgr.notify(NOTIFY_ID, notification); // 启动为前台服务 startForeground(NOTIFY_ID, notification); }}

我们在onCreate函数中调用了showNotification函数显示通知,并且在最后调用startForeground将服务设置为前台服务。在AndroidManifest.xml注册之后我们就可以启动该Service了。效果如图1-12所示。▲图1-12 WeatherService效果1.2.4 AIDL(Android接口描述语言)

AIDL(Android接口描述语言)是一种接口描述语言,通常用于进程间通信。编译器根据AIDL文件生成一个系列对应的Java类,通过预先定义的接口以及Binder机制达到进程间通信的目的。说白了,AIDL就是定义一个接口,客户端(调用端)通过bindService来与远程服务端建立一个连接,在该连接建立时会返回一个IBinder对象,该对象是服务端Binder的BinderProxy,在建立连接时,客户端通过asInterface函数将该BinderProxy对象包装成本地的Proxy,并将远程服务端的BinderProxy对象赋值给Proxy类的mRemote字段,就是通过mRemote执行远程函数调用。

在客户端新建一个AIDL文件,如图1-13所示。▲图1-13 新建AIDL文件

在SsoAuth.aidl文件中会默认有一个basicTypes函数,我们在程序后面添加一个ssoAuth的函数用于SSO授权。代码如下:interface SsoAuth { void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); /** * 实现SSO授权 */ void ssoAuth(String userName, String pwd);}

因为客户端是调用端,因此,只需要定义AIDL文件,此时Rebuild一下工程就会生成一个SsoAuth.java类,该类根据SsoAuth.aidl文件生成,包含了我们在AIDL文件中定义的函数。因为AIDL通常用于进程间通信,因此,我们新建一个被调用端的工程,我们命名为aidl_server,然后将客户端的AIDL文件夹复制到aidl_server的app/src/main目录下,结构如图1-14所示。▲图1-14 server中的AIDL

此时相当于在客户端和被调用端都有同一份SsoAuth.aidl文件,它们的包名、类名完全一致,生成的SsoAuth.java类也完全一致,这样在远程调用时它们就能够拥有一致的类型。Rebuild被调用端工程之后就会生成SsoAuth.java文件,该文件中有一个Stub类实现了SsoAuth接口。我们首先需要定义一个Service子类,然后再定义一个继承自Stub的子类,并且在Service的onBind函数中返回这个Stub子类的对象。示例代码如下:public class SinaSsoAuthService extends Service { SinaSsoImpl mBinder = new SinaSsoImpl(); @Override public void onCreate() { super.onCreate(); Log.e("","### sso auth created") ; } @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; } // 继承自Stub类,在这里实现ssoAuth函数 class SinaSsoImpl extends SsoAuth.Stub { @Override public void ssoAuth(String userName, String pwd) throws RemoteException { Log.e("", "这里是新浪客户端, 执行SSO登录啦,用户名 : " + userName + ", 密码 : " + pwd) ; } @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { } }}

从上述代码中我们看到,实际上完成功能的是继承自Stub的SinaSsoImpl类,Service只提供了一个让SinaSsoImpl依附的外壳。完成SinaSsoAuthService之后我们需要将它注册在被调用端应用的Manifest中,注册代码如下:

然后先运行被调用端(也就是Server端)应用,并且在客户端中完成调用Server的代码。客户端Activity的代码如下:public class MainActivity extends AppCompatActivity { SsoAuth mSsoAuth ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 执行操作 findViewById(R.id.sso_btn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if ( mSsoAuth == null ) { // 绑定远程服务,并且进行登录 bindSsoAuthService(); } else { doSsoAuth(); } } }); } private void bindSsoAuthService() { Intent intent = new Intent("book.aidl_server.service.SinaSsoAuthService") ; bindService(intent, mConnection, Context.BIND_AUTO_CREATE) ; } ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { // 建立连接之后将Binder转换为mSsoAuth mSsoAuth = SsoAuth.Stub.asInterface(iBinder) ; doSsoAuth(); } @Override public void onServiceDisconnected(ComponentName componentName) { mSsoAuth = null; } } ; private void doSsoAuth() { try { // 执行登录,实际上调用的是Server端的ssoAuth函数 mSsoAuth.ssoAuth("Mr.Simple", "pwd123"); } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onDestroy() { super.onDestroy(); unbindService(mConnection); }}

在上述Activity程序中,运行程序后点击登录按钮时会向Server端发起连接Service请求,在建立连接之后会将Binder对象转换为SsoAuth对象,然后调用SsoAuth对象的ssoAuth函数。此时的ssoAuth函数实际上调用的就是Server端中SinaSsoImpl类的实现。运行程序后点击登录按钮,如图1-15所示。▲图1-15 客户端的登录界面

输出结果,如图1-16所示。▲图1-16 AIDL远程调用

这一切的核心都是通过AIDL文件生成的Stub类以及其背后的Binder机制。首先我们看看生成的SsoAuth.java,Stub类就是该文件中的内部类。代码如下:// 根据SsoAuth.aidl生成的接口public interface SsoAuth extends android.os.IInterface{ /** Stub类继承自Binder,并且实现了SsoAuth接口 */ public static abstract class Stub extends android.os.Binder implements book.jtm_chap01.SsoAuth { private static final java.lang.String DESCRIPTOR = "book.jtm_chap01.SsoAuth"; public Stub(){ this.attachInterface(this, DESCRIPTOR); } /** * 将Binder转换为 book.jtm_chap01.SsoAuth接口或者包装为一个Proxy */ public static book.jtm_chap01.SsoAuth asInterface(android.os.IBinder obj){ if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof book.jtm_chap01.SsoAuth))) { return ((book.jtm_chap01.SsoAuth)iin); } return new book.jtm_chap01.SsoAuth.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder(){ return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{ switch (code){ case INTERFACE_TRANSACTION:{ reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_basicTypes:{ data.enforceInterface(DESCRIPTOR); // 代码省略 return true; } case TRANSACTION_ssoAuth: // 执行ssoAuth函数时提交给Binder的数据 { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); java.lang.String _arg1; _arg1 = data.readString(); this.ssoAuth(_arg0, _arg1); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } // 本地代理,通过Binder与服务端的对象进行交互 private static class Proxy implements book.jtm_chap01.SsoAuth{ private android.os.IBinder mRemote; Proxy(android.os.IBinder remote){ mRemote = remote; } @Override public android.os.IBinder asBinder(){ return mRemote; } // 代码省略 /** * 实现SSO授权 */ @Override public void ssoAuth(java.lang.String userName, java.lang.String pwd) throws android.os.RemoteException{ android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(userName); _data.writeString(pwd); mRemote.transact(Stub.TRANSACTION_ssoAuth, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_ssoAuth = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException; // 实现SSO授权 public void ssoAuth(java.lang.String userName, java.lang.String pwd) throws android.os.RemoteException;}

在SsoAuth.java中自动生成了SsoAuth接口,该接口中有一个ssoAuth函数。但最,重要的是生成了一个Stub类,该类继承自Binder类,并且实现了SsoAuth接口。Stub里面最重要的就是asInterface()这个函数,在这个函数中会判断obj参数的类型,如果该obj是本地的接口类型,则认为不是进程间调用,此时将该obj转换成SsoAuth类型;否则会通过自动生成的另一个内部类Proxy来包装obj,将其赋值给Proxy中的mRemote字段。Proxy类也实现了SsoAuth接口,不同的是它是通过Binder机制来与远程进程进行交互,例如,在ssoAuth ()函数中,Proxy将通过Binder机制向服务端传递请求和数据,它请求的类型为TRANSACTION_ssoAuth,参数分别是String类

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载