Android集成腾讯X5实现文档浏览功能

Android内部没有控件来直接显示文档,跳转WPS或其他第三方文档App体验性不好,使用腾讯X5内核能很好的解决的这一问题。
一、下载腾讯X5内核
1.前往https://x5.tencent.com/下载Android的内核,新版本的腾讯X5可以直接在bulid.gradle集成   api 'com.tencent.tbs.tbssdk:sdk:43697',如果是在App里集成可以把api换成implementation

2.AndroidStudio导入腾讯X5

a.把下载好的jar包导入libs,然后run as,再把jnilibs导入main包下

b. module的build.gradle添加cpu适配

3.Application中腾讯X5初始化,在onCreate()方法调用init方法

​​  QbSdk.initX5Environment(this, new QbSdk.PreInitCallback() {
            @Override
            public void onCoreInitFinished() {

            }

            @Override
            public void onViewInitFinished(boolean b) {
                Log.e("xxx","hasLoad"+b); 
            //此处将内核加载是否成功的状态保存到本地,SharePreference工具类可换为自己的
                SharedPreferenceUtils.saveBoolean(getApplicationContext(),"hasLoad",b);
            }
        });

4.应用内调用,无论是否加载内核,都是把在线的文档下载到本地然后打开,不同的是,未加载内核会借助QQ浏览器或其他App的文件浏览功能类似微信(这个调用,腾讯X5已自动处理,我们无需关心),而加载内核后,使用X5的TbsReaderView来打开文件,接下来就是具体代码。

a.bulid.gradle中集成Retrofit,RxJava和进度管理器ProgressManager

    implementation 'io.reactivex:rxandroid:1.2.1'
    implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    implementation 'io.reactivex:rxjava:1.2.6'
    implementation 'me.jessyan:progressmanager:1.5.0'

写网络下载工具

public interface LoadFileApi {

    @GET
    Call<ResponseBody> loadPdfFile(@Url String fileUrl);

}
public class LoadFileModel {

    //使用该方法来下载文件
    public static void loadPdfFile(String url, Callback<ResponseBody> callback) {

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://www.baidu.com/")
                .client(ProgressManager.getInstance().with(new OkHttpClient.Builder())
                        .build())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        LoadFileApi mLoadFileApi = retrofit.create(LoadFileApi.class);
        if (!TextUtils.isEmpty(url)) {
            Call<ResponseBody> call = mLoadFileApi.loadPdfFile(url);
            call.enqueue(callback);
        }

    }
}

b.具体调用

首先注册文件进度下载监听

 private ProgressDialog commonProgressDialog;//切换为自己的进度控件
 private ProgressInfo mLastDownloadingInfo;
 ProgressManager.getInstance().addResponseListener(url, new ProgressListener() {
                                                @Override
                                                public void onProgress(ProgressInfo progressInfo) {
                                                    if (mLastDownloadingInfo == null) {
                                                        mLastDownloadingInfo = progressInfo;
                                                    }

                                                    //因为是以请求开始时的时间作为 Id ,所以值越大,说明该请求越新
                                                    if (progressInfo.getId() < mLastDownloadingInfo.getId()) {
                                                        return;
                                                    } else if (progressInfo.getId() > mLastDownloadingInfo.getId()) {
                                                        mLastDownloadingInfo = progressInfo;
                                                    }

                                                    int progress = mLastDownloadingInfo.getPercent();
                                                    commonProgressDialog.setProgress(progress);
                                                    Log.d("xxx", "--Download-- " + progress + " %  " + mLastDownloadingInfo.getSpeed() + " byte/s  " + mLastDownloadingInfo.toString());
                                                    if (mLastDownloadingInfo.isFinish()) {
                                                        //说明已经下载完成
                                                        commonProgressDialog.dismiss();
                                                        Log.d("xxx", "--Download-- finish");
                                                    }
                                                }

                                                @Override
                                                public void onError(long id, Exception e) {
                                                    e.printStackTrace();
                                                    new Handler().post(new Runnable() {
                                                        @Override
                                                        public void run() {
                                                                commonProgressDialog.dismiss();
                                                        }
                                                    });

                                                }
                                            });
                                        }

然后打开文件

private static final String DOWN_DIR = Environment.getExternalStorageDirectory()
            + File.separator+"Download"; //文件下载的路径,可以自定义
