在新活动中从 Intent 获取时,带有私有构造函数的 Parcelized 密封类会引发无法访问的错误,但在其他任何地方都有效?

Ben*_*654 6 kotlin

我制作了一个特殊的密封类来验证我的可空 URL,这样我就可以安全轻松地知道它的状态(无、无效、有效)

sealed class UrlType : Parcelable {
    @Parcelize class Valid private constructor(val url: String) : UrlType() {
        companion object : CompanionTest<UrlType.Valid, String>(::Valid)
    }

    @Parcelize object None : UrlType()
    @Parcelize class Invalid(val invalidUrl: String) : UrlType()

    companion object {

        fun getUrlType(url: String?): UrlType {
            return if (url == null) {
                UrlType.None
            } else if (!url.isValidUrl()) {
                UrlType.Invalid(url)
            } else {
                Valid.create(url)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
open class CompanionTest<out T, in A>(creator: (A) -> T) {

    private var creator: ((A) -> T)? = creator

    fun create(arg1: A): T {
        return creator!!(arg1)
    }
}
Run Code Online (Sandbox Code Playgroud)
@Parcelize
data class NewsPost(
        val id: String,
        val title: String,
        val description: String?,
        val webContentUrl: UrlType,
        val imageUrl: UrlType,
        val created: String,
        val updated: String,
        val feeds: List<Feed>
) : Parcelable
Run Code Online (Sandbox Code Playgroud)

当我第一次尝试这个想法时,我怀疑它不会因为私有构造函数和 Parcelize 而起作用,但我做了一些测试,它确实起作用了。

我还有一个密封类来指示我的活动的启动类型,可以从多个路径输入

sealed class NewsLaunchType : Parcelable {
    @Parcelize object FeedList : NewsLaunchType()
    @Parcelize class FeedDetail(val newsFeedType: NewsFeedType) : NewsLaunchType()
    @Parcelize class PostDetail(val newsPost: NewsPost) : NewsLaunchType()
}
Run Code Online (Sandbox Code Playgroud)

当我去启动我的活动时NewsLaunchType.PostDetail(newsPost),应用程序崩溃并出现以下错误

fun startActivity(){
    val intent = Intent(context, NewsActivity::class.java).apply {                      
      putExtra("key", NewsLaunchType.PostDetail(newsPost))
    }      
    startActivity(intent)
}
Run Code Online (Sandbox Code Playgroud)
 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_news)

        val newsActivityLaunchType = intent.getParcelableExtra<NewsLaunchType>("key")
}
Run Code Online (Sandbox Code Playgroud)
2018-11-08 11:08:33.897 17030-17030/com.something.internal E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.something.internal, PID: 17030
    java.lang.IllegalAccessError: Method 'void com.something.somethingkit.helper.UrlType$Valid.<init>(java.lang.String)' is inaccessible to class 'com.something.somethingkit.helper.UrlType$Valid$Creator' (declaration of 'com.something.somethingkit.helper.UrlType$Valid$Creator' appears in /data/app/com.something.internal-QTiupS4yw25rZK5uXP7UCQ==/base.apk:classes2.dex)
        at com.something.somethingkit.helper.UrlType$Valid$Creator.createFromParcel(Unknown Source:11)
        at android.os.Parcel.readParcelable(Parcel.java:2798)
        at com.something.somethingkit.model.news.local.NewsPost$Creator.createFromParcel(Unknown Source:25)
        at android.os.Parcel.readParcelable(Parcel.java:2798)
        at com.something.screens.news.NewsLaunchType$PostDetail$Creator.createFromParcel(Unknown Source:13)
        at android.os.Parcel.readParcelable(Parcel.java:2798)
        at android.os.Parcel.readValue(Parcel.java:2692)
        at android.os.Parcel.readArrayMapInternal(Parcel.java:3059)
        at android.os.BaseBundle.unparcel(BaseBundle.java:257)
        at android.os.Bundle.getParcelable(Bundle.java:888)
        at android.content.Intent.getParcelableExtra(Intent.java:7734)
        at com.something.screens.news.NewsActivity.onCreate(NewsActivity.kt:31)
        at android.app.Activity.performCreate(Activity.java:7183)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2908)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3030)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6938)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
Run Code Online (Sandbox Code Playgroud)

现在这并不完全出乎意料,除了如果我尝试在我创建它的同一位置将其拉出它确实有效

 fun startActivity(){
        val intent = Intent(context, NewsActivity::class.java).apply {                      
          putExtra("key", NewsLaunchType.PostDetail(newsPost))
        }      

        val test = intent.getParcelableExtra<NewsLaunchType>("key")
        startActivity(intent)
    }
Run Code Online (Sandbox Code Playgroud)

如果我将它添加到片段包中,并将新片段添加/替换到我的活动中,它也可以工作。

companion object {
    fun newInstance(newsPost: NewsPost): NewsDetailFragment {
                val bundle = Bundle()
                bundle.putParcelable(extraNewsPost, newsPost)

                val fragment = NewsDetailFragment()
                fragment.arguments = bundle
                return fragment
            }
}
Run Code Online (Sandbox Code Playgroud)
 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val newsPost = arguments?.getParcelable<NewsPost>(extraNewsPost)

}
Run Code Online (Sandbox Code Playgroud)

注意 - 在跨活动传递它时,有一层额外的密封类正在进行,但如果我只是尝试跨活动传递 NewsPost,它也会崩溃,所以额外的层不是问题。

这似乎是 Kotlin Parcelize 功能的一个错误。

是否有任何理由为什么在活动之间传递它会引发错误,但在片段之间传递它却不会?如果有一个原因......关于我如何在活动中传递这个的任何建议?

Evg*_*urg 0

文档明确指出:

主构造函数应该是可访问的(非私有)

不知道为什么它在传递到片段时起作用,而在传递到活动时不起作用。就我而言,我通过将类构造函数设置为内部来解决这个问题。