Android ParcelFileDescriptor实现进程间通信

需求

一个通信通道,实现跨进程的的Socket网络通信。

具体的通信通道的图如下。

需求分析
  1. 我们需要一个进程一直做通信通道的事情,业务进程把数据通过进程间通信交给通信进程。
  2. 通信进程通过Socket通道将数据发给网络另外一端的通信进程。
  3. 接收端的通信进程把数据再交给业务进程。
android进程间通信基本方式

android进程间通信是使用Binder来传数据,而Binder传输的数据,有一个最为基本的要求,就是要实现Parcelable接口。

ParcelFileDescriptor

ParcelFileDescriptor是android提供的一个数据结构。

文件描述符,是一种程序读写已打开文件、socket的对象。

FileDescriptor 对象,它代表了原始的Linux文件描述符
ParcelFileDescriptor对象,是原始文件描述符的一个复制,对象跟fd不同,但都是操作同一个底层文件流以及文件位置指针

public class ParcelFileDescriptor 
extends Object implements Parcelable, Closeable

ParcelFileDescriptor是可以用于进程间Binder通信的FileDescriptor。支持stream 写入和stream 读出

public static class ParcelFileDescriptor.AutoCloseInputStream 
extends FileInputStream 

public static class ParcelFileDescriptor.AutoCloseOutputStream 
extends FileOutputStream 

我们可以使用

ParcelFileDescriptor open (File file, int mode)

来将PacecelFileDescriptor 与File对应起来,以实现进程间的文件共享。

我们也可以使用

ParcelFileDescriptor[] createPipe ()

来建立一个pipe通信通道,ParcelFileDescriptor数组第一个元素是read端,第二个元素是write端,通过write端的AutoCloseOutputStream和read端的AutoCloseInputStream,我们就可以实现进程见的数据流传输了。

完整的跨进程数据传输方案如下:
  1. 文件传输

    两端业务层都把Uri对应的ParcelFileDescriptor发送给通信层,发送端通信层从AutoCloseInputStream中取数据发送,接收端通信层获取到数据后,写入到AutoCloseOutputStream中。

  2. 流传输

发送端:

  1. 业务层调用getOutputStream向通信层发起请求

  2. 通信层通过creatPipe 建立一个ParcelFileDescriptor数组,并将write端的pipe[1]返回给业务层

  3. 业务层得到pipe[1](ParcelFileDescriptor)后,可以通过AutoCloseOutputStream写入数据

  4. 从通信层的pipe[0]的AutoCloseInputStream中读出数据通过socket发送出去

接收端:

  1. 业务层调用getInputStream向通信层发起请求

  2. 通信层通过creatPipe 建立一个ParcelFileDescriptor数组,并将read端的pipe[0]返回给业务层

  3. 业务层得到pipe0后,可以通过AutoCloseInputStream读取数据。(如没有数据,则阻塞,一直等到有数据为止)

  4. socket中读取数据,写入到通信层的pipe[1]的AutoCloseOutputStream。(pipe[1]一旦写入,第三步中pipe[2]就可以读取出数据)

简单的ParcelFileDescriptor使用------pipe

java 复制代码
public class DemoParcefliledescriptor extends AppCompatActivity {
 
    private static final String TAG = "DemoPFD";
 
    private static final String[] PERMISSIONS = {
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
    };
    private static final int PERMISSIONS_CODE = 3006;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo_null);
 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(PERMISSIONS, PERMISSIONS_CODE);
        }
 
        FileOutputStream outputStream = new FileOutputStream(getStreamFd());
        try {
            outputStream.write(99);
            outputStream.write(98);
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
 
    private FileDescriptor getStreamFd() {
        ParcelFileDescriptor[] pipes = null;
 
        try {
            pipes = ParcelFileDescriptor.createPipe();
            new TransferThread(new ParcelFileDescriptor.AutoCloseInputStream(pipes[0])).start();
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        return pipes[1].getFileDescriptor();
    }
 
    static class TransferThread extends Thread {
        InputStream in;
        FileOutputStream out;
 
        TransferThread(InputStream in, FileOutputStream out) {
            this.in = in;
            this.out = out;
        }
 
        TransferThread(InputStream in) {
            this.in = in;
 
            File outFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/zlq_pdf");
            Log.i(TAG, "File: " + outFile.getAbsolutePath());
 
            try {
                out = new FileOutputStream(outFile);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
 
        @Override
        public void run() {
            byte[] buf = new byte[1024*2];
 
            int len;
            try {
                while((len=in.read(buf)) > 0) {
                    out.write(buf, 0, len);
                    Log.i(TAG, "out:" + len);
 
                }
 
                in.close();
                out.flush();
                out.getFD().sync();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
相关推荐
恋猫de小郭3 小时前
Android Studio 正式版 10 周年回顾,承载 Androider 的峥嵘十年
android·ide·android studio
aaaweiaaaaaa6 小时前
php的使用及 phpstorm环境部署
android·web安全·网络安全·php·storm
工程师老罗8 小时前
Android记事本App设计开发项目实战教程2025最新版Android Studio
android
pengyu12 小时前
系统化掌握 Dart 编程之异常处理(二):从防御到艺术的进阶之路
android·flutter·dart
消失的旧时光-194312 小时前
android Camera 的进化
android
基哥的奋斗历程13 小时前
Openfga 授权模型搭建
android·adb
Pakho love1 天前
Linux:文件与fd(被打开的文件)
android·linux·c语言·c++
勿忘初心911 天前
Android车机DIY开发之软件篇(九) NXP AutomotiveOS编译
android·arm开发·经验分享·嵌入式硬件·mcu
lingllllove1 天前
PHP中配置 variables_order详解
android·开发语言·php
消失的旧时光-19431 天前
Android-音频采集
android·音视频