32

我有一个活动通过在 Web 视图中显示后拦截重定向 URL 来进行 OAuth 身份验证。但是,由于某种原因,onPageFinished 函数被调用了两次,这真的弄乱了我的应用程序。这是代码:

public class WebViewActivity extends Activity {
private WebView gWebView;
final String REDIRECT_URI = "https://localhost:5000/receive_code";
final String CLIENT_ID = "can't post it here";
final String CLIENT_SECRET = "can't post it here";
final String SCOPE = "basic names genomes analyses";
Hashtable<String, String> riskPairs;

public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.webview);

    gWebView = (WebView) findViewById(R.id.webView1);

    gWebView.loadUrl("https://api.23andme.com/authorize/?redirect_uri="
            + REDIRECT_URI + "&response_type=code&client_id=" + CLIENT_ID
            + "&scope=" + SCOPE);

    Log.d("WEBVIEW", "got to webpage");

    gWebView.setWebViewClient(new WebViewClient() {

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            if (url.startsWith(REDIRECT_URI)) {
                Log.d("WEBVIEW", "onpagefinished is called");
                System.out.println("got to override");
                if (url.indexOf("code=") != -1) {
                    //if the query contains code
                    String queryString = null;
                    try {
                        queryString = new URL(url).getQuery();
                    } catch (MalformedURLException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println(queryString);
                    String[] params = queryString.split("&");
                    String code = null;
                    for (String param : params) {
                        if (param.startsWith("code=")) {
                            code = param.substring(param.indexOf('=') + 1);
                        }
                    }
                    gWebView.setVisibility(View.GONE);
                    new PostRequest().execute(code);
                    // don't go to redirectUri
                }
            }
        }
    });


}
class PostRequest extends AsyncTask<String,Void,String>{ code getting client data...}

PS请不要将此标记为重复...我在 StackOverflow 上阅读了一个类似的问题,并且调用 ShouldOverrideUrlLoading 对我不起作用(这就是我首先使用 onPageFinished() 的原因)。

4

8 回答 8

35

如果 onPageStarted 方法启动 onPageFinished 后 url 正常,但如果 onPageStarted 启动后 url 重定向 shouldOverrideUrlLoading 然后 onPageFinished。您应该只检查加载 URL 是否被重定向

private boolean isRedirected;

@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {   

  if (!isRedirected) {      
    //Do something you want when starts loading
  }

  isRedirected = false;
}

如果 URL 被重定向,回调将启动此函数

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {

  view.loadUrl(url);
  isRedirected = true;
  return true;
}

在 onPageFinished 做某事之前检查回调是否进入了 shouldOverrideUrlLoading 方法

@Override
public void onPageFinished(WebView view, String url) {

  if (!isRedirected) {
    //Do something you want when finished loading 
  }
}
于 2014-08-28T11:18:54.970 回答
15

也许它可以帮助某人。

我有一些问题 - 方法 onPageFinished 调用了两次。

webView.getProgress();

第一次执行 webView.getProgress() == 89,第二次执行 == 100。100 表示页面加载完成。

于 2020-02-20T21:27:08.407 回答
8

所以,以上答案都没有给我一个解决方案,因为我的问题不是重定向,webview 在同一个网页上简单地调用了 onPageFinished 3 次。我的解决方案如下,主要思想是在同一个 url 上只执行一次你的功能。

private String urlFinished = " ";

@Override
public void onPageFinished(WebView view, String url) {

    Logger.d(TAG, "onPageFinished");

    if (!urlFinished.equals(url)) {
      //Place your code here
    }

    urlFinished = url;

    super.onPageFinished(view, url);
}
于 2018-08-20T15:02:41.437 回答
3

我正在调查一个事件,其中 m.yotube.com 触发了两个 onPageFinished 事件,但它似乎不是由重定向到我引起的。经过一些研究,我发现有一个额外的 onPageFinished 由 didFinishNavigation 触发,然后是由 didStopLoading 触发的其他页面也收到。

堆栈跟踪 #1 堆栈跟踪 #2

也可以看看:

https://chromium.googlesource.com/chromium/src.git/+/master/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java

    @Override
    public void didFinishNavigation(final String url, boolean isInMainFrame, boolean isErrorPage,
            boolean hasCommitted, boolean isSameDocument, boolean isFragmentNavigation,
            Integer pageTransition, int errorCode, String errorDescription, int httpStatusCode) {
        ...
        if (client != null && isFragmentNavigation) {
            client.getCallbackHelper().postOnPageFinished(url);
        }
    }

    @Override
    public void didStopLoading(String validatedUrl) {
        if (validatedUrl.length() == 0) validatedUrl = ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL;
            AwContentsClient client = getClientIfNeedToFireCallback(validatedUrl);
        if (client != null && validatedUrl.equals(mLastDidFinishLoadUrl)) {
            client.getCallbackHelper().postOnPageFinished(validatedUrl);
        mLastDidFinishLoadUrl = null;
        }
    }

我收到额外的 onPageFinished 调用的另一个实例(甚至在 onPageStarted 之前!)是当我在 Fragments 中使用 webview.restoreState() 时。尝试恢复上次查看的页面时,它会触发两个 onPageFinished 事件。

于 2017-09-19T10:37:43.057 回答
2

当加载的 url 不工作时,Android 出于某种原因调用 onPageFinished() 两次(和 onPageStarted() 三次!)。临时解决方案是将redirect_uri更改为工作网站的url;在这种情况下,我将其更改为https://www.google.com/(哈哈,对不起 Google)。然后 onPageFinished 只被调用一次。

但是-我仍然想知道为什么当加载的 url 不能正常工作时 webview 的行为会有所不同,还有什么比将 redirect_uri 更改为 google.com 更好的解决方案。

于 2013-08-18T19:53:10.510 回答
1

这个技巧对我有帮助(不推荐但有帮助)

private boolean alreadyEvaluated = false;

    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {

        Logger.d(TAG, "onPageStarted");

        super.onPageStarted(view, url, favicon);
    }

    @Override
    public void onPageFinished(WebView view, String url) {

        Logger.d(TAG, "onPageFinished");

        if (!alreadyEvaluated) {
            alreadyEvaluated = true;
            view.loadUrl("javascript:window.MyJavaScript.getPageText(document.getElementsByTagName('body')[0].innerText);");
        } else {
            alreadyEvaluated = false;
        }

        super.onPageFinished(view, url);
    }
于 2017-08-06T05:00:52.207 回答
1

由于 onPageFinished 被多次调用,您可以在进一步执行任何操作之前检查该方法是否已被调用。

@Override
public void onPageFinished(WebView view, String url) {
     if (loaded) {
          return;
     }

     // Do something...

     loaded = true;
}
于 2020-04-02T00:35:15.200 回答
0

这是另一种解决方法(不是最好的解决方法)。由于(在我的情况下)第二次触发非常快,我认为我可以依靠System.currentTimeMillis()

    webView.webViewClient = object : WebViewClient() {
    private var recentPageFinish = 0L
    override fun onPageFinished(webview: WebView, url: String) {
        if (System.currentTimeMillis() - recentPageFinish > 1000) {
            parsePage(webview)
        }
        recentPageFinish = System.currentTimeMillis()
    }
}

此解决方法可防止任何其他(短时间,1000 毫秒)链式触发,并从第一个触发器调用 parsePage()。onPageFinished()无论如何,由于 javascripts (counters/frames/web-api-calls/etc) 正在运行,复杂的网页/网站不会在触发时准备好。通常,如果您要解析页面,您应该在onPageFinished()触发后等待一段时间。

于 2021-09-14T14:11:34.840 回答