使 facebook 登录与 Android Webview 一起工作

Posted

技术标签:

【中文标题】使 facebook 登录与 Android Webview 一起工作【英文标题】:Making facebook login work with an Android Webview 【发布时间】:2012-09-20 19:35:13 【问题描述】:

我只是想在 android 的 WebView 上实现 facebook 登录。问题是在我单击 html 页面上的 facebook 按钮并在 Facebook 对话框中插入用户名和密码之后。 url 重定向只是给我一个黑页。

   @Override
public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);

    WebView webview = new WebView(this);        
    webview.setWebChromeClient(new WebChromeClient());  
    webview.getSettings().setPluginState(PluginState.ON);
    webview.getSettings().setjavascriptEnabled(true); 
    webview.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
    webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
    webview.setWebViewClient(new WebViewClient());
    webview.loadUrl("http://peoplehunt.crowdscanner.com/hunt"); 
    setContentView(webview);

这是我的 HTML 页面上的 Facebook 常规 javascript API,当点击 facebook 按钮时会调用此函数。

$("#login_facebook").click(function() 

                    FB.login(function(response) 
                            //This function should be called
                            if (response.authResponse) 
                            FB.api('/me?fields=name,email,picture,id&type=large', function(response) 
                                    //console.log("email "+response.email);
                                    $("#submitHandle").hide();
                                    $("#loader").show();
                                    //console.log('Good to see you, ' + response.picture + '.');
                                    var theUsername = response.name;
                                    theUsername = theUsername.replace(/ /g, '_')+"_"+response.id+"@meetforeal.com";
                                    //console.log(theUsername);
                                    $("input[name=email]").val(encodeURIComponent(response.email));
                                    $("input[name=lastName]").val(encodeURIComponent(response.name));
                                    $("input[name=avatarImage]").val(response.picture);
                                    $("input[name=userName]").val(encodeURIComponent(theUsername));
                                    $("#msg_twitter").fadeIn("slow");
                                    $("#submitHandle").show();
                                    $("#loader").hide();
                                    $("#user").attr("action","/crowdmodule/auth/registerattendeefacebook");
                                    $("#user").submit();
                            );
                             else 
  //console.log('User cancelled login or did not fully authorize.');
 
, scope: 'email');

关于如何在 Facebook 对话页面上重定向后获取响应的任何想法?谢谢。

【问题讨论】:

【参考方案1】:

此代码对我有用!我的页面上有一个 LIKE、SHARE、Comments 按钮。

    private lateinit var myWebView: WebView;
private val target_url = "https://www.webkomph.com"
private val target_url_prefix = "webkomph.com"
private var mContext: Context? = null
private var mWebviewPop: WebView? = null
private var mContainer: ConstraintLayout? = null
override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);
    val cookieManager = CookieManager.getInstance();
    cookieManager.setAcceptCookie(true)

    myWebView  = findViewById(R.id.webkom_site);
    mContainer = findViewById(R.id.webview_frame);
    val webkomBlue = Color.parseColor("#1c71bc")
    myWebView.setBackgroundColor(webkomBlue);
    myWebView.background
    val webSettings = myWebView.settings;
    webSettings.setJavaScriptEnabled(true);
    webSettings.setAppCacheEnabled(true);
    webSettings.javaScriptCanOpenWindowsAutomatically = true;
    webSettings.setSupportMultipleWindows(true)
    myWebView.setWebViewClient(UriWebViewClient())
    myWebView.setWebChromeClient(UriChromeClient())
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 
        cookieManager.setAcceptThirdPartyCookies(myWebView, true)
    
    myWebView.loadUrl(target_url);
    mContext = this.applicationContext


override fun onBackPressed() 
    if (myWebView.canGoBack()) 
        myWebView.goBack()
     else 
        super.onBackPressed()
    


