【问题标题】:Using 'this' as Context in the init block of activity?在活动的初始化块中使用“this”作为上下文?
【发布时间】:2020-05-11 21:18:27
【问题描述】:

我正在使用 kotlin 开发一个 android 应用程序。

我有一个DereDatabaseHelper 类,它有一个init 块,它使用通过类参数(?)给出的context

DereDatabaseHelper是这样的。

class DereDatabaseHelper(context: Context) {
    val manifestFile: File
    val fumensDBFile: File
    val fumenFolder: File

    val musicIDToInfo: MutableMap<Int, MusicInfo> = HashMap()
    val fumenIDToMusicID: SparseIntArray = SparseIntArray()

    init {
        val datadir = context.getExternalFilesDir(null).parentFile.parentFile

DereDatabaseHelper 类在SongListActivity 中被实例化,如下所示。

class SongListActivity : AppCompatActivity() {
    var dereDatabaseHelper : DereDatabaseHelper
    init {
        dereDatabaseHelper = DereDatabaseHelper(this)
    }

我认为这段代码是正确的,但是这段代码抛出了NullPointerException

java.lang.NullPointerException: 尝试调用虚方法

'java.io.File android.content.Context.getExternalFilesDir(java.lang.String)'

在空对象引用上

android.content.ContextWrapper.getExternalFilesDir(ContextWrapper.java:253) 在 com.kyhsgeekcode.dereinfo.model.DereDatabaseHelper.(DereDatabaseHelper.kt:21) 在 com.kyhsgeekcode.dereinfo.SongListActivity.(SongListActivity.kt:31)

init 块中执行时this 是否为空,我应该使用什么初始化样式来解决这个问题?

【问题讨论】:

    标签: android kotlin constructor nullpointerexception initialization


    【解决方案1】:

    constructor 或在您的情况下为 init 块未完全初始化活动。

    Android系统初始化activity,然后调用onCreate方法。所以你应该做以下事情

    override fun onCreate(savedInstanceState: Bundle?) {
     // create instance of DareDatabaseHelper 
    }
    

    为什么它不适用于构造函数?

    考虑下面的代码sn-p

    var myActivity = MyActivity() // This doesn't start MainActivity
    
    // This is how you start an activity
    val intent = Intent(context, MyActivity::class.java)
    startActivity(intent)
    

    当您启动任何活动时,您从不实例化活动类,为什么?

    因为这是 android 系统的责任,所以当您执行 startActivity(intent) 时,android 系统会使用默认的 constructor 实例化您的 activity 类,然后执行所有初始化(即提供 context)并且一旦 @ 987654330@ 已完全初始化,您的活动的onCreate 方法被调用,您可以在其中完成初始化。

    【讨论】:

    • 你的意思是android不只是实例化我的Activity类然后调用onCreate而是在实例化我的Activity类的同时调用onCreate
    • 感谢您的更新答案和详细解释,mightyWOZ!
    【解决方案2】:

    永远不要使用 Activity 的构造函数来做任何涉及 Context 的事情。 Android 使用其唯一的空构造函数(通过反射)实例化活动,然后设置活动的各种字段之前它曾经调用onCreate()。在 Activity 中执行任何操作的第一个安全入口点位于 onCreate()

    你也不能在构造函数中调用Activity(它本身就是一个上下文)的方法。

    你也不能以任何方式使用上下文来设置属性,因为他们会在onCreate之前尝试访问上下文:

    class MyActivity: AppCompatActivity() {
        val assets: AssetManager = getAssets() // This will cause a crash
    }
    

    为避免使您的属性可以为空,您可以执行以下任一操作,这样您就可以避免在调用 onCreate() 之前实例化您的类:

    class SongListActivity : AppCompatActivity() {
        lateinit var dereDatabaseHelper : DereDatabaseHelper
    
        override fun onCreate() {
            super.onCreate
            dereDatabaseHelper = DereDatabaseHelper(this)
        }
    

    class SongListActivity : AppCompatActivity() {
        val dereDatabaseHelper by lazy { DereDatabaseHelper(this) }
    }
    

    【讨论】:

    • 现在我明白在 Activity 的构造函数和 init 块中做任何事情都不是一个好主意(init 和构造函数是否相同?)。但是现在我想知道为什么它会导致 NullPointerException,因为 this 在 java 中不应该是 null,即使在构造函数中也是如此。你的意思是“this as Context”是null,因为它还没有完全初始化?
    • 不,您传递给 DereDatabaseHelper 构造函数的 this 不为空。但在内部,Activity.getExternalFilesDir() 正在调用mBase.getExternalFilesDir(),而mBase(一个Java-default-scoped Context 实例)仍然为null,因为它还没有被分配。因此,您自己的代码中不会发生 NullPointerException。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-15
    • 2015-09-19
    • 1970-01-01
    • 1970-01-01
    • 2014-09-13
    • 2017-03-21
    相关资源
    最近更新 更多