I got this error when try running this code to show the GitHub API description, name, and avatar image in the android app (still cannot figure it out to fix the error). Any help is welcome :)
This is my app structure:
adapter
|--> RecyclerViewAdapter
database
|--> AppDao
|--> AppDatabase
dependencyinjection
|--> AppModule
model
|--> NewsData
|--> NewsList
|--> Owner
network
|--> RetroRepository
|--> RetroServiceInterface
viewmodel
|--> MainActivityViewModel
MainActivity
MyRoomRetroApplication
================================================================
SOURCE CODE
AppModule
package com.example.newsapplication.dependencyinjection
import android.content.Context
import com.example.newsapplication.database.AppDao
import com.example.newsapplication.database.AppDatabase
import com.example.newsapplication.network.RetroServiceInterface
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
@Provides
@Singleton
fun getAppDatabase(context: Context): AppDatabase{
return AppDatabase.getAppDBInstance(context)
}
@Provides
@Singleton
fun getAppDao(appDatabase: AppDatabase): AppDao{
return appDatabase.getAppDao()
}
val BASE_URL = "https://api.github.com/search/"
// Function to return instance of RetroServiceInterface + Mark as @Provides and @Singleton
@Provides
@Singleton
fun getRetroServiceInstance(retrofit: Retrofit): RetroServiceInterface{
return retrofit.create(RetroServiceInterface::class.java)
}
// Function to return retrofit instance + Mark as @Provides and @Singleton
@Provides
@Singleton
fun getRetroInstance(): Retrofit{
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
NewsData
package com.example.newsapplication.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "news") // Table of databases
data class NewsData(
@PrimaryKey(autoGenerate = true)@ColumnInfo(name = "id")val id: Int = 0,
@ColumnInfo(name = "name")val name: String?,
@ColumnInfo(name = "description")val description: String?,
@ColumnInfo(name = "owner")val owner: Owner)
NewsList
package com.example.newsapplication.model
data class NewsList(val items: List<NewsData>) //Create items list
Owner
package com.example.newsapplication.model
import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type
data class Owner(val avatar_url: String?)
class TypeConverterOwner {
// Type Converter Function
val gson: Gson = Gson() // Using Gson library to convert Object to String
@TypeConverter
fun stringToSomeObjectList(data: String?): Owner? {
if (data == null) return null
val listType: Type = object : TypeToken<Owner?>() {}.type
return gson.fromJson<Owner?>(data, listType)
}
@TypeConverter
fun someObjectListToString(someObject: Owner?): String? {
return gson.toJson(someObject)
}
}
RetroRepository
package com.example.newsapplication.network
import androidx.lifecycle.LiveData
import com.example.newsapplication.database.AppDao
import com.example.newsapplication.model.NewsData
import com.example.newsapplication.model.NewsList
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import javax.inject.Inject
// Make the API call in here
// Inject to have the access to RetroServiceInterface and DAO
class RetroRepository @Inject constructor(private val retroServiceInterface: RetroServiceInterface,
private val appDao: AppDao){
fun getAllRecords(): LiveData<List<NewsData>> {
return appDao.getAllRecords()
}
fun insertRecord(newsData: NewsData){
appDao.insertRecords(newsData)
}
// Get the data from github api ...
fun makeApiCall(query: String?){
val call: Call<NewsList> = retroServiceInterface.getDataFromAPI(query!!)
call?.enqueue(object : Callback<NewsList>{
override fun onResponse(call: Call<NewsList>, response: Response<NewsList>) {
if(response.isSuccessful){
// Delete all records after getting data, delete data in table and insert with the new fetched data to the table
appDao.deleteAllRecords()
response.body()?.items?.forEach {
insertRecord(it)
}
}
}
override fun onFailure(call: Call<NewsList>, t: Throwable) {
TODO("Not yet implemented")
}
})
}
}
RetroServiceInterface
package com.example.newsapplication.network
import com.example.newsapplication.model.NewsList
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query
interface RetroServiceInterface {
@GET("repositories")
fun getDataFromAPI(@Query("q")query: String): Call<NewsList>
}
MainActivityViewModel
package com.example.newsapplication.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import com.example.newsapplication.model.NewsData
import com.example.newsapplication.network.RetroRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
// Annotate with HiltViewModel when using Hilt
// Also inject constructor to capture instance of the repositories
@HiltViewModel
class MainActivityViewModel @Inject constructor(private val repository: RetroRepository): ViewModel(){
fun getAllRepositoryList(): LiveData<List<NewsData>>{
return repository.getAllRecords()
}
fun makeApiCall(){
repository.makeApiCall("ab")
}
}
MainActivity
package com.example.newsapplication
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.newsapplication.adapter.RecyclerViewAdapter
import com.example.newsapplication.model.NewsData
import com.example.newsapplication.viewmodel.MainActivityViewModel
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var recyclerViewAdapter: RecyclerViewAdapter
private lateinit var context:Context
// For debug purposes
//private var TAG:String? = "MyActivity"
//private var message:String? = ""
fun setContext(con:Context){
context = con
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setContext(this)
initViewModel()
initMainViewModel()
}
private fun initViewModel(){
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.apply {
layoutManager = LinearLayoutManager(this@MainActivity)
val decoration = DividerItemDecoration(applicationContext, DividerItemDecoration.VERTICAL)
addItemDecoration(decoration)
recyclerViewAdapter = RecyclerViewAdapter()
adapter = recyclerViewAdapter
}
}
private fun initMainViewModel(){
val viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
viewModel.getAllRepositoryList().observe(this, Observer<List<NewsData>>{
recyclerViewAdapter.setListData(it)
recyclerViewAdapter.notifyDataSetChanged()
})
viewModel.makeApiCall()
}
}
MyRoomRetroApplication
package com.example.newsapplication
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class MyRoomRetroApplication: Application() {
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
news_list_row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView android:id="@+id/image_avatar_url"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center"
android:layout_marginStart="10dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@color/black"/>
<TextView android:id="@+id/tvDesc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:textSize="16sp"
android:maxLines="3"
android:textColor="@color/black"/>
</LinearLayout>
</LinearLayout>
build.gradle (Project)
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '7.2.2' apply false
id 'com.android.library' version '7.2.2' apply false
id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
}
task clean(type: Delete) {
delete rootProject.buildDir
}
build.gradle (App)
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.example.newsapplication"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
ext {
versions = "2.4.3"
}
dependencies {
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.5.0'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
// Below dependency for using retrofit + gson.
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.google.code.gson:gson:2.8.6'
// Below dependency for using picasso image loading library
implementation 'com.squareup.picasso:picasso:2.71828'
// Kotlin Extensions and Caroutines support for Room
implementation("androidx.room:room-ktx:$versions")
// Kotlin Annotation Processing Tool (kapt)
kapt("androidx.room:room-compiler:$versions")
implementation("androidx.room:room-runtime:$versions")
// Test helpers
testImplementation("androidx.room:room-testing:$versions")
// Paging 3 Integration
implementation("androidx.room:room-paging:$versions")
// Kotlin implementation
implementation('androidx.navigation:navigation-fragment-ktx:2.5.2')
implementation('androidx.navigation:navigation-ui-ktx:2.5.2')
// Recycler view
implementation('androidx.recyclerview:recyclerview:1.2.1')
// Feature module support
implementation('androidx.navigation:navigation-dynamic-features-fragment:2.5.2')
// Testing navigation
androidTestImplementation('androidx.navigation:navigation-testing:2.5.2')
// Jetpack compose integration
implementation('androidx.navigation:navigation-compose:2.5.2')
// ViewModel
implementation('androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1')
implementation('androidx.lifecycle:lifecycle-extensions:2.2.0')
// Glide
implementation('com.github.bumptech.glide:glide:4.12.0')
// Hilt
implementation('com.google.dagger:hilt-android:2.33-beta')
kapt('com.google.dagger:hilt-android:2.33-beta')
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
Error when try running app:
2022-09-14 22:50:18.249 14537-14537/com.example.newsapplication E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.newsapplication, PID: 14537
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.newsapplication/com.example.newsapplication.MainActivity}: java.lang.RuntimeException: Cannot create an instance of class com.example.newsapplication.viewmodel.MainActivityViewModel
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3676)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3813)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:101)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2308)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7898)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
Caused by: java.lang.RuntimeException: Cannot create an instance of class com.example.newsapplication.viewmodel.MainActivityViewModel
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:204)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:322)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:304)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:278)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:128)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153)
at com.example.newsapplication.MainActivity.initMainViewModel(MainActivity.kt:50)
at com.example.newsapplication.MainActivity.onCreate(MainActivity.kt:35)
at android.app.Activity.performCreate(Activity.java:8290)
at android.app.Activity.performCreate(Activity.java:8269)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1384)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3657)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3813)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:101)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2308)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7898)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
Caused by: java.lang.InstantiationException: java.lang.Class<com.example.newsapplication.viewmodel.MainActivityViewModel> has no zero argument constructor
at java.lang.Class.newInstance(Native Method)
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:202)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:322)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:304)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:278)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:128)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153)
**at com.example.newsapplication.MainActivity.initMainViewModel(MainActivity.kt:50)**
**at com.example.newsapplication.MainActivity.onCreate(MainActivity.kt:35)**
at android.app.Activity.performCreate(Activity.java:8290)
at android.app.Activity.performCreate(Activity.java:8269)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1384)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3657)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3813)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:101)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2308)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7898)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
2022-09-14 22:50:18.262 14537-14537/? I/Process: Sending signal. PID: 14537 SIG: 9