代码之家  ›  专栏  ›  技术社区  ›  Jason

什么是android binder“事务?“

  •  13
  • Jason  · 技术社区  · 6 年前

    我得到一个 TransactionTooLargeException 当在两个运行于一个APK的Android进程之间发送消息时。每条消息只包含少量数据, much smaller than the 1 mb total (as specified in the docs) .

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

    1. 我有一个 android.os.TransactionTooLargeException 如果每条消息超过200KB。

    2. 我有一个 android.os.DeadObjectException 如果每条消息小于200KB

    3. 添加 Thread.sleep(1) 似乎解决了这个问题。我也不能例外 Thread.sleep

    Looking through the Android C++ code ,看起来像 transaction 由于未知原因失败,并解释为这些异常之一

    问题

    1. 什么是“A” 交易 “?
    2. 什么定义了事务中的内容?在给定的时间内是否有一定数量的事件?或者只是事件的最大数量/大小?
    3. 是否有方法“刷新”事务或等待事务完成?
    4. 避免这些错误的正确方法是什么?(注意:把它分成小块只会引发一个不同的例外)


    代码

    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>
    

    主活动.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服务连接.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)
        }
    }
    

    边界服务.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
        }
    }
    

    生成.渐变*

    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) 
    
    2 回复  |  直到 6 年前
        1
  •  3
  •   Pravin Divraniya    6 年前

    service?.send(message) transact() onTransact() This This

    Parcel TransactionTooLargeException DeadObjectException this blog

    IBinder.FLAG_ONEWAY transact(int, Parcel, Parcel, int)

    MyServiceConnection

    fun unBind() { context.unbindService(this) }

    override fun onStart() {
            super.onStart()
            myServiceConnection.bind()
        }
    
        override fun onStop() {
            super.onStop()
            myServiceConnection.unBind()
        }
    

        2
  •  4
  •   aanshu    6 年前

    TransactionTooLargeException

    //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);
    

    byte[] byteArr = getIntent().getByteArrayExtra("image");
    Bitmap bmp = BitmapFactory.decodeByteArray(byteArr, 0, byteArr.length);
    

    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;
    }
    

    [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);