【发布时间】:2014-08-17 20:52:53
【问题描述】:
我正在尝试获取 WebView 的屏幕截图,但是,WebView#getDrawingCache(true) 是一个昂贵的调用并且会冻结 UI 线程。
尽管如此,任何将其移至 AsyncTask 或单独线程的尝试都会导致“必须在 UI 线程上调用所有 WebView 方法”错误。
是否有解决方法,这样我就可以在后台线程中处理 WebView#getDrawingCache(true) 而不会冻结 UI 线程?
这是一个崩溃的代码示例
public class TouchTask extends AsyncTask<Object, Void, Void> {
Runnable completionCallback;
@Override
protected Void doInBackground(Object... objects) {
Integer id = (Integer) objects[0];
completionCallback = (Runnable) objects[1];
touch(id);
return null;
}
@Override
protected void onPostExecute(Void res) {
super.onPostExecute(res);
completionCallback.run();
}
}
/*
Updates the webview's bitmap in cache
*/
public Pair<String, Bitmap> touch(Integer id) {
CustomWebView webView = getWebView(id);
String title = webView.getTitle();
Bitmap screenie = getScreenshotFromWebView(webView);
Pair<String, Bitmap> res = new Pair<String, Bitmap>(title, screenie);
bitmapCache.put(id, res);
return res;
}
public void touchAsync(Integer id, Runnable callback) {
new TouchTask().execute(id, callback);
}
public Bitmap getScreenshotFromWebView(WebView webView) {
webView.setDrawingCacheEnabled(true);
webView.buildDrawingCache(true);
Bitmap screenieBitmap = null;
if ((webView.getDrawingCache(true) != null) && (!webView.getDrawingCache(true).isRecycled())) {
screenieBitmap = webView.getDrawingCache(false);
screenieBitmap = compressScreenshot(webView.getDrawingCache(false));
}
return screenieBitmap;
}
public Bitmap compressScreenshot(Bitmap screenie) {
Display display = activity.getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
return Bitmap.createScaledBitmap(screenie, width / 3, height / 3, true);
}
下面是运行代码:
new TouchTask().execute(id, callback);
这是错误
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:300)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.RuntimeException: java.lang.Throwable: A WebView method was called on thread 'AsyncTask #3'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {16dba051} called on null, FYI main Looper is Looper (main, tid 1) {16dba051})
at android.webkit.WebView.checkThread(WebView.java:2185)
at android.webkit.WebView.getTitle(WebView.java:1321)
at com.nubelacorp.javelin.activities.helpers.browseractivity.TabManager.touch(TabManager.java:53)
at com.nubelacorp.javelin.activities.helpers.browseractivity.TabManager$TouchTask.doInBackground(TabManager.java:145)
at com.nubelacorp.javelin.activities.helpers.browseractivity.TabManager$TouchTask.doInBackground(TabManager.java:138)
at android.os.AsyncTask$2.call(AsyncTask.java:288)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.Throwable: A WebView method was called on thread 'AsyncTask #3'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {16dba051} called on null, FYI main Looper is Looper (main, tid 1) {16dba051})
at android.webkit.WebView.checkThread(WebView.java:2175)
at android.webkit.WebView.getTitle(WebView.java:1321)
at com.nubelacorp.javelin.activities.helpers.browseractivity.TabManager.touch(TabManager.java:53)
at com.nubelacorp.javelin.activities.helpers.browseractivity.TabManager$TouchTask.doInBackground(TabManager.java:145)
at com.nubelacorp.javelin.activities.helpers.browseractivity.TabManager$TouchTask.doInBackground(TabManager.java:138)
at android.os.AsyncTask$2.call(AsyncTask.java:288)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
【问题讨论】:
-
你可以在非UI线程中调用getDrawingCache(true)
-
@pskink 你确定吗?我相信我试过了,我得到了一个错误
-
我试过了,我没有收到错误
-
@pskink 你能展示你使用的代码吗?我用一个崩溃的异步任务示例代码更新了我的问题。
-
这很好,旧线程覆盖了 run() 方法...