为啥当 getParcelable 位于不同的 parceable 时会实例化不同的 parceable

Posted

技术标签:

【中文标题】为啥当 getParcelable 位于不同的 parceable 时会实例化不同的 parceable【英文标题】:why the different parceable get instantiated when getParcelable is on different one为什么当 getParcelable 位于不同的 parceable 时会实例化不同的 parceable 【发布时间】:2021-05-19 13:36:09 【问题描述】:

android 中有一些可打包的类

@Parcelize
class ParcelableClassA(private val member: Int) : IAParcelable 
    init 
        Log.e("+++", "+++ ParcelableClassA::init, this: $this")
    

@Parcelize
class ParcelableClassB(private val member: String) : IBParcelable 
    init 
        Log.e("+++", "+++ ParcelableClassB::init, this: $this")
    

在某些地方它被实例化并放入一个包中以创建一个活动。

Intent(context, TheActivity::class.java).apply 
    val bundle = Bundle()
    bundle.putParcelable(EXTRAS_CLASS_A, ParcelableClassA(1))
    bundle.putParcelable(EXTRAS_CLASS_B, ParcelableClassB("B")
...
    putExtras(bundle)

    context.startActivity(this)

在活动类中:

override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)

        val classA = intent.extras?.getParcelable(EXTRAS_CLASS_A) as? IAParcelable
        val classB = intent.extras?.getParcelable(EXTRAS_CLASS_B) as? IBParcelable


预计只有一份打印输出"+++ ParcelableClassA::init, this: $this" 但实际上看到 ParcelableClassA 的两个实例被创建,一个在

intent.extras?.getParcelable(EXTRAS_CLASS_A)

和其他也发生的时候

intent.extras?.getParcelable(EXTRAS_CLASS_B)

被调用。所以 ParcelableClassA::init 和 ParcelableClassB::init 都被调用了两次。

如果额外放入的数据越多,调用的ParcelableClassA的实例化就越多,这只是浪费。

这是预期的行为还是这里的做法不正确?

更新: 似乎在 BaseBundle 中,如果在 unparcel() 中并且 mParcelledData 不为空,它将具有此行为。但不确定为什么会发生。 如果一步一步调试,mParcelledData 总是为空,没有这个问题,但是如果只是运行它就会显示这个问题。

void unparcel() 
        synchronized (this) 
            final Parcel source = mParcelledData;
            if (source != null) 
                initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
             else 
                if (DEBUG) 
                    Log.d(TAG, "unparcel "
                            + Integer.toHexString(System.identityHashCode(this))
                            + ": no parcelled data");
                
            
        
    

【问题讨论】:

【参考方案1】:

看起来像是在使用intent.extra?导致问题。

如果更改为

override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)

        var bundle  = intent.extras

        val classA = bundle?.getParcelable(EXTRAS_CLASS_A) as? IAParcelable
        val classB = bundle?.getParcelable(EXTRAS_CLASS_B) as? IBParcelable


似乎没有看到副本。

【讨论】:

看起来像 Android 框架中的一个错误。您在哪个版本的 Android 上进行测试?您是否检查过运行不同 Android 版本的多台设备? 在 api 29 模拟器上运行。 kotlin intent.extras 可能会返回一个新的 Bundle(mExtras),同时使用 bundle = intent.extras,似乎 unparcel() 中的 initializeFromParcelLocked() 将清除 mParcelledData,因此 dup 停止。不确定这是否属实,因为如果在调试中步骤它没有问题。/ 通常Bundle 只拆包一次。这是一个惰性初始化,因此只要有对 Bundle 内容的引用就会发生。

以上是关于为啥当 getParcelable 位于不同的 parceable 时会实例化不同的 parceable的主要内容,如果未能解决你的问题,请参考以下文章

当按钮位于 UITableViewCell 上时,为啥 UIButton showsTouchWhenHighlighted 不起作用?

当设备位于我的用户目录中时,为啥 Android 模拟器会报告“未知虚拟设备”?

bundle.getparcelable java.lang.classnotfoundexception怎么解决

为啥当控制器位于 asp.net 核心应用程序的单独程序集中时,TestServer 无法找到控制器?

当 XAMPP 不在分区的根目录中时,为啥它不能工作?

为啥 p[:] 在这两种情况下的工作方式不同?