2012年10月23日

Android与iPhone应用开发的差异性与相似性比较

作者 非鱼

对Android开发仍然没有经验,只是通过几本书熟悉了其中的开发理念与框架,根据自己的理解浅谈一下,错漏之处敬请指正。

1、程序的入口。

iPhone应用程序的入口是AppDelegate,在其中初始化UIWindow对象,然后将Windows显示出来。如果是游戏,就直接在这个Window上画东西了,一般的应用,你需要建一个UIViewController作为Root界面,将它添加到Window中,Window就负责将它显示出来,剩下的完成功能,和调用显示其它ViewController都是这个Root界面的任务了。调用时你需要初始化另一个ViewController,然后弹出它,或者push它到导航队列中。在大部分应用中,这个Root VC并不是一个普通的UIViewController子类,而是UINavigationController或者UITabBarController,它们负责统管界面调度,你还需要指定另外一个UIViewController的子类作为它们的Root界面。

Android应用程序的入口并不是在代码中指定的,而是在AndroidManifest.xml文件中指定的。Android应用中每个界面都是相互独立的,没有直接的相互调用关系,各个Activity之间是完全解耦的,相互不可见。Application对象也不负责启动某一个Activity。因此,你在xml文件中指定哪一个作为初始加载对象,系统就为你加载哪一个,随时可以改成另外一个界面。

2、界面间的关系。

如上所述,iPhone中每一个ViewController都了解它将要调用的子ViewController,并且以强类型来初始化,并加载。也可以直接调用子ViewController中的变量和方法,为其赋值。整个应用程序最后编译成一个完整的文件,密不可分。无论何时调用执行该程序,都先从AppDelegate入手,找到初始View,把所需要的参数传递过去。如果要在启动时直接显示另一个View,必须经由Root,由它来处理所有传递进来的参数,再去调用所需要的ViewController才行。

而Android在这个问题上解决的非常彻底,你不需要知道自己的程序中其它Activity的名字,只需要知道它们的功能和接口即可调用。系统也可以不经由你的Root页面直接调用你的某一个Activity,同样其它的程序也可以直接把你的程序中的某一个页面的功能当成自己程序中的页面一样的来调用,只需要知道它的接口和功能。被调用时这个Activity的行为也像在那个程序中一样,弹出,完成自己的任务,消失,回退到调用它的那个界面上去。

3、界面的开发。

就像当年微软用VB的拖放式开发统治了入门级程序开发界一样,苹果用一个强大的InterfaceBuilder迅速的将自己的objective-c语言普及开来,其实这个语言的学习难度远大于Java。在iPhone的界面设计中,布局方式很简单,你把控件放在哪儿,它就在哪。为了适应界面旋转,某些控制会默认提供自适应窗口大小的功能,当然你也可以对大部分控件指定自适应大小的功能,设定让它距离某个边界的宽度保持不变,然后是否允许拉伸高度或宽度,就可以了。而比较复杂的界面你必须在旋转的时候在代码里精细的控制每一个控件的位置才可以。

Android的基于Java的界面布局要灵活的多,本身提供了几个容器,为其中的子控制提供流式布局,表格布局,或相关位置布局等。而且eclipse也提供了可视化的拖放操作的界面开发工具。但是由于Android的界面是xml方式保存的,对整个界面的复杂度会有一定的限制,某些复杂的布局实现起来会比较麻烦,比如本身是表格式布局,而其中的某个按钮或图片需要跨越多个元素这一类的。另外,基于xml的实现方式,Android很好的实现了界面的重用和样式的重用。在一个单独的xml中定义一个样式名,包含背景颜色字体大小之类的样式,然后在别的xml中定义控件时就可以直接引用这个style。这一点比iPhone上方便十倍。

4、界面与逻辑的交互。

iPhone使用了MVC的方式实现界面与逻辑的分离,而代码中的控件变量和界面中的控件本身的关联,可以通过在InterfaceBuilder中按住Ctrl拖放的方式建立关联,然后你就可以直接在代码中操作这个控件了。假如你在界面中删除了一个控件,而代码不做任何改变,对该控件的赋值或取值操作不会引发任何异常。

