安卓加载PDF文件的几种方式

前端时间产品提了一个需求,在app内部加载PDF.ios平台有WKWebView可以直接以html的方式加载,但是安卓没有.查阅了相关资料,有以下几种方式.

1.连接Google服务器解析

安卓的WebView不支持PDF解析,因此通过连接Google的一个服务器转换成功后返回给WebView显示.但是Google的服务,总不能都让用户手机上装VPN吧.

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

2.用第三方库加载

Github上有一个Java开源项目 https://github.com/barteksc/AndroidPdfViewer,gradle依赖即可使用.作为一个组件,使用是比较方便的.不过,真的确定因为一个仅仅展示即过的功能,要引入一个类库?

3.使用Moliza开源的Pdf.js

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

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

如果要求支持的功能不是很多,用安卓提供的PdfRender就可以满足需求了.PdfRender加载多页的话可以配合ViewPager或者RecyclerView使用.需要注意的是使用PdfRender需要先将PDF文件下载到本地,是线程不安全的,并且API>=21才能使用.

这里提供的示例是阿里巴巴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);
        }
    }

}

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**

5.直接使用第三方软件打开

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

        File file = new File(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");
        startActivity(intent);

如果不是必须要在app内加载,这是一种比较好的方式

猜你喜欢

转载自blog.csdn.net/wzhseu/article/details/80580900