项目中用到过mupdf第三方库来开发android应用直接打开pdf文件的功能,为了更多的了解mupdf库上网找资料发现一篇个人文章写的还不错,拿来记录一下:

一时兴起

因为自己前段时间一直在做故事会的一个客户端,当然是非官方版,主要是由于自己的兴趣所致。以前就挺喜欢看故事会的,所以就希望做一个故事会的客户端,在手机上随时随地地看。因为故事会的官方APP的体验实在是太差了,而且资源还不全(而且还收费),所以我就打算自己做一个,然后去收集网上的资源。因为网上的故事会是以PDF文件的形式出现的,刚开始我想调用手机上的软件例如WPS等来阅读,后来一想反正做那就做PDF文件阅读功能吧,反正也没做过,正好试试呗。

一波三折

说做就做,刚开始使用的开源库是PDFView,就结果来说,软件体积太大了,而且使用的时候加载有点慢了,所以当时就不是太想使用这个库。后来没办法妥协呗,想了一个折中的办法,在开始的时候,利用PDFView提供的方法将PDF文件转换成一张张的图片保存到手机里,等加载的时候直接使用Picasso等加载图片的库来加载本地图片,这样总体来说还是不错的,直接加载图片比直接解析PDF文件要快很多,所以就出现了第一个版本,有兴趣的可以下载看一看。

后来,又随便在网上搜了搜,又找到一个不错的开源库,也就是MuPdf,这个和PDFView相比体积小,只有原来的一半,这已经是一个很大的诱惑了,没什么比体积小更让人扛不住的了。然后就开始找一些实现代码,最后找到了一个Eclipse版本的,它的阅读方式是横屏滑动阅读,我更喜欢竖屏阅读,所以就改成用ListView来显示,而且重要的是可以直接去显示,而不需要生成图片在手机里(因为这个做了实现封装),所以我就改了改,然后使用LruCache来保证不会出现内存溢出的情况,Demo运行还是不错的。

因为我的那个故事会客户端是使用AndroidStudio开发的,所以必须把MuPdf移植到AndroidStudio上。因为MuPdf需要调用so文件,所以我就先做了一个Demo,看看能不能运行出来,结果没问题再移植,然后悲催就开始了!!

本来so接触的就不多(其实很少%>_<%),所以怎么导入就是一个大问题,去网上搜有的说是建立jniLibs文件夹,然后放在里面,然而并无卵用;还有的说将so文件压缩成jar文件,然后放在libs里面,同样无卵用,最后真是要崩溃了。折腾了一下午,出去吃个晚饭,路上又搜了一种解决方法,告诉自己回去再试最后一次,不行我就放弃了,吃完饭回来试一下居然可以了,终于不在停止运行了。

问题出在哪我想暂时是找不到了,大概就是因为运行是没有正确的加载so文件,所以一些底层方法不能调用而崩溃了。

解决方法

    task nativeLibsToJar(type: Zip, description: "create a jar archive of the native libs"){destinationDir file("$projectDir/libs")baseName "Native_Libs2"extension "jar"from fileTree(dir: "libs", include: "**/*.so")into "lib"}tasks.withType(JavaCompile){compileTask -> compileTask.dependsOn(nativeLibsToJar)}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

将这一段代码加在build.gradle里,同时将libmupdf.so文件放在如下位置:

图中的Native_Libs2.jar是编译之后出现的,如果编译之后没有出现这个文件,那么可能就不会运行成功了。

哦,对了还要注意的是MuPdf的那些类的包名必须是com.artifex.mupdf,如下:

不然好像也不能运行成功。

柳暗花明

因为在AndroidStudio上可以运行成功了,所以我就打算将原来的客户端重新改写一下,将PdfViewer替换成MuPdf,一来可以减小APK的体积,二来也不用将PDF文件转换成图片来显示,很方便,等完成之后再将软件上传上来让有兴趣的看看。

下载

MuPdf的Demo-Eclipse版

MuPdf的Demo-AndroidStudio版

故事会客户端-PDFView版

故事会客户端-MuPdf版


附:经朋友测试,发现如果打开的PDF文件如果是损坏的话,那么应用就直接崩溃了,其次如果快速的关闭和打开应用,那么应用也会崩溃。这里我上传一份解决上述问题的版本(AndroidStudio版),它解决了上述问题,其实也就是换了一个so库,不过确实不会崩溃就是了。还需要注意的是新版的加载so库的方法和旧版不一样,大家在build.gradle中可以看到。

新版下载

