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();
            }
        }
    }
}
相关推荐
xiangpanf19 小时前
Laravel 10.x重磅升级:五大核心特性解析
android
robotx1 天前
安卓线程相关
android
消失的旧时光-19431 天前
Android 面试高频:JSON 文件、大数据存储与断电安全(从原理到工程实践)
android·面试·json
dalancon1 天前
VSYNC 信号流程分析 (Android 14)
android
dalancon1 天前
VSYNC 信号完整流程2
android
dalancon1 天前
SurfaceFlinger 上帧后 releaseBuffer 完整流程分析
android
用户69371750013841 天前
不卷AI速度,我卷自己的从容——北京程序员手记
android·前端·人工智能
程序员Android1 天前
Android 刷新一帧流程trace拆解
android
墨狂之逸才1 天前
解决 Android/Gradle 编译报错:Comparison method violates its general contract!
android
阿明的小蝴蝶1 天前
记一次Gradle环境的编译问题与解决
android·前端·gradle