欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

作为一个java后端程序员,我是怎么接手android项目,并完成移动端的pdf处理需求的?

程序员文章站 2022-03-12 09:15:39
本篇文章核心:Android调用第三方应用,打开pdf文件作为一个纯的java后端程序员,最近因为公司需要,不得不暂时负责维护一个android项目。现在项目到了需要修改的时候了。App功能涉及到预览pdf,并且还要支持添加注释、书签、查看文档目录等功能,需求还能更复杂一些吗?对于一个纯的java后端程序员来讲,大概很少有更难受的事情了,我太难了。所以最后我选择了跟我的领导大吵一架,接着愤而辞职!!!来表达我的不满!!!至于为什么会有这篇文章,哼哼… …此时… …我想你已经猜到答案了。什么?你...

本篇文章核心:Android调用第三方应用,打开pdf文件
作为一个纯的java后端程序员,最近因为公司需要,不得不暂时负责维护一个android项目。
现在项目到了需要修改的时候了。
App功能涉及到预览pdf,并且还要支持添加注释、书签、查看文档目录等功能,需求还能更复杂一些吗?对于一个纯的java后端程序员来讲,大概很少有更难受的事情了,我太难了。
所以最后我选择了跟我的领导大吵一架,接着愤而辞职!!!来表达我的不满!!!
至于为什么会有这篇文章,哼哼… …
此时… …我想你已经猜到答案了。
什么?你没猜到?那先容我卖个关子,在文章最后,再讲为什么有这篇文章。

现在,先进入主题。讲一讲android怎么调用第三方应用打开pdf文件

当然,你肯定早就在其他地方看到解决方法了,并且他们还提供了各种Intent,比如调用播放mp3的应用,播放mp4的应用,查看图片的应用等等。
这么一讲,我写这篇文章不是显得很鸡肋吗,直接转载别人的文章,不是更轻松吗?
绝对不是!作为一个“资(菜)深(鸡)”的android程序员,我列出了写代码的过程中遇到的所有问题,和思考问题的一些方式,以及解决方案。
其中包括困扰了我这个“大牛”两天的问题。

调用第三方应用打开pdf文件,也有两种方式。

方式一

列出所有能打开pdf的应用,让用户选择一个应用打开。
首先贴出实现效果:
作为一个java后端程序员,我是怎么接手android项目,并完成移动端的pdf处理需求的?

主要代码如下:

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW); 
Uri uri = Uri.fromFile(new File(path)); 
intent.setDataAndType(uri, "application/pdf");
startActivity(intent);

你很容易就能在其他博客中看到这种方式对不对?但是在android7.0以上的系统中使用就会有问题了,报错信息如下:android.os.FileUriExposedException: file:///xxx.pdf exposed beyond app through Intent.getData()
作为一个java后端程序员,我是怎么接手android项目,并完成移动端的pdf处理需求的?
作为一个java后端程序员,我是怎么接手android项目,并完成移动端的pdf处理需求的?

这是因为Android7.0 以上,对于权限相关控制更为严格,更何况笔者是基于android9.0系统进行开发,所以需要按照如下方式做一些配置。

1.在 res目录下创建provider_paths.xml
作为一个java后端程序员,我是怎么接手android项目,并完成移动端的pdf处理需求的?

2.provider_paths.xml内容

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

3. AndroidManifest.xml 添加节点

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@drawable/ic_launcher"
    android:supportsRtl="true"
    android:theme="@style/Theme.AppCompat.Light.NoActionBar"
    android:usesCleartextTraffic="true">
     … …
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>
</application>

4.获取uri方式修改

//Uri uri = Uri.fromFile(new File(path));
Uri 获取方式从上面的代码替换为下面
Uri uri = FileProvider.getUriForFile(getApplicationContext(), 
getApplicationContext().getPackageName() + ".provider", new File(path));

5.提供完整代码

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW); 
intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION );
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

Uri uri = FileProvider.getUriForFile(getApplicationContext(), 
getApplicationContext().getPackageName() + ".provider", new File(path)); 
intent.setDataAndType(uri, "application/pdf");
startActivity(intent);

注意此处,Intent的Flags设置。必须给予读的权限,否则会出现”pdf损坏,无法打开”的提示。
作为一个java后端程序员,我是怎么接手android项目,并完成移动端的pdf处理需求的?
否则出现”pdf损坏,不能读取“。
作为一个java后端程序员,我是怎么接手android项目,并完成移动端的pdf处理需求的?

