我在TransactionTooLargeException
从单个APK运行的两个Android进程之间发送消息时收到了消息.每条消息只包含少量数据,远小于总计1 mb(如文档中所指定).
我创建了一个测试应用程序(下面的代码)来解决这个问题,并注意到三件事:
android.os.TransactionTooLargeException
如果每条消息都超过200 kb 我得到了一个.
android.os.DeadObjectException
如果每条消息都低于200kb,我得到了一个
添加一个Thread.sleep(1)
似乎已经解决了这个问题.我不能得到一个例外Thread.sleep
通过Android C++代码,似乎transaction
失败的原因不明,并被解释为其中一个异常
什么是" transaction
"?
什么定义了交易中的内容?在给定时间内是一定数量的事件吗?或者只是最大数量/大小的事件?
有没有办法"刷新"交易或等待交易完成?
什么是避免这些错误的正确方法?(注意:将其分解为更小的部分只会引发不同的异常)
AndroidManifest.xml中
MainActivity.kt
class MainActivity : AppCompatActivity() { private lateinit var sendDataButton: Button private val myServiceConnection: MyServiceCOnnection= MyServiceConnection(this) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) myServiceConnection.bind() sendDataButton = findViewById(R.id.sendDataButton) val maxTransactiOnSize= 1_000_000 // i.e. 1 mb ish // Number of messages val n = 10 // Size of each message val bundleSize = maxTransactionSize / n sendDataButton.setOnClickListener { (1..n).forEach { i -> val bundle = Bundle().apply { putByteArray("array", ByteArray(bundleSize)) } myServiceConnection.sendMessage(i, bundle) // uncommenting this line stops the exception from being thrown // Thread.sleep(1) } } } }
MyServiceConnection.kt
class MyServiceConnection(private val context: Context) : ServiceConnection { private var service: Messenger? = null fun bind() { val intent = Intent(context, BoundService::class.java) context.bindService(intent, this, Context.BIND_AUTO_CREATE) } override fun onServiceConnected(name: ComponentName, service: IBinder) { val newService = Messenger(service) this.service = newService } override fun onServiceDisconnected(name: ComponentName?) { service = null } fun sendMessage(what: Int, extras: Bundle? = null) { val message = Message.obtain(null, what) message.data = extras service?.send(message) } }
BoundService.kt
internal class BoundService : Service() { private val serviceMessenger = Messenger(object : Handler() { override fun handleMessage(message: Message) { Log.i("BoundService", "New Message: ${message.what}") } }) override fun onBind(intent: Intent?): IBinder { Log.i("BoundService", "On Bind") return serviceMessenger.binder } }
*的build.gradle
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 27 defaultConfig { applicationId "com.example.boundservicestest" minSdkVersion 19 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.android.support:appcompat-v7:27.1.1' }
堆栈跟踪
07-19 09:57:43.919 11492-11492/com.example.boundservicestest E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.boundservicestest, PID: 11492 java.lang.RuntimeException: java.lang.reflect.InvocationTargetException at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:448) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) Caused by: java.lang.reflect.InvocationTargetException at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) Caused by: android.os.DeadObjectException: Transaction failed on small parcel; remote process probably died at android.os.BinderProxy.transactNative(Native Method) at android.os.BinderProxy.transact(Binder.java:764) at android.os.IMessenger$Stub$Proxy.send(IMessenger.java:89) at android.os.Messenger.send(Messenger.java:57) at com.example.boundservicestest.MyServiceConnection.sendMessage(MyServiceConnection.kt:32) at com.example.boundservicestest.MainActivity$onCreate$1.onClick(MainActivity.kt:30) at android.view.View.performClick(View.java:6294) at android.view.View$PerformClick.run(View.java:24770) at android.os.Handler.handleCallback(Handler.java:790) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6494) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Pravin Divra.. 7
1)什么是“交易”?
当客户端进程调用服务器进程时(在我们的示例中service?.send(message)
),它会传输表示要调用的方法的代码以及封送数据(宗地)。此调用称为事务。客户端Binder对象调用,transact()
而服务器Binder对象在onTransact()
方法中接收此调用。检查此和此。
2)什么定义了交易的内容?在给定时间内是否有一定数量的事件?还是仅仅是事件的最大数量/大小?
通常,这是由Binder协议决定的,它们使用代理(由客户端)和存根(由服务)。代理接受您的高级Java / C ++方法调用(请求),并将其转换为包裹(编组),然后将事务提交给Binder内核驱动程序并进行阻止。另一方面(在Service流程中),Stub会侦听Binder内核驱动程序,并在收到回调后将包裹解组为服务可以理解的丰富数据类型/对象。
如果是Android Binder框架,则通过transact()发送的数据是一个Parcel(这意味着我们可以发送Parcel对象支持的所有类型的数据。),存储在Binder事务缓冲区中.Binder事务缓冲区的固定大小有限,当前为1Mb,该进程正在进行的所有交易都共享该数据。因此,如果每个消息超过200 kb,则5个或更少的正在运行的事务将导致限制超过并抛出TransactionTooLargeException
。因此,即使有许多交易正在进行,即使有许多交易正在进行,也可能引发此异常。将会看到一个活动DeadObjectException
如果它使用在另一个进程中运行的服务,而该进程在执行请求的过程中终止,则异常。在Android中杀死进程的原因很多。检查此博客以获取更多信息。
3)是否可以“刷新”交易或等待交易完成?
transact()
默认情况下,对客户端线程的调用(在process1中运行)会阻塞,直到onTransact()
在远程线程(在process2中运行)执行完为止。因此,事务API实际上在Android中是同步的。如果您不希望transact()调用阻塞,则可以传递IBinder.FLAG_ONEWAY标志(标记为transact(int,Parcel,Parcel,int))立即返回,而无需等待任何返回值。您必须实现您为此定制的IBinder实现。
4)避免这些错误的正确方法是什么?(注意:将其分解成较小的部分只会抛出不同的异常)
一次限制交易数量。执行真正必要的事务(一次所有正在进行的事务的消息大小必须小于1MB)。
确保正在运行其他Android组件的进程(应用程序进程除外)。
注意:-Android支持Parcel在不同进程之间发送数据。一个Parcel既可以包含将在IPC的另一侧进行平整化的平整数据(使用此处的各种方法来写入特定类型,也可以使用通用的Parcelable接口),还可以包含对活动IBinder对象的引用,这将导致另一侧接收到该数据。与包裹中的原始IBinder连接的代理IBinder。
将服务与活动绑定的正确方法是在Activity onStart()上绑定服务,然后在onStop()上取消绑定,这是Activity的可见生命周期。
在您的情况下,在MyServiceConnection
类中添加方法:
fun unBind() {
context.unbindService(this)
}
在您的Activity类中:
override fun onStart() { super.onStart() myServiceConnection.bind() } override fun onStop() { super.onStop() myServiceConnection.unBind() }
希望这会帮助你。
1)什么是“交易”?
当客户端进程调用服务器进程时(在我们的示例中service?.send(message)
),它会传输表示要调用的方法的代码以及封送数据(宗地)。此调用称为事务。客户端Binder对象调用,transact()
而服务器Binder对象在onTransact()
方法中接收此调用。检查此和此。
2)什么定义了交易的内容?在给定时间内是否有一定数量的事件?还是仅仅是事件的最大数量/大小?
通常,这是由Binder协议决定的,它们使用代理(由客户端)和存根(由服务)。代理接受您的高级Java / C ++方法调用(请求),并将其转换为包裹(编组),然后将事务提交给Binder内核驱动程序并进行阻止。另一方面(在Service流程中),Stub会侦听Binder内核驱动程序,并在收到回调后将包裹解组为服务可以理解的丰富数据类型/对象。
如果是Android Binder框架,则通过transact()发送的数据是一个Parcel(这意味着我们可以发送Parcel对象支持的所有类型的数据。),存储在Binder事务缓冲区中.Binder事务缓冲区的固定大小有限,当前为1Mb,该进程正在进行的所有交易都共享该数据。因此,如果每个消息超过200 kb,则5个或更少的正在运行的事务将导致限制超过并抛出TransactionTooLargeException
。因此,即使有许多交易正在进行,即使有许多交易正在进行,也可能引发此异常。将会看到一个活动DeadObjectException
如果它使用在另一个进程中运行的服务,而该进程在执行请求的过程中终止,则异常。在Android中杀死进程的原因很多。检查此博客以获取更多信息。
3)是否可以“刷新”交易或等待交易完成?
transact()
默认情况下,对客户端线程的调用(在process1中运行)会阻塞,直到onTransact()
在远程线程(在process2中运行)执行完为止。因此,事务API实际上在Android中是同步的。如果您不希望transact()调用阻塞,则可以传递IBinder.FLAG_ONEWAY标志(标记为transact(int,Parcel,Parcel,int))立即返回,而无需等待任何返回值。您必须实现您为此定制的IBinder实现。
4)避免这些错误的正确方法是什么?(注意:将其分解成较小的部分只会抛出不同的异常)
一次限制交易数量。执行真正必要的事务(一次所有正在进行的事务的消息大小必须小于1MB)。
确保正在运行其他Android组件的进程(应用程序进程除外)。
注意:-Android支持Parcel在不同进程之间发送数据。一个Parcel既可以包含将在IPC的另一侧进行平整化的平整数据(使用此处的各种方法来写入特定类型,也可以使用通用的Parcelable接口),还可以包含对活动IBinder对象的引用,这将导致另一侧接收到该数据。与包裹中的原始IBinder连接的代理IBinder。
将服务与活动绑定的正确方法是在Activity onStart()上绑定服务,然后在onStop()上取消绑定,这是Activity的可见生命周期。
在您的情况下,在MyServiceConnection
类中添加方法:
fun unBind() {
context.unbindService(this)
}
在您的Activity类中:
override fun onStart() { super.onStart() myServiceConnection.bind() } override fun onStop() { super.onStop() myServiceConnection.unBind() }
希望这会帮助你。