可能原帖中存在实例状态问题的原因是,Android 的默认行为是为每个新意图启动一个新活动。这就是为什么在网络回调后 GrkEngineer 没有看到 onRestoreInstanceState 被调用的原因。
将请求令牌存储为共享首选项是一种解决方案,以便可以从 OAuth 网络回调后启动的新活动中访问它。
我最初尝试使用共享首选项,它似乎工作得很好。但是,我认为这不是最佳解决方案。理想情况下,您需要强制 Android 将回调传递给您的原始活动(我将在下面解释原因)。
我尝试使用 singleTask 和 singleInstance 启动模式部分成功地完成了此操作,但感觉不正确,并且 Android 文档暗示这些模式不建议一般使用。
经过大量查找文档和测试,我发现在创建意图时使用以下标志会导致 Android 将意图传递到现有活动的实例中(如果已被销毁,则重新创建)。
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
我需要将回调处理程序交给原始活动的原因是为了与 Android AccountManager 集成。 我使用以下示例作为起点:
http://developer.android.com/resources/samples/SampleSyncAdapter/index.html
与AccountManager认证机制集成的关键部分之一是传递到您的活动中以启动身份验证过程的AccountAuthenticatorResponse。
我发现实现这个的一个大问题是保持对AccountAuthenticatorResponse对象的引用。它被传递到您的AuthenticatorActivity中,您需要在身份验证完成后调用它的方法,以便标准帐户UI保持正确的状态。然而,我遇到了最初GrkEngineer遇到的同样的问题。当我尝试在OAuth回调后重新启动我的OAuth认证器活动时,我总是得到一个新的实例,它已经失去了对AccountAuthenticatorResponse对象的引用,我看不到任何持久化该对象的方法。
关键是使用上面描述的intent标志。
我的AbstractAccountAuthenticator使用FLAG_ACTIVITY_NEW_TASK启动AuthenticatorActivity。它获取请求令牌(使用AsyncTask)并启动浏览器询问用户授权。
OAuthCallbackHandlerActivity注册处理我的自定义回调方案。当用户授权后调用它时,它使用Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP意图调用AuthenticatorActivity。
这将导致我的原始AuthenticatorActivity被重新激活。AccountAuthenticatorResponse对象仍然可用(以及请求令牌/密钥,我在OnSaveInstanceState中保存了它们)。现在,该活动可以获取访问令牌(再次使用AsyncTask),然后调用AccountAuthenticatorResponse对象上的完成方法。
使其工作的关键是使用我提到的意图标志,并确保AuthenticatorActivity在您的应用程序任务中启动,而不是帐户管理器任务中启动。FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_SINGLE_TOP仅会在它们位于同一任务中时才导致现有活动实例被重用。因此,如果要返回的活动在其他任务中启动,则原始实例将无法重用。
我在模拟器上测试了这个过程,使用Dev Tools立即终止我的AuthenticatorActivity,以便测试重建过程。使用onSaveInstanceState/onRestoreInstanceState保存请求令牌/密钥效果很好。我甚至不必担心恢复AccountAuthenticatorResponse对象。Android自己就恢复了它 - 真神奇!