nub*_*ela 9 android android-webview
我正在尝试抓取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);
}
Run Code Online (Sandbox Code Playgroud)
这是运行代码:
new TouchTask().execute(id, callback);
Run Code Online (Sandbox Code Playgroud)
这是错误
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)
Run Code Online (Sandbox Code Playgroud)
要从后台线程获取屏幕截图,您必须使用其他方法.
我们将创建一个Canvas由a 支持Bitmap,然后询问WebView,或者持有WebView(将讨论你以后需要它的原因)的容器,在这个画布上绘制自己.最后,所Bitmap使用的Canvas将保留所需的屏幕截图.然后可以将其刷新到存储.
我已修改你AsyncTask的代表裸骨实现.目前,它只显示Bitmap正在doInBackground(...)方法内部生成- 关闭主线程.
public class TouchTask extends AsyncTask<Object, Void, Bitmap> {
@Override
protected Bitmap doInBackground(Object... objects) {
// Create a new Bitmap with dimensions of the WebView
Bitmap b = Bitmap.createBitmap(webView.getWidth(),
webView.getHeight(), Bitmap.Config.ARGB_8888);
// Canvas that is backed by the `Bitmap` and used by webView to draw on
Canvas c = new Canvas(b);
// WebView draws itself
webView.draw(c);
// Return bitmap
// OR: Compress the bitmap here itself
// No need to do that in `onPostExecute(...)`
return b;
}
// There's no need to actually do anything inside `onPostExecute(..)`
// Compressing and saving the bitmap to sdcard can be handled
// in `doInBackground(...)` itself
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (bitmap != null) {
FileOutputStream fos;
try {
fos = new FileOutputStream(new File("/sdcard/webview_screenshot.png"));
bitmap.compress(CompressFormat.PNG, 100, fos);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
在测试时,我还发现这种方法比使用时间少getDrawingCache().我还没有试过找到原因.
...or the container holding the WebView(will discuss why you would need this later)...
考虑WebView滚动时的场景.如果您使用上述方法,获得的屏幕截图将不准确 - 屏幕截图将显示WebView滚动为0- 顶部和左侧对齐.要WebView在滚动状态下捕获偶数,请将其放在容器中 - 比方说Framelayout.除了以下更改之外,该过程仍然类似:我们将FrameLayout's在创建位图时使用宽度和高度:
Bitmap b = Bitmap.createBitmap(frameLayout.getWidth(),
frameLayout.getHeight(), Bitmap.Config.ARGB_8888);
Run Code Online (Sandbox Code Playgroud)
WebView我们不会问问,而是要求FrameLayout自己画画:
frameLayout.draw(c);
Run Code Online (Sandbox Code Playgroud)
这应该做到这一点.
| 归档时间: |
|
| 查看次数: |
2103 次 |
| 最近记录: |