inner class UriWebViewClient : WebViewClient() 
    override fun shouldOverrideUrlLoading(
        view: WebView,
        url: String
    ): Boolean 
        val host = Uri.parse(url).host
        //Log.d("shouldOverrideUrlLoading", url);
        if(host == null || host == target_url_prefix) 
            // This is my web site, so do not override; let my WebView load
            // the page
            if (mWebviewPop != null) 
                mWebviewPop!!.setVisibility(View.GONE)
                mContainer?.removeView(mWebviewPop)
                mWebviewPop = null
            
            return false
        


        if (host == "m.facebook.com" || host == "www.facebook.com" || url.contains("ret=login")) 
            return false
        
        // Otherwise, the link is not for a page on my site, so launch
        // another Activity that handles URLs
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
        startActivity(intent)
        return true
    

    override fun onPageFinished(view: WebView?, url: String) 
        // Facebook redirects to this url once a user has logged in, this is a blank page so we override this
        // http://www.facebook.com/connect/connect_to_external_page_widget_loggedin.php?............
        if (url.contains("plugins/close_popup.php?reload") || url.contains("dialog/close_window") || url.contains("facebook.com/dialog/plugin.optin?")) 

            if(mWebviewPop != null)
                mWebviewPop!!.visibility = View.GONE
                mContainer!!.removeView(mWebviewPop)
                mWebviewPop = null
                if(myWebView != null && (url.contains("dialog/close_window") || url.contains("facebook.com/dialog/plugin.optin?")))
                    myWebView?.reload();
                
            


            return
        
        super.onPageFinished(view, url)
    
    override fun onReceivedSslError(
        view: WebView, handler: SslErrorHandler,
        error: SslError
    ) 
        Log.d("onReceivedSslError", "onReceivedSslError")
        //super.onReceivedSslError(view, handler, error);
    


inner class UriChromeClient : WebChromeClient() 
    override fun onCreateWindow(
        view: WebView, isDialog: Boolean,
        isUserGesture: Boolean, resultMsg: Message
    ): Boolean 
        mWebviewPop = WebView(mContext)
        mWebviewPop!!.setVerticalScrollBarEnabled(false)
        mWebviewPop!!.setHorizontalScrollBarEnabled(false)
        mWebviewPop!!.setWebViewClient(UriWebViewClient())
        mWebviewPop!!.getSettings().setJavaScriptEnabled(true)
        mWebviewPop!!.getSettings().setSavePassword(false)
        mWebviewPop!!.layoutParams = FrameLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT
        )
        mContainer?.addView(mWebviewPop)
        val transport = resultMsg.obj as WebViewTransport
        transport.webView = mWebviewPop
        resultMsg.sendToTarget()
        return true
    

    override fun onCloseWindow(window: WebView) 
        Log.d("onCloseWindow", "called")
    

【讨论】:

【参考方案2】:

这是一个示例 Android 项目: Github: Android_Popup_Webview_handler_example

这是一个 Android Studio 项目,展示了如何在 Android Webview 中处理弹出窗口。 大多数开源浏览器不支持打开弹出窗口。

弹出窗口在许多网站使用的 OAuth 登录中尤其重要(例如,www.feedly.com)。 此项目中的弹出窗口在对话框中打开,可以通过关闭按钮或按 Back 或如果弹出窗口自行关闭(就像大多数登录身份验证流程中发生的情况一样)关闭。

【讨论】:

Facebook 登录在这种情况下有效。谷歌登录不允许用户代理问题。 @vivek Sinha 这与另一个问题有关。从 2017 年 4 月起,Google 停止支持从 webview 登录。【参考方案3】:

可能不是一个总是可行的答案,但另一个选择是从“弹出然后 JS”样式 OAuth 登录切换到非弹出“redirect_uri”OAUth 样式,将它们发送到登录页面,然后在成功/失败之后它们被发送到完成身份验证的“某些 uri err other”(例如:您自己网站上的另一个页面)。 FWIW。

