Hyeonseo Yang
Hyeonseo Yang

Reputation: 1128

Using 'this' as Context in the init block of activity?

I am developing an android application with kotlin.

I have a DereDatabaseHelper class which has init block that uses a context given through class parameter(?)

The DereDatabaseHelper is like this.

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

The DereDatabaseHelper class is instantiated here in SongListActivity like this.

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

I thought that this code was correct, but this codes throws NullPointerException.

java.lang.NullPointerException: Attempt to invoke virtual method

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

on a null object reference at

android.content.ContextWrapper.getExternalFilesDir(ContextWrapper.java:253) at com.kyhsgeekcode.dereinfo.model.DereDatabaseHelper.<init>(DereDatabaseHelper.kt:21) at com.kyhsgeekcode.dereinfo.SongListActivity.<init>(SongListActivity.kt:31)

Is this null when the execution is in init block and what initialization style should I use to fix this?

Upvotes: 2

Views: 2630

Answers (2)

mightyWOZ
mightyWOZ

Reputation: 8315

Activities are not completely initialized by the constructor or in your case init block.

Android system initializes activities and then calls the onCreate method. So you should do the following

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

Why it doesn't work with the constructor?

consider the following code snippet

var myActivity = MyActivity() // This doesn't start MainActivity

// This is how you start an activity
val intent = Intent(context, MyActivity::class.java)
startActivity(intent)

When you start any activity you never instantiate the activity class, why?

Because that is the responsibility of android system, when you do startActivity(intent) android system instantiates your activity class using the default constructor and then does all the initialization (ie. providing context) And once the activity is completely initialized the onCreate method of your activity is called where you can do your end of initialization.

Upvotes: 2

Tenfour04
Tenfour04

Reputation: 93571

Never use the constructor of an Activity to do anything that involves the Context. Android instantiates Activities using their sole empty constructor (via reflection), and then sets up the activity's various fields before it ever calls onCreate(). Your first safe entry point to do anything in your Activity is in onCreate().

You also can't call methods of the Activity (which is itself a Context) in the constructor.

You also can't use the context in any way to even set up properties, because they'll try to access the context before onCreate:

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

To avoid having to make your property nullable, you can do either of the following, which allow you to avoid having your class instantiated until after onCreate() is called:

class SongListActivity : AppCompatActivity() {
    lateinit var dereDatabaseHelper : DereDatabaseHelper

    override fun onCreate() {
        super.onCreate
        dereDatabaseHelper = DereDatabaseHelper(this)
    }

or

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

Upvotes: 5

Related Questions