Android 什么是安卓活页夹;交易?“;

Android 什么是安卓活页夹;交易?“;,android,transactions,android-service,ipc,android-binder,Android,Transactions,Android Service,Ipc,Android Binder,当从一个APK运行的两个Android进程之间发送消息时,我得到了一个TransactionTooLargeException。每条消息只包含少量数据 我创建了一个测试应用程序(代码如下)来处理这种现象,并注意到三件事: 如果每条消息超过200KB,我会得到一个android.os.TransactionTooLargeException 如果每条消息小于200kb,我会得到一个android.os.DeadObjectException 添加线程。sleep(1)似乎解决了这个问题。我无法获得

当从一个APK运行的两个Android进程之间发送消息时,我得到了一个
TransactionTooLargeException
。每条消息只包含少量数据

我创建了一个测试应用程序(代码如下)来处理这种现象,并注意到三件事:

  • 如果每条消息超过200KB,我会得到一个
    android.os.TransactionTooLargeException

  • 如果每条消息小于200kb,我会得到一个
    android.os.DeadObjectException

  • 添加
    线程。sleep(1)
    似乎解决了这个问题。我无法获得
    线程的任何异常。sleep

  • ,似乎
    事务
    由于未知原因失败,并被解释为这些异常之一

    问题
  • 什么是“
    事务”
  • 什么定义了事务中的内容?在给定的时间内是否有一定数量的事件?或者只是活动的最大数量/规模
  • 有没有办法“刷新”事务或等待事务完成
  • 避免这些错误的正确方法是什么?(注意:将其拆分为更小的部分只会引发不同的异常)


  • 代码 AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest package="com.example.boundservicestest"
              xmlns:android="http://schemas.android.com/apk/res/android">
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
    
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
            </activity>
            <service android:name=".BoundService" android:process=":separate"/>
        </application>
    
    </manifest>
    
    MyServiceConnection.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)
                }
            }
        }
    }
    
    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)
        }
    }
    
    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
        }
    }
    
    BoundService.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)
                }
            }
        }
    }
    
    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)
        }
    }
    
    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'
    }
    
    Stacktrace

    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) 
    

    1.什么是“交易”?

    在远程过程调用期间,参数和调用的返回值作为存储在活页夹事务缓冲区中的地块对象进行传输。如果参数或返回值太大,无法放入事务缓冲区,则调用将失败并抛出

    2。什么定义了事务中的内容?在给定的时间内是否有一定数量的事件?或者只是活动的最大数量/规模? 仅限尺寸 活页夹事务缓冲区有一个有限的固定大小,当前为1Mb,该大小由流程中所有正在进行的事务共享

    3。是否有办法“刷新”事务或等待事务完成?

    没有

    4。避免这些错误的正确方法是什么?(注意:将其拆分为更小的部分只会引发不同的异常)

    据我所知,您的消息对象可能具有图像字节数组或其他大小超过1mb的内容。不要用包裹的方式邮寄

    选项1:对于图像,我认为您应该通过Bundle传递URI。使用毕加索,因为它不会多次下载图像

    选项2[不推荐]压缩字节数组,因为它可能无法压缩到所需大小

    //Convert to byte array
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
    byte[] byteArr = stream.toByteArray();
    
    Intent in1 = new Intent(this, Activity2.class);
    in1.putExtra("image",byteArr);
    
    然后在活动2中:

    byte[] byteArr = getIntent().getByteArrayExtra("image");
    Bitmap bmp = BitmapFactory.decodeByteArray(byteArr, 0, byteArr.length);
    
    选项3[推荐]使用文件读/写,并通过bundle传递uri

    写入文件:

    private void writeToFile(String data,Context context) {
        try {
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(context.openFileOutput("filename.txt", Context.MODE_PRIVATE));
            outputStreamWriter.write(data);
            outputStreamWriter.close();
        }
        catch (IOException e) {
            Log.e("Exception", "File write failed: " + e.toString());
        } 
    }
    
    读取文件:

    private String readFromFile(Context context) {
    
        String ret = "";
    
        try {
            InputStream inputStream = context.openFileInput("filename.txt");
    
            if ( inputStream != null ) {
                InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
                String receiveString = "";
                StringBuilder stringBuilder = new StringBuilder();
    
                while ( (receiveString = bufferedReader.readLine()) != null ) {
                    stringBuilder.append(receiveString);
                }
    
                inputStream.close();
                ret = stringBuilder.toString();
            }
        }
        catch (FileNotFoundException e) {
            Log.e("login activity", "File not found: " + e.toString());
        } catch (IOException e) {
            Log.e("login activity", "Can not read file: " + e.toString());
        }
    
        return ret;
    }
    
    选项4(使用gson) 写入对象

    [YourObject] v = new [YourObject]();
    Gson gson = new Gson();
    String s = gson.toJson(v);
    
    FileOutputStream outputStream;
    
    try {
      outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
      outputStream.write(s.getBytes());
      outputStream.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    
    如何读回:

     FileInputStream fis = context.openFileInput("myfile.txt", Context.MODE_PRIVATE);
     InputStreamReader isr = new InputStreamReader(fis);
     BufferedReader bufferedReader = new BufferedReader(isr);
     StringBuilder sb = new StringBuilder();
     String line;
     while ((line = bufferedReader.readLine()) != null) {
         sb.append(line);
     }
    
     String json = sb.toString();
     Gson gson = new Gson();
     [YourObject] v = gson.fromJson(json, [YourObject].class);
    
    1)什么是“交易”?

    当客户端进程调用服务器进程(在我们的例子中是
    service?.send(message)
    )时,它会传输表示要调用的方法的代码以及封送数据(包裹)。此调用称为事务。客户端绑定器对象调用
    transact()
    ,而服务器绑定器对象在
    onTransact()
    方法中接收此调用。检查并确认

    2)什么定义了事务中的内容?在给定的时间内是否有一定数量的事件?或者只是活动的最大数量/规模?

    一般来说,这是由绑定协议决定的。它们使用代理(由客户端)和存根(由服务)。代理接受高级Java/C++方法调用(请求)并将其转换为包(编组),然后将事务提交给Binder内核驱动程序和块。另一方面,存根(在服务过程中)侦听Binder内核驱动程序,并在收到回调时将包裹解组为服务可以理解的丰富数据类型/对象

    在Android Binder framwork的情况下,通过transact()发送数据(这意味着我们可以发送包裹对象支持的所有类型的数据。),存储在Binder事务缓冲区中。Binder事务缓冲区具有有限的固定大小,目前为1Mb,由流程中所有正在进行的事务共享。因此,如果每条消息的大小超过200 kb,则5个或更少的运行事务将导致超出限制并抛出
    TransactionTooLargeException
    。因此,当有许多事务正在进行时,甚至当大多数单个事务的大小适中时,也会引发此异常。如果使用在执行请求期间死亡的另一进程中运行的服务,则活动将看到<代码> Debug ObjultExabue>代码>异常。在安卓系统中,有很多理由可以扼杀进程。查看更多信息

    3)是否有办法“刷新”事务或等待事务完成?

    默认情况下,对
    transact()
    的调用会阻止客户端线程(在process1中运行),直到
    onTransact()
    在远程线程(在process2中运行)中执行完毕。因此,在安卓中,事务API本质上是同步的。如果不希望transact()调用被阻止,则可以传递标志(标志到)以立即返回,而无需等待任何返回值。为此,您必须实现自定义IBinder实现

    4)避免这些错误的正确方法是什么?(注意:将其拆分为更小的部分只会引发不同的异常)

  • 一次限制事务数。执行真正必要的事务(一次所有正在进行的事务的消息大小必须小于1MB)
  • 使苏尔