在 Android 的 AccountManager
中实现自定义帐户类型时,我在登录流程中遇到以下问题:
登录应该通过 OAuth 提供商进行。因此,我创建了一个 SignInActivity
,它启动一个 WebView
并启动一个 OAuth 流程。这工作正常,当接收到 my-custom-scheme://callback
的回调时,WebView
检测到它,接收 code
querystring 参数并完成流程。使用 WebView
的缺点是,即使用户可能已经在浏览器中有一个 Activity session ,但该 session 不会在 WebView
中使用,因此用户将不得不在 WebView
中再次登录。
为了解决这个问题,我尝试改用 AndroidManifest.xml
中的 intent-filter
,如下所示:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="my-custom-scheme" android:path="callback"/>
</intent-filter>
我没有在 SignInActivity
中打开 WebView
,而是启动浏览器 Intent 并等待浏览器点击 my-custom-scheme://回调
.
Intent browserIntent = new Intent(Intent.ACTION_VIEW, "http://oauth2provider/authorize");
startActivity(browserIntent);
finish();
在我的 SignInActivity
中,我有以下代码来处理回调:
if (intent != null && intent.getData() != null && getString("my-custom-scheme").equals(intent.getData().getScheme())) {
String code = getIntent().getData().getQueryParameter("code");
// complete oauth flow
}
这行得通。但是,问题(终于!):
- 如果用户未登录,浏览器 Intent 将显示 oauth 提供商的登录页面。用户登录后,Chrome 将重定向到 my-custom-scheme://callback 并且
SignInActivity
将启动以处理 Intent 。由于此 Activity 是不可见的,因此浏览器将在登录页面上保持打开状态,并且对用户而言,它看起来就像什么也没发生过一样。浏览器永远不会关闭。 - 如果用户已经登录,oauth 提供商将直接重定向到 my-custom-scheme://callback。在这种情况下,浏览器选项卡会自动关闭,但浏览器本身保持打开状态(没有可见的选项卡)。
所以我的问题是:在重定向到 my-custom-scheme://callback 之后,有没有办法让浏览器的行为有所不同?理想情况下,我希望它在重定向到回调后简单地关闭,并返回到 Activity 堆栈中的前一个 Activity (即返回到从头开始启动 SignInActivity
的 Activity )。
最佳答案
我使用下一种方法解决了同样的问题。
假设我们有带有 Sign In
按钮的 MainActivity
。 不是直接单击该按钮启动浏览器,而是使用 startActivityForResult
方法启动 SignInActivity
。它用于我可以处理登录流程的结果。
startActivityForResult(new Intent(this, SignInActivity.class), requestCode);
SignInActivity
负责:
- 打开浏览器登录页面
- 捕获重定向到
custom-scheme://callback
- 使用从重定向 url 接收到的 token 完成登录流程
- 返回结果给
MainActivity
因此,它的 onCreate
方法如下所示:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
if (intent != null && intent.getData() != null && "custom-scheme".equals(intent.getData().getScheme())) {
String code = getIntent().getData().getQueryParameter("code");
// complete oauth flow
} else {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, "http://oauth2provider/authorize")
.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_FROM_BACKGROUND);
startActivity(browserIntent);
finish();
}
}
将标志设置为浏览器 Intent 。
这样,如果 SignInActivity
从 MainActivity
打开,它只会在浏览器中打开登录页面,如果它打开并捕获重定向 url,它会完成登录流程并发送适当的请求。
完成登录流程后,将代码发送到回调方法中的某个端点,您应该执行以下操作:
setResult(Activity.RESULT_OK);
this.finish();
自然地,您会从该端点收到 access_token。您可以将它存储在此处的某处,在成功回调中或将其返回到 MainActivity
以在那里处理它。
作为 SignInActivity
的布局,您可以只使用页面中央的 ProgressBar
。在 SignInActivity
打开捕获重定向 url (custom-scheme://callback
) 后,它将在登录流程完成期间出现。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ProgressBar
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
/>
</RelativeLayout>
这是 AndroidManifest.xml
中 SignInActivity
的声明
<activity android:name=".SignInActivity"
android:launchMode="singleTask"
android:noHistory="true"
>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="custom-scheme" android:host="callback"/>
</intent-filter>
</activity>
关于android - 浏览器 Intent 并返回正确的 Activity (关闭打开的选项卡),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25831981/