Controller和Activity
  • Controller:Controller本职工作是控制UI和处理逻辑,其中UI Controller作为对View的Controll是很关键的,另外数据传递和IPC也算逻辑的一部分,所以放到这一节。UIViewController在 -viewDidLoad中初始化操作, 在-viewWillAppear 中可以加一些改变界面和状态的操作。 UIViewController整个生命周期 viewDidLoad–> viewWillAppear–> viewDidAppear –> 运行态–> viewWillDisAppear–> viewDidDisAppear-> viewDidUnload UIViewController === Activity UIViewController在 -viewDidLoad中初始化操作, 在-viewWillAppear 中可以加一些改变界面和状态的操作。
  • Activity在onCreate()中初始化操作, 在onResume()中可以加一些改变界面和状态的操作。
数据传递
  • Android
  • Intent,又叫意图或者目的,是不同组件之间相互通讯的纽带,封装了不同组件之间通讯的条件。可以通过Extra向Intent中放入一些基本数据,也可以利用Bundle通过Serializable接口或者Parcelable接口非基本数据或者对象,使用起来非常简单和灵活。通常的Activity 跳转都是使用Intent。
  • 利用全局静态数据,public static成员变量,或者单例模式,这个很容易理解,就是通过一个全局变量去传递数据。
  • 使用文件,这个没什么好说的,保存到文件,然后在另一个地方从文件中取出。
  • 广播BroadCast,是Android四大组件之一,应用程序可以拥有任意数量的广播接收器以对所有它感兴趣的通知信息予以响应。要实现广播,首先需要注册一个BroadCastReceiver,通过sendBroadcast这个方法来发送,之后在BroadCastReceiver的onReceiver接收消息。
  • iOS
  • 对象的@property属性值,这种方式主要用于在界面跳转时的数据传递。
  • 给App的Delegate设置成员变量,在代码中可以通过获得当前App的delegate而获得成员变量,这种方式和设置全局变量类似。
  • 通过extern定义全局变量,或者单例模式,这点和Android一样。
  • 使用文件,或者NSUserdefault来传递,原理和Android一样。
  • NSNotificationCenter,与Android BroadCast对应,可以看作iOS的广播。观察者通过addObserver向NSNotificationCenter注册特定的消息通知和特定的接收函数,发送者通过postNotificationName向NSNotificationCenter发送特定的消息,之后观察者在接收函数中处理消息。与Android的BroadCast有一点不同的是NSNotificationCenter只在App内,Android BroadCast是可以在系统级。
IPC 进程间通信方式
  • Android:与iOS相比,Android的进程间通信方式有很多,如ContentProvider,Activity,广播和AIDL等,不过这些方式的本质都是binder。
  • Content Provider是Android四大组件之一,它提供了一种在多个应用程序之间数据共享的方式。应用程序可以利用Content Provider完成查询数据,修改数据,添加数据,删除数据。需要创建ContentResolver对象与ContentProvider进行交互,通过指定的URI确定要访问ContentProvider数据集。
  • Activity很好理解,就是可以在自己的App跳转到别的程序的界面。但跨进程访问并不需要指定Context对象和Activity的Class对象,而需要指定的是要访问的Activity所对应的Action,有些Activity还需要指定一个Uri。
  • 广播,前面说过广播可以在系统级,所以广播能跨进程通信,一些系统通知经常会用到它,例如通知时区改变、电池电量低、拍摄了一张照片或者用户改变了语言选项等等。
  • AIDL实际上是一种接口定义语言。通过这种语言定义接口后,Eclipse插件会自动生成相应的Java代码接口代码。它需要创建AIDL文件和一个Service来配合使用。
  • Binder。这是最底层的进程通信了。主要是有ServiceManager这样一个东西来控制,所有的服务都要通过它来注册,提供它的binder地址,其它程序如果需要哪种服务就向它申请,在ServiceManager获得了对端地址之后,你们两就通过Binder驱动进行通信了。
  • iOS : 而对于iOS来说,为了避免数据冲突和更好的提供更好的安全机制,iOS提供了沙盒机制。尽管如此,iOS还是提供了若干进程间通信机制,如URL Scheme、剪切版、CoreCFMessagePort和CFNotificationCenter等等。
  • URL Schema就是iOS内的应用调用协议,应用A可以声明自定义的调用协议,就如http/https那样,当另一个应用B打算在应用内打开应用A时,可以打开使用A自定义的协议开头的URL来打开A,除了协议头,URL中还可以附加其它参数。
  • 剪切版很好理解,用户可以跨应用拷贝了一段文字,图片,文档等。在代码中对应的类是:UIPasteboard。
  • CFMessagePort,通过mach port实现的。它需要消息接收者和消息发送者。消息发送者需要通过CFMessagePortCreateLocal、CFMessagePortCreateRunLoopSource、CFRunLoopAddSource来注册监听,并自己实现回调方法。发送者通过CFMessagePortCreateRemote生成一个Remote的CFMessagePortRef,并通过CFMessagePortSendRequest发送数据。遗憾的是iOS7以后不能用这个方法了。 CFNotificationCenter通知可以是系统级别的,它的原理和NSNotificationCenter一样。CFNotificationCenter只提供类方法CFNotificationCenterGetDistributedCenter获取通知发布中心,需要通过CFNotificationCenterAddObserver添加所要指定监听消息名和观察者到通知发布中心,当消息接收到的时候函数指针指向的函数将被执行一次。发送者只需要通过CFNotificationCenterPostNotification发送消息名、对象还有user info就可以了。