FWIW facebook 说“如果你正在做 WebView 将它们重定向到https://www.facebook.com/connect/login_success.html”我的直觉是(只有一个 WebView 和)使用 OAuth 登录的情况,所以他们会添加一些身份验证然后您可以收集到 login_success.html 的参数,而不是正常的网络流程...

另一种可能性可能是 override javascript postMessage 函数,这样您就可以捕获 即将返回父窗口的内容。

【讨论】:

【参考方案4】:

我在我的 android 应用程序上遇到了同样的问题。 问题的原因是 FB 登录 javascript 在新窗口中打开新页面。然后它会在登录成功后尝试关闭它并发送一些 javascript 身份验证代码。 WebView 通常是“仅单个窗口”,因此它没有地方可以返回,因此是空白屏幕。

请按照我的工作代码中的流动示例。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
android:background="#0099cc"
tools:context=".MyActivity" 
android:id="@+id/webview_frame">

<WebView
    android:id="@+id/webview"
    android:layout_
    android:layout_
/>

ID 为“webview”的 Webview 是我的内容的主视图。 以下是我的活动代码。

public class MyActivity extends Activity 

/* URL saved to be loaded after fb login */
private static final String target_url="http://www.example.com";
private static final String target_url_prefix="www.example.com";
private Context mContext;
private WebView mWebview;
private WebView mWebviewPop;
private FrameLayout mContainer;
private long mLastBackPressTime = 0;
private Toast mToast;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_urimalo);
    // final View controlsView =
    // findViewById(R.id.fullscreen_content_controls);
    CookieManager cookieManager = CookieManager.getInstance(); 
    cookieManager.setAcceptCookie(true); 
    mWebview = (WebView) findViewById(R.id.webview);
    //mWebviewPop = (WebView) findViewById(R.id.webviewPop);
    mContainer = (FrameLayout) findViewById(R.id.webview_frame);
    WebSettings webSettings = mWebview.getSettings();
    webSettings.setJavaScriptEnabled(true);
    webSettings.setAppCacheEnabled(true);
    webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
    webSettings.setSupportMultipleWindows(true);
    mWebview.setWebViewClient(new UriWebViewClient());
    mWebview.setWebChromeClient(new UriChromeClient());
    mWebview.loadUrl(target_url);

    mContext=this.getApplicationContext();




private class UriWebViewClient extends WebViewClient 
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) 
        String host = Uri.parse(url).getHost();
        //Log.d("shouldOverrideUrlLoading", url);
        if (host.equals(target_url_prefix)) 
        
            // This is my web site, so do not override; let my WebView load
            // the page
            if(mWebviewPop!=null)
            
                mWebviewPop.setVisibility(View.GONE);
                mContainer.removeView(mWebviewPop);
                mWebviewPop=null;
            
            return false;
        

        if(host.equals("m.facebook.com"))
        
            return false;
        
        // Otherwise, the link is not for a page on my site, so launch
        // another Activity that handles URLs
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        startActivity(intent);
        return true;
    

    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler,
            SslError error) 
        Log.d("onReceivedSslError", "onReceivedSslError");
        //super.onReceivedSslError(view, handler, error);
    


class UriChromeClient extends WebChromeClient 

    @Override
    public boolean onCreateWindow(WebView view, boolean isDialog,
            boolean isUserGesture, Message resultMsg) 
        mWebviewPop = new WebView(mContext);
        mWebviewPop.setVerticalScrollBarEnabled(false);
        mWebviewPop.setHorizontalScrollBarEnabled(false);
        mWebviewPop.setWebViewClient(new UriWebViewClient());
        mWebviewPop.getSettings().setJavaScriptEnabled(true);
        mWebviewPop.getSettings().setSavePassword(false);
        mWebviewPop.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        mContainer.addView(mWebviewPop);
        WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
        transport.setWebView(mWebviewPop);
        resultMsg.sendToTarget();

        return true;
    

    @Override
    public void onCloseWindow(WebView window) 
        Log.d("onCloseWindow", "called");
    



