在安卓项目中,加载PDF文件,是一个比较常见的需求。又分为两大类,

1.加载网络PDF

2.加载一个本地静态PDF。

查阅资料,纵观网上在安卓中打开PDF的各种方式,大致可以分为以下几类,

1.直接使用第三方软件打开(包括浏览器打开和第三方软件打开)

如果是在app内部加载PDF文件,虽然安卓原生API对于PDF的支持又不是太好,但是各种第三方专门的阅读或者办公软件支持的是很不错的,可以通过Intent配置data和type实现,:

其中,在实际需求中又会分为加载本地PDF和网络PDF的情况。

使用浏览器打开PDF:(APP外部打开,适用于加载网络PDF)

public static void openPDFInBrowser(Context context, String url) {
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.w("error", "Activity was not found for intent, " + intent.toString());
}
}

使用浏览器打开PDF:(APP外部打开,适用于加载网络PDF)

使用手机上已安装的可以打开PDF的第三方软件来打开PDF:(APP外部打开,适用于加载本地PDF)
(使用这种方式缺点是:手机上如果没有一个可以打开PDF的软件,那么就尴尬了~

 public static void openPDFInNative(Context context, String FILE_NAME) {
File file = new File(context.getExternalCacheDir(),FILE_NAME);
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.fromFile(file);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(uri, "application/pdf");
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.w("URLSpan", "Activity was not found for intent, " + intent.toString());
}
}

加载本地文件

2.连接Google服务器解析

安卓的WebView不支持PDF解析,因此通过连接Google的一个服务器转换成功后返回给WebView显示。但是,但是,但是呢,大家都懂的,天朝和Google之间有一道高高的墙。方法还是贴出来,作为国际化APP的一种方案。

mWebView.loadUrl("http://docs.google.com/gview?embedded=true&url="+ pdfUrl);   

3.用第三方库加载

Github上有一个Java开源项目 https://github.com/barteksc/AndroidPdfViewer ,

这个库的大致原理,是内置了一个PDF解析器,以流的方式将网络PDF从网上Down下来,然后再以流的方式将其还原成PDF展示出来(具体细节没关注)。亲测中,这个库每次进入webview页面都会解析加载一遍PDF,如果PDF过大,费时无缓存不说,最致命的问题乃是,

APK包体积会瞬间增大15M左右,

具体原因不明,估计应该是内置PDF解析器的问题。于是,此方法被我抛弃了。

4.使用Moliza开源的Pdf.js(推荐)

这个JS类库也是很强大的,配合原生的WebView使用,支持预览,缩放,翻页的功能,实现效果和WKWebView没差。同样也有体积问题,全部放到本地apk的话包大小差不多会增加5M左右。但相比上一种方式还是轻量一些:

http://www.jb51.net/article/136364.htm

5.使用安卓自带的PdfRenderer类加载

如果要求支持的功能不是很多,用安卓提供的PdfRender就可以满足需求了。PdfRender加载多页的话可以配合ViewPager或者RecyclerView使用.需要注意的是使用PdfRender需要先将PDF文件下载到本地,是线程不安全的,并且API>=21才能使用。因为这种方式是将PDF下载到本地,于是就产生了新问题:占内存。如果是静态的PDF文件不大还好,但是如果是频繁加载网络PDF的需求,那就头疼了,这种方式需要做好定时清理删除PDF的工作,否则,GG。