垃圾回收
  • Android 对于垃圾回收,由于Android使用Java语言,所以就自动沿用了Java的GC机制,Java有两种方式进行垃圾标记,一种是引用计数,一种是可达性分析算法,但一般选择可达性分析算法,因为引用计数会有循环引用问题。对于垃圾清理有四种方式:标记清理、复制清理、标记整理和分带收集。不同的GC器会选择不同的清理方式。Android有自己的虚拟机,5.0以前是Dalvik,以后是ART,不同的虚拟机也有不同的GC器,不同的GC器也会选择不同的清理方式。
  • iOS 对于iOS,iOS 5以前都是使用OC的手动回收机制,也就是引用计数,它的规则是这样的:如果需要持有一个对象,那么对其发送retain,如果之后不再使用该对象,那么需要对其发送release或者autorelease,每一次对retain,alloc或者new的调用,需要对应一次release或autorelease调用。所以我在网上很多博客里看到的iOS的代码都还有release和autorelease。 iOS5以后苹果引入了ARC内存管理机制,这是一种编译期的内存管理方式,从此不再需要手动释放对象了,要注意的是这里的ARC并不是GC,而只是由编译器自动添加retain和release代码,归根到底还是引用计数。ARC带来的好处是代码变得简单,代码的总量变少了,代码高速化,由于使用编译器管理引用计数,减少了低效代码的可能性。
  • 防止内存泄漏 尽管两个平台现在都已经有了自动处理垃圾的机制,但是我们还是需要小心处理某些对象和过程,以防发生内存泄漏。 Android可能的内存泄漏,例如使用完Bitmap之后没有recycle,使用完Sqlite数据库后没有关闭数据库,ListView中没有使用ViewHolder,使用完广播没有注销,使用Thread没有置空。 iOS里例如OC上的循环引用,NSTimer 会持有target,block循环引用变量,使用了NSNotificationCenter后没有注销,View中存在大量对象,显示结束时,没有及时置空,图片加载的复用等等。
Model(存储)

Android

  • Shared Preferences:是用来存储一些Key/Value类似的成对的基本数据类型。它只能存储基本数据类型,也即int, long, boolean, String, float。它是采用了XML格式将数据存储到设备中,在DDMS中的File Explorer中的/data/data/shares_prefs下。一般我们用Shared Preferences保存一些简单的用户数据。
  • 内部存储与外部存储:内部存储指的是手机内置的存储空间,使用内部存储主要有二个方式,一个是文件操作,一个是文件夹操作。外部存储就是读写sdcard上的文件。
  • Sqlite3:是一款开源的嵌入式关系型数据库,可移植性好、易使用、内存开销小,一般手机上的小型存储都会用到sqlite。android.database.sqlite包封装了很多SQLite操作的API,如insert、delete、update等等。但是我更喜欢用execSQL(String sql)来直接执行sql命令。

iOS:

  • iOS都是沙盒存储,数据都在App的目录下,iOS的存储方式有以下几种:
  • NSUserDefaults与Android的SharedPreferences原理相同。用来保存应用程序设置和属性、用户保存的数据。NSUserDefaults可以存储的数据类型包括:NSData、NSString、NSNumber、NSDate、NSArray、NSDictionary。
  • NSKeyedArchiver:归档采用归档的形式来保存数据,该数据对象需要遵守NSCoding协议。对象对应的类必须提供encodeWithCoder和initWithCoder方法,对对象进行编码和解码。 使用属性列表:保存数据到documents目录,这个一般用来保存一些简单的应用数据。
  • Sqlite3:iOS上也使用Sqlite,不过语言没有使用OC,而是用的C语言,而且没有提供那么多API,需要自己执行sql命令。
  • Core Data:是对SQLite的封装,提供了更高级的持久化方式。在对数据库操作时,不需要使用sql语句。在CoreData中主要使用的几个类。 NSManagedObjectModel 相当于实体,不过它包含了实体间的关系;NSManagedObjectContext操作实际内容,数据的增删改查;NSPersistentStoreCoordinator 相当于数据库的连接器 NSFetchRequest 相当于查询语句;NSPredicate相当于查询条件。其实感觉有点像Android中的Content Provider的形式。
View

