I have a variable in my class that extends BroadcastReceiver and implements one additional method called isNetworkAvailable. When I call the method within the class it works but it results in "Unresolved reference" when called from outside. The preexisting methods of the class are also accessible.
Any ideas?
class MainActivity : AppCompatActivity() {
private var connectivityReceiver: BroadcastReceiver = object: BroadcastReceiver(){
override fun onReceive(context: Context, arg1: Intent) {
if ( isNetworkAvailable(this#MainActivity) ) { // Works just as it's supposed to.
// ...
}
}
fun isNetworkAvailable(context: Context?): Boolean {
// ...
}
}
override fun onCreate(savedInstanceState: Bundle?) {
// ...
connectivityReceiver.onReceive() // Accessible if arguments are provided
connectivityReceiver.isNetworkAvailable(this#MainActivity) // ERROR: Unresolved reference
}
}
#Fred answer is correct, it is because compiler don't know that the BroadcastReceiver has the function called isNetworkAvailable.
Kotlin is strong in type interference, you don't need to specify explicitly that the variable is of type BroadcastReceiver it tells the compiler that the object is of type BroadcastReceiver which does not have any function called isNetworkAvailable().
Just remove the explicit type declaration from the variable
private var connectivityReceiver = object: BroadcastReceiver() {...}
Kotlin will automatically assign the correct type using its inferred type of anonymous object.
You're variable connectivityReceiver is of type BroadcastReceiver, which has no method isNetworkAvailable. Unfortunately, it's not as simple as this because for Kotlin connectivityReceiver is nothing but a BroadcastReceiver.
To make your method available you can create a specific class and not an anonymous object.
class MyBroadcaseReceiver : BroadcastReceiver(){
override fun onReceive(context: Context, arg1: Intent) {
if ( isNetworkAvailable(this#MainActivity) ) { // Works just as it's supposed to.
// ...
}
}
fun isNetworkAvailable(context: Context?): Boolean {
// ...
}
}
Then in the activity just use
private var connectivityReceiver: MyBroadcaseReceiver = MyBroadcaseReceiver()
Note that if you do something like
private var connectivityReceiver: BroadcaseReceiver = MyBroadcaseReceiver()
you'll end up in the same issue since connectivityReceiver will be a BroadcastReceiver and not the class where isNetworkAvailable is defined.
There's also the possibility of just removing the explicit type:
private var connectivityReceiver = object: BroadcastReceiver(){
override fun onReceive(context: Context, arg1: Intent) {
if ( isNetworkAvailable(this#MainActivity) ) { // Works just as it's supposed to.
// ...
}
}
fun isNetworkAvailable(context: Context?): Boolean {
// ...
}
}
Kotlin's inference should be able to pick up the method then. The reason why I think this is not a great approach is because it only works if you want to keep this object expression private - see object expressions
Note that anonymous objects can be used as types only in local and private declarations. If you use an anonymous object as a return type of a public function or the type of a public property, the actual type of that function or property will be the declared supertype of the anonymous object, or Any if you didn't declare any supertype. Members added in the anonymous object will not be accessible.
Basically means in your case if you remove private the member isNetworkAvailable won't be accessible anymore. I believe code is meant to change a lot and especially a class like that eventually should go to its own place as it becomes more complex and makes it easier to test. This is of course just a personal opinion.
Related
I have seen quite a few examples in Kotlin where an activity class has a companion object to encapsulate the creation of a start intent like the following. It seems particularly Java inspired.
class HomeActivity : AppCompatActivity() {
companion object {
fun newStartIntent(context: Context): Intent {
val intent = Intent(context, HomeActivity::class.java)
return intent
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.setContentView(R.layout.activity_home)
// ...
}
// ...
}
Since Kotlin has top level functions, why not skip the companion object and just have a top level function?
fun newHomeActivityStartIntent(context: Context): Intent {
val intent = Intent(context, HomeActivity::class.java)
return intent
}
class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.setContentView(R.layout.activity_home)
// ...
}
// ...
}
There is nothing wrong in your approach actually. I thought of a few reasons why I would choose a companion object over top-level functions:
Top-level functions visible for everyone, thus every time you start typing new... you will get a list of partially irrelevant results;
Companion objects can hold private values that you would not like to make open to the public and keep them visible only within your class but still make them static. Maybe there are some arguments that are calculated under this function invocation and passed with intent, and you would like to hide these calculations or arguments keys;
This is not your case but still relevant: using companion object you can make all constructors private and control all arguments passed to object initialization. This is how Singleton can be created in Kotlin;
Opinionated For me personally it makes things look tidy. I usually extract only simple and relatively vastly used functions. Like Date conversion functions, or math function calculations.
It is a matter of style. Just pick one and be consistent!
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.
I've just recently started going on about android developing not long after I've got stuck.
I want to know how can I get MainActivity's context and it's functions globaly (if that's possible)
The only method I've got to work is passing the context as an argument to another classes' constructor. this#MainActivity doesn't work nor does trying to get a function such as MainActivity.getContext() which returns the context.
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webView1.settings.javaScriptEnabled = true
webView1.webViewClient = Client(this) //Initialize the class
webView1.loadUrl("https://www.urbs.curitiba.pr.gov.br/mobile/cartao")
}
fun getContext(): Context //Not accessible from Client
{
return getContext()
}
}
//Client class
class Client(context: Context): WebViewClient()
{
val context: Context = context //This works
override fun onPageFinished(view: WebView, url: String)
{
webView1.loadUrl("https://kotlinlang.org/docs/reference/this-expressions.html")
Toast.makeText(context, "Uau", Toast.LENGTH_SHORT).show()
//using this#MainActivity as context is not defined
}
}
It would be great if I could use functions from the mainactivity class but I'm not really familiarized with the language and therefore I can't do so (if it's possible) right now.
This is my first post at stackoverflow and I'm really sorry if it is low quality, I'm not yet familiriazed with the system.
Welcome to StackOverflow!
In your specific case, there's no need to pass an instance of Context through the constructor, you can instead access the WebView's context property: all View subclasses in Android have a reference to a Context instance.
//Client class
class Client : WebViewClient()
{
override fun onPageFinished(view: WebView, url: String)
{
webView1.loadUrl("https://kotlinlang.org/docs/reference/this-expressions.html")
Toast.makeText(view.context, "Uau", Toast.LENGTH_SHORT).show()
}
}
In general, if you ever need a reference to Context, prefer passing it explicitly rather than storing it in global variables - this way you'll be able to prevent memory leaks.
MainActivity.getContext() means there is a public static method called getContext in MainActivity class while there isn't;
about your scenario, you already have passed a MainActivity instance to the Client class so you can just cast it and call getContext() method:
class Client(context: Context): WebViewClient()
{
private val context: MainActivity = context as MainActivity //This works
override fun onPageFinished(view: WebView, url: String)
{
webView1.loadUrl("https://kotlinlang.org/docs/reference/this-expressions.html")
Toast.makeText(context, "Uau", Toast.LENGTH_SHORT).show()
//now its possible
}
}
about accessing a class instances (like your MainActivity) globally, you can always put your instances in a public static variable and access them from everywhere(but implementing this for Android Activity class is a bit tricky )
I am trying to add a "static" method to my MyApplication class in kotlin
I have added (as a property) the variable :
private var context: Context? = null
in method:
override fun onCreate()
I added:
context = applicationContext
then I add a companion object like this
companion object {
#JvmStatic fun getMyApplicationContext(): Context?
{
return MyApplication().context
}
}
when I call this method from other parts of the application like
MyApplication.getMyApplicationContext() it always returns null. I have gleaned all this from several sources but I am not sure if it is anywhere near correct or not.
It sounds like you want a global application context object. Now casting aside my dislike for global variables, I think you are pretty close.
I think you just need to add the variable into the MyApplication classes companion object and use that directly. You only need the #JvmField annotation if you're going to access the field from Java.
class MyApplication {
companion object {
#JvmField
var context: Context? = null
// Not really needed since we can access the variable directly.
#JvmStatic fun getMyApplicationContext(): Context? {
return context
}
}
override fun onCreate() {
...
MyApplication.context = appContext
}
}
I am new with Kotlin and little bit stack with intentService. Manifest shows me an error that my service doesn't contain default constructor, but inside service it looks ok and there are no errors.
Here is my intentService:
class MyService : IntentService {
constructor(name:String?) : super(name) {
}
override fun onCreate() {
super.onCreate()
}
override fun onHandleIntent(intent: Intent?) {
}
}
I was also try another variant:
class MyService(name: String?) : IntentService(name) {
but when I try to run this service I still get an error:
java.lang.Class<com.test.test.MyService> has no zero argument constructor
Any ideas how to fix default constructor in Kotlin?
Thanks!
As explained here your service class needs to have parameterless consturctor. Change your implementation to in example:
class MyService : IntentService("MyService") {
override fun onCreate() {
super.onCreate()
}
override fun onHandleIntent(intent: Intent?) {
}
}
The Android documentation on IntentService states that this name is only used for debugging:
name String: Used to name the worker thread, important only for debugging.
While not stated explicitly, on the mentioned documentation page, the framework needs to be able to instantiate your service class and expects there will be a parameterless constructor.