Android匿名共享內(nèi)存

在開發(fā)中,如果在Binder傳輸傳輸比較大的數(shù)據(jù),會報TransactionTooLargeException錯誤異常,這個異常是因為Binder驅(qū)動對內(nèi)存限制引起的,默認(rèn)進(jìn)程在打開Binder驅(qū)動的時候,都限制為1M-8K的大小,也就是說,在默認(rèn)情況下,我們傳輸?shù)臄?shù)據(jù)不能大于這個數(shù)值
下面是通過Binder驅(qū)動傳輸一個1M數(shù)據(jù)的例子

ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        try {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            byte[] a = new byte[1024 * 1024 * 1];//1M
            data.writeByteArray(a);
            service.transact(12345, data, reply, 0);//發(fā)送數(shù)據(jù)到Service進(jìn)程
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
};
//通過bindService啟動一個Service
Intent intent = new Intent(this, RemoteService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);

程序運行之后會報TransactionTooLargeException


異常消息

這就是傳輸?shù)臄?shù)據(jù)超過限制大小所報的錯誤,所以傳輸大數(shù)據(jù)的時候,就可以通過Android匿名共享內(nèi)存的方法來解決這個問題

Android匿名共享內(nèi)存例子

發(fā)送端(AshmemActivity)
public class AshmemActivity extends AppCompatActivity {

    ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            try {
                String str = "發(fā)送端的數(shù)據(jù)";
                //創(chuàng)建一個MemoryFile對象,第二個參數(shù)為占用內(nèi)存的大小,這里為字符串字節(jié)數(shù)組的長度
                MemoryFile memoryFile = new MemoryFile("larger_data", str.getBytes().length);
                //將字符串轉(zhuǎn)化為字節(jié)數(shù)組寫入MemoryFile
                memoryFile.writeBytes(str.getBytes(), 0, 0, str.getBytes().length);
                //通過反射獲取MemoryFile的文件描述符,到時遠(yuǎn)程就可以通過這個參數(shù)獲取到數(shù)據(jù)
                Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
                FileDescriptor des = (FileDescriptor) method.invoke(memoryFile);

                Parcel data = Parcel.obtain();
                //將文件描述符打包到Parce
                data.writeFileDescriptor(des);
                //還有一些網(wǎng)上例子是把文件描述符封裝為ParcelFileDescriptor,再通過writeParcelable打包進(jìn)去
                //ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(des);
                //data.writeParcelable(pfd);
                //這兩種方法都可行,只是遠(yuǎn)程進(jìn)程獲取的方式有些不同

                //發(fā)送數(shù)據(jù)的字節(jié)數(shù)組長度,作用是告訴遠(yuǎn)程傳輸數(shù)據(jù)的長度
                data.writeInt(str.getBytes().length);
                Parcel reply = Parcel.obtain();
                //這里固定業(yè)務(wù)碼為88888
                service.transact(88888, data, reply, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ashmem);
        //啟動AshmenSerice服務(wù)
        Intent intent = new Intent(this, AshmemService.class);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mServiceConnection);
    }
}
接收端(AshmemService)
public class AshmemService extends Service {
    public AshmemService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new AshmenBinder();
    }

    class AshmenBinder extends Binder {
        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
            if (code == 88888) {
                try {
                    //獲取文件描述符
                    ParcelFileDescriptor pfd = data.readFileDescriptor();
                    //獲取傳輸數(shù)據(jù)的大小
                    int size = data.readInt();
                    //根據(jù)文件描述符獲取InputStream
                    InputStream inputStream = new FileInputStream(pfd.getFileDescriptor());
                    //定義一個緩沖區(qū)
                    byte[] bytes = new byte[1024];
                    //把數(shù)據(jù)寫入緩沖區(qū)
                    inputStream.read(bytes, 0, size);
                    //把字節(jié)數(shù)組轉(zhuǎn)換為字符串
                    String message = new String(bytes, 0, size, UTF_8);
                    //打印數(shù)據(jù)
                    Log.i("CJT", "接受到的數(shù)據(jù)為:" + message);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return true;
            } else {
                return super.onTransact(code, data, reply, flags);
            }
        }
    }
}

這樣就完成了一次跨進(jìn)程傳輸大數(shù)據(jù)的簡單流程,可能有人會想,不對呀,你就傳遞一個字符串,算哪門子大數(shù)據(jù)
那現(xiàn)在我們就根據(jù)上面通訊的例子,再把一張圖片從接收端(AshmemService)傳遞回發(fā)送端(AshmemActivity)