看了作者写的上述过程感觉作者很用心在学习。但其中gradle文件中加的那个生成jar包的task我不太明白作者的目的,难道没有jar包就不能使用mupdf吗?我们项目里就没有其对应的jar包只有so文件啊。当然我们项目里直接将用so文件编写的一个demo引入到项目中了。

另一关于此知识的文章:

一、基本实现

 1,导入so库;

2,声明库文件中方法

[java] view plaincopyprint?
  1. </pre><pre name="code" class="java">public class MuPDFCore {
  2. /* load our native library */
  3. static {
  4. System.loadLibrary("mupdf");
  5. }
  6. /* Readable members */
  7. private int pageNum = -1;;
  8. private int numPages = -1;
  9. public float pageWidth;
  10. public float pageHeight;
  11. /* The native functions */
  12. private static native int openFile(String filename);
  13. private static native int countPagesInternal();
  14. private static native void gotoPageInternal(int localActionPageNum);
  15. private static native float getPageWidth();
  16. private static native float getPageHeight();
  17. public static native void drawPage(Bitmap bitmap, int pageW, int pageH,
  18. int patchX, int patchY, int patchW, int patchH);
  19. public static native RectF[] searchPage(String text);
  20. public static native int getPageLink(int page, float x, float y);
  21. public static native boolean hasOutlineInternal();
  22. public static native boolean needsPasswordInternal();
  23. public static native boolean authenticatePasswordInternal(String password);
  24. public static native void destroying();
  25. public MuPDFCore(String filename) throws Exception {
  26. if (openFile(filename) <= 0) {
  27. throw new Exception("Failed to open " + filename);
  28. }
  29. }
  30. public int countPages() {
  31. if (numPages < 0)
  32. numPages = countPagesSynchronized();
  33. return numPages;
  34. }
  35. private synchronized int countPagesSynchronized() {
  36. return countPagesInternal();
  37. }
  38. /* Shim function */
  39. public void gotoPage(int page) {
  40. if (page > numPages - 1)
  41. page = numPages - 1;
  42. else if (page < 0)
  43. page = 0;
  44. if (this.pageNum == page)
  45. return;
  46. gotoPageInternal(page);
  47. this.pageNum = page;
  48. this.pageWidth = getPageWidth();
  49. this.pageHeight = getPageHeight();
  50. }
  51. public synchronized PointF getPageSize(int page) {
  52. gotoPage(page);
  53. return new PointF(pageWidth, pageHeight);
  54. }
  55. public synchronized void onDestroy() {
  56. destroying();
  57. }
  58. public synchronized void drawPage(int page, Bitmap bitmap, int pageW,int pageH, int patchX, int patchY, int patchW, int patchH) {
  59. gotoPage(page);
  60. drawPage(bitmap, pageW, pageH, patchX, patchY, patchW, patchH);
  61. }
  62. public synchronized int hitLinkPage(int page, float x, float y) {
  63. return getPageLink(page, x, y);
  64. }
  65. public synchronized RectF[] searchPage(int page, String text) {
  66. gotoPage(page);
  67. return searchPage(text);
  68. }
  69. public synchronized boolean hasOutline() {
  70. return hasOutlineInternal();
  71. }
  72. public synchronized boolean needsPassword() {
  73. return needsPasswordInternal();
  74. }
  75. public synchronized boolean authenticatePassword(String password) {
  76. return authenticatePasswordInternal(password);
  77. }
  78. }

  3,使用