这个问题的关键是onCreateWindow。一个新窗口被创建并插入到框架布局中,并在成功时删除。我在 shouldOverrideUrlLoading 添加了删除。

【讨论】:

if 条件应为 host.equals("m.facebook.com") || host.equals("www.facebook.com") 以匹配在重定向到移动版本之前首先加载 www 的情况。 我尝试了同样的方法,但我仍然得到空白页,有什么解决办法吗? 当使用任何包含target="_blank" 的链接时,这给我带来了问题 - mContainer 在某些情况下为空会触发空指针异常。为了解决这个问题,我只需要将以下行:mContainer.addView(mWebviewPop); 替换为:if (mContainer != null) mContainer.addView(mWebviewPop); 我还没有在旧的 Android 设备上测试过,但是在 API 21+ 上你需要添加:if(Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.LOLLIPOP) cookieManager.getInstance().setAcceptThirdPartyCookies(myWebView, true); 我遵循了这段代码。但没有运气。出现FB登录页面。输入凭据并点击登录后,它卡在那里。【参考方案5】:

我的回答与这里的其他人基本相似,因为我创建了第二个WebView 来托管 Facebook 登录页面,而不是尝试通过重定向来解决问题。但是,我选择将登录WebView 放在它自己的Fragment 中,并给它自己专用的WebViewClientWebChromeClient 子类。我认为这样可以更轻松地了解每个组件所扮演的角色,以及哪些对象需要哪些设置和行为。

我还使用WebChromeClient.onCloseWindow() 来检测 Facebook 的 JavaScript 何时想要关闭登录窗口。从不同的答案来看,这比我最初采用的方法要强大得多。

在您的Activity 布局中,您将拥有托管 cmets 的“主要”WebView,以及用于FacebookWebLoginFragment 的容器。登录 Fragment 在需要时即时创建,然后在 Facebook 的登录 JavaScript 请求关闭其窗口时删除。

我的Activity 布局如下所示:

<include layout="@layout/toolbar_common" />

<FrameLayout
    android:id="@+id/main_layout"
    android:layout_
    android:layout_>
    <FrameLayout
        android:id="@+id/web_view_fragment_container"
        android:layout_
        android:layout_
        android:overScrollMode="never"
        />
    <!-- Used for Facebook login associated with comments -->
    <FrameLayout
        android:id="@+id/facebook_web_login_fragment_container"
        android:layout_
        android:layout_
        android:overScrollMode="never"
        android:visibility="gone"
        />
</FrameLayout>

在您的Activity 中,您需要代码来显示和隐藏 Facebook 网络登录片段。我使用 Otto 事件总线,所以我有如下事件处理程序。 (这里没有具体针对这个问题;我包含此代码只是为了让您了解登录 Fragment 如何适合整体结构。)

@Subscribe
public void onShowFacebookWebLoginEvent(ShowFacebookWebLoginEvent event) 
    FacebookWebLoginFragment existingFragment = getFacebookWebLoginFragment();
    if (existingFragment == null) 
        mFacebookWebLoginFragmentContainer.setVisibility(View.VISIBLE);
        createFacebookWebLoginFragment(event);
    


@Subscribe
public void onHideFacebookWebLoginEvent(HideFacebookWebLoginEvent event) 
    FacebookWebLoginFragment existingFragment = getFacebookWebLoginFragment();
    if (existingFragment != null) 
        mFacebookWebLoginFragmentContainer.setVisibility(View.GONE);
        FragmentManager fm = getSupportFragmentManager();
        fm.beginTransaction()
                .remove(existingFragment)
                .commit();
    


@Nullable
private FacebookWebLoginFragment getFacebookWebLoginFragment() 
    FragmentManager fm = getSupportFragmentManager();
    return (FacebookWebLoginFragment) fm.findFragmentById(R.id.facebook_web_login_fragment_container);