Android中逻辑代码与界面实现是完全分离的,没有任何关联关系。xml的界面定义中为每一个控件定义了id,如果要在代码中获取该控件对象并操作它的属性,你需要在代码中用findbyid,然后再加一个显式类型转换来关联到它。如果经常需要用到,需要像iPhone里面一样把它定义成全局变量,然后在整个界面加载时完成这个转换。如果你在界面中删除了原本的这个控件,那findbyid就找不到它了。

5、生命周期控制。

单个界面的生命周期在两个系统中基本上是一样的,只是两边的名字略有不同,都是创建,显示,隐藏,销毁,而对整个Application的生命周期的控制上,Android貌似缺少两个回调函数,无法实现让你在程序被结束之前安全的保存一些数据,而是要把这个保存动作放到某个Activity中才更可靠一些。

6、总结。

iPhone的系统设计是封闭的,这种封闭体现在各个方面。程序都运行在自己的沙盒中,没有权限操作任何非本程序目录下的东西,非本应用中的程序,只有几个系统允许你访问的东西提供了专用的访问接口,比如获取系统的照片,联系人之类的,除此之外的,你完全访问不了,也不能允许别人来访问你的数据。要做到跨程序的调用和数据传递,需要开放一个很复杂的sdk,给别人提供完整的头文件和具体实现的二进制代码,比如camara+,或者支付宝。

Android的系统设计的开发性也同样体现在各个方面。虽然每个程序也运行在自己的沙盒中(用Linux的用户名体系实现的沙盒),但是SD卡里的目录是全局公开的,所有程序都可以访问这里的文件。你只要知道别人程序的数据的位置和格式,就可以去读取或修改。而且你也可以把自己的应用中的某些功能实现的跟系统的一样,但是可以提供更好的体验,然后被其它应用调用。比如你可以让自己的拍照应用提供完整的美化功能,让自己的邮件提供更丰富的联系人管理功能,而只要暴露给系统的调用接口一致,那别人开发程序的时候完全不用关心自己需要去哪个拍照应用里取照片,只要要求系统提供一个这样的调用给自己的程序,系统会把所有可拍照的应用程序列表显示出来让用户挑一个,拍照并美化完成后就可以把结果照片返回给调用程序。在iPhone上开发任何程序,只要出现了图片的内容,你都要提供一套自己的查看图片的功能,这种功能完全是重复开发,目前最简单的方式也不过是去github上下载别人的图片管理器的源码,整个的编译到你的程序里去。而在Android中,你就可以直接让系统提供一个图片查看器来显示你的某一张图片,当用户按下返回,仍然回到你的程序中。这种开放性是iPhone程序员们梦寐以求的,却求之不得的。

两者的差异性还有很多,比如在Android中你可以让自己的程序实现系统级的事件监听和定时任务,当事件发生或定时到达时,系统会调用你指定的那个代码来执行一些操作,你几乎可以做任何事,包括拦截下这条消息不允许其它程序继续处理,因此可以实现电话、短信拦截。而iPhone完全封闭了类似的接口,你不能监听系统事件,只能通过你的服务器推送一条消息给系统,你不能做全局定时,只能将一个定时消息放入系统的本地通知中。当这两种通知在屏幕上显示时,用户完全可能忽略它,然后你的应用无法做出任何动作响应,因此连自动回复微博或邮件都做不到。

但是由于这种开放性带来的负面问题是,程序的权限过高,而该权限所导致的可能的后果,需要完全由用户自行负责。如果你开发了一个没有任何功能的拍照程序或图片查看程序,却定义了相同的接口。而用户在未经验证或思考的情况下安装了你的程序,并在系统第一次询问的时候选择了你的程序来处理这两种请求并选择了不再重复询问,那除非他卸载你的程序重新选择,否则所有其它调用该接口获取图片的应用都不正常了。更严重的例子是短信和网络的权限,一旦一个应用在安装的时候被允许了该权限(不允许就根本无法安装),那它以后做了什么用户可能根本就不知道,因为它自己调用短信接口发送短信,根本不会进入系统的短信列表。这种判断完全需要由用户自己来做出,对用户的认知能力和专业水平提出了相当高的要求,而实际上,Android用户绝大部分肯定是缺少这种能力的。