使Facebooklogin与Android Web视图一起工作

我只是试图在Android的WebView上实现Facebooklogin。 问题是我点击了我的HTML页面上的Facebookbutton,然后在Facebook对话框中input用户名和密码。 urlredirect只是给我一个黑色的页面。

@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,当点击facebookbutton时,这个函数被调用。

 $("#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对话框页面redirect后得到回应的想法? 谢谢。

我的android应用程序有同样的问题。 问题的原因是FBloginJavaScript在新窗口打开一个新的页面。 然后尝试closures它,并在login成功后返回一些JavaScript授权码。 WebView通常是“单一窗口”,所以它没有地方回去,因此黑屏。

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

 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#0099cc" tools:context=".MyActivity" android:id="@+id/webview_frame"> <WebView android:id="@+id/webview" android:layout_width="fill_parent" android:layout_height="fill_parent" /> 

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。 一个新的窗口被创build并被插入到框架布局中,并在成功时被移除。 我在shouldOverrideUrlLoading添加了删除。

关于这个问题 的最佳答案 ,你只需要实现你使用的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); } 

它对我来说工作得很好。 希望它也可以帮助你:)

在您的WebViewClient覆盖shouldOverrideUrlLoading() 。 在这里searchshouldOverrideUrlLoading 。 此外,还有一个参数可以传递给Facebook的loginAPI; 我认为这是redirect_uri。 这应该会帮助你识别login成功的时候,在你的shouldOVerrideUrlLoading() ,你只需要检测被加载的url,如果它是你指定的redirect_uri,那么只需在该方法中返回true,closureswebview或任何你想要在login成功的时候。

以上答案太旧了,它不适用于最新的Facebook SDK版本2.7。 花了四个小时后,我发现它几乎没有变化。 以下代码可以在最新的SDK下正常工作。

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

 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#0099cc" tools:context=".MainActivity" android:id="@+id/webview_frame"> <WebView android:id="@+id/webView" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </FrameLayout> 

这是Android Activity代码片段

  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"); } } } 

尝试移动你的webview到XML布局文件。 空白页错误是由于js脚本失败而导致oAuthloginredirect到授权接受页面。 你可以通过将你的webview移动到xml布局来解决这个问题。

我的回答基本上与这里的其他一些相似,因为我创build了第二个WebView来托pipeFacebooklogin页面,而不是尝试用redirect来解决问题。 但是,我select将loginWebView放在自己的Fragment ,并赋予它自己的专用WebViewClientWebChromeClient子类。 我认为这使得查看每个组件扮演的angular色以及哪些对象需要哪些设置和行为更容易一些。

我也利用WebChromeClient.onCloseWindow()来检测Facebook的JavaScript何时想closureslogin窗口。 这比我原先追求的方法更强大,从另一个不同的答案。

在您的Activity布局中,您将拥有托pipe注释的“主” WebViewFacebookWebLoginFragment的容器。 loginFragment是在需要时即时创build的,然后在Facebook的loginJavaScript请求closures其窗口时被删除。

我的Activity布局看起来像这样:

 <include layout="@layout/toolbar_common" /> <FrameLayout android:id="@+id/main_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:id="@+id/web_view_fragment_container" android:layout_width="match_parent" android:layout_height="match_parent" android:overScrollMode="never" /> <!-- Used for Facebook login associated with comments --> <FrameLayout android:id="@+id/facebook_web_login_fragment_container" android:layout_width="match_parent" android:layout_height="match_parent" android:overScrollMode="never" android:visibility="gone" /> </FrameLayout> 

在您的Activity ,您需要显示和隐藏Facebooknetworkinglogin片段的代码。 我使用Otto事件总线,所以我有如下的事件处理程序。 (这里没有什么特别的问题,我只是为了给你一个关于login 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附近时,应该有权处理设备后退button。 这一点很重要,因为Facebookloginstream程包含了离开login页面的导航function,用户可以期待后退button返回login页面。 所以,在我的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_width="match_parent" android:layout_height="match_parent"> <WebView android:id="@+id/web_view" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> 

这是FacebookWebLoginFragment代码。 请注意,它依靠WebChromeClient的子类来检测FacebookloginJavaScript何时准备closures窗口(即删除片段)。 另请注意,此loginWebView与主WebView (包含注释UI)之间没有直接通信; 身份validation令牌通过第三方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开发设置中configuration了“有效的OAuthredirectURI”。 如果您在日志中看到类似这样的错误:

url已阻止:此redirect失败,因为redirectURI未在应用的客户端OAuth设置中列入白名单。 确保客户端和Web OAuthlogin处于打开状态,并将所有应用程序域添加为有效的OAuthredirectURI。

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

玩的开心! 一个复杂的解决scheme,似乎是一个非常简单的问题。 从积极的方面来看,Android已经给了开发人员很多的控制权。

可能不是一个永远可行的答案,但另一种select是从“popup然后JS”样式OAuthlogin切换到非popup“redirect_uri”OAUth样式,在那里它们发送到login页面,然后成功/失败后,他们得到发送到“一些其他”(例如:在你自己的网站上的另一页),完成authentication。 FWIW。

FWIW在Facebook说:“如果你正在做WebViewredirect到https://www.facebook.com/connect/login_success.html ”我的直觉是,这是为了(只有一个单一的WebView和)使用OAuthlogin,所以他们会添加一些身份validation参数login_success.html,然后你可以收集,所以不是正常的networkingstream量…

另一种可能是重写 javascript postMessage函数,以便可以捕获返回到父窗口的内容。