private void createFacebookWebLoginFragment(ShowFacebookWebLoginEvent event) 
    FragmentManager fm = getSupportFragmentManager();
    FacebookWebLoginFragment fragment = (FacebookWebLoginFragment) fm.findFragmentById(R.id.facebook_web_login_fragment_container);
    if (fragment == null) 
        fragment = FacebookWebLoginFragment.newInstance(event.getOnCreateWindowResultMessage());
        fm.beginTransaction()
                .add(R.id.facebook_web_login_fragment_container, fragment)
                .commit();
    

FacebookWebLoginFragment 存在时,应该授予它处理设备后退按钮按下的权限。这很重要,因为 Facebook 登录流程包括离开登录页面的导航功能,并且用户希望返回按钮返回登录。所以,在我的Activity,我有这个:

@Override
public void onBackPressed() 
    boolean handled = false;
    FacebookWebLoginFragment facebookWebLoginFragment = getFacebookWebLoginFragment();
    if (facebookWebLoginFragment != null) 
        handled = facebookWebLoginFragment.onBackPressed();
    
    if (!handled) 
        WebViewFragment fragment = getWebViewFragment();
        if (fragment != null) 
            handled = fragment.onBackPressed();
        
    
    if (!handled) 
        finish();
    

FacebookWebLoginFragment 的布局非常简单:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_
             android:layout_>
    <WebView
        android:id="@+id/web_view"
        android:layout_
        android:layout_
        />
</FrameLayout>

这是FacebookWebLoginFragment 代码。请注意,它依赖于WebChromeClient 的子类来检测 Facebook 登录 JavaScript 何时准备好关闭窗口(即删除片段)。另请注意,此登录 WebView 与包含 cmets UI 的主要 WebView 之间没有直接通信;身份验证令牌通过第三方 cookie 传递,这就是为什么您必须确保在主 WebView 上启用第三方 cookie 支持。

import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Bundle;
import android.os.Message;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;

import butterknife.Bind;
import butterknife.ButterKnife;

/**
 * Hosts WebView used by Facebook web login.
 */
public class FacebookWebLoginFragment extends BaseFragment 
    private static final String LOGTAG = LogHelper.getLogTag(FacebookWebLoginFragment.class);

    @Bind(R.id.web_view) WebView mFacebookLoginWebView;

    private WebChromeClient mFacebookLoginWebChromeClient;
    private Message onCreateWindowResultMessage;

    public static FacebookWebLoginFragment newInstance(Message onCreateWindowResultMessage) 
        FacebookWebLoginFragment fragment = new FacebookWebLoginFragment();
        fragment.onCreateWindowResultMessage = onCreateWindowResultMessage;
        return fragment;
    

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) 
        View rootView = inflater.inflate(R.layout.frag_facebook_web_login, container, false);
        ButterKnife.bind(this, rootView);
        return rootView;
    

    @Override
    public void onViewCreated(View v, @Nullable Bundle savedInstanceState) 
        super.onViewCreated(v, savedInstanceState);

        mFacebookLoginWebView.setVerticalScrollBarEnabled(false);
        mFacebookLoginWebView.setHorizontalScrollBarEnabled(false);
        mFacebookLoginWebView.setWebViewClient(new FacebookLoginWebViewClient());
        mFacebookLoginWebView.getSettings().setJavaScriptEnabled(true);
        mFacebookLoginWebView.getSettings().setSavePassword(false);
        mFacebookLoginWebView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));

        mFacebookLoginWebChromeClient = makeFacebookLoginWebChromeClient();
        mFacebookLoginWebView.setWebChromeClient(mFacebookLoginWebChromeClient);

        WebView.WebViewTransport transport = (WebView.WebViewTransport) onCreateWindowResultMessage.obj;
        transport.setWebView(mFacebookLoginWebView);
        onCreateWindowResultMessage.sendToTarget();
        onCreateWindowResultMessage = null; // This seems to eliminate a mysterious crash
    

    @Override
    public void onDestroy() 
        mFacebookLoginWebChromeClient = null;
        super.onDestroy();
    

    /**
     * Performs fragment-specific behavior for back button, and returns true if the back press
     * has been fully handled.
     */
    public boolean onBackPressed() 
            if (mFacebookLoginWebView.canGoBack()) 
                mFacebookLoginWebView.goBack();
             else 
                closeThisFragment();
            
            return true;
    

    private void closeThisFragment() 
        EventBusHelper.post(new HideFacebookWebLoginEvent());
    

    class FacebookLoginWebViewClient extends WebViewClient 
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) 
            // Only allow content from Facebook
            Uri uri = Uri.parse(url);
            String scheme = uri.getScheme();
            if (scheme != null && (TextUtils.equals(scheme, "http") || TextUtils.equals(scheme, "https"))) 
                if (UriHelper.isFacebookHost(uri)) 
                    return false;
                
            
            return true;
        
    

    private WebChromeClient makeFacebookLoginWebChromeClient() 
        return new WebChromeClient() 
            @Override
            public void onCloseWindow(WebView window) 
                closeThisFragment();
            
        ;
    

