需求
一个通信通道,实现跨进程的的Socket网络通信。
具体的通信通道的图如下。
需求分析
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,我们就可以实现进程见的数据流传输了。
完整的跨进程数据传输方案如下:
-
两端业务层都把Uri对应的ParcelFileDescriptor发送给通信层,发送端通信层从AutoCloseInputStream中取数据发送,接收端通信层获取到数据后,写入到AutoCloseOutputStream中。
-
流传输
发送端:
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
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(); } } } }
借助ParcelFileDescriptor将Uri转为bitmap
private Bitmap uriToBitmap(Uri selectedFileUri) { Bitmap bitmap = null; try { ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(selectedFileUri, "r"); FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor); parcelFileDescriptor.close(); } catch (IOException e) { e.printStackTrace(); } return bitmap; } /* * bitmap转base64 * */ private String bitmapToBase64(Bitmap bitmap) { String result = null; ByteArrayOutputStream baos = null; try { if (bitmap != null) { baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); baos.flush(); baos.close(); byte[] bitmapBytes = baos.toByteArray(); result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (baos != null) { baos.flush(); baos.close(); } } catch (IOException e) { e.printStackTrace(); } } return result; } /*end*/ /** * base64转为bitmap * * @param base64Data * @return */ private Bitmap base64ToBitmap(String base64Data) { byte[] bytes = Base64.decode(base64Data, Base64.DEFAULT); return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); } 调用: ByteArrayOutputStream baos = new ByteArrayOutputStream(); String base64 = bitmapToBase64(uriToBitmap(imageUri)); base64ToBitmap(base64).compress(Bitmap.CompressFormat.PNG, 100, baos); byte[] bytes = baos.toByteArray(); glide可加载bytes