作为一个java后端程序员,我是怎么接手android项目,并完成移动端的pdf处理需求的?
本篇文章核心:Android调用第三方应用,打开pdf文件
作为一个纯的java后端程序员,最近因为公司需要,不得不暂时负责维护一个android项目。
现在项目到了需要修改的时候了。
App功能涉及到预览pdf,并且还要支持添加注释、书签、查看文档目录等功能,需求还能更复杂一些吗?对于一个纯的java后端程序员来讲,大概很少有更难受的事情了,我太难了。
所以最后我选择了跟我的领导大吵一架,接着愤而辞职!!!来表达我的不满!!!
至于为什么会有这篇文章,哼哼… …
此时… …我想你已经猜到答案了。
什么?你没猜到?那先容我卖个关子,在文章最后,再讲为什么有这篇文章。
现在,先进入主题。讲一讲android怎么调用第三方应用打开pdf文件。
当然,你肯定早就在其他地方看到解决方法了,并且他们还提供了各种Intent,比如调用播放mp3的应用,播放mp4的应用,查看图片的应用等等。
这么一讲,我写这篇文章不是显得很鸡肋吗,直接转载别人的文章,不是更轻松吗?
绝对不是!作为一个“资(菜)深(鸡)”的android程序员,我列出了写代码的过程中遇到的所有问题,和思考问题的一些方式,以及解决方案。
其中包括困扰了我这个“大牛”两天的问题。
调用第三方应用打开pdf文件,也有两种方式。
方式一
列出所有能打开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()
这是因为Android7.0 以上,对于权限相关控制更为严格,更何况笔者是基于android9.0系统进行开发,所以需要按照如下方式做一些配置。
1.在 res目录下创建provider_paths.xml
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损坏,无法打开”的提示。
否则出现”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程序员,事情怎么会如此顺利??
简单测试后发现,果然还有问题!!!
什么情况,为什么不能添加注释?不能添加书签?
还有这个… …流文件?
于是,我陷入了深深的沉思。开始“全网搜”历程,诸如“android使用第三方应用打开pdf 不能编辑?”、“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
上一篇: 性能工具之linux常见日志统计分析命令