Kotlin Class causes App to crash - android

I tried to incorporate classes into my program, but now I can't get my program to work (no errors, just crashes). I have isolated the part that causes the crash in an extra project. Can someone help me?
package com.mietvertrag.simon.test
imports...
class MainActivity : AppCompatActivity() {
data class Contract(var adr:CharSequence, var dat:CharSequence, var mitr:CharSequence, var num:CharSequence)
lateinit var v1: Contract
lateinit var vertrag1: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
v1.adr = "Treestreet 24"
vertrag1 = findViewById(R.id.text)
}
}

You're trying to set a value to a property of a class instance that has not been initialized:
v1.adr = "Treestreet 24"
So initialize it:
v1 = Contract("Treestreet 24", "", "", "") <br/>
As for the no errors, just crashes:
you must use the Logcat to review the errors or you will never succeed in building apps.

I think the problem is v1 not initialize yet.

Related

Android findViewById() returns null when trying to make a utility/helper class

I am currently developing an app and I am trying to make a Utils file with some general functions that I can use throughout my application to avoid having to copy/paste the same function many times. I have a Utils class file and I am passing the current Activity into that class. For some reason, when I call findViewById() in init, it returns null and the app crashes. I have tried logging the activity that is being passed into the Utils class, and to my eyes it appears like the right activity, so I am not sure why it is failing. I will show the relevant code below:
Utils.kt (There is more, but it isn't relevant to the question)
class Utils(activity: Activity) {
private var currentActivity: Activity
private var fragmentManager: FragmentManager
private var topAppBar: MaterialToolbar
val activitiesList = listOf(
"recipes", "budget", "inventory", "customers",
"reports"
)
init {
currentActivity = activity
println(currentActivity)
fragmentManager = (activity as AppCompatActivity).supportFragmentManager
topAppBar = currentActivity.findViewById(R.id.top_app_bar)
}
}
MainActivity.kt (Again there is more, but not relevant)
class MainActivity : AppCompatActivity() {
private lateinit var drawerLayout: DrawerLayout
private lateinit var topAppBar: MaterialToolbar
private lateinit var utils: Utils
private val fragmentManager = supportFragmentManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fragmentManager.beginTransaction().replace(R.id.frame_layout, HomeFragment()).commit()
utils = Utils(this#MainActivity)
topAppBar = findViewById(R.id.top_app_bar)
drawerLayout = findViewById(R.id.drawer_layout)
val navigationView: NavigationView = findViewById(R.id.navigation_view)
The error comes from the Utils.kt file in the last line of actual code where I try to assign topAppBar to the view of id top_app_bar, but it returns null. My println (yes I know I should be using Log.i but println is quicker to type) in the Utils class returns com.example.bakingapp.MainActivity#501533d, which is what I would expect if I am understanding what is happening correctly. Any insight as to why I can't find the views would be appreciated.
I figured out the problem. It is a scope issue. I am trying to find the id of a view outside of the current Activity/Fragment I am looking at. I realized then a lot of my code could/should be moved into the Fragment I was trying to target and this has fixed my issues.

Kotlin, Dagger and Realm - DI concept problem

I'm starting this brand new project only for fun, but at the first steps I got a problem, there it goes:
I have this class "Note", it's a realm class as you can see below
#RealmClass
open class Note
#Inject constructor (#PrimaryKey var id: String,
var text: String,
var badge: NoteBadge?
) : RealmObject() {
fun getRandomNoteText(): String = "supposed to be random"
}
Then I have my Main activity class, where I already got Dagger working.
class MainActivity : AppCompatActivity() {
#Inject lateinit var note: Note
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
DaggerNoteComponent.create().inject(this)
Log.d("MAIN_ACTIVITY", note.getRandomNoteText())
Log.d("MAIN_ACTIVITY", note.badge?.getRandomBadgeText())
}
}
It got tricky in terms of concepts, the code above doesn't compile, I have to add this line to my Note class to make it work:
constructor(): this(UUID.randomUUID().toString(), "", NoteBadge()){}
However there I have NoteBadge() I'm creating an instance of NoteBadge manually, that's bad, I would like dagger did that.
I've tried sending a null value as parameter, but them I have a null NoteBadge at my MainActivity.
So do you have any idea how to fix that and make my dependency injection fully funcional? With no manual instance initializations?
EDIT -> Paste NoteComponent
#Component(modules = [NoteBadgeModule::class])
interface NoteComponent {
fun inject(activity: MainActivity)
}

how to instantiate an object from a class in kotlin

I am learning Kotlin, and I googled how to create a class in kotlin. So, I created the below class as a test.
In the main activity, I am trying to instantiate an object from the class Board, but i get the following error:
classifier Board does not have a companion object
please let me know how to intantiate an object of an the class Board?
MainActivity:
class ActMain : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout_act_main)
Board board = new Board(name = "ABC");
}
}
Board.kt:
data class Board(val name: String) {
var age: Int = 0
}
Kotlin does not use new.
Board board = new Board(name = "ABC");
is incorrect. Use
val board = Board("ABC")
Your code reflects the Java syntax... sort of. Kotlin has type inference, so you don't need to specify the class type. However, if you do specify it, it's different from Java:
val board: Board = Board("ABC")
Semi-colons are also not generally used in Kotlin, although they won't break the compilation if you use them.
name = "ABC" just isn't valid syntax no matter if it's Java or Kotlin. Actually it is (from #hotkey): https://kotlinlang.org/docs/reference/functions.html#named-arguments
Unlike Java, in Kotlin this is the correct way
MainActivity.kt
class ActMain : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout_act_main)
val board = Board("ABC")
board.age = 12
}
}
Board.kt
class Board(val name: String) {
var age: Int = 0
}
try to forget java
val board = Board("name")
in kotlin
when you want to declare new object yon can do like this.
val board = Board("ABC")
if you declare object by using val keyword. it look as you use final in java. the variable which you declared can not recreate again.
var board = Board("ABC")
if you use var to declare it look as normal variable in java
Anyway in kotlin you will see something that It doesn't contain in java such as
scoping function as link below. it will help you write your code is more easily.
https://kotlin.guide/scoping-functions
I hope this help :)

