Android之预览PDF文件
上周临时收到客户的说明书,说要放在应用里显示,本来觉得一个非常简单的事情没想到一波三折。
第一波:我使用了pdfViewer控件:(相了解的可以查看:https://github.com/barteksc/AndroidPdfViewer)
遇到的坑就是 因为设备是横屏的,这个控件在手机上预览pdf很不错,我一下子没有找到横向铺满的方法,因此显示起来页面显的非常小,由于时间比较急没有去细细研究所以弃用了。
第二波:到这个时候我就立马换了一个思路。改用WebView,过程稍微复杂点 但是效果还是不错的 满足了需求。
首先,下载PDF.js放到assets目录下。如下:
然后就是使用:布局文件就不贴出来了。
private void loadWebPdf() {
webView = (WebView) findViewById(R.id.pdf_webView);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccess(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
webSettings.setAllowUniversalAccessFromFileURLs(true);
webSettings.setAllowUniversalAccessFromFileURLs(true);
}
webView.loadUrl("file:///android_asset/pdfjs/web/viewer.html?file=" + "file:///android_asset/vehicle-help-chinese.pdf");
}
感觉一切都是相当的完美。如果是一般应用到这里也就没什么问题了。
第三波: 运行的时候发现系统一直报 Error inflating class android.webkit.WebView.这就很尴尬了,然后查了下,试了几个方法都没有用,后面仔细看还有For security reasons, WebView is not allowed in privileged processesFor security reasons, WebView is not allowed in privileged processes(出于安全原因,特权进程中不允许使用WebView。出于安全原因,特权进程中不允许使用WebView)。这就很尴尬了,忘了说了应用是系统及应用系统版本7.1.2。(Android开发系统应用(android.uid.system)使用webview报错)。
查了下 谷歌不知道从啥时候开始全面禁止系统应用使用webview。查看源码获悉在WebviewFactory代码中某个静态实例初始化之前判断api版本并抛出错误。知道是这个原因后,心里有底了,方法总比困难多嘛,既然准备用WebView来预览pdf了就不要轻易放弃。方向如下:
在使用webview之前,使用hook
(钩子)方法,给该变量赋值。哈哈 这样就可以避免这个问题了;代码如下:
/**
* 使用WebView避免系统检查抛出异常
*/
public static void checkWebView() {
LogUtils.d(TAG, "checkWebView()");
int sdkInt = Build.VERSION.SDK_INT;
try {
//拿到 WebViewFactory 类
Class<?> factoryClass = Class.forName("android.webkit.WebViewFactory");
//拿到类对应的 field
Field field = factoryClass.getDeclaredField("sProviderInstance");
//field为private,设置为可访问的
field.setAccessible(true);
//拿到 WebViewFactory 的 sProviderInstance 实例
//sProviderInstance 是 static 类型,不需要传入具体对象
Object sProviderInstance = field.get(null);
if (sProviderInstance != null) {
return;
}
Method getProviderClassMethod;
if (sdkInt > 22) {
getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass");
} else if (sdkInt == 22) {
getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass");
} else {
return;
}
getProviderClassMethod.setAccessible(true);
Class<?> providerClass = (Class<?>) getProviderClassMethod.invoke(factoryClass);
Class<?> delegateClass = Class.forName("android.webkit.WebViewDelegate");
Constructor<?> providerConstructor = providerClass.getConstructor(delegateClass);
if (providerConstructor != null) {
LogUtils.d(TAG, "providerConstructor != null");
providerConstructor.setAccessible(true);
Constructor<?> declaredConstructor = delegateClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
//利用反射创建了 sProviderInstance
sProviderInstance = providerConstructor.newInstance(declaredConstructor.newInstance());
//完成 sProviderInstance 赋值
field.set("sProviderInstance", sProviderInstance);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
搞定收工,预览了下效果还是蛮不错的。只是加载稍微有点点慢。
最近在优化内存发现这个由于用的WebView内存占用比较大,而且退出后内存降不下去。导致整个应用内存占用比较大。测试了很大方法都不能从根本上解决,最后只好把界面设置成一个单独的进程。界面退出的时候直接System.exit(0);