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);
    }
相关推荐
阿巴斯甜5 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker6 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95277 小时前
Andorid Google 登录接入文档
android
黄林晴8 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab21 小时前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android