现在,最棘手的一点是对您现有的WebView 进行必要的更改,因为您可能已经围绕它准备了相当多的代码,并且您需要了解需要做什么改变。

首先,确保您启用了 JavaScript,并且它支持多个窗口。

WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setSupportMultipleWindows(true);

您确实不需要需要致电setJavaScriptCanOpenWindowsAutomatically(true)

查看其他一些答案,您可能认为您需要使用分配给您的WebViewWebViewClient,并覆盖shouldOverrideUrlLoading()。这不是必需的。重要的是WebChromeClient,它需要覆盖onCreateWindow()

所以...接下来,将自定义 WebChromeClient 子类分配给您的 WebView

mWebView.setWebChromeClient(new WebChromeClient() 
    @Override
    public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) 
        String url = null;
        Message href = view.getHandler().obtainMessage();
        if (href != null) 
            view.requestFocusNodeHref(href);
            url = href.getData().getString("url");
        
        LogHelper.d(LOGTAG, "onCreateWindow: " + url);

        // Unfortunately, url is null when "Log In to Post" button is pressed
        if (url == null || UriHelper.isFacebookHost(Uri.parse(url))) 
            // Facebook login requires cookies to be enabled, and on more recent versions
            // of Android, it's also necessary to enable acceptance of 3rd-party cookies
            // on the WebView that hosts Facebook comments
            CookieHelper.setAcceptThirdPartyCookies(mWebView, true);

            EventBusHelper.post(new ShowFacebookWebLoginEvent(resultMsg));
         else 
            LogHelper.d(LOGTAG, "Ignoring request from js to open new window for URL: " + url);
        
        return true;
    
);

您会注意到这是对UriHelper.isFacebookHost() 的第二次调用。我没有万无一失的方法来确定这一点,但这就是我所做的:

public static boolean isFacebookHost(Uri uri) 
    if (uri != null && !TextUtils.isEmpty(uri.getHost())) 
        String host = uri.getHost().toLowerCase();
        return host.endsWith("facebook.com") || host.endsWith("facebook.net");
    
    return false;

您还会注意到对CookieHelper.setAcceptThirdPartyCookies() 的调用。这是代码:

public static void setAcceptThirdPartyCookies(WebView webView, boolean accept) 
    CookieManager cookieManager = CookieManager.getInstance();

    // This is a safeguard, in case you've disabled cookies elsewhere
    if (accept && !cookieManager.acceptCookie()) 
        cookieManager.setAcceptCookie(true);
    
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 
        cookieManager.setAcceptThirdPartyCookies(webView, accept);
    