//打开文件
    private void openFile(String filePath,String fileName) {
        boolean hasLoad = SharedPreferenceUtils.getBoolean(mContext, "hasLoad");//是否记载内核
        if (hasLoad){
        //展示文件的Activity
            Intent intentDoc = new Intent(this,FileDisplayActivity.class);
            intentDoc.putExtra("path",filePath);
            intentDoc.putExtra("fileName",fileName);
            startActivity(intentDoc);
        }else {
            if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                Toast.makeText(this,"当前SD不可用",Toast.LENGTH_LONG).show();
                return;
            }
            try {
                File file = new File(DOWN_DIR, fileName);
                if (file.exists()){
                    QbSdk.openFileReader(this, file.getAbsolutePath(), null, new ValueCallback<String>() {
                        @Override
                        public void onReceiveValue(String s) {
                            Log.e("xxx",s);
                        }
                    });
                } else {
                    downLoadFile(filePath,fileName);
                }

            } catch (Exception e) {
                e.printStackTrace();
                ToastUtils.showShortToast(mContext, "已下载过了");
            }

        }
    }

    private void downLoadFile(final String url, final String fileName) {
        commonProgressDialog.show();
        LoadFileModel.loadPdfFile(url, new Callback<ResponseBody>() {
            @Override
            public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
                InputStream is = null;
                byte[] buf = new byte[2048];
                int len;
                FileOutputStream fos = null;
                try {
                    ResponseBody responseBody = response.body();
                    if (responseBody != null) {
                        is = responseBody.byteStream();
                        final File file = new File(DOWN_DIR, fileName);
                        fos = new FileOutputStream(file);
                        while ((len = is.read(buf)) != -1) {
                            fos.write(buf, 0, len);
                        }
                        fos.flush();
                        //未加载内核调用方法
                        QbSdk.openFileReader(mContext, file.getAbsolutePath(), null, new ValueCallback<String>() {
                            @Override
                            public void onReceiveValue(String s) {
                                Log.e("xxx",s);
                            }
                        });
                        ToastUtils.showLongToast(MeetingDetailActivity.this, "下载成功,保存在Download文件夹下");
                    }
                } catch (Exception e) {
                    commonProgressDialog.dismiss();
                    ProgressManager.getInstance().notifyOnErorr(url,e);
                    ToastUtils.showLongToast(MeetingDetailActivity.this, "下载失败");
                } finally {
                    try {
                        if (is != null)
                            is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    try {
                        if (fos != null)
                            fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
                t.printStackTrace();
                ToastUtils.showShortToast(MeetingDetailActivity.this, "下载失败");
                commonProgressDialog.dismiss();
            }
        });

    }

FileDisplayActivity代码,对于一些包名可以切换为自己的包名,还有一些第三方库像进度展示,也可以切换为自己的进度控件

import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;

import com.anshi.oatencentschool.R;
import com.anshi.oatencentschool.utils.DialogBuild;
import com.anshi.oatencentschool.utils.StatusBarUtils;
import com.anshi.oatencentschool.utils.ToastUtils;
import com.anshi.oatencentschool.utils.WeakHandler;
import com.anshi.oatencentschool.utils.filetool.LoadFileModel;
import com.anshi.oatencentschool.utils.filetool.SuperFileView2;
import com.kaopiz.kprogresshud.KProgressHUD;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import me.jessyan.progressmanager.ProgressListener;
import me.jessyan.progressmanager.ProgressManager;
import me.jessyan.progressmanager.body.ProgressInfo;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;


public class FileDisplayActivity extends AppCompatActivity {


    private String TAG = "FileDisplayActivity";
    //Tencent 提供的TBS阅读浏览功能,不借助第三方软件打开office和pdf文件
    private SuperFileView2 mSuperFileView;

    private String filePath;
    private TextView mTextView;
    private static final String DOWN_DIR = Environment.getExternalStorageDirectory()
            + File.separator+"Download";
    private String fileName;
    private KProgressHUD commonProgressDialog;
    private WeakHandler weakHandler = new WeakHandler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            int progress = msg.arg1;
            if (progress==100){
                commonProgressDialog.setProgress(progress);
                commonProgressDialog.dismiss();
            }else {
                commonProgressDialog.setProgress(progress);
            }
            return true;
        }
    });

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_file_display);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON|WindowManager.LayoutParams.FLAG_FULLSCREEN);
        commonProgressDialog = DialogBuild.getBuild().createCommonProgressDialog(this, "下载中");
        init();
    }


    /**
     * 初始化
     */
    public void init() {
        mTextView = (TextView) findViewById(R.id.file_album_name);
        fileName = getIntent().getStringExtra("fileName");
        int lastIndexOf = fileName.lastIndexOf(".");
        String substring = fileName.substring(0, lastIndexOf);
        mTextView.setText(substring);
        mSuperFileView = (SuperFileView2) findViewById(R.id.mSuperFileView);
        mSuperFileView.setOnGetFilePathListener(new SuperFileView2.OnGetFilePathListener() {
            @Override
            public void onGetFilePath(SuperFileView2 mSuperFileView2) {
                getFilePathAndShowFile(mSuperFileView);
            }
        });
        Intent intent = this.getIntent();
        String path = (String) intent.getSerializableExtra("path");
        if (!TextUtils.isEmpty(path)) {
            Log.d(TAG, "文件path:" + path);
            setFilePath(path);
        }
        mSuperFileView.show();
        ProgressManager.getInstance().addResponseListener(path, new ProgressListener() {
            @Override
            public void onProgress(ProgressInfo progressInfo) {
                int percent = progressInfo.getPercent();
                Message obtain = Message.obtain();
                obtain.arg1 = percent;
                weakHandler.sendMessage(obtain);
            }

            @Override
            public void onError(long id, Exception e) {

            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        StatusBarUtils.setWindowStatusBarColor(this,R.color.color_main);


    }

    /**
     * 显示文件
     * @param mSuperFileView2 控件
     */
    private void getFilePathAndShowFile(SuperFileView2 mSuperFileView2) {
        if (getFilePath().contains("http")&&!new File(DOWN_DIR, fileName).exists()) {//网络地址要先下载
            downLoadFile(getFilePath(),fileName,mSuperFileView2);
        } else {
            try {
                mSuperFileView2.displayFile(new File(DOWN_DIR, fileName));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }




    public void setFilePath(String fileUrl) {
        this.filePath = fileUrl;
    }

    private String getFilePath() {
        return filePath;
    }


    /**
     * 回退上一级菜单
     * @param view 控件
     */
    public void onClick(View view) {
        finish();
    }


    private void downLoadFile(final String url, final String fileName,final SuperFileView2 mSuperFileView2) {
        commonProgressDialog.show();
        LoadFileModel.loadPdfFile(url, new Callback<ResponseBody>() {
            @Override
            public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
                InputStream is = null;
                byte[] buf = new byte[2048];
                int len;
                FileOutputStream fos = null;
                try {
                    ResponseBody responseBody = response.body();
                     is = responseBody.byteStream();
                    final File file = new File(DOWN_DIR, fileName);
                    fos = new FileOutputStream(file);
                    while ((len = is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                    }
                    fos.flush();
                    mSuperFileView2.displayFile(file);
                    ToastUtils.showLongToast(FileDisplayActivity.this, "下载成功,保存在Download文件夹下");
                } catch (Exception e) {
                    commonProgressDialog.dismiss();
                    ToastUtils.showLongToast(FileDisplayActivity.this, "下载失败");
                } finally {
                    try {
                        if (is != null)
                            is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    try {
                        if (fos != null)
                            fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
                t.printStackTrace();
                ToastUtils.showShortToast(FileDisplayActivity.this, "下载失败");
                commonProgressDialog.dismiss();
            }
        });

      }
}

布局Layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:id="@+id/activity_file_display"
    android:layout_width="match_parent"
    android:fitsSystemWindows="true"
    android:layout_height="match_parent">
    <android.support.v7.widget.Toolbar
        android:layout_width="match_parent"
        android:layout_height="46dp"
        android:background="@color/color_main"
        android:id="@+id/file_toolbar"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:id="@+id/file_album_name"
            android:layout_gravity="center"
            android:gravity="center"
            android:maxLines="2"
            android:ellipsize="end"
            android:textSize="14sp"
            android:textColor="#FFF"
            />
    </android.support.v7.widget.Toolbar>
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/file_toolbar"
        >
        <com.anshi.oatencentschool.utils.filetool.SuperFileView2
            android:id="@+id/mSuperFileView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_gravity="center|start"
            android:layout_height="wrap_content">

            <RadioButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:button="@null"
                android:onClick="onClick"
                android:id="@+id/back_icon"
                android:drawableStart="@drawable/svg_left_back_blur"
                android:layout_marginStart="10dp"
                />
        </RelativeLayout>
        <!--<RadioButton-->
            <!--android:layout_width="wrap_content"-->
            <!--android:layout_height="wrap_content"-->
            <!--android:button="@null"-->
            <!--android:visibility="gone"-->
            <!--android:layout_gravity="end|bottom"-->
            <!--android:onClick="saveLocal"-->
            <!--android:layout_marginBottom="10dp"-->
            <!--android:layout_marginEnd="10dp"-->
            <!--android:drawableStart="@drawable/vector_drawable_down_load"-->
            <!--android:layout_marginStart="10dp"-->
            <!--/>-->
    </FrameLayout>
</RelativeLayout>

SuperFileView2代码,这个自定义的类是从https://github.com/ZhongXiaoHong/superFileView导入

import android.content.Context;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import com.tencent.smtt.sdk.TbsReaderView;

import java.io.File;

/**
 *
 * Created by 12457 on 2017/8/29.
 */

public class SuperFileView2 extends FrameLayout {

    private static String TAG = "SuperFileView";
    private TbsReaderView mTbsReaderView;
    private Context context;

    public SuperFileView2(Context context) {
        this(context, null, 0);
    }

    public SuperFileView2(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SuperFileView2(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mTbsReaderView = new TbsReaderView(context, new TbsReaderView.ReaderCallback() {
            @Override
            public void onCallBackAction(Integer integer, Object o, Object o1) {

            }
        });
        this.addView(mTbsReaderView, new LinearLayout.LayoutParams(-1, -1));
        this.context = context;
    }


    private OnGetFilePathListener mOnGetFilePathListener;


    public void setOnGetFilePathListener(OnGetFilePathListener mOnGetFilePathListener) {
        this.mOnGetFilePathListener = mOnGetFilePathListener;
    }


    private TbsReaderView getTbsReaderView(Context context) {
        return new TbsReaderView(context, new TbsReaderView.ReaderCallback() {
            @Override
            public void onCallBackAction(Integer integer, Object o, Object o1) {

            }
        });
    }

    public void displayFile(File mFile) {
        if (mFile != null && !TextUtils.isEmpty(mFile.toString())) {
            //增加下面一句解决没有TbsReaderTemp文件夹存在导致加载文件失败
            String bsReaderTemp = Environment.getExternalStorageDirectory()+ File.separator+"TbsReaderTemp";
            File bsReaderTempFile =new File(bsReaderTemp);
            if (!bsReaderTempFile.exists()) {
               Log.d("xxx","准备创建/storage/emulated/0/TbsReaderTemp!!");
                boolean mkdir = bsReaderTempFile.mkdir();
                if(!mkdir){
                    Log.d("xxx","创建/storage/emulated/0/TbsReaderTemp失败!!!!!");
                }
            }

            //加载文件
            Bundle localBundle = new Bundle();
            Log.d("xxx",mFile.toString());
            localBundle.putString("filePath", mFile.toString());

            localBundle.putString("tempPath", bsReaderTemp);

            if (mTbsReaderView == null){
                mTbsReaderView = getTbsReaderView(context.getApplicationContext());
                String fileType = getFileType(mFile.toString());
                boolean bool = mTbsReaderView.preOpen(fileType,false);
                if (bool) {
                    try {
                        mTbsReaderView.openFile(localBundle);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }else {
                String fileType = getFileType(mFile.toString());
                boolean bool = mTbsReaderView.preOpen(fileType,false);
                if (bool) {
                    try {
                        mTbsReaderView.openFile(localBundle);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }

        } else {
            Log.d("xxx","文件路径无效!");
        }


    }

    /***
     * 获取文件类型
     *
     * @param paramString
     * @return
     */
    private String getFileType(String paramString) {
        String str = "";

        if (TextUtils.isEmpty(paramString)) {
            Log.d(TAG, "paramString---->null");
            return str;
        }
        Log.d(TAG, "paramString:" + paramString);
        int i = paramString.lastIndexOf('.');
        if (i <= -1) {
            Log.d(TAG, "i <= -1");
            return str;
        }


        str = paramString.substring(i + 1);
        Log.d(TAG, "paramString.substring(i + 1)------>" + str);
        return str;
    }

    public void show() {
        if(mOnGetFilePathListener!=null){
            mOnGetFilePathListener.onGetFilePath(this);
        }
    }

    /***
     * 将获取File路径的工作,“外包”出去
     */
    public interface OnGetFilePathListener {
        void onGetFilePath(SuperFileView2 mSuperFileView2);
    }

    public void onStopDisplay() {
        if (mTbsReaderView != null) {
            mTbsReaderView.onStop();
        }
    }
}

加载内核后打开文件

未加载内核打开文件

结后语:加载内核后App体积会增加70M左右,带来的是更好的用户体验,如果不想这样做,只需要将Application中腾讯X5的初始化方法去掉即可

猜你喜欢

转载自blog.csdn.net/hardWork_yulu/article/details/102636818