近日,又在简网的工程上面发现了一个使用上的错误而导致的ANR:
private void asyncLoadPageImage(final List<String> imageUrls) {
Log.d(TAG, "asyncLoadPageImage");
if (zhiyueApplication.isArticleNeedImage() && imageUrls != null && imageUrls.size() > 0) {
Log.d(TAG, "asyncLoadPageImage size = " + imageUrls.size());
new Handler().postDelayed(new Runnable() {
public void run() {
imageLoader.load(imageUrls, new ArticleImageLoader.Callback() {
public void finished(boolean success, String url, File localFile) {
if (success && webView != null) {
webView.getSettings().setLoadsImagesAutomatically(true);
resetImageUrls.remove(url);
String localImageUrl = "file://" + localFile.getAbsolutePath();
String md5 = MD5String.getMD5(localImageUrl);
webView.loadUrl(HtmlBuilder.buildchangeImageSrcScript(md5, localImageUrl));
}
}
});
}
}, 100);
} else {
Log.d(TAG, "asyncLoadPageImage = " + imageUrls);
}
}
上述这段代码编译运行都不会出现问题,但还是有一个毛病,显然开发者误以为
new Handler().postDelayed(newRunnable()
可以开启一个子线程去做一些耗时处理,于是连方法名都是asyncLoad。但实际上这样使用还是在主线程里去实现。我们接着看下去,注意这里的imageLoader不是那个著名的第三方框架ImageLoader,而是开发者自己封装的。我们来看看load()方法中做了哪些事情:
public void load(List<String> imageUrls, Callback callback) {
if (imageUrls != null && imageUrls.size() > 0) {
cancelAll();
innerCallback = new InnerCallback(imageUrls, callback);
for (String imageUrl : imageUrls) {
imageFetcher.loadImageToLocal(imageUrl, innerCallback);
}
}
}
貌似没有问题,接着看下循环里的代码:
public void loadImageToLocal(String url, ImageWorker.Download2LocalCallback callback) {
zhiyueImageFetcher.load2Local(url, callback);
}
这个封装简单易懂。继续:
public void load2Local(String url, Download2LocalCallback callback) {
File localFile = getLocalFileObject(url);
if (localFile.exists()) {
if (localFile.length() > 0) {
asyncCallback(callback, true, url, localFile);
return;
}
else {
localFile.delete();
}
}
else {
Log.d(TAG, "image not exist " + localFile.getAbsolutePath() + ", url = " + url);
}
Download2LocalTask download2LocalTask = new Download2LocalTask(url, callback);
multiUrlQueryController.add(url, download2LocalTask);
download2LocalTask.execute();
}
来到本次事故的事发地。注意,直到上面这个方法中,我们还在主线程。看看上面方法,我们在主线程中创建了文件,并判断文件是否存在。嗯,这是一个耗时操作,如果这里出现了ANR,接下来的代码执行的怎么样,都没必要讲了。可以看出开发者意识到文件的处理是个耗时的过程,只不过是他用错了而已。
所以这里出现ANR的原因是错误使用new Handler().postDelayed(newRunnable()