另一件让一些人感到困惑的事情是 Facebook 开发设置中“有效 OAuth 重定向 URI”的配置。如果您在日志中看到这样的错误:

URL 被阻止:此重定向失败,因为重定向 URI 未在应用的客户端 OAuth 设置中列入白名单。确保客户端和 Web OAuth 登录已打开,并将您的所有应用程序域添加为有效的 OAuth 重定向 URI。

...那么你会想看看这个答案:https://***.com/a/37009374

玩得开心!一个看似非常简单的问题的复杂解决方案。从积极的方面来说,Android 为开发者提供了很大的控制权。

【讨论】:

【参考方案6】:

上面的答案太旧了,它不适用于最新的 Facebook sdk 2.7 版。在花了 4 个小时之后,我发现它的一些变化。以下代码适用于最新的 SDK。

下面提到的是XML布局文件。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
android:background="#0099cc"
tools:context=".MainActivity"
android:id="@+id/webview_frame">

<WebView
    android:id="@+id/webView"
    android:layout_
    android:layout_
    />

</FrameLayout>

这是Android Activity代码sn-p

    public class MainActivity extends AppCompatActivity 

    private WebView webView;
    private WebView mWebviewPop;
    private FrameLayout mContainer;
    private Context mContext;

    private String url = "https://www.YourWebsiteAddress.com";
    private String target_url_prefix = "www.YourWebsiteAddress.com";


    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Get outer container
        mContainer = (FrameLayout) findViewById(R.id.webview_frame);

        webView = (WebView)findViewById(R.id.webView);

        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setDomStorageEnabled(true);
        webSettings.setAppCacheEnabled(true);
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        webSettings.setSupportMultipleWindows(true);

        //These two lines are specific for my need. These are not necessary
        if (Build.VERSION.SDK_INT >= 21) 
            webSettings.setMixedContentMode( WebSettings.MIXED_CONTENT_ALWAYS_ALLOW );
        

        //Cookie manager for the webview
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.setAcceptCookie(true);

        webView.setWebViewClient(new MyCustomWebViewClient());
        webView.setWebChromeClient(new UriWebChromeClient());
        webView.loadUrl("https://www.YourWebsiteAddress.com");


        mContext=this.getApplicationContext();
    


    @Override
    public void onBackPressed() 
        if(webView.isFocused() && webView.canGoBack()) 
            webView.goBack();
         else 
            super.onBackPressed();
        
    

private class MyCustomWebViewClient extends WebViewClient 
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) 
            String host = Uri.parse(url).getHost();

            if( url.startsWith("http:") || url.startsWith("https:") ) 

                if(Uri.parse(url).getPath().equals("/connection-compte.html")) 
                    Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.YourWebsiteAddress.com"));
                    startActivity(browserIntent);

                    return true ;
                

                if (host.equals(target_url_prefix)) 
                    if (mWebviewPop != null) 
                        mWebviewPop.setVisibility(View.GONE);
                        mContainer.removeView(mWebviewPop);
                        mWebviewPop = null;
                    
                    return false;
                
                if (host.equals("m.facebook.com") || host.equals("www.facebook.com") || host.equals("facebook.com")) 
                    return false;
                
                // Otherwise, the link is not for a page on my site, so launch
                // another Activity that handles URLs
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                startActivity(intent);
                return true;
            
            // Otherwise allow the OS to handle it
            else if (url.startsWith("tel:")) 
                Intent tel = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
                startActivity(tel);
                return true;
            
            //This is again specific for my website
            else if (url.startsWith("mailto:")) 
                Intent mail = new Intent(Intent.ACTION_SEND);
                mail.setType("application/octet-stream");
                String AdressMail = new String(url.replace("mailto:" , "")) ;
                mail.putExtra(Intent.EXTRA_EMAIL, new String[] AdressMail );
                mail.putExtra(Intent.EXTRA_SUBJECT, "");
                mail.putExtra(Intent.EXTRA_TEXT, "");
                startActivity(mail);
                return true;
            
            return true;
        

        @Override
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) 
            Log.d("onReceivedSslError", "onReceivedSslError");
            //super.onReceivedSslError(view, handler, error);
        

        @Override
        public void onPageFinished(WebView view, String url) 
            if(url.startsWith("https://m.facebook.com/v2.7/dialog/oauth"))
                if(mWebviewPop!=null)
                
                    mWebviewPop.setVisibility(View.GONE);
                    mContainer.removeView(mWebviewPop);
                    mWebviewPop=null;
                
                view.loadUrl("https://www.YourWebsiteAddress.com");
                return;
            
            super.onPageFinished(view, url);
        
    

