尚拙

一个分享技术、学习成长的个人博客网站

0%

Android 实现应用内在线查看 PDF 文件

最近有个需求,在 App 内查看使用第三方签名的电子协议,电子协议为 PDF 文件。

上网查了一下,要实现 App 内在线查看 PDF,主要思路都是先把 PDF 文件下载下来,再通过 GitHub 第三方的库,打开 PDF 文件。

参考文章

使用腾讯TBS打开Office文件 https://www.jianshu.com/p/8bd1c4918857

Android应用内展示word、excel、pdf、ppt等文件 https://www.jianshu.com/p/3f57d640b24d

常用的第三库:

PdfViewPager
https://github.com/voghDev/PdfViewPager
AndroidPdfViewer
https://github.com/barteksc/AndroidPdfViewer
android-pdfview
https://github.com/JoanZapata/android-pdfview
腾讯浏览服务
https://tes.qq.com/tbs/guide.html

这里我选择了使用腾讯浏览服务的 SDK 查看 PDF 文件。

主要步骤如下:

一、下载腾讯浏览服务 SDK 并集成到应用

腾讯浏览服务官网 https://tes.qq.com/tbs/guide.html

sdk中主要包含一个jar包和一个.so文件,将jar包和.so文件导入到lib目录

二、NDK 设置

打开对应 module 中的 build.gradle 文件,

在文件的 android{} 中的 defaultConfig{} 里,

添加如下配置: ndk{abiFilters “armeabi”}

android {
defaultConfig {
ndk {
abiFilters "armeabi-v7a", "armeabi"
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}

三、AndroidManifest.xml 里加入权限声明

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

<uses-permission android:name="android.permission.INTERNET" />

<uses-permission android:name="android.permission.READ_PHONE_STATE" />

四、初始化 X5 内核

// 设置是否在没有WiFi的情况下下载内核   
//QbSdk.setDownloadWithoutWifi(true);
//初始化X5内核
QbSdk.initX5Environment(this, new QbSdk.PreInitCallback() {
@Override
public void onCoreInitFinished() {
}
@Override
public void onViewInitFinished(boolean b) {
//x5內核初始化完成的回调,为true表示x5内核加载成功,否则表示x5内核加载失败,会自动切换到系统内核。
}
});

五、将 PDF 文件下载到本地,然后使用 SDK 里面的 TbsReaderView 打开 PDF 文件。

下载文件的方法,项目中是使用的 Retfrofit 和 Okhttp,并且进行了的封装,并不通用。所以下载文件的方法请自行编写。

1、xml 布局页面

<android.support.v7.widget.LinearLayoutCompat
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.activity.ReadPdfAgreementActivity">
</android.support.v7.widget.LinearLayoutCompat>

2、Activity 核心代码

public class ReadPdfAgreementActivity extends BaseAbstractActivity {

@BindView(R.id.ll_root)
LinearLayoutCompat mLlRoot;
private String TAG = "ReadPdfAgreementActivity";
private TbsReaderView mTbsReaderView
private String url;

@Override
protected void onCreate() {
ButterKnife.bind(this);
setContentView(R.layout.activity_read_pdf_agreement);
url = getIntent().getStringExtra(Constants.AGREEMENT_URL);
mTbsReaderView = new TbsReaderView(this, new TbsReaderView.ReaderCallback() {
@Override
public void onCallBackAction(Integer integer, Object o, Object o1) {
}
});
mLlRoot.addView(mTbsReaderView, new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
downLoadPDF();
}

private void downLoadPDF() {

RxRestClient.builder()
.url(url)
.build()
.download()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ResponseBody>() {
@Override
public void onSubscribe(Disposable d) {

}

@Override
public void onNext(ResponseBody responseBody) {
InputStream is = null;
byte[] buf = new byte[2048];
int len = 0;
FileOutputStream fos = null;
try {
is = responseBody.byteStream();
long total = responseBody.contentLength();
File fileDir = getAgreementCacheDir();
if (!fileDir.exists()) {
fileDir.mkdirs();
}
File fileN = getCacheFile(url);
if (!fileN.exists()) {
boolean mkdir = fileN.createNewFile();
}else {
fileN.delete();
}
fos = new FileOutputStream(fileN);
long sum = 0;
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
sum += len;
int progress = (int) (sum * 1.0f / total * 100);
}
fos.flush();

displayPdfFile(fileN);
} catch (Exception e) {
PLog.d(TAG, "文件下载异常 = " + e.toString());
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
}
try {
if (fos != null)
fos.close();
} catch (IOException e) {
}
}
}

@Override
public void onError(Throwable e) {
PLog.d(TAG, "文件下载失败");
File file = getCacheFile(url);
if (!file.exists()) {
PLog.d(TAG, "删除下载失败文件");
file.delete();
}
}

@Override
public void onComplete() {

}
});
}

private void displayPdfFile(File file){
if (file != null && !TextUtils.isEmpty(file.toString())) {
//加载文件
Bundle localBundle = new Bundle();
localBundle.putString("filePath", file.toString());
localBundle.putString("tempPath", getExternalFilesDir("TbsReaderTemp").getAbsolutePath());
if (mTbsReaderView != null) {
boolean bool = this.mTbsReaderView.preOpen(getFileType(mFile.toString()), false);
if (bool) {
mTbsReaderView.openFile(localBundle);
}
}
}
}

/***
* 获取缓存目录
*
* @return
*/
private File getAgreementCacheDir() {
String path = getExternalFilesDir("agreement").getAbsolutePath();
return new File(path);
}
/***
* 绝对路径获取缓存文件
*
* @param url
* @return
*/
private File getCacheFile(String url) {
String fileName = "agreement" + "." + getFileType(url);
File cacheFile = new File(getAgreementCacheDir(),fileName);
return cacheFile;
}

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

if (TextUtils.isEmpty(paramString)) {
return str;
}
int i = paramString.lastIndexOf('.');
if (i <= -1) {
return str;
}
str = paramString.substring(i + 1);
return str;
}

@Override
protected void onDestroy() {
super.onDestroy();
if (mTbsReaderView != null) {
mTbsReaderView.onStopDisplay();
}
}
}

3、退出 Activity 销毁 TbsReaderView

退出页面时,一定要销毁 TbsReaderView,否则下次进来无法打开文件。

@Override
protected void onDestroy() {
super.onDestroy();
if (mSuperFileview != null) {
mSuperFileview.onStopDisplay();
}
}