Home > Enterprise >  App Crash when trying to list item from hilt and dagger
App Crash when trying to list item from hilt and dagger

Time:08-02

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)

CodePudding user response:

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
    }

CodePudding user response:

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) // this is wrong
    }

you need to use binding ..either viewbinding or databinding

Databinding

CodePudding user response:

You are using lateinit property which is binding has not been initialized.

You are intialize in

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        languageList = binding.languageList // here
        languageList.layoutManager = LinearLayoutManager(context)

        observeLanguage()

    }

which is not intialize. so you have intialize as like

  • private lateint val languageList: RecyclerView? = binding?.languageList
  • intialize languageList in onCreateView
  • Related