http://benbeng.leanote.com/post/Handoff%E7%BC%96%E7%A8%8B%E6%8C%87%E5%8D%97-part1
关于Handoff
Handoff是iOS 8 和 OS X v10.10中引入的功能,可以让同一个用户在多台设备间传递项目。
Handoff能让用户从一台设备开始一个项目,然后切换至其他设备继续进行,这一切都是无缝的,每台设备都无需重新配置。例如,用户正在Mac上的Safari浏览一片长文章,随后他可以切换到附近一台已使用相同Apple ID 登入iCloud的iOS设备上,在这台iOS设备上的Safiri中继续浏览相同的网页,页面滚动的位置和原设备上的一样。
Apple的应用,比如Safari、邮件、地图、联系人、备忘录、日历和提醒事项,在iOS8 和 OS X v10.10中使用公共API来实现Handoff功能。第三方开发者也可以使用相同的API来在应用中实现Handoff,这些应用需要有同一个Team ID。此类应用必须通过App Store发布或者使用注册开发者签名。
Handoff交互
传递用户的活动包括3个阶段:
- 为用户在你的app中的每一个活动创建一个user activity 对象。
- 定期使用用户的最新信息更新user activity 对象。
- 当用户请求时,在不同的设备上继续用户的活动。
基于文件的app(也就是基于NSDocument或者UIDocument子类的app)为Handoff的这3个阶段提供了内建支持。响应者对象(NSResponder和UIResponder的子类)为更新user activity和管理当前状态提供了内建支持。你的app可以直接创建、更新和继续user activity,特别是在app delegate中。
Handoff主要是依赖Foundation中的一个类NSUserActivity,该类支持UIKit和AppKit中的一小部分API。app将用户活动的信息封装在NSUserActivity对象中,这些activity就用来在其他设备上继续活动。特定user activity的Handoff需要最开始的应用把活动的NSUserActivity对象指定为当前activity,保存相关信息,然后把数据发给另一台设备。Handoff只在设备间传递能描述用户活动的必要信息,而大规模的数据同步则由iCloud来处理。
在“另一台”设备上,会通知用户有一项活动可以继续。如果用户选择继续该活动,系统会启动合适的app,并提供activity中的数据。user activity只能在与原app有相同Team ID 并且支持这种activity类型的app中继续。在app的Infp.plist中的NSUserActivityTypes键下可以设置支持的activity类型。所以,“另一台”设备选择启动哪个app是基于:目标Team ID,起始的NSUserActivity对象的activity type 属性,还可能包括activity对象的title属性。“另一台”设备上的app可以根据user activity对象中userInfo字典的内容来配置UI界面和状态,以实现用户活动的无缝切换。
另外,如果继续一个activity需要更多的数据,不能使用第一种传输机制来高效传输,那么resuming app可以让起始app的user activity对象在应用间打开流来传输更多数据。例如,用户正在写一封含有图片的电子邮件,那么最好的方法就是流来把这些数据传输到另一台设备上。更多信息参见Using Continuation Streams。
iOS和OS X中基于文件的app已自动支持Handoff,详见Supporting a User Activity in Document-Based Apps(本文内)。
User Activity对象
NSUserActivity对象封装了特定设备、特定应用中用户活动的状态,它是Handoff机制中主要的对象。起始app为每个它支持传递给另一台设备的用户活动创建一个user activity 对象。例如,网页浏览器会为每一个正在浏览网页的标签页或者窗口创建一个user activity 对象。但是只有在前台的标签页或者窗口对应的activity 对象才是当前有效的,只有当前有效的activity才能用于继续用户活动(continuation好难翻译。。。。。。。。)。
NSUserActivity对象通过它的activityType和title属性来区分。NSUserActivity的userInfo字典里有状态数据,它还有一个叫needsSave的脏标志(dirty flag)来支持delegete的延迟更新状态。NSUserActivity的方法addUserInfoEntriesFromDictionary:允许delegate和其他委托(clients)把状态数据合并到userInfo字典里。
更多信息参见NSUserActivity Class Reference。
User Activity Delegate
User activity delegate是一个遵从NSUserActivityDelegate协议的对象。它通常是app中顶层对象,比如view controller 或者app delegate。delegate管理着activity与app的交互。
NSUserActivity的delegate属性就是user activity delegate,负责更新NSUserActivity对象userInfo字典中的数据,以便它可以传递给另一台设备。当系统需要activity更新时,比如活动要在另一台设备上继续之前,系统会调用delegate的userActivityWillSave:方法。你可以实现这个回调来更新对象中承载数据的属性,例如userInfo、title等。一旦系统调用这个方法,它会把needsSave重置为NO。如果userInfo或者其他承载数据的属性又发生变化的话,把这个值改为YES。
另一方面,除了实现上面所说的delegate的userActivityWillSave:方法之外,你还可以让UIKit或者AppKit自动管理user activity。通过设置响应者对象的userActivity属性,并且实现响应者的updateUserActivityState:回调,app就会选择自动管理user activity,详见Managing a User Activity With Responders(本文内)。选择一个合适你的实现方法。(This arrangement is preferred if it works for your user activity.)
更多信息参见NSUserActivityDelegate Protocol Reference。
App框架支持
UIKit和AppKit在document、responder和app delegate中为Handoff提供了支持。尽管不同平台之间有一些细小的差别,但是允许app保存和恢复user activity的基本机制是相同的,API也是相同的。
在基于文件的app中支持User Activity
如果你在app的Info.plist里为每个CFBundleDocumentTypes入口添加一个NSUbiquitousDocumentUserActivityType键值对,这样iOS和OS X中基于文件的app就自动自动支持Handoff了。如果有这个键的话,NSDocument和UIDocument就会为特定文件类型的基于iCloud的文件,自动创建NSUserActivity对象。NSUbiquitousDocumentUserActivityType的值是一个字符串,表示NSUserActivity对象的activity type。也就是说,你为基于文件的app所支持的文件类型都提供了一个activity type。多个文件类型可以有同一个activity type。NSDocument和UIDocument会自动把fileURL属性放到activity对象userInfo字典的NSUserActivityDocumentURLKey键下。
在OS X中,如果app delegate方法application:continueUserActivity:restorationHandler:返回NO,或者没有实现,那么AppKit可以自动恢复用上面方式创建的NSUserActivity对象。在这种情况,文件会使用NSDocumentController的方法openDocumentWithContentsOfURL:display:completionHandler: 来打开,并且会收到一条restoreUserActivityState: 消息。
更多信息参见Adopting Handoff in Document-Based Apps、NSDocument Class Reference 和 UIDocument Class Reference。
用响应者来管理user activity
如果你把user activity设置为一个响应者对象的userActivity属性,UIKit 和 AppKit就可以管理user activity。当响应者知道activity的状态是已修改(dirty)时,它必须要把activity对象的needsSave属性置为YES。系统会在合适时机自动保存NSUserActivity对象,首先会通过回调updateUserActivityState:给响应者一个机会来更新activity的状态。你的响应者子类必须重写updateUserActivityState:方法来给user activity对象添加状态数据。如果多个响应者共用一个NSUserActivity对象,当系统更新user activity对象时,它们都会收到updateUserActivityState:回调。在更新回调发送之前,activity对象的userInfo字典会被清空。
在OS X中,NSUserActivity对象由AppKit管理,并与响应者相关联。根据main window 和响应者链,activity对象会自动变为当前有效,也就是在文件的window变为main window时。但是在iOS中,NSUserActivity对象由UIKit管理,你必须显式调用becomeCurrent或者当app进入前台时,给在视图层级中的UIViewController对象设置文件的NSUserActivity对象。
响应者可以把它的userActivity属性置为nil来断开与activity的关联。当一个由app框架管理的NSUserActivity对象没有关联的响应者和文件时,它就自动无效了。
更多信息参见Adopting Handoff in Responders、 NSResponder Class Reference 和UIResponder Class Reference。
使用app delegate来继续activity
在一个不是基于文件的app中,app delegate就是继续user activity的主要入口。当用户选择要继续一个activity时,Handoff会启动合适的app,然后给app delegate发送一条application:willContinueUserActivityWithType:消息。app让用户知道activity马上就可以继续。同时,当app delegate收到 application:continueUserActivity:restorationHandler: 消息时,NSUserActivity 对象会传递过来。你应该实现这个方法,通过user activity对象来恢复activity,配置app的界面。
application:continueUserActivity:restorationHandler:消息包括一个恢复处理block,如果你的app使用其他的响应者或者文件对象来恢复user activity,那你可以调用这个block。创建这些对象(如果已经有的话就不用创建了),传入block的NSarray参数中。系统会给每个对象发送一条restoreUserActivityState:消息,并传入user activity对象。每个对象可以用activity的userInfo数据来恢复。关于恢复处理block的更多信息,参见NSApplicationDelegate Protocol Reference中的application:continueUserActivity:restorationHandler: 方法。
如果你没有实现application:continueUserActivity:restorationHandler:方法或者返回NO,并且你的app是基于文件的,AppKit可以自动恢复activity,这在Supporting User Activity in Document-Based Apps中有描述。更多信息参见Continuing an Activity。
(第一部分完)