[java] view plaincopyprint?
  1. public class MuPDFActivity extends BaseActivity {
  2. private MuPDFCore core;
  3. private String mFileName;
  4. private ListView mDocListView;
  5. private View mButtonsView;
  6. private boolean mButtonsVisible;
  7. private EditText mPasswordView;
  8. private TextView mFilenameView;
  9. private SeekBar mPageSlider;
  10. private TextView mPageNumberView;
  11. private ViewSwitcher mTopBarSwitcher;
  12. private ProgressBar loadingPB;
  13. private MyMuPDFPageAdapter pdfPageAdapter;
  14. @Override
  15. public void onCreate(Bundle savedInstanceState) {
  16. super.onCreate(savedInstanceState);
  17. if (core == null) {
  18. File sdcardDir = Environment.getExternalStorageDirectory();
  19. // 得到一个路径,内容是sdcard的文件夹路径和名字
  20. String path = sdcardDir.getPath() + "/MyMobileDownlod/test.pdf";
  21. int lastSlashPos = path.lastIndexOf('/');
  22. mFileName = new String(lastSlashPos == -1 ? path
  23. : path.substring(lastSlashPos + 1));
  24. System.out.println("Trying to open " + path);
  25. try {
  26. core = new MuPDFCore(path);
  27. } catch (Exception e) {
  28. }
  29. if (core != null && core.needsPassword()) {
  30. return;
  31. }
  32. }
  33. if (core == null) {
  34. AlertDialog alert = new AlertDialog.Builder(this).create();
  35. alert.setTitle(R.string.open_failed);
  36. alert.setButton(AlertDialog.BUTTON_POSITIVE, "Dismiss",
  37. new DialogInterface.OnClickListener() {
  38. public void onClick(DialogInterface dialog, int which) {
  39. finish();
  40. }
  41. });
  42. alert.show();
  43. return;
  44. }
  45. createUI(savedInstanceState);
  46. }
  47. public void createUI(Bundle savedInstanceState) {
  48. mDocListView = new ListView(this);
  49. pdfPageAdapter = new MyMuPDFPageAdapter(this, core);
  50. mDocListView.setAdapter(pdfPageAdapter);
  51. mButtonsView = getLayoutInflater().inflate(R.layout.buttons, null);
  52. mFilenameView = (TextView) mButtonsView.findViewById(R.id.docNameText);
  53. loadingPB = (ProgressBar) mButtonsView.findViewById(R.id.loadingPB);
  54. mTopBarSwitcher = (ViewSwitcher) mButtonsView
  55. .findViewById(R.id.switcher);
  56. mTopBarSwitcher.setDisplayedChild(0);
  57. mTopBarSwitcher.setVisibility(View.VISIBLE);
  58. loadingPB.setVisibility(View.INVISIBLE);
  59. mFilenameView.setText("Name:" + mFileName
  60. + String.format("    SumPage:%d", core.countPages()));
  61. RelativeLayout layout = new RelativeLayout(this);
  62. layout.addView(mDocListView);
  63. layout.addView(mButtonsView);
  64. layout.setBackgroundResource(R.drawable.tiled_background);
  65. setContentView(layout);
  66. }
  67. public void onDestroy() {
  68. if (core != null)
  69. core.onDestroy();
  70. core = null;
  71. super.onDestroy();
  72. }
  73. @Override
  74. protected void dispatchMsgOP(Message msg) {
  75. super.dispatchMsgOP(msg);
  76. if (1 == msg.what) {
  77. Toast.makeText(MuPDFActivity.this, "loading", Toast.LENGTH_SHORT)
  78. .show();
  79. loadingPB.setVisibility(View.VISIBLE);
  80. } else {
  81. loadingPB.setVisibility(View.INVISIBLE);
  82. }
  83. }
  84. }

 4,展示效果【实际应用】

实际使用说明:

当前PDF阅读器的实质是一个ListView,从而可以实现当前页面的重新布局,以达到实际需求。如:title栏变更效果,增加“分享”等后续操作。

二、so库的使用

1,导入so库源文件

so库分包有armeabi,arm64-v8a,armeabi-v7a等,是针对不同的ARM设备的包。armeabi,armeabi-v7a是32位ARM设备,arm64-v8a是64位ARM设备。arm64-v8a是向下兼容的,每个包有自己的优化和处理,最理想的条件是每个设备类型都有对应的so库文件。

注意事项是arm64-v8a中一定要有全部的armeabi的源文件,否则在调用到armeabi中独有的就会出错。当存在arm64-v8a中没有armeabi的so库文件时,只能删除整个包,保证程序能够正常运行。

2,关联库文件

[java] view plaincopyprint?
  1. <span style="font-size:18px;">    static {
  2. System.loadLibrary("mupdf");//加载库文件
  3. }</span>

生命so库文件中存在的方法,从而能够正常使用。后续调用,只是类本身之间的调用,遵循类的规则即可。


三、使用细节注意

1,包名一致【JNI调用规则】

Java_ + 包名(com.lucyfyr) 类名(HelloWorld) + 接口名(printJNI):必须要按此JNI规范来操作;

so库文件中方法在使用时,必须保证使用环境的包名与so库文件编译生成的包名一致。


2,so库的注意事项

注意事项:arm64-v8a中一定要有全部的armeabi的源文件,否则在调用到armeabi中独有文件方法时就会出错。当存在arm64-v8a中没有armeabi的so库文件时,只能删除整个包,保证程序能够正常运行。或者重新编译生成so库文件。

这里是源码