Kotlin Error: Dagger does not support injection into private fields

I use in kotlin activity ViewPager and I want in Kotlin Fragment use the dagger injection. I have got Error: Dagger does not support injection into private fields.
In Java Fragment the dagger injection work.
Why can i not inject dagger in kotlin faragment ?
in my kotlin activity
mPagerAdapter = object : FragmentPagerAdapter(supportFragmentManager) {
private val mFragments = arrayOf(KotlinFragment(), JavaFragment())
private val mFragmentNames = arrayOf(getString(R.string.cashdocuments), getString(R.string.action_absmysql))
override fun getItem(position: Int): Fragment {
return mFragments[position]
}
override fun getCount(): Int {
return mFragments.size
}
override fun getPageTitle(position: Int): CharSequence {
return mFragmentNames[position]
}
}
my kotlin fragment
class KotlinFragment : Fragment() {
#Inject
internal var mSharedPreferences: SharedPreferences? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(activity.application as SamfantozziApp).dgaeacomponent().inject(this)
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
val rootView = inflater!!.inflate(R.layout.activity_absserver, container, false)
return rootView
}
}
messages gradle build
You have mistake here:
#Inject
internal var mSharedPreferences: SharedPreferences? = null
This looks like you added #Inject annotation to the KotlinFragment class
Please change it to this and it will work:
var mSharedPreferences: SharedPreferences? = null
#Inject set
Here is the link to the documentation: https://kotlinlang.org/docs/reference/annotations.html
Accidentally I came across to my own answer and have to confess, that in fact it isn't working (at least for my use-case). Please consider Avilio's answer which worked for me also: substitute internal with lateinit.
Old answer
Remove internal modifier. Dagger needs at least package-private access in order to access annotated field. In Kotlin internal modifier is not a substitution for Java's package-private access modifier.
For detailed explanation of differences between modifiers in Java and Kotlin refer to Fragmented podcast's episode #101 - "Learning Kotlin – visibility modifiers, internal modifier, modules", as well as the official docs.
As simple you can do this, change this line
#Inject
internal var mSharedPreferences: SharedPreferences? = null
To
#set:Inject
internal var mSharedPreferences: SharedPreferences? = null
this work like charm in my case.
I had the same error, even upon removing the internal keyword. So I replaced internal with lateinit and it worked.
#Inject
lateinit var mSharedPreferences: SharedPreferences
Also, this worked for me as well for late initialization variable
I followed the above advise of not making the field internal. But, that was not enough. Kapt still converting the var to private during compilation.
I had to add the #JvmField annotation to make it behave better.
The answer I found was here:
https://discuss.kotlinlang.org/t/kapt-converting-public-fields-to-private-during-compilation/11757
Remove the internal modifier. And I also had to declare lateinit and remove the Optional.
#Inject
internal var mSharedPreferences: SharedPreferences? = null
Just change this to
#Inject
lateinit var mSharedPreferences: SharedPreferences
P.S. You can't use lateinit with nullables.
Have you defined fun inject(fragment: KotlinFragment) in your ApplicationComponent? Because it looks like your error message is saying exactly that.
EDIT: maybe you haven't provided SharedPreferences in your Module like this:
#Module
public class AndroidModule {
private final TimrApplication application;
public AndroidModule(TimrApplication application) {
this.application = application;
}
#Provides
SharedPreferences provideSharedPreferences(){
return PreferenceManager.getDefaultSharedPreferences(application);
}
}
In My case, I changed
#Inject
var locationDBWrapper: LocationDBWrapper? = null
to
#Inject
lateinit var locationDBWrapper: LocationDBWrapper
Issue got resolved
After updating to Android Studio 3.5, most of the answers don't work. The consolidated solution is :
1. If you want to use lateinit:
#Inject lateinit var mSharedPreferences: SharedPreferences
2. If you don't want to use lateinit:
var mSharedPreferences: SharedPreferences? = null
#Inject set(value) {
field = value
}
OR
#Inject
#JvmField
var mSharedPreferences: SharedPreferences
NOTE: These methods don't work anymore. You get the error not a valid name: <set-?>Provide
#set:Inject
var mSharedPreferences: SharedPreferences? = null
var mSharedPreferences: SharedPreferences? = null
#Inject set
You can't make injected vars private, but you can make them protected, which is effectively the same in a final class (and in Kotlin classes are final by default unless you use the "open" modifier). Also, use lateinit modifier like it has been said already.
#Inject
protected lateinit var mSharedPreferences: SharedPreferences
If your class is not open, this might produce a warning in Android Studio saying "'protected' visibility is effectively 'private' in a final class" - you can just suppress that with:
#Suppress("ProtectedInFinal")

Unable to set ViewModel on Kotlin

New to Kotlin language, trying Android Architecture Components. Trying to set a ViewModel for my LifecycleActivity in Kotlin language:
class FooActivity : LifecycleActivity() {
private var mViewModel: FooViewModel? = null
..
override fun onCreate(savedInstanceState: Bundle?) {
mViewModel = ViewModelProviders.of(this).get(FooViewModel.class) <-- error here
Getting Name expected: and expecting )
What am I missing?
The class usage is wrong. With Kotlin you use: FooViewModel::class.java

Categories

Resources