How "this" keyword actually works inside Intent constructor arguement? - android

Intent construcotr's arguements require object of Context class but down here inside this code I am passing "this" which means object of MainActivity class object. How it works? because it requires Context Class instance but i am passing MainActivity's instance.
const val EXTRA_MESSAGE = "com.example.myfirstApp.MESSAGE"
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun sendMessage(view: View){
val editText = findViewById<EditText>(R.id.editText)
val message = editText.text.toString()
val intent =Intent(this,DisplayMessageActivity::class.java).apply{
// if I use MainActivity() instead of this , gives me error
putExtra(EXTRA_MESSAGE, message)
}
startActivity(intent)
}
}
Here, this means MainActivity's instance, but if I use MainActivity() instead of this it gives me error. Whereas this and MainActivity() are same thing here.

In Android Activity, Service, BroadcastReceiver, Application all extends Context. So you can pass them in place of Context.
You can see all the class that an Activity class extends here.

Related

How do I return data from Activity to calling class?

I have an Activity called LoginActivity which, in turn, creates an instance of Auth class, passing this to it (Auth has context: Context in constructor):
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val authinstance = Auth(this);
setContentView(R.layout.activity_login)
...
class Auth constructor(val context: Context) {
...
Auth in turn starts a new Activity:
val intent = Intent(context, AuthWebViewActivity::class.java)
intent.putExtra("url", url)
startActivity(context,intent,null)
From AuthWebViewActivity some data must come back to Auth. How do I handle this? I am not sure that starting another Activity from a class like this is a good idea, there is probably a better way. However I would like to keep AuthWebViewActivity simple and handle all the authentication code in Auth.

Call Application context in Activity

I'm trying to call my database (made with Room) from an activity on Android, but it needs the application context, and if I passed "application : Application" in the constructor of my Activity, the build crash and tell me :
java.lang.Class<com.exemple.instabus.PhotoActivity> has no zero argument constructor
Here is my code :
class PhotoActivity(application: Application) : AppCompatActivity() {
private val pictureDao = PictureDatabase.getDatabase(app)
//Some code ....
I need a context, i've tried to pass "this" but i got another error
Can someone give me some help please, I'm a beginner in this technology
EDIT:
Here is my database class, just to show you why I need an Application Context
#Database(entities = [Picture::class], version = 1, exportSchema = false)
abstract class PictureDatabase : RoomDatabase(){
abstract fun pictureDao() : PictureDao
companion object{
#Volatile
private var INSTANCE : PictureDatabase? = null
fun getDatabase(context: Context): PictureDatabase {
if(INSTANCE == null){
synchronized(this){
INSTANCE = Room.databaseBuilder(
context.applicationContext,
PictureDatabase::class.java,
"pictures.db"
).build()
}
}
return INSTANCE!!
}
}
}
Activity is something we do declare in the manifest and then start them using intent, However, the creation of an instance of an activity is done by the system and not by us. An instance is created using constructor, but if it is us then we can have any number of overloaded constructors. But the system needs only one constructor which should be a zero parameter constructor and it should be public.
So your activity signature
class PhotoActivity(application: Application) : AppCompatActivity() {
should be changed to
class PhotoActivity() : AppCompatActivity() {
To call the fun getDatabase(context: Context): PictureDatabase you can pass this from the activity. Activity is an indirect child of Context.
You can do it in the following ways,
private val pictureDao by lazy{ PictureDatabase.getDatabase(this) }
private lateinit var pictureDao:PictureDatabase
then in onCreate() initialize it
final override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.your_layout)
PictureDatabase.getDatabase(this)
//your logic goes here
}
First create a ViewModel, and inside the ViewModel you can access the your Dao, pictureDao.
class PictureViewModel(application: Application) :
AndroidViewModel(application) {
val pictureDao: PictureDao
init {
pictureDao =
PictureDatabase.getDatabase(application).pictureDao()
}
}
Then in your activity, initialize the ViewModel. And access your Dao.
class PhotoActivity : AppCompatActivity() {
private lateinit var pictureViewModel: PictureViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_photo)
pictureViewModel =
ViewModelProviders.of(this).get(PictureViewModel::class.java)
//You can now access your Dao class here:
val pictureDao = pictureViewModel.pictureDao
}
}
You should not pass Application to the constructor. You should pass applicationContext to getDatabase(), like private val pictureDao = PictureDatabase.getDatabase(applicationContext)

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?
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) }
}
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.

How to get previous activity in android Kotlin

Lets say my default activity is MainActivity and I start another activity DepositActivity without using finish() in MainActivity
Now how can I access the instance of MainActivity inside DepositActivity
Now as how can I access the instance of MainActivity inside DepositActivity
AFAIK That is not possible to access instance of one activity in other Activity
if you have this type of requirement than Try to manage using Fragments
If you want to retrieve some result from DepositActivity use startActivityForResult(..., DepositActivity::class.java) method. In MainActivity override onActivityResult method:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
// retrieve data using 'data' variable
}
In DepositActivity you need to set data using method setResult() before finishing DepositActivity.
If you want to pass some data to DepositActivity use intent for that, for example:
val intent = Intent(this, DepositActivity::class.java)
intent.putExtra("Extra_Name", /*Some Data*/)
startActivity(intent)
Not Recommended: Use static reference to MainActivity (don't forget to delete it in onDestroy() method):
class MainActivity : AppCompatActivity() {
companion object {
#SuppressLint("StaticFieldLeak")
#JvmStatic
var instance: MainActivity? = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
instance = this
}
override fun onDestroy() {
instance = null
super.onDestroy()
}
}
In DepositActivity you can access it like this:
MainActivity.instance?./* call some method or property */
But you should not rely on onDestroy() being called, cause there are situations where the system will simply kill the activity's hosting process without calling this method (or any others) in it... So you can have memory leak
You need to declare as companion object variable and method in MainActivity. Static type of variables and methods are declared as companion object in Kotlin.
Look at below example,
Declare variables and methods in MainActivity,
val value : String = "hello from Main"
companion object {
lateinit var instance : MainActivity
fun getInstancem() : MainActivity {
return instance
}
}
Use this instance and print value in DepositActivity like,
Log.d("log_in_second_activity", "message " + MainActivity.getInstancem().value)
You can see log message.
Hope this will give you hint.

How to change activity from onPostExecute() method using kotlin

I have an activity called LoginActivity in my app (using kotlin). This will send a post request to an API as an Async task. I want to change the activity from onPostExecute method to MainWindowActivity after getting response from the API.
I cannot change the activity:
val intent = Intent(this, MainWindowActivity::class.java)
startActivity(intent)
since this will only return LoginActivity.PostJsonAsync
(PostJsonAsync is a class which extends AsyncTask)
To refer an outer class instance (LoginActivity) from inner class (PostJsonAsync) in Kotlin, you must make your class as inner (they can access outer class instance) by adding inner keyword before class.
inner class PostJsonAsync : AsyncTask<Void, Void, Void>() {
override fun onPostExecute(result: Void?) {
super.onPostExecute(result)
val intent = Intent(this#LoginActivity, LoginActivity::class.java)
startActivity(intent)
}
}
Simple:
val intent = Intent(name_of_your_activity:class.this, MainWindowActivity::class.java)
startActivity(intent)

Categories

Resources