StephenChen
StephenChen

Reputation: 773

Android- Webview onPageFinished Called Twice

I have an activity that does OAuth authentication by intercepting the redirect url once it show up in the webview. However, the onPageFinished function is somehow called twice for some reason, which really messes up my application. Here's the code:

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...}

P.S. Please don't mark this as a duplicate...I've read a similar question on StackOverflow and calling ShouldOverrideUrlLoading does not work for me(which is why I used onPageFinished() in the first place).

Upvotes: 43

Views: 38808

Answers (9)

Samet Sarıyıldız
Samet Sarıyıldız

Reputation: 21

The solution best way;

Problem --> Android device onPagefinished calling twice. (At the IOS device has not problem)

Solution --> Before OnLoadStop You can Call onLoadStart method that define bool param in this section.

  • onLoadStart: (controller,url) async{ //bool param define this section },

  • onLoadStop: (controller, url) async { if (bool param) { // than controlenter code here } }

Upvotes: 0

Stan
Stan

Reputation: 6571

Here is one more workaround (not the best one). Since (in my case) second time it triggers very fast, I assumed I can rely on System.currentTimeMillis() like:

    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()
    }
}

this workaround prevents any other (short timed, 1000ms) chained triggerings and it calls parsePage() from the first trigger. Anyway, complex webpages/websites won't be ready on onPageFinished() trigger due to javascripts (counters/frames/web-api-calls/etc) running. Usually, if you are going to parse the page you should wait some time after onPageFinished() triggers.

Upvotes: 0

6rchid
6rchid

Reputation: 1302

Since onPageFinished is being called more than once, you can simply check if the method is already called before executing anything further.

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

     // Do something...

     loaded = true;
}

Upvotes: 1

thegr8
thegr8

Reputation: 431

Maybe it helps someone.

I have the some problem - method onPageFinished called twice.

webView.getProgress();

at the first execution webView.getProgress() == 89, and at the second == 100. 100 means page loading complete.

Upvotes: 33

Teng-pao Yu
Teng-pao Yu

Reputation: 1443

I'm looking into an event where m.yotube.com triggers two onPageFinished events but it does not seem like caused by redirection to me. After some studies I found that there is one extra onPageFinished triggered by didFinishNavigation before the one triggered by didStopLoading which other pages also receives.

stack trace #1 stack trace #2

See also:

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

Another instance I receive extra onPageFinished calls (even before onPageStarted!) is when I utilize webview.restoreState() in Fragments. it fires two onPageFinished events when trying to resume last viewed page.

Upvotes: 3

Balazs F.
Balazs F.

Reputation: 410

So, none of the above answers gave me a solution, because my problem wasn't redirecting, the webview simply called onPageFinished 3 times on the same webpage. My solution is as below, the main idea is to perform your functions on the same url only once.

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

Upvotes: 9

Abhishek
Abhishek

Reputation: 3398

This trick helps me(Not Recommended But Helps)

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

Upvotes: 1

Samvel Kartashyan
Samvel Kartashyan

Reputation: 720

If the url is OK after onPageStarted method starts onPageFinished, but if the url is redirected after onPageStarted starts shouldOverrideUrlLoading and then onPageFinished. You should just check if the loading URL is redirected or not

private boolean isRedirected;

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

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

  isRedirected = false;
}

If the URL is redirected the callback starts this function

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

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

Before doing something in onPageFinished check if the callback has entered into shouldOverrideUrlLoading method

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

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

Upvotes: 36

StephenChen
StephenChen

Reputation: 773

Android for some reason calls onPageFinished() twice(and onPageStarted() three times!) when the loaded url is not a working one. The temporary solution is changing the redirect_uri to the url of a working website; in this case, I changed it to https://www.google.com/ (lol, sorry Google). onPageFinished is then only called once.

BUT- I do still want answers on why webview behaves differently when the loaded url is not a working one, and what is a better solution than changing the redirect_uri to google.com.

Upvotes: 2

Related Questions