方法二

指定应用启动。即,用户不需要选择,默认使用我们选择的pdf阅读器打开。
同样需要按方法一种的前两步,添加provider_paths.xml,修改AndroidManifest.xml
贴出主要代码:

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setPackage("com.buildtoconnect.pdfreader");
intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION );
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

//7.0 兼容
Uri uri = FileProvider.getUriForFile(getApplicationContext(), 
getApplicationContext().getPackageName() + ".provider", new File(path)); 
intent.setDataAndType(uri, "application/pdf");
startActivity(intent);

最主要的代码是这一行:

intent.setPackage("com.buildtoconnect.pdfreader");

设置要使用的pdf阅读器的包名,此处我使用的是17pdf阅读器。如果使用adobe的pdf阅读器,它的包名为“com.adobe.reader”,关于包名的获取,可以使用反编译,也可以利用安卓的api,获取手机中安装的应用,遍历查找,建议自行搜索。网上这类的文章很多,笔者是使用的反编译的方式获取的。

到此,使用第三方应用打开pdf的两种方式介绍完毕。具体使用哪一种根据各自的业务需求决定。
但是!!
等等… …毕竟说了当前属于“资(菜)深(鸡)”android程序员,事情怎么会如此顺利??
简单测试后发现,果然还有问题!!!
作为一个java后端程序员,我是怎么接手android项目,并完成移动端的pdf处理需求的?
什么情况,为什么不能添加注释?不能添加书签?
还有这个… …流文件?
作为一个java后端程序员,我是怎么接手android项目,并完成移动端的pdf处理需求的?

于是,我陷入了深深的沉思。开始“全网搜”历程,诸如“android使用第三方应用打开pdf 不能编辑?”、“android使用第三方应用打开pdf 不能添加注释?”
这一沉思,就是两天,在这期间,我注意到一个细节。看图:
作为一个java后端程序员,我是怎么接手android项目,并完成移动端的pdf处理需求的?

没错,文件名发生了变化,“软件测试计划.pdf”在阅读器中打开,变成了“122333444.pdf”
所以我推断出,用这种方式打开pdf,实际上不是打开的原文件,而是生成了一个临时文件,且是只读的临时文件。
这里顺带提一下,这个问题不是pdf阅读器本身的问题,因为在adobe中,用这种方式打开,同样不能编辑,提示“受保护的文件,不能执行操作”。

接下来就是黑暗的全网搜时段,甚至最后我都要放弃了,打算自己尝试做应用内打开算了,不然就陷入了无底洞呀。

就在这时候,我翻到一篇博客(博客地址忘记记录啦),提到另一种方式,处理android.os.FileUriExposedException: file:///xxx.pdf exposed beyond app through Intent.getData() 异常。
于是我尝试了以下写法,并且最终问题解决,附上主要源码

//使用17pdf阅读器 打开pdf  com.buildtoconnect.pdfreader
 Intent intent = new Intent();
 intent.setAction(Intent.ACTION_VIEW);
 intent.setPackage("com.buildtoconnect.pdfreader");
 intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION );
 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

 File pdfFile = new File(path);
 if(!pdfFile.exists()){
     Toast.makeText(getApplicationContext(),
      "pdf文件不存在!", Toast.LENGTH_SHORT).show();
     return;
 }
 //7.0 兼容
 Uri uri = Uri.fromFile(new File(path));
 StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
 StrictMode.setVmPolicy(builder.build());

 intent.setDataAndType(uri, "application/pdf");
 startActivity(intent);

就是这一段:

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
 StrictMode.setVmPolicy(builder.build());

前后折腾了两天,总算是搞定了。

最后,如果只是预览pdf,并且需要在应用内打开pdf,推荐使用github上面的一个开源库PdfViewer,项目地址: https://github.com/barteksc/AndroidPdfViewer
使用比较简单,比较容易实现功能。
如果需要在应用内打开pdf,建议可以参考这篇文章:android 在线预览pdf文件(目前最全)

咳咳,现在揭晓谜底的时候到了。为什么还有这篇文章了?
当然是因为我对技术孜孜不倦的钻研态度,即便是已经离职,还是要把问题解决了!!
???不信。
好吧,辞职当然不能随便辞的,文章开头就随便一说,既然接了项目,还是得做才行。哈哈哈… …

本文地址:https://blog.csdn.net/VA_AV/article/details/107133209