接收端(傳輸圖片)(AshmemService)
public class AshmemService extends Service {
    //...省略其他代碼
    class AshmenBinder extends Binder {
        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
            if (code == 88888) {
                try {
                    //獲取文件描述符
                    ParcelFileDescriptor pfd = data.readFileDescriptor();
                    //獲取傳輸數(shù)據(jù)的大小
                    int size = data.readInt();
                    //根據(jù)文件描述符獲取InputStream
                    InputStream inputStream = new FileInputStream(pfd.getFileDescriptor());
                    //定義一個緩沖區(qū)
                    byte[] bytes = new byte[1024];
                    //把數(shù)據(jù)寫入緩沖區(qū)
                    inputStream.read(bytes, 0, size);
                    //把字節(jié)數(shù)組轉(zhuǎn)換為字符串
                    String message = new String(bytes, 0, size, UTF_8);
                    //打印數(shù)據(jù)
                    Log.i("CJT", "接受到的數(shù)據(jù)為:" + message);
                    
                    //返回數(shù)據(jù)給接收端
                    //獲取一張本地圖片資源的Bitmap
                    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.larger_image);
                    //將Bitmap轉(zhuǎn)換為字節(jié)數(shù)組
                    ByteArrayOutputStream stream = new ByteArrayOutputStream();
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
                    byte[] img = stream.toByteArray();
                    //創(chuàng)建一個MemoryFile對象
                    MemoryFile file = new MemoryFile("larger_bitmap", img.length);
                    //將字節(jié)數(shù)組寫入
                    file.getOutputStream().write(img);
                    //反射獲取文件描述符
                    Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
                    FileDescriptor des = (FileDescriptor) method.invoke(file);
                    //通過writeParcelable打包文件描述符
                    reply.writeParcelable(ParcelFileDescriptor.dup(des), 0);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return true;
            } else {
                return super.onTransact(code, data, reply, flags);
            }
        }
    }
}
發(fā)送端(接收圖片)(AshmemActivity)
public class AshmemActivity extends AppCompatActivity {

    ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            try {
                String str = "發(fā)送端的數(shù)據(jù)";
                //創(chuàng)建一個MemoryFile對象,第二個參數(shù)為占用內(nèi)存的大小,這里為字符串字節(jié)數(shù)組的長度
                MemoryFile memoryFile = new MemoryFile("larger_data", str.getBytes().length);
                //將字符串轉(zhuǎn)化為字節(jié)數(shù)組寫入MemoryFile
                memoryFile.writeBytes(str.getBytes(), 0, 0, str.getBytes().length);
                //通過反射獲取MemoryFile的文件描述符,到時遠(yuǎn)程就可以通過這個參數(shù)獲取到數(shù)據(jù)
                Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
                FileDescriptor des = (FileDescriptor) method.invoke(memoryFile);

                Parcel data = Parcel.obtain();
                //將文件描述符打包到Parce
                data.writeFileDescriptor(des);
                //還有一些網(wǎng)上例子是把文件描述符封裝為ParcelFileDescriptor,再通過writeParcelable打包進(jìn)去
                //ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(des);
                //data.writeParcelable(pfd);
                //這兩種方法都可行,只是遠(yuǎn)程進(jìn)程獲取的方式有些不同

                //發(fā)送數(shù)據(jù)的字節(jié)數(shù)組長度,作用是告訴遠(yuǎn)程傳輸數(shù)據(jù)的長度
                data.writeInt(str.getBytes().length);
                Parcel reply = Parcel.obtain();
                //這里固定業(yè)務(wù)碼為88888
                service.transact(88888, data, reply, 0);
                //發(fā)送后等待遠(yuǎn)程返回
                //獲取文件描述符
                ParcelFileDescriptor pfd = reply.readParcelable(getClassLoader());
                //獲取數(shù)據(jù)流
                FileInputStream fileInputStream = new FileInputStream(pfd.getFileDescriptor());
                //將輸入流解碼為Bitmap
                Bitmap bitmap = BitmapFactory.decodeStream(fileInputStream);
                //設(shè)置到ImageView控件上
                imageView.setImageBitmap(bitmap);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };
    //省略部分代碼...
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容