android中打开查看pdf文件可用mupdf相关推荐

  1. 新窗口打开vue项目中的静态pdf文件,并做权限控制

    问题:公司系统做安全测试,其中一个问题是没有登录的情况下,直接访问 "项目地址/operate.pdf "此地址,会展示pdf文件. 描述:点击系统内的一个按钮会打开新窗口展示系统 ...

  2. LabVIEW如何打开Acrobat PDF文件

    LabVIEW如何打开Acrobat PDF文件 如何能够在LabVIEW的VI中打开一个Acrobat Reader的文档? 解答: 在LabVIEW中打开一个PDF文件最大的困难就是寻找一种可靠的 ...

  3. 网页在线打开PDF_网站中在线查看PDF之TouchPDF

    一.网页在线打开PDF_网站中在线查看PDF之TouchPDF TouchPDF是用于jQuery的简单Web PDF查看器.它基于pdf.js库,并支持用于滑动页面和缩放的移动手势. 可以实现的功能 ...

  4. android中打开pdf,在Android应用程序中打开PDF

    我正在开发应用程序,需要在设备中打开pdf文件, 我实际上已经获得了与大多数示例类似的网络上的代码.但是,事情是我无法打开文件,控件直接转到"Exception"部分. 以下是以下 ...

  5. 如何在项目中使用pdf.js查看PDF文件

    最近在做后台管理类的系统,需要在web页面阅读pdf文件.发现一个很好用的插件 pdf.js,简单的介绍一下 官方文档地址:http://mozilla.github.io/pdf.js/ 在线演示地 ...

  6. android 在线预览pdf文件

    android原生webView不支持预览pdf文件,ios却可以,所以android想要实现在线预览webView要通过其他方法,有以下几种方法: 1.andorid原生自带的pdf管理库,主要提供 ...

  7. linux强制移除pdf密码,分享|如何在 Linux 中从一个 PDF 文件中移除密码

    今天,我碰巧分享一个受密码保护的 PDF 文件给我的一个朋友.我知道这个 PDF 文件的密码,但是我不想透露密码.作为代替,我只想移除密码并发送文件给他.我开始在因特网上查找一些简单的方法来从 PDF ...

  8. H5 页面如何查看 PDF 文件

    我们知道在 Chrome.Firefox 等浏览器可以查看 PDF 文件,不需要额外安装什么插件,这是由于 pdf.js 被预置到了这些浏览器中. pdf.js 是一款使用 HTML5 Canvas ...

  9. 《Adobe Acrobat DC经典教程》—第1章1.11节在阅读模式下查看PDF文件

    本节书摘来自异步社区<Adobe Acrobat DC经典教程>一书中的第1章1.11节在阅读模式下查看PDF文件,作者[美]Lisa Fridsma(丽莎 弗里斯玛) , Brie Gy ...

最新文章

  1. IdentityServer4关于多客户端和API的最佳实践【含多类型客户端和API资源,以及客户端分组实践】【中】...
  2. Codeforces Round #423 (Div. 2, rated, based on VK Cup Finals) C. String Reconstruction 并查集
  3. php加断点,使用dump函数,给php加断点测试
  4. jQuery的过滤遍历
  5. python中xlwt的局限,Python xlwt 生成Excel和设置特定单元格不可编辑
  6. fedora12下pppoe服务器的搭建
  7. sparksql整合hive
  8. xfce上安装mysql_在Fedora 33/32系统上安装PostgreSQL 13数据库的说明
  9. fopen 參数具体解释
  10. ntp server 配置参数_NTP时间服务器的配置
  11. ssm-学子商城-项目第十二天
  12. Hive窗口函数经典案例(保姆级案例)
  13. 关于飞信的协议以及验证码
  14. 8位12指令微程序CPU设计
  15. 前后端分离图片验证码session获取问题
  16. InDesign 教程如何创建和修改和使用母版页
  17. opencv实战——机器视觉检测和计数
  18. Linux 文件系统类型 文件系统结构 与Windows文件系统的比较
  19. 最简单的8421码计算方法
  20. 吉林计算机专业二本大学排名,吉林有哪些好二本大学?

热门文章

  1. linux中curl 访问接口返回:“Disallowed Key Characters ”(php CI框架)
  2. 圆台下料展开计算方法_圆台展开的方法
  3. BeautifulSoup抓取百思不得姐图片
  4. EPC总承包的概念理解
  5. 有向图强连通分量的Tarjan算法——转自BYVoid
  6. 3、CTS 编译调试如何打开log
  7. 死磕java底层(三)—反射、动态代理和注解
  8. 2015年计算机外文参考文献,计算机毕业设计外文参考文献
  9. 虚拟主机cPanel面板查看服务器的访问日志
  10. guest用户计算机管理中没有,win10guest用户怎么开启 没有本地用户和组