知方号

知方号

Android ParcelFileDescriptor实现进程间通信<安卓Parcel>

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

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

需求

一个通信通道,实现跨进程的的Socket网络通信。 具体的通信通道的图如下。

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

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

ParcelFileDescriptor

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

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

Android跨进程传图片或者大数据(解决TransactionTooLargeException)_binder传递bitmap-CSDN博客

android中非aidl实现进程间通信(编写顺序的parcel写入与读出)

在android源码中基于Binder对象的通信随处可见,几乎可以认定为以 I 打头的class,都具有进程间通信能力,如:IServiceManager,IContentProvider等。

在源码中实现的方式也可概括为两种:

1. 通过aidl来生成对Binder的操作。 2.手动调用IBinder.transact编写按照顺序写入与读出的parcel代码实现。

第一种方法网上案例较多,不多说。第二种方法实现源码参考:ActivityManagerNative,ActivityManagerProxy

关于第二种方法的实现本人做了一个demo,请看以下代码。

package dw.test; import java.util.HashMap; import android.os.Binder;import android.os.IBinder;import android.os.IInterface;import android.os.Parcel;import android.os.RemoteException;import android.util.Log;/** * 负责接收指令({@link CmdCode}),并将指令派发到相应的处理器({@link CmdDispatcher.Callback}) */public final class CmdDispatcher extends Binder implements IInterface{ private static final String LOG_TAG = CmdDispatcher.class.getSimpleName();public static final String DESCRIPTOR = CmdDispatcher.class.getName();/** * 存储所有指令处理器 * map.key = {@link CmdCode} */private HashMap mCallbacks = new HashMap();/** * 针对某个指令的处理 * @see #addCallback * @see #removeCallback */public interface Callback {/** * @param code 请求指令集{@link CmdCode.Request},响应 指令集{@link CmdCode.Response} * @param data 数据 {@link Parcel} * @param reply 处理data的结果 {@link Parcel} * @return */public boolean onTransact(int code, Parcel data, Parcel reply);}/** * 当client端调用 {@link IBinder#transact(int, Parcel, Parcel, int)}时,将会回调本方法。 */@Overrideprotected boolean onTransact(int code, Parcel data, Parcel reply,int flags) throws RemoteException {dispatch(code,data,reply); return true;}/** * 得到某个指令处理器并调用 */private void dispatch(int code, Parcel data, Parcel reply) {Log.i(LOG_TAG, "dispatch reply enter");Callback callback = mCallbacks.get(code);if(callback!=null){callback.onTransact(code, data, reply);}Log.i(LOG_TAG, "dispatch reply exit");}@Overridepublic IBinder asBinder() {return this;}@Overridepublic String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic IInterface queryLocalInterface(String descriptor) {return this;}/** * 针对某一个指令,如:请求指令集{@link CmdCode.Request},响应 指令集{@link CmdCode.Response} * 添加回调处理 * @param code 指令编码 * @param callback 针对某一个指定的处理 {@link Callback} */public void addCallback(int code,Callback callback) {mCallbacks.put(code, callback);} public void removeCallback(int code) {mCallbacks.remove(code);}} package dw.test;/** * 定义指令集 */public interface CmdCode {public interface BaseCode {/** * 每个parcel的头 */public static final int PARCEL_HEAD = 0xffff;public static final int RESULT_SUCCESS = 0x0001;public static final int RESULT_ERROR = 0x0002;}/** * 请求指令集 */public interface Request extends BaseCode{public static final int REQUEST = 0x0001;}/** * 响应指令集 */public interface Response extends BaseCode {public static final int RESPONSE = 0x0001;}} package dw.test; import dw.test.CmdDispatcher.Callback; import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.Parcel;import android.util.Log; /** * RemoteService作为一个独立进程存在. */public class RemoteCmdService extends Service implements Callback,CmdCode.Request{private static final String LOG_TAG = RemoteCmdService.class.getSimpleName();private final CmdDispatcher mCmdDispatcher = new CmdDispatcher();@Overridepublic IBinder onBind(Intent intent) {mCmdDispatcher.addCallback(REQUEST, this);return mCmdDispatcher;}@Overridepublic void onCreate() {super.onCreate();} @Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.i(LOG_TAG, "onStartCommand enter");return super.onStartCommand(intent, flags, startId);}@Overridepublic boolean onTransact(int code, Parcel data, Parcel reply) {Log.i(LOG_TAG, "remove service handle Reply enter");data.enforceInterface(CmdDispatcher.DESCRIPTOR);//读取包头int head = data.readInt();if(head==PARCEL_HEAD) {String handeResult = data.readString();reply.writeInt(RESULT_SUCCESS);Log.i(LOG_TAG, handeResult);} else {reply.writeInt(RESULT_ERROR); }Log.i(LOG_TAG, "remove service handle Reply exit");return true;} } package dw.test.activity; import android.app.Activity;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.Parcel;import android.os.RemoteException;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import dw.test.CmdCode;import dw.test.CmdDispatcher;import dw.test.R;import dw.test.RemoteCmdService; public class MainActivity extends Activity implements OnClickListener , CmdCode.Request,CmdCode.Response{ private static final String LOG_TAG = MainActivity.class.getSimpleName(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.findViewById(R.id.test_remote_binder_btn).setOnClickListener(this); } /** * 连接并调用远程服务 */ private void testRemote(){ Intent intent = new Intent(MainActivity.this,RemoteCmdService.class); //绑定远程服务 bindService(intent, new ServiceConnection() {@Overridepublic void onServiceDisconnected(ComponentName name) {}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {replyTo(service);}}, BIND_AUTO_CREATE); } private void replyTo(IBinder service) {Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();data.writeInterfaceToken(CmdDispatcher.DESCRIPTOR);//写入包头data.writeInt(PARCEL_HEAD);//写入要发送的字符数据data.writeString("serviceConnected");//当然你也可以传递一个binder对象过去作为callback,这样两个进程间就可以交互了。//data.writeStrongBinder(IBinder binder); try {//调用远程MESSAGE_REQUEST服务service.transact(REQUEST, data, reply,0);} catch (RemoteException e) {//ignore}//MESSAGE_REQUEST服务所返回的结果 int result = reply.readInt();if(RESULT_SUCCESS==result) {Log.i(LOG_TAG, "ok");}data.recycle();reply.recycle();} @Overridepublic void onClick(View v) {int id = v.getId();if(R.id.test_remote_binder_btn==id){testRemote();}} } 基于Android 匿名共享内存实现跨进程实时传输大量图片或数据

aidl传输文件有大小1M限制,单次传输不适合传递大数据,可以通过Android匿名共享内存的方法来解决这个问题, 使用aidl传递共享内存引用ParcelFileDescriptor方式传递图片信息。具体实现如下

一、service端 1.1.aidl文件IIpcService.aidl 定义,这里主要用到pfd参数 interface IIpcService {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/// void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,// double aDouble, String aString);void register2Server(String packageName,IIpcServiceListener ipcServiceListener);void unregister2Server(String packageName);String processClientRequest(String packageName,String clientRequest,inout ParcelFileDescriptor pfd);} 1.2 service端 处理客户端传递的图片流

引用ParcelFileDescriptor ,将获取的ParcelFileDescriptor转换成Bitmap 并回调给ui层显示

public String processClientRequest(String packageName, String clientRequest, ParcelFileDescriptor pfd) {     Log.i(TAG, "processClientRequest 11 packageName:" + packageName    + " clientRequest:" + clientRequest + " pfd:" + pfd);    String ret = clientRequest;    FileDescriptor fileDescriptor = pfd.getFileDescriptor();    FileInputStream fis = null;    try {        fis = new FileInputStream(fileDescriptor);        Bitmap rawBitmap = BitmapFactory.decodeStream(fis);        ret += " process success!";         Log.i(TAG, "processClientRequest 222 rawBitmap ByteCount:" + rawBitmap.getByteCount() + " mUiShow:" + mUiShow);         if (null != mUiShow) {            mUiShow.showBitmap(rawBitmap);        }    } catch (Exception e) {        Log.i(TAG, "processClientRequest 22 error:" + e);        e.printStackTrace();    } finally {        try {            if (fis != null) {                fis.close();            }        } catch (IOException e) {                Log.i(TAG, "processClientRequest 33 error:" + e);        }    }     Log.i(TAG, "processClientRequest 22 end ret:" + ret);    return ret;} 1.3 也可以处理客户端传递的字节数组 数据引用,处理代码如下 public String processClientRequest(String packageName, String clientRequest, ParcelFileDescriptor pfd) {     Log.i(TAG, "processClientRequest 11 packageName:" + packageName    + " clientRequest:" + clientRequest + " pfd:" + pfd);    String ret = clientRequest;    FileDescriptor fileDescriptor = pfd.getFileDescriptor();    FileInputStream fis = null;    try {        fis = new FileInputStream(fileDescriptor);         byte [] content = new byte[5];        fis.read(content);        Log.i(TAG, "processClientRequest 111 content:" + content);        for(int i=0;i

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至lizi9903@foxmail.com举报,一经查实,本站将立刻删除。