君の知らないBundle

3942 ワード

Bundleの基本概念
Androidでは、Bundleは主にデータを転送するために使用され、キー値ペアでデータを保存します.Bundleを使用してActivity間でデータを転送することがよくあります.データ型は、基本タイプまたは対応する配列、またはオブジェクトまたはオブジェクト配列です.Bundleがオブジェクトまたはオブジェクト配列を渡す場合は、SerializableまたはParcelableインタフェースを実装する必要があります.
Androidの設計により、同じアプリケーションのActivityが異なるプロセスで実行される可能性があるため、データ転送を処理する際にプロセスをまたぐ場合がある.では、プロセス間でオブジェクトをどのように伝達しますか?AndroidではBinderによってプロセス間通信が実現される.Parcelは読み取りデータを格納するコンテナで、AndroidシステムのBinder通信ではParcelクラスを使用してクライアントとサービス側のデータのインタラクションを行い、AIDLのデータもParcelによってインタラクションされます.Java空間とC++ではParcelが実現されています.C/C++ではメモリを直接使用してデータを読み出すので、より効率的です.
Bundleデータ転送異常
Bundleの使用は私たちが見ているほど簡単ではありません.Bundleを使用する場合、いくつかの制限があります.この問題を一例で示します.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Bundle bundle = new Bundle();
                int[] array = new int[1024 * 1024];
                bundle.putIntArray("array", array);
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                intent.putExtras(bundle);
                startActivity(intent);
            }
        });
    }
}

上記の例では、Bundleを介してSecondActivityにデータを渡します.整数配列であるため,合計4 kBのデータがSecondActivityに伝達された.プログラムを実行すると、SecondActivityが起動していないことがわかり、ログにエラーが発生しました.
com.viclee.myapplication E/JavaBinder: !!! FAILED BINDER TRANSACTION !!!

内容からこの異常はBinderに関係していることが分かるが,この異常を生じたのは,Binderを用いてデータを交換するbufferサイズが1 Mのシステムによって制限されているためである.
では、転送データ量を512 kBに減らすなど、転送データ量を減らすと、安全に転送できますか?
プログラムを再実行すると、プログラムに異常が発生したことがわかりました.今回の異常内容は以下の通りです.
F/ActivityManager( 1068): android.os.TransactionTooLargeException
F/ActivityManager( 1068): at android.os.BinderProxy.transactNative(Native Method)
F/ActivityManager( 1068): at android.os.BinderProxy.transact(Binder.java:504)
F/ActivityManager( 1068): at android.app.ApplicationThreadProxy.scheduleLaunchActivity(ApplicationThreadNative.java:808)
F/ActivityManager( 1068): at com.android.server.am.ActivityStackSupervisor.realStartActivityLocked(ActivityStackSupervisor.java:1297)
F/ActivityManager( 1068): at com.android.server.am.ActivityStackSupervisor.attachApplicationLocked(ActivityStackSupervisor.java:632)
F/ActivityManager( 1068): at com.android.server.am.ActivityManagerService.attachApplicationLocked(ActivityManagerService.java:6572)
F/ActivityManager( 1068): at com.android.server.am.ActivityManagerService.attachApplication(ActivityManagerService.java:6637)
F/ActivityManager( 1068): at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:491)
F/ActivityManager( 1068): at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2502)
F/ActivityManager( 1068): at com.android.server.am.HwActivityManagerService.onTransact(HwActivityManagerService.java:232)
F/ActivityManager( 1068): at android.os.Binder.execTransact(Binder.java:454)

今度は何のためですか?ネット上では、Binderがデータ交換のために使用するbufferをアプリケーション全体で共有しているためだと言われています.したがって,Bundleを介してActivity間で伝達されるデータサイズは1 Mよりも小さい.
この問題を回避する方法については、できるだけintentで大きなデータを直接転送しないでください.このようなニーズがあれば、ファイルや共有メモリなどの静的転送を考慮することができます.
Bundle原理分析
Bundleのソースコードを確認することで、Bundle内部はHashMapではなくArrayMapによって実現されていることがわかりました.
Androidはどうしてこのように設計したのですか?
まずそれらの実現を見てみましょう.
ArrayMap内部は2つの配列を用いてデータ格納され、1つの配列はkeyのhash値を記録し、もう1つの配列はvalue値を記録し、内部は二分法を用いてkeyをソートし、二分法を用いてデータの追加、削除、検索を行うため、小数データ量操作にのみ適しており、データ量が大きい場合は性能が劣化する.一方,HashMap内部は配列+チェーンテーブルの構造であり,データ量が少ない場合,HashMapのEntry ArrayはArrayMapより多くのメモリを消費する.
Bundleを使用するシーンの多くは小さなデータ量であるため、ArrayMapを使用してデータを保存することは、操作速度とメモリ使用量の両方において優れているため、Bundleを使用してデータを転送することで、より高速でメモリ使用量を少なくすることができます.