Issue
I am new to android development. My app crash when I am trying to list items from hilt generation. I can't figure the problem. It would be nice if anyone can help me out. Many thanks.
KC
AppModule
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
fun provideTestString() = "This is a string we will inject"
@Provides
@Singleton
fun provideDatabase(
app: Application,
callback: LanguageDatabase.Callback
) = Room.databaseBuilder(app, LanguageDatabase::class.java, "language_database")
.fallbackToDestructiveMigration()
.addCallback(callback)
.build()
@Provides
fun provideLanguageDao(db: LanguageDatabase) = db.languageDao()
@ApplicationScope
@Provides
@Singleton
fun provideApplicationScope() = CoroutineScope(SupervisorJob())
}
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class ApplicationScope
Language
@Entity(tableName = "language_table")
data class Language(
val isChecked: Boolean,
val language: String,
@PrimaryKey(autoGenerate = true) val id: Int = 0
)
Language Adapter
class LanguageAdapter(private val language: List<Language>) :
RecyclerView.Adapter<LanguageAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.item_language, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(language[position])
}
override fun getItemCount(): Int = language.size
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(index: Language) {
itemView.findViewById<CheckBox>(R.id.isChecked).isChecked = index.isChecked
itemView.findViewById<TextView>(R.id.language).text = index.language
}
}
}
LanguageDao
@Dao
interface LanguageDao {
@Query("SELECT * FROM language_table")
fun getLanguage(): Flow<List<Language>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(language: Language)
@Update
suspend fun update(language: Language)
@Delete
suspend fun delete(language: Language)
}
Language Database
@Database(entities = [Language::class], version = 3)
abstract class LanguageDatabase: RoomDatabase() {
abstract fun languageDao(): LanguageDao
class Callback @Inject constructor(
private val database: Provider<LanguageDatabase>,
@ApplicationScope private val applicationScope: CoroutineScope
): RoomDatabase.Callback(){
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
// db operation
val dao = database.get().languageDao()
applicationScope.launch {
dao.insert(Language(true, "English"))
dao.insert(Language(true, "Spanish"))
dao.insert(Language(false, "Russian"))
dao.insert(Language(true, "Japanese"))
dao.insert(Language(true, "Korean"))
dao.insert(Language(true, "Hindi"))
dao.insert(Language(true, "Chinese"))
dao.insert(Language(true, "Italian"))
dao.insert(Language(true, "Arabic"))
dao.insert(Language(true, "German"))
}
}
}
}
Language Fragment
@AndroidEntryPoint
class LanguageFragment: Fragment(R.layout.language_fragment){
private lateinit var binding: LanguageFragmentBinding
private lateinit var languageList: RecyclerView
@Inject
lateinit var testString: String
private val viewModel: LanguageViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.language_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
languageList = binding.languageList
languageList.layoutManager = LinearLayoutManager(context)
observeLanguage()
}
private fun observeLanguage() {
viewModel.languages.observe(viewLifecycleOwner) {
languageList.adapter = LanguageAdapter(it)
}
}
}
Language View Model
@HiltViewModel
class LanguageViewModel @Inject constructor(
private val languageDao: LanguageDao
) : ViewModel() {
val languages = languageDao.getLanguage().asLiveData()
}
Main Activity
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
replacementFragment(HomeFragment())
binding.bottomNavigationView.setOnItemSelectedListener{
when(it.itemId){
R.id.home -> replacementFragment(HomeFragment())
R.id.language -> replacementFragment(LanguageFragment())
}
true
}
}
private fun replacementFragment(fragment: Fragment){
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.fragmentContainerView, fragment)
fragmentTransaction.commit()
}
}
MyApplication
@HiltAndroidApp
class MyApplication : Application(){
}
Activity Main
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainerView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/fragmentContainerView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/bottom_nav"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Item Language
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<CheckBox
android:id="@+id/isChecked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:minWidth="48dp"
android:minHeight="48dp" />
<TextView
android:id="@+id/language"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/isChecked"
android:layout_alignBottom="@+id/isChecked"
android:layout_toEndOf="@+id/isChecked"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
tools:text="English" />
</RelativeLayout>
Language fragment
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
tools:context=".LanguageFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/language_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Error Message
2022-08-01 22:16:51.818 17977-17977/? E/Zygote: accessInfo : 1
2022-08-01 22:16:51.853 17977-17977/? E/le.testfragmen: Unknown bits set in runtime_flags: 0x8000
2022-08-01 22:16:55.810 17977-17977/com.example.testfragment E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.testfragment, PID: 17977
kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized
at com.example.testfragment.LanguageFragment.onViewCreated(LanguageFragment.kt:37)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3019)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:551)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1840)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1758)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1701)
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:488)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:237)
at android.app.ActivityThread.main(ActivityThread.java:8167)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:496)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1100)
Solution
Use databinding or viewbinding as per your requirement
private var _binding: LanguageFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = LanguageFragmentBinding.inflate(inflater, container, false)//Here is need to change.
val view = binding.root
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
Answered By - Sandesh KhutalSaheb
Answer Checked By - Marilyn (JavaFixing Volunteer)