ParcelFileDescriptor+PdfRenderer在Android渲染显示PDF文件

ParcelFileDescriptor 是一个非常重要的类,用于表示一个文件描述符(File Descriptor,简称 FD),它可以让文件或数据通过进程间通信(IPC)进行共享。

1. 基本概念

  • ParcelFileDescriptorandroid.os 包下的类,通常用来操作文件或文件流,尤其是需要跨进程传递文件句柄时。
  • 它封装了一个底层的文件描述符,可以表示普通文件、管道、套接字等数据源。

2. 常见用途

  1. 跨进程共享文件:通过 Binder、AIDL 等机制,将文件描述符传递给其他进程。
  2. ContentProvider 配合 :在 ContentProvider 中,可以使用 ParcelFileDescriptor 返回一个文件句柄,而不是整个文件内容。
  3. 与大文件交互:通过流的方式读取文件内容,避免直接将大文件加载到内存中。

3. 创建方式

ParcelFileDescriptor 的创建方式多种多样,以下是常见的几种:

(1)通过 open 方法打开文件

java 复制代码
File file = new File("/path/to/file");
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);

常见模式:

  • MODE_READ_ONLY:只读模式。
  • MODE_WRITE_ONLY:只写模式。
  • MODE_READ_WRITE:读写模式。
  • MODE_APPEND:追加模式。

(2)通过 createPipe 创建管道

创建一个管道,用于进程间通信:

java 复制代码
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
ParcelFileDescriptor readSide = pipe[0];
ParcelFileDescriptor writeSide = pipe[1];

(3)通过 createSocketPair 创建套接字对

创建一对本地套接字:

java 复制代码
ParcelFileDescriptor[] socketPair = ParcelFileDescriptor.createSocketPair();
ParcelFileDescriptor socket1 = socketPair[0];
ParcelFileDescriptor socket2 = socketPair[1];

(4)通过 fromFd 使用已有文件描述符

如果已有文件描述符,可以直接创建:

java 复制代码
int fd = ...; // 已有的文件描述符
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(fd);

4. 读取和写入数据

ParcelFileDescriptor 本身不提供直接的读写操作,通常通过它获取 FileInputStreamFileOutputStream 来操作数据:

读取数据

java 复制代码
FileInputStream fis = new FileInputStream(pfd.getFileDescriptor());
byte[] buffer = new byte[1024];
int readBytes = fis.read(buffer);
fis.close();

写入数据

java 复制代码
FileOutputStream fos = new FileOutputStream(pfd.getFileDescriptor());
fos.write("Hello, ParcelFileDescriptor!".getBytes());
fos.close();

5. 关闭资源

ParcelFileDescriptor 使用后需要关闭,以防资源泄漏:

java 复制代码
pfd.close();

或者使用 try-with-resources 自动关闭:

java 复制代码
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)) {
    // 使用 pfd
}

6. 常见方法

方法 描述
getFileDescriptor() 返回底层的 FileDescriptor 对象。
detachFd() 分离底层的文件描述符,用作其他用途。
close() 关闭文件描述符,释放资源。
getStatSize() 返回文件的大小(如果支持)。
checkError() 检查是否发生错误。

7. 使用场景举例

(1)在 ContentProvider 中共享文件

java 复制代码
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
    File file = new File(context.getFilesDir(), "example.txt");
    return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}

(2)AIDL 中跨进程传递文件描述符

java 复制代码
@Override
public ParcelFileDescriptor getFileDescriptor() {
    File file = new File("/path/to/file");
    return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}

PdfRenderer

是 Android 提供的一个类,用于解析和渲染 PDF 文件。它需要通过 ParcelFileDescriptor 来获取 PDF 文件的描述符,从而读取和渲染 PDF 文件的页面。如果需要实现类似 PDF 阅读器的分页浏览,可以将 PdfRendererParcelFileDescriptor 结合 ViewPager 或 RecyclerView。每页创建一个 Bitmap,并动态加载和回收页面内容。

java 复制代码
    private void renderPage(File file) {

        Observable.create((ObservableOnSubscribe<Pair<Integer, Bitmap>>) emitter -> {

                    try {
                        ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
                        renderer = new PdfRenderer(parcelFileDescriptor);
                        int w = getResources().getDisplayMetrics().widthPixels;
                        int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics());
                        int h = getResources().getDisplayMetrics().heightPixels - padding;
                        ArrayList<Bitmap> pageList = new ArrayList<>();
                        // let us just render all pages
                        final int pageCount = renderer.getPageCount();
                        for (int i = 0; i < pageCount; i++) {
                            PdfRenderer.Page page = renderer.openPage(i);
                            Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
                            Canvas canvas = new Canvas(bitmap);
                            canvas.drawColor(Color.WHITE);
                            // say we render for showing on the screen
                            page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
                            // do stuff with the bitmap
                            pageList.add(bitmap);
                            // mulRenderAdapter.add(i, bitmap);
                            // close the page
                            page.close();
                            emitter.onNext(new Pair<>(i, bitmap));
//                            if (i == pageCount-1){
//                                runOnUiThread(this::dismissLoading);
//                            }
                        }
                        // close the renderer
                    } catch (Exception e) {
                        e.printStackTrace();
                        runOnUiThread(this::dismissLoading);
                    } finally {
                        if (renderer != null) {
                            renderer.close();
                        }
                    }
                }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new BaseObserver<Pair<Integer, Bitmap>>() {
                    @Override
                    public void onSuccess(Pair<Integer, Bitmap> pair) {
                        dismissLoading();
                        mulRenderAdapter.add(pair.first, pair.second);
                    }

                    @Override
                    public void onFailure(Throwable e) {
                        dismissLoading();
                    }
                });
//        new Handler().postDelayed(this::dismissLoading, 3 * 1000);
    }
相关推荐
麻辣炖土豆儿8 分钟前
Android BottomNavigationView不加icon使text垂直居中,完美解决。
android
听我一言1 小时前
Qt for android : 简单实现弹窗创建文件,并使用JNI进行读写实例
android·开发语言·qt
小兵小卒1 小时前
ElectronSharp,.Net跨平台的多一种选择
android·macos·c#·.net·wpf
Aric1 小时前
android mqtt demo
android
alexhilton2 小时前
Compose多平台 (CMP) 开发的四个实用技巧
android·kotlin·android jetpack
氤氲息3 小时前
Android fragment的写
android
ytuglt4 小时前
Mac M1处理器uiautomatorviewer 使用
android·macos
柯南二号5 小时前
Android JecPack组件之LifeCycles 使用详解
android·jetpack·lifecycles
万亿少女的梦1685 小时前
基于php求职招聘系统设计
android·php·求职招聘
xvch6 小时前
Kotlin 2.1.0 入门教程(一)
android·kotlin