Reputation: 1128
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
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
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