安卓使用腾讯tbs查看pdf/word文件(解决x5内核下载问题)

 

     

 引言: 项目需要预览pdf、word等文件,但是用户不一定安装了wps这样的软件,因此需要在项目里支持查看这些文件。笔者本身是不想使用webview的,因此尝试了很多原生的,但是对于doc文件基本没有什么好的方式,到最后还是用了腾讯的tbs文件游览服务。接入tbs坑较多,主要是x5内核的下载和各种配置问题,花费了两天才整理好,因此分享出来防止大家踩坑。

一、首先梳理一下选择tbs的理由

      对于doc,docx文件,除tbs外似乎只有一种方式,利用poi解析doc,然后自己渲染成html。笔者尝试了这种方式,发现其原理是读取doc文件后,再把相关元素加上html的标签,用本地webview加载,html标签与doc文件毕竟有很大差距,因此展示效果很差,只能选择tbs。

     然后是关于pdf的查看,这种方式略多,下面大致介绍下:

  1.   pdf.js  这是火狐推出的可以在安卓上使用,并且文件并不大,使用难度相对不大
  2. 安卓原生的pdfRender,笔者使用感觉还可以,因为使用安卓自带的pdfRender,不需要额外导入文件,加载速度也还好,如果只需要显示pdf,推荐
  3. github上开源的pdfviewer,功能较为强大,但是因为会导致包至少增大无法被接受,项目地址https://github.com/barteksc/AndroidPdfViewer
  4. 采用外部链接在线预览,但是目前基本是国外链接,会被墙,不考虑

     最后能够良好展示word文件的只有tbs,因此只能用它了。

二、tbs接入及踩坑

     首先说一下坑的问题,实际上在本地利用tbs打开文件腾讯的文档是没有写的,因为腾讯想让我们优先使用他们的QbSdk,这样会优先打开QQ浏览器,现在的本地打开的方法是各路大神从源码找出来的,可能有各种疏忽,所以坑多。(不过腾讯的文档也怪扯淡的)

     1.初始配置

           先上官方文档: 腾讯浏览器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);
        }
}

                上面的基本就是按照文档的说法了,但是离真正打开还有十万八千里,下面开始踩坑。

三、踩坑

      1.provider

        当你运行尝试打开文件后,会报错:找不到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>

             2.X5内存下载

                在使用过程中,经常会报各种类找不到,然后在使用tbs打开文件时,明明是支持的文件,但是却说不支持,都是由于X5内核没有下载导致的。(因为内部很多方法使用了反射,X5的代码没有下载,因此会出现各种类找不到)

                下面说一下X5初始化出现的各种问题:

  1.    网络要求,由于X5内核30M,通常在wifi下下载 (根据项目需要决定,后来我们最终决定在流量下也可以下载)
  2.    继续下载问题,在上一次下载失败后,下一次由于有缓存,会导致即不能用,也不能重新下载 
  3.   初始化是否完成,官方没有提供相关的方法

     也就是说,只要一次性没下载完,以后下载就没戏了,除非卸载重装,而且网络不符合要求也会中止下载

                然后是我的解决总结,再详细阐述解释:

    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,自己实现即可

 

     3.使用

     tbsview你在同一时间只能加载一处,因此请做好回收工作,可以设置成静态的,也可以将加载的activity设置成instance的,具体看你。

    另外展示的时候会有最近文件字样,以及可能底色有问题,都可以通过逐层分析子view更改,具体百度即可。

 

项目地址     https://github.com/qieting/TbsUserDemo      

这个项目地址和上面博客讲的略有出入,因为博客是后期补充完善的,但是项目没改

腾讯TBS是一款基于Chromium内核的浏览器内核,可以在Android应用中实现WebView的功能。如果在使用TBS时出现加载失败的问题,可能是由于以下原因造成的: 1. TBS内核未正确初始化:TBS内核必须在应用启动时进行初始化,否则会导致后续的加载失败。您可以在Application的onCreate()方法中添加以下代码进行初始化: ```java QbSdk.initX5Environment(getApplicationContext(), null); ``` 2. TBS内核版本不兼容:如果您的应用中使用TBS内核版本与当前设备上安装的Chrome或WebView版本不兼容,可能会导致TBS内核加载失败。您可以尝试升级或降级TBS内核版本,以解决兼容性问题。 3. 缺少必要的权限:TBS内核需要读取设备存储的权限,如果您的应用未获取相关权限,可能会导致内核加载失败。您可以在Manifest文件中添加以下权限声明: ```xml <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> ``` 4. 设备不支持TBS内核:部分设备可能不支持TBS内核,导致加载失败。您可以在加载TBS内核前,使用以下代码检查当前设备是否支持TBS内核: ```java if (QbSdk.isTbsCoreInited()) { // TBS内核已经初始化 } else { // TBS内核未初始化,需要进行初始化 } ``` 如果设备不支持TBS内核,您可以使用系统自带的WebView或其他第三方的WebView替代TBS内核。 5. 其他原因:TBS内核加载失败可能还有其他原因,例如网络连接问题、内存不足等。您可以查看日志信息,寻找更详细的错误信息,以便进一步排查问题。 总之,TBS内核加载失败可能是由于多种原因造成的,需要根据具体情况进行排查和解决
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值