16

I want a HTML/javascript application, running in a WebView to make AJAX calls that are handled by the Java code.
Ideal would be to just intercept the call (easy, just use shouldOverrideUrlLoading()) and 'return' some data.
However, I don't find a way to 'return' a response to the WebView, other than calling a javascript function using loadUrl().
This will not work for me, as the HTML/javascript app is a drop-in application which I don't control. As far as the HTML/javascript app concerns, it just does an AJAX call and receives some data back.

Any thoughts on this?

S1LENT WARRIOR
  • 11,704
  • 4
  • 46
  • 60
Erik
  • 5,681
  • 8
  • 31
  • 32

4 Answers4

35

Good news everyone: With API level 11, they put in the shouldInterceptRequest method into the WebViewClient class. It also fires on requests the application inside the WebView triggers. You can override it as follows:

@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url)
{
    if (magicallyMatch(url))
        return new WebResourceResponse("application/json", "utf-8", magicallyGetSomeInputStream());

    return null;
}

From the Android Reference:

public WebResourceResponse shouldInterceptRequest (WebView view, String url)

Since: API Level 11

Notify the host application of a resource request and allow the application to return the data. If the return value is null, the WebView will continue to load the resource as usual. Otherwise, the return response and data will be used. NOTE: This method is called by the network thread so clients should exercise caution when accessing private data.

Parameters

view The WebView that is requesting the resource.

url The raw url of the resource.

Returns

A WebResourceResponse containing the response information or null if the WebView should load the resource itself.

Also check WebResourceResponse.

Hope this helps.

Nappy
  • 3,016
  • 27
  • 39
13

You can use the JavascriptInterface to intercept the AJAX calls along with JQuery methods ajaxStart and ajaxComplete in following way:

// our JavascriptInterface
public class AjaxHandler {

    private static final String TAG = "AjaxHandler";
    private final Context context;

    public AjaxHandler(Context context) {
        this.context = context;
    }

    public void ajaxBegin() {
        Log.w(TAG, "AJAX Begin");
        Toast.makeText(context, "AJAX Begin", Toast.LENGTH_SHORT).show();
    }

    public void ajaxDone() {
        Log.w(TAG, "AJAX Done");
        Toast.makeText(context, "AJAX Done", Toast.LENGTH_SHORT).show();
    }
}

And here is how the AjaxHandler is used in Activity:

    public class MainActivity extends Activity {

    private static final String TAG = "MainActivity";

    private WebView webView;

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

        setContentView(R.layout.main);

        // get web view
        webView = (WebView) findViewById(R.id.web); 

        // configure web view 
        final WebSettings webSettings = webView.getSettings();
        webSettings.setBuiltInZoomControls(true);
        webSettings.setJavaScriptEnabled(true);

        webView.loadUrl("http://foo.com");

        // add javascript interface
        webView.addJavascriptInterface(new AjaxHandler(this), "ajaxHandler");

        // override onPageFinished method of WebViewClient to handle AJAX calls 
        webView.setWebViewClient(new WebViewClient() {

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

                    view.loadUrl("javascript:$(document).ajaxStart(function (event, request, settings) { " +
                            "ajaxHandler.ajaxBegin(); " + // Event called when an AJAX call begins
                            "});");
                    view.loadUrl("javascript:$(document).ajaxComplete(function (event, request, settings) { " +
                            "ajaxHandler.ajaxDone(); " + // Event called when an AJAX call ends
                            "});");

            });
    }
}

The main idea is taken from here and presented with some tweaks.
Although its a little late to submit an answer but hope this helps others as well!

S1LENT WARRIOR
  • 11,704
  • 4
  • 46
  • 60
  • It's great solution. But how can I get the request parameters in AjaxHandler from the js code? – b-boy sh1ft Jul 06 '15 at 10:13
  • 1
    Great solution, thanks. Also, I just want to let know that ajaxStop could be better in this case because is triggered when the ajax actually stops loading. source: http://stackoverflow.com/a/4421115/706833 – Luis Pereira Aug 03 '15 at 17:09
  • @S1LENT WARRIOR I put code as it is but any how Toast not show – Mansukh Ahir Apr 16 '16 at 04:20
  • can get ajax response after send form data? form webpage display in webview from remote url – Mansukh Ahir Apr 16 '16 at 07:21
  • 1
    This works great! However I had to decorate the local ajaxDone and ajaxBegin methods with the attribute @JavascriptInterface as described here: https://developer.android.com/reference/android/webkit/WebView.html – Fredrik Johansson Feb 09 '18 at 10:58
  • This code is not clear, I copied it and not works !!, Can you please make a project on gitHub – A. Albrg Jun 09 '20 at 16:09
0

In the end I used the method described in the question anyway;

I intercepted some specific requests like: foo://bar/get/1?callback=foobar then parsed the URL and called a javascript callback function with the actual data.
The callback function was defined in the query-part of the URL, so in the above example that would be: foobar();

Calling that function was easy, just use: yourWebView.loadUrl("javascript:foobar('somedata')");

S1LENT WARRIOR
  • 11,704
  • 4
  • 46
  • 60
Erik
  • 5,681
  • 8
  • 31
  • 32
  • 1
    Erik can u please elaborate, how u have overloadedURL for the AJAX request handling and also tell me how to handle the AJAX request from Java.. also i dun know the call intercept mechanism... i m trapped in same problem ... Help needed.. Thanks – jyotin Mar 30 '11 at 06:48
0

The year is 2023, and I faced the same problem.

I solved it using the shouldInterceptRequest() method. The hack is to filter through the requests that are being intercepted and act accordingly.

shouldInterceptRequest() method intercepts all requests that are made on your WebView. In my case, I know the exact requests that are being made and in that case, the exact URLs associated with the requests.

@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {

  String sURLPatternExpected = "";
  if (request.getUrl().toString().contains(sURLExpected)) {

    String sExactURLExpected = "";
    if (!request.getUrl().toString().equals(sExactURLExpected)) {
      //  User is going outside your expected URL.
    } else {
      //  User is going outside your expected URL.
    }

  }

  return null;
}

In the above snippet, you first check whether a request URL matches a pattern you're looking for. For instance, if you're interested in URLs like http://example.com/app/resource?message=hello, filtering in the above way, will prevent you from actioning on URLs like http://example.com/favicon.ico.

Next thing would be to now check for the exact URLs you expect in the filtered group of URLs.

Finally, I return null to let the WebView handle the request normally.

Edward Quixote
  • 350
  • 5
  • 16