在開發(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) {
}
};
//省略部分代碼...
}