赞
踩
引言: 项目需要预览pdf、word等文件,但是用户不一定安装了wps这样的软件,因此需要在项目里支持查看这些文件。笔者本身是不想使用webview的,因此尝试了很多原生的,但是对于doc文件基本没有什么好的方式,到最后还是用了腾讯的tbs文件游览服务。接入tbs坑较多,主要是x5内核的下载和各种配置问题,花费了两天才整理好,因此分享出来防止大家踩坑。
对于doc,docx文件,除tbs外似乎只有一种方式,利用poi解析doc,然后自己渲染成html。笔者尝试了这种方式,发现其原理是读取doc文件后,再把相关元素加上html的标签,用本地webview加载,html标签与doc文件毕竟有很大差距,因此展示效果很差,只能选择tbs。
然后是关于pdf的查看,这种方式略多,下面大致介绍下:
最后能够良好展示word文件的只有tbs,因此只能用它了。
首先说一下坑的问题,实际上在本地利用tbs打开文件腾讯的文档是没有写的,因为腾讯想让我们优先使用他们的QbSdk,这样会优先打开QQ浏览器,现在的本地打开的方法是各路大神从源码找出来的,可能有各种疏忽,所以坑多。(不过腾讯的文档也怪扯淡的)
先上官方文档: 腾讯浏览器tbs接入文档
下面进行大致梳理
1. 导入依赖
在app的build.gradle文件中增加依赖
api 'com.tencent.tbs.tbssdk:sdk:43939'
2.增加权限混淆,首次启动初始化冷启动优化(一定要加混淆,不然release版本不能用的
这里不做详细介绍,直接看官方文档,照着来就可以了。另外官方文档的异常上报措施可忽略。官方文档还有一个替换webview的,这种是指如果你项目本来使用了webview,但是现在你想换成使用腾讯的x5内核,那么就需要按照腾讯的做法替换,如果不想替换,那可以不用变,因为接入腾讯x5内核后,安卓原生的webview仍然是可以用的。
3、使用
从这里开始文档就相当坑爹了,腾讯文档被没有讲如何初始化x5内核,甚至x5内核到底初始化成功了都没有介绍。
首先介绍一下x5内核,x5内核就是QQ浏览器的内核,对安卓的webview做了一层封装,在你的手机各个app可以共享,比如qq、QQ游览器,微信之间一个初始化了,其他的就可以使用。(但是笔者实践,只有你安装了QQ浏览器才可以共享,QQ微信无效)。
然后是x5内核的初始化代码:(建议放在Application中)
- QbSdk.initX5Environment(this, new QbSdk.PreInitCallback() {
- @Override
- public void onCoreInitFinished() {
-
- }
-
- @Override
- public void onViewInitFinished(boolean b) {
- //这里被回调,并且b=true说明内核初始化并可以使用
- //如果b=false,内核会尝试安装,你可以通过下面监听接口获知
- //另外第一次安装app不会被调用
- }
- });
-
- QbSdk.setTbsListener(new TbsListener() {
- @Override
- public void onDownloadFinish(int i) {
- //tbs内核下载完成回调
- //但是只有i等于100才算完成,否则失败
- //此时大概率可能由于网络问题
- //如果失败可增加网络监听器
- }
-
- @Override
- public void onInstallFinish(int i) {
- //内核安装完成回调,通常到这里也算安装完成,但是在
- //极个别情况也会出现加载失败,比如笔者在公司内网下偶现,可以忽略
- }
-
- @Override
- public void onDownloadProgress(int i) {
- //下载进度监听
- }
- });

上面的代码会自动对x5内核进行初始化,如果没有x5内核会自动下载。
之后就是使用sdk打开文件了,首先是腾讯文档写的打开文件方式:(坑较少,同时不是本文的目的,不做过多介绍)
- //优先打开QQ浏览器,如果没有则利用X5内核软件内打开,
- //如果X5内核也没有就弹窗让别的软件打开
- QbSdk.openFileReader(getContext(),filepath,null,null);
然后就是使用TbsReaderView在软件内打开:
- Bundle bundle = new Bundle();
- //指定文件路径
- bundle.putString("filePath", filePath);
- //指定腾讯文件缓存路径
- bundle.putString("tempPath", Environment.getExternalStorageDirectory()
- .getPath()+"/dsadsa");
- //预加载,判断格式是否正确,其中的parseFile方法是获取文件后缀
- boolean result = mTbsView.preOpen(parseFileType(filePath), false);
- if (result) {
- mTbsView.openFile(bundle);
- } else {
- Log.e(TAG, "Type is not support");
- }
这里再附上获取文件后缀方法,parseFileType
- private String parseFileType(String path) {
- if (TextUtils.isEmpty(path)) {
- return "";
- } else {
- return path.substring(path.lastIndexOf(".")+1);
- }
- }
上面的基本就是按照文档的说法了,但是离真正打开还有十万八千里,下面开始踩坑。
当你运行尝试打开文件后,会报错:找不到com.tencent.smtt.utils.FileProvider或者找不到provider,官方文档竟然一点没提,气愤呀。
在AndroidManifest.xml文件中增加:
- <provider
- android:name="com.tencent.smtt.utils.FileProvider"
- android:authorities="${applicationId}.provider"
- android:exported="false"
- android:grantUriPermissions="true">
- <meta-data
- android:name="android.support.FILE_PROVIDER_PATHS"
- android:resource="@xml/x5webview_file_paths" />
- </provider>
然后在res的xml文件夹下新建文件夹x5webview_file_paths.xml (文件名自己定,只要和上面的meta_data的resource对应上即可),内容如下:
- <?xml version="1.0" encoding="utf-8"?>
- <paths>
- <external-path name="sdcard" path="."/>
- </paths>
在使用过程中,经常会报各种类找不到,然后在使用tbs打开文件时,明明是支持的文件,但是却说不支持,都是由于X5内核没有下载导致的。(因为内部很多方法使用了反射,X5的代码没有下载,因此会出现各种类找不到)
下面说一下X5初始化出现的各种问题:
- 网络要求,由于X5内核30M,通常在wifi下下载 (根据项目需要决定,后来我们最终决定在流量下也可以下载)
- 继续下载问题,在上一次下载失败后,下一次由于有缓存,会导致即不能用,也不能重新下载
- 初始化是否完成,官方没有提供相关的方法
也就是说,只要一次性没下载完,以后下载就没戏了,除非卸载重装,而且网络不符合要求也会中止下载
然后是我的解决总结,再详细阐述解释:
reset重置+布尔值记录初始结果+网路监听器+进度监听
上面2.1.3介绍了X5下载问题,sdk会自动判断是否初始化并下载,但是用户网络不一定符合要求,而且用户也可能打开接着关闭导致存在缓存无法再次初始化,甚至在初始化过程中调用tbsReadview的相关代码也会导致缓存问题,真的是恶心呀。
通过各种搜索百度谷歌,发现下面几个方法:
- //重置化sdk,这样就清除缓存继续下载了
- QbSdk.reset(context);
- //手动开始下载,此时需要先判定网络是否符合要求
- TbsDownloader.startDownload(context);
- //是否需要下载内核,作用比较奇葩
- //该方法会在完全没下载的时候返回true,在
- //加载完成和存在缓存无法继续下载时返回flase
- //这个方法可以用来判断是否存在缓存需要重置
- boolean need =TbsDownloader.needDownload(context, false)
接下来就结合上面的方法和具体x5下载的流程,详细分析。
首先是每次启动前调用sdk初始化加速流程:
- private static void BeforeSdk(Context context) {
- // 在调用TBS初始化、创建WebView之前进行如下配置
- HashMap<String, Object> map = new HashMap<>();
- map.put(TbsCoreSettings.TBS_SETTINGS_USE_SPEEDY_CLASSLOADER, true);
- map.put(TbsCoreSettings.TBS_SETTINGS_USE_DEXLOADER_SERVICE, true);
- QbSdk.initTbsSettings(map);
- QbSdk.setDownloadWithoutWifi(!mOnlyWifi);
- QbSdk.disableAutoCreateX5Webview();
- //强制使用系统内核,看你需求
- //QbSdk.forceSysWebView();
- }
然后就是下载过程,在这里我们设置了几个变量记录是否初始化成功
- private static boolean mOnlyWifi = false;
- //记录是否加载完成
- private static boolean mInit = false;
- //网络接收器是用来处理网络中断导致的加载问题
- private static ConnectReceiver mConnectionReceiver;
- //这个key是sp存储的,用来记录上一次是否加载成功
- //因为有的时候需要立刻用到tbs展示文件,但是tbs可能还没加载好
- //因此我们在sp里面做记录,看是否需要等待加载
- //如果上次加载好了,那这次肯定也很快,可以等待
- //如果上次没加载好,那还是提示用户过一会再用吧
- private static String TBS_INIT_KEY = "tbs_init_key";
-
-
- //该方法是用来判断tbs是否加载完成的,供外部调用
- public static boolean initFinish() {
- //如果上次加载成功,那么便认为当前的未加载成功由于还未加载完等
- //不做额外处理
- if(SPUtils.getBoolean(TBS_INIT_KEY,false)){
- return mInit;
- }
- //上次没加载完,那就根据状态判断是否重置
- if (!mInit && !TbsDownloader.isDownloading()) {
- QbSdk.reset(mContext);
- resetSdk(mContext);
- if (!mOnlyWifi || NetUtils.isWifiConnection(mContext))
- TbsDownloader.startDownload(mContext);
- }
- return mInit;
- }
-
-
-

接着就是具体的下载设置:
- resetSdk(context);
- QbSdk.setTbsListener(new TbsListener() {
- @Override
- public void onDownloadFinish(int i) {
- //成功时i为100
- if (i != 100) {
- //此处存在一种情况,第一次启动app,init不会自动回调,
- // 此处额外加一层,判断网络监听器是否为空并作出处理
- if (mConnectionReceiver == null)
- initNetWorkCallBack();
- else {
- initFinish();
- }
- }
- Log.d(TAG, "load" + i);
- //tbs内核下载完成回调
- }
-
- @Override
- public void onInstallFinish(int i) {
- //只要运行到这里,我们就认为加载完成了
- //但是也发现一些异常,在公司内网运行到这里也加载失败了,先忽略
- mInit = true;
- if (mConnectionReceiver != null) {
- mContext.unregisterReceiver(mConnectionReceiver);
- mConnectionReceiver = null;
- }
- Log.d(TAG, "finish" + i);
- //内核安装完成回调,
- }
-
- @Override
- public void onDownloadProgress(int i) {
- //下载进度监听
- Log.d(TAG, "progress" + i);
- }
- });
- QbSdk.initX5Environment(context, new QbSdk.PreInitCallback() {
- @Override
- public void onCoreInitFinished() {
- //x5内核初始化完成回调接口,此接口回调并表示已经加载起来了x5,有可能特殊情况下x5内核加载失败,切换到系统内核。
- }
-
- @Override
- public void onViewInitFinished(boolean b) {
- //x5內核初始化完成的回调,为true表示x5内核加载成功,否则表示x5内核加载失败,会自动切换到系统内核。
- //该方法在第一次安装app打开不会回调
- mInit = b;
- Log.e(TAG, "加载内核是否成功:" + b);
- if (!mInit) {
- initNetWorkCallBack();
- }
-
- if (!mInit && TbsDownloader.needDownload(context, false) && !TbsDownloader.isDownloading()) {
- initFinish();
- }
- SPUtils.putBoolean(TBS_INIT_KEY, mInit).apply();
- }
- });

至此tbs的初始化基本完成,代码示例中的SPUtils就是SharedPreferences,自己实现即可
tbsview你在同一时间只能加载一处,因此请做好回收工作,可以设置成静态的,也可以将加载的activity设置成instance的,具体看你。
另外展示的时候会有最近文件字样,以及可能底色有问题,都可以通过逐层分析子view更改,具体百度即可。
项目地址 https://github.com/qieting/TbsUserDemo
这个项目地址和上面博客讲的略有出入,因为博客是后期补充完善的,但是项目没改
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。