创建和布局 iOS xib 文件( nib) 直接在代码中布局 storyboard 拖控件 Android 1.xml 在 java 代码中以 id 找到空间并处理逻辑 推荐 Java 使用 View 和布局 iOS 和 Android 都是基于 ViewGroup的派生类, 一个ViewGroup的生命周期经历3个阶段,分别是measure,layout和draw。 控件 作为苹果这样一个使用闭源框架和设计主导的公司,其特点其实在控件中有所体现。Google的感觉就是扔给你一堆控件你可以随意组合,自由搭配。虽然都是给你一堆控件,但是苹果已经给你制定好了一些规范,并推荐应该怎样做。例如用UINavigationController创建一个需要层次化流程的界面,用UITarBarController创建我们大多数App 的主要功能页,用UIPageControl创建引导页或者广告栏,用UISearchBar创建搜索栏。Android中虽然有类似的比如TabHost这种控件,但是想要创造UITarBarController和UINavigationController搭配的效果,还是需要很多代码的。就这点来看,iOS方便了很多,只需要一些简单的官方控件就能构建起一个App。 TableView和ListView,列表是使用频率比较高的控件,Android的ListView使用了适配器Adapter来处理ListView的显示,这里使用了适配器模式。而iOS使用DataSource和delegate来代替Adapter的作用,可以通过dataSource 和delegate来设置每一行的外观样式,每一行的高度等等。 React Native 是按照 DataSource 的方式来做的

多线程

Android中有主线程也叫UI线程,如果我们在UI线程进行了长时间(大于5s)的耗时操作,那么就会出现ANR(Application Not Responding)。iOS中也有主线程的概念,尽管没有ANR,但是长时间的占用主线程进行耗时操作将会带来非常差的体验感,所以Android和iOS都需要异步处理。 Android Android与异步相关的工具有以下几种: Runnable/Thread 是Java中的,可以扩展继承Thread类来创建一个线程,也可以继承Runnable接口来创建一个线程,再结合Android中的Handler即可以在子线程中更新主线程UI了。 ExecutorServie线程池也是Java中的概念。它的作用是通过重复利用已经创建的线程来避免创建和销毁线程造成的消耗,同时提高响应速度和线程的可管理性。当Android中有一些不需要更新UI的操作,通常采用这个方法。

IntentService它是继承于Service并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统Service一样。当任务执行完后,IntentService会自动停止,而不需要我们去手动控制。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行。它处理业务流程非常方便。 AsyncTask是Android提供的轻量级的异步类,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程。它最重要的两个方法,一个是doInBackground(params):处理耗时事务,并把结果返回。一个是onPostExecute(result)可以使用在doInBackground得到的结果处理操作UI。很多人都推荐使用它处理需要更新UI的操作。

Service作为四大组件之一,用于在后台管理和处理一些耗时的逻辑线程或者某些需要长期运行的线程。这里要说明的是Service不是运行在后台线程的,它仍然是运行在UI主线程,所以如果要用它处理耗时任务,你还是得在其中创建新线程。它的作用是能在后台对线程进行良好的管理,对外提供AIDL接口。Service的一个经常的应用是保持着心跳连接。

iOS iOS中异步工具有以下几种: NSThread这套方案是经过苹果封装后的,并且完全面向对象的。所以你可以直接操控线程对象,非常直观和方便。NSThread轻量级,使用简单需要自己管理线程的生命周期、线程同步、加锁、睡眠以及唤醒等。线程同步对数据的加锁会有一定的系统开销。所以我们一般不会用这个。

GCD是苹果为多核的并行运算提出的解决方案,会自动合理地利用更多的CPU内核,最重要的是它会自动管理线程的生命周期,完全不需要我们管理,我们只需要告诉干什么就行。它使用C语言,由于使用了Block,使得使用起来更加方便,而且灵活。很多App都会使用这套方案。它有两个概念:任务和队列。 任务分同步和异步。同步任务会在当前线程执行,不会另开线程,会阻塞当前线程,直到block中的任务执行完毕(block在当前线程中执行)。异步任务会另开线程,在别的线程执行。当前线程会直接往下执行,不会阻塞当前线程。(block在新线程中执行)。队列用于存放任务。一共有两种队列,串行队列一次只执行一个线程,FIFO。并行队列:一次可以执行多个线程。 NSOperation和NSOperationQueue。NSOperation 是苹果公司对 GCD的封装,完全面向对象,所以使用起来更好理解。NSOperation 和NSOperationQueue分别对应GCD的任务和队列,它的用法和GCD很像。与GCD不同的是它的任务能被取消,队列可以暂停、恢复,NSOperation还可以被子类化。网络上支持GCD和NSOpration都大有人在,各说各的理,总的来说这两种多线程方式都是被现在的开发人员接收的。

looper &runloop

Android里的消息处理机制:消息是存放在一个消息队列中,应用程序的主线程围绕这个消息队列进入一个无限循环的,直到应用程序退出。Looper就是消息处理机制的关键,每一个线程都有唯一的Looper,主线程中的Looper默认开启。如果队列中有消息,应用程序的主线程就会把它取出来,并分发给相应的Handler进行处理;如果队列中没有消息,应用程序的主线程就会进入空闲等待状态,等待下一个消息的到来。

这里要注意的是其它线程不是默认开启的。但是如果你需要线程在处理完一些事情后,不要自己停止,那么就需要使用Looper。Looper由四个部分组成:

Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。 Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。 MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。

Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。 iOS也有类似的东西名叫runloop,它的作用其实和looper 是一样,每个线程只有一个Runloop,在主线程中默认启动,一直循环接收消息。