这里提供的示例是阿里巴巴Android开发手册,放到assets目录下.

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MainActivity extends AppCompatActivity { private ViewPager vpPdf; private LayoutInflater mInflater;
private ParcelFileDescriptor mDescriptor;
private PdfRenderer mRenderer; public static final String FILE_NAME = "alibaba.pdf"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); mInflater = LayoutInflater.from(this);
vpPdf =findViewById(R.id.vp_pdf); try {
openRender();
} catch (IOException e) {
e.printStackTrace();
}
} @TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void openRender() throws IOException { File file = new File(getExternalCacheDir(), FILE_NAME);
if (!file.exists()) {
//复制文件到本地存储
InputStream asset = getAssets().open(FILE_NAME);
FileOutputStream fos = new FileOutputStream(file);
byte[] buffer = new byte[1024]; int size;
while ((size = asset.read(buffer)) != -1) {
fos.write(buffer, 0, size);
} asset.close();
fos.close();
} //初始化PdfRender
mDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
if (mDescriptor != null) {
mRenderer = new PdfRenderer(mDescriptor);
} //初始化ViewPager的适配器并绑定
MyAdapter adapter = new MyAdapter();
vpPdf.setAdapter(adapter);
} @Override
protected void onDestroy() {
//销毁页面的时候释放资源,习惯
try {
closeRenderer();
} catch (IOException e) {
e.printStackTrace();
}
super.onDestroy();
} private void closeRenderer() throws IOException{
mRenderer.close();
mDescriptor.close(); } class MyAdapter extends PagerAdapter{ @Override
public int getCount() {
return mRenderer.getPageCount();
} @Override
public boolean isViewFromObject(View view, Object object) {
return view==object;
} @Override
public Object instantiateItem(ViewGroup container, int position) {
View view = mInflater.inflate(R.layout.item_pdf, null); PhotoView pvPdf = view.findViewById(R.id.iv_pdf);
pvPdf.enable(); if (getCount() <= position) {
return view;
} PdfRenderer.Page currentPage = mRenderer.openPage(position);
Bitmap bitmap = Bitmap.createBitmap(1080, 1760, Bitmap.Config
.ARGB_8888);
currentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
pvPdf.setImageBitmap(bitmap);
//关闭当前Page对象
currentPage.close(); container.addView(view);
return view;
} @Override
public void destroyItem(ViewGroup container, int position, Object object) {
//销毁需要销毁的视图
container.removeView((View) object);
}
} }

assets

PdfRender的大致调用方式是:

  • 初始化PdfRender,将PDF文件转换为ParcelFileDescriptor作为入参
  • 通过getPageCount()获得Pdf的总页数
  • 循环遍历,通过openPage()获得一个封装了当前页Pdf参数的内部类Page
  • 创建一个Bitmap对象,,用上步获取的Page加载,相当于把当前Pdf页的内容渲染到了Bitmap上
  • 对Bitmap进行你想要的操作,每次循环结束后关闭当前Page.

大部分都是常规操作,但要注意两点:

1,每次循环创建的Page对象在使用完毕后必须调用close().否则其内部的openPage()判断会抛异常;

2.Bitmap占用内存较大,如果用ViewPager加载一定要重写Adapter中的destroyItem(),及时的将需要销毁的视图从父容器中移除,否则快速滑动几页后会OOM

转自:https://www.jianshu.com/p/f68e1087ea36 