private class UriWebChromeClient extends WebChromeClient 

        @Override
        public boolean onCreateWindow(WebView view, boolean isDialog,
                                      boolean isUserGesture, Message resultMsg) 
            mWebviewPop = new WebView(mContext);
            mWebviewPop.setVerticalScrollBarEnabled(false);
            mWebviewPop.setHorizontalScrollBarEnabled(false);
            mWebviewPop.setWebViewClient(new MyCustomWebViewClient());
            mWebviewPop.getSettings().setJavaScriptEnabled(true);
            mWebviewPop.getSettings().setSavePassword(false);
            mWebviewPop.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT));
            mContainer.addView(mWebviewPop);
            WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
            transport.setWebView(mWebviewPop);
            resultMsg.sendToTarget();

            return true;
        

        @Override
        public void onCloseWindow(WebView window) 
            Log.d("onCloseWindow", "called");
        

    


【讨论】:

你能解释一下/connection-compte.html吗?从 facebook 登录成功后,您将用户发送到这里吗?【参考方案7】:

尝试将您的 webview 移动到 xml 布局文件。空白页错误是由于将 oAuth 登录重定向到授权接受页面时 js 脚本失败导致的。您可以通过将 webview 移动到 xml 布局来解决此问题。

【讨论】:

【参考方案8】:

在您的WebViewClient 中覆盖shouldOverrideUrlLoading()。搜索shouldOverrideUrlLoadinghere。此外,还有一个参数可以传递给 facebook 的登录 API;我认为它是redirect_uri。这应该可以帮助您识别登录何时成功并且在您的shouldOVerrideUrlLoading() 中,您只需要检测正在加载的 url,如果它是您指定的 redirect_uri,那么只需在该方法中返回 true 并关闭 webview 或任何您想登录成功的时候。

【讨论】:

【参考方案9】:

关于the best answer到this question,只需要实现你使用的WebViewClient类的onPageFinished方法即可。

public void onPageFinished(WebView view, String url) 
    // First, get the URL that Facebook's login button is actually redirecting you to.
    // It should be something simulator to https://www.facebook.com/dialog/return/arbiter?relation=opener&close=true
    String webUrl = webView.getUrl();
    // Pass it to the LogCat so that you can then use it in the if statement below.
    Log.d.println(TAG, webUrl);

    if (url.startsWith("The url that you copied from the LogCat")) 
        // Check whether the current URL is the URL that Facebook's redirecting you to.
        // If it is - that's it - do what you want to after the logging process has finished.
        return;
    

    super.onPageFinished(view, url);
 

对我来说效果很好。希望对你也有帮助:)

【讨论】:

以上是关于使 facebook 登录与 Android Webview 一起工作的主要内容,如果未能解决你的问题,请参考以下文章

使用 xmpp 的 Android-facebook 聊天客户端

与 MethodChannel 一起使用的自定义 Facebook 登录

令牌过期后的Android Facebook登录

Android Asmack xmpp 登录仅适用于我自己的 Facebook 帐户

Android Facebook 与无效密钥哈希的集成

在 Android Studio 中加载 Facebook 登录按钮时出现异常