I've built an headless webview in an Android application for scrape an URL from a webpage. Every time I retrieve an URL I may need to redoing the scraping in the webpage with this URL. I'm using RxJava to handle these operations concurrently, and I'm using the flatMap
function to make a recursive call.
The problem is that I need to dispose the WebView in the mainThread, so I tried to add .unsubscribeOn(AndroidSchedulers.mainThread())
but it seems it doesn't work, and the dispose()
method in HeadlessRequest
is called in the last thread I called observeOn(Schedulers.computation())
. What should I change to execute the dispose()
method in the mainThread?
This is my code:
HeadlessRequest
public class HeadlessRequest implements Disposable {
...
private class HeadlessWebView extends WebView {
...
private void destroyWebView() {
this.removeAllViews();
this.clearCache(false);
this.loadUrl("about:blank");
this.onPause();
this.removeAllViews();
this.destroy();
this.isDisposed = true;
}
}
@Override
public void dispose() {
// This doesn't print the mainThread id
Log.d(TAG, "Disposing on thread " + Thread.currentThread().getId());
this.webView.destroyWebView();
this.webView = null;
}
@Override
public boolean isDisposed() {
return (this.webView == null || this.webView.isDisposed);
}
}
NetworkUtils
public static Single<Document> downloadPageHeadless(final String url, final int delay, final Context context) {
return Single.create((SingleEmitter<Document> emitter) -> {
try {
emitter.setDisposable(new HeadlessRequest(url, USER_AGENT, delay, context, emitter::onSuccess, emitter::onError));
} catch (Exception e) {
emitter.onError(e);
}
}).unsubscribeOn(AndroidSchedulers.mainThread()) // It MUST be executed on the mainThread
.subscribeOn(AndroidSchedulers.mainThread());
}
ServerService
private static Single<String> resolveRecursive(String url, Context context) {
Server server = getServerInstance(url, context);
if (server == null) {
return Single.error(new UnsupportedOperationException("Server for " + url + " not supported"));
} else if (server.isVideo()) {
return server.resolve(url, context); // This method return a Single with observeOn(Schedulers.computation())
} else {
return server.resolve(url, context)
.observeOn(Schedulers.computation())
.flatMap(resolvedUrl -> resolveRecursive(resolvedUrl, context));
}
}
public static Single<String> resolveURL(String url, Context context) {
return resolveRecursive(url, context)
.observeOn(AndroidSchedulers.mainThread());
}
At the end I found another method to dispose the webview in the mainThread without RxJava. I've used the post
method of the WebView.
private void destroyWebView() {
this.post(() -> {
this.removeAllViews();
this.clearCache(false);
this.loadUrl("about:blank");
this.onPause();
this.removeAllViews();
this.destroy();
this.isDisposed = true;
});
}