android:加载PDF几种方法汇总对比的更多相关文章

  1. jquery动态加载js三种方法实例

    这里为你提供了三种动态加载js的jquery实例代码哦,由于jquery是为用户提供方便的,所以利用jquery动态加载文件只要一句话$.getScript(\"test.js\" ...

  2. vue路由页面加载的几种方法~

    懒加载 (1)定义:懒加载也叫延迟加载,即在需要的时候进行加载,随用随载. (2)为什么需要懒加载: 在单页应用中,如果没有应用懒加载,运用webpack打包后的文件将会异常的大,造成进入首页时,需要 ...

  3. jq 加载的几种方法

    jQuery加载的几种方式 //①页面加载完之前执行,与嵌入的js加载方式一样 (function ($) {     alert('start'); })(jquery); //②页面加载后执行 $ ...

  4. 【转】实现ViewPager懒加载的三种方法

    方法一 在Fragment可见时请求数据.此方案仍预加载了前后的页面,但是没有请求数据,只有进入到当前Framgent时才请求数据. 优点:实现了数据的懒加载缺点:一次仍是三个Framgment对象, ...

  5. mapper加载的3种方法

    <!-- mapper加载有3种方法: 1:通过resource或url加载单个mapper 2:通过mapper接口类名加载单个mapper 3:通过package批量加载多个mapper(推 ...

  6. WP & Win10开发:实现ListView下拉加载的两种方法

    1.通过ListView控件的ContainerContentChanging方法.该方法在列表项被实例化时触发,在列表项最后一个项目实例化的时候触发刷新数据逻辑就可以实现下拉加载了. 代码如下:// ...

  7. vue实现懒加载的几种方法

    vue实现惰性加载是基于: 1.ES6的异步机制 components: { comp: (resolve, reject) => {} } 2. webpack的代码分割功能 require. ...

  8. easyui combobox 动态加载的两种方法

    reload 方法 javascript代码 //指定id 和 text 否则始终选择第一个 $('#contact_city').combobox({ valueField:'id', textFi ...

  9. Android -- 加载大图片的方法

    在android中要加载一张大图片到内存中如果通过如下方式进行: Bitmap bitmap= BitmapFactory.decodeFile("/sdcard/a.jpg"); ...

  10. Android数据传递的五种方法汇总

    Android开发中,在不同模块(如Activity)间经常会有各种各样的数据需要相互传递,我把常用的几种 方法都收集到了一起.它们各有利弊,有各自的应用场景. 我现在把它们集中到一个例子中展示,在例 ...

随机推荐

  1. [转帖]LSM树详解

    https://zhuanlan.zhihu.com/p/181498475 LSM树(Log-Structured-Merge-Tree)的名字往往会给初识者一个错误的印象,事实上,LSM树并不像B ...

  2. 关于sar的学习

    关于sar的学习 背景 公司一套基于某冷门Python架构的系统前几天出现异常卡顿. 当时安装的时候必须使用ubuntu系统. 所以当时默认安装的ubuntu1804, 本来想尝试使用一下sar查看卡 ...

  3. [转帖]Centos 7 查看磁盘io ,找出占用io读写很高的进程

    1,先用iostat查看磁盘io 是否读写负载很高 用iostat -x 1 10 如果 iostat 没有,要 yum install sysstat安装这个包,第一眼看下图红色圈圈的那个如果%ut ...

  4. 《Javascript高级程序设计》读书笔记——构造函数与原型

    构造函数与原型 构造函数模式 最简单的构造函数: function Person(name, age, job) { this.name = name; this.age = age; this.jo ...

  5. 行云部署成长之路--慢SQL优化之旅 | 京东云技术团队

    ​ 当项目的SQL查询慢得像蜗牛爬行时,用户的耐心也在一点点被消耗,作为研发,我们可不想看到这样的事.这篇文章将结合行云部署项目的实践经验,带你走进SQL优化的奇妙世界,一起探索如何让那些龟速的查询飞 ...

  6. JS中every的简单使用

    every 方法 every()方法用于检测数组中的所有元素是否都满足指定条件. every()方法会遍历数组的每一项,如果有一项不满足条件,则返回false,剩余的项将不会再执行检测. 如果遍历完数 ...

  7. Leetcode 42题 接雨水(Trapping Rain Water) Java语言求解

    题目链接 https://leetcode-cn.com/problems/trapping-rain-water/ 题目内容 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱 ...

  8. 基于.Net Core3.1 MVC + EF Core的项目(一)框架的初步搭建

    项目暂时分为六大块,结构如图所示 代码地址是  https://github.com/hudean/VacantCloud-   里面有许多没有完成,不过一些大致的内容都写的差不多了,权限认证依赖注入 ...

  9. 5.2 Windows驱动开发:内核取KERNEL模块基址

    模块是程序加载时被动态装载的,模块在装载后其存在于内存中同样存在一个内存基址,当我们需要操作这个模块时,通常第一步就是要得到该模块的内存基址,模块分为用户模块和内核模块,这里的用户模块指的是应用层进程 ...

  10. LeetCode刷题日记 2020/08/18

    给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: 给定的有序链表: [-10 ...