ParcelFileDescriptor
是一个非常重要的类,用于表示一个文件描述符(File Descriptor,简称 FD),它可以让文件或数据通过进程间通信(IPC)进行共享。
1. 基本概念
ParcelFileDescriptor
是android.os
包下的类,通常用来操作文件或文件流,尤其是需要跨进程传递文件句柄时。- 它封装了一个底层的文件描述符,可以表示普通文件、管道、套接字等数据源。
2. 常见用途
- 跨进程共享文件:通过 Binder、AIDL 等机制,将文件描述符传递给其他进程。
- 与
ContentProvider
配合:在ContentProvider
中,可以使用ParcelFileDescriptor
返回一个文件句柄,而不是整个文件内容。 - 与大文件交互:通过流的方式读取文件内容,避免直接将大文件加载到内存中。
3. 创建方式
ParcelFileDescriptor
的创建方式多种多样,以下是常见的几种:
(1)通过 open
方法打开文件
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
创建管道
创建一个管道,用于进程间通信:
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
ParcelFileDescriptor readSide = pipe[0];
ParcelFileDescriptor writeSide = pipe[1];
(3)通过 createSocketPair
创建套接字对
创建一对本地套接字:
ParcelFileDescriptor[] socketPair = ParcelFileDescriptor.createSocketPair();
ParcelFileDescriptor socket1 = socketPair[0];
ParcelFileDescriptor socket2 = socketPair[1];
(4)通过 fromFd
使用已有文件描述符
如果已有文件描述符,可以直接创建:
int fd = ...; // 已有的文件描述符
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(fd);
4. 读取和写入数据
ParcelFileDescriptor
本身不提供直接的读写操作,通常通过它获取 FileInputStream
或 FileOutputStream
来操作数据:
读取数据
FileInputStream fis = new FileInputStream(pfd.getFileDescriptor());
byte[] buffer = new byte[1024];
int readBytes = fis.read(buffer);
fis.close();
写入数据
FileOutputStream fos = new FileOutputStream(pfd.getFileDescriptor());
fos.write("Hello, ParcelFileDescriptor!".getBytes());
fos.close();
5. 关闭资源
ParcelFileDescriptor
使用后需要关闭,以防资源泄漏:
pfd.close();
或者使用 try-with-resources
自动关闭:
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)) {
// 使用 pfd
}
6. 常见方法
方法 | 描述 |
---|---|
getFileDescriptor() | 返回底层的 FileDescriptor 对象。 |
detachFd() | 分离底层的文件描述符,用作其他用途。 |
close() | 关闭文件描述符,释放资源。 |
getStatSize() | 返回文件的大小(如果支持)。 |
checkError() | 检查是否发生错误。 |
7. 使用场景举例
(1)在 ContentProvider 中共享文件
@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 中跨进程传递文件描述符
@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 阅读器的分页浏览,可以将 PdfRenderer
和 ParcelFileDescriptor
结合 ViewPager 或 RecyclerView。每页创建一个 Bitmap
,并动态加载和回收页面内容。
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);
}