progress bar loading not showing response from server? - android

I am developing news app but TopHeadlinesFragment loading progress bar not showing response from server
what I want to know where I am making mistake what I have to do in order to show response from server. Maybe something wrong with my observer in topHeadlinesFragment.kt or koin network module
below my screenshot of the app
loading progress
below my TopHeadlinesFragment.kt
class TopHeadlinesFragment : Fragment() {
private lateinit var binding: FragmentTopHeadlinesBinding
private val viewModel by viewModel<MainViewModel>()
private lateinit var topHeadlinesAdapter: TopHeadlinesAdapter
// private val newsRepository: NewsRepository by inject()
//3
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding =
DataBindingUtil.inflate(inflater, R.layout.fragment_top_headlines, container, false)
binding.lifecycleOwner = this
topHeadlinesAdapter = TopHeadlinesAdapter()
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initViewModel()
// loadingAds()
}
private fun initViewModel() {
viewModel.sportList.observe(this, Observer { result ->
when (result) {
is Results.Success -> {
val newList = result.data
if (newList != null) {
topHeadlinesAdapter.updateData(newList)
}
binding.recyclerView.adapter = topHeadlinesAdapter
topHeadlinesAdapter.notifyDataSetChanged()
viewModel.showLoading.observe(this, Observer {showLoading ->
pb.visibility = if (showLoading) View.VISIBLE else View.GONE
})
}
is Results.Failure -> {
viewModel.showLoading.observe(this, Observer {showLoading ->
pb.visibility = if (showLoading) View.INVISIBLE else View.GONE
})
}
}
viewModel.loadNews()
})
}
}
below NewsRepository.kt
class NewsRepository(
private val sportNewsApi: SportNewsInterface,
private val sportNewsDao: SportNewsDao
) {
companion object{
const val TAG= "Error"
}
val data = sportNewsDao.getAllData()
suspend fun refresh() = withContext(Dispatchers.IO) {
val articles = sportNewsApi.getNewsAsync().body()?.articles
if (articles != null) {
sportNewsDao.addAll(articles)
Log.e(TAG,"Error")
Results.Success(articles)
} else {
Results.Failure("MyError")
}
}
}
below My MainViewModel.kt
class MainViewModel(val newsRepository: NewsRepository) : ViewModel(), CoroutineScope {
// Coroutine's background job
val job = Job()
// Define default thread for Coroutine as Main and add job
override val coroutineContext: CoroutineContext = Dispatchers.Main + job
private val _showLoading = MutableLiveData<Boolean>()
private val _sportList = MutableLiveData<Results>()
val showLoading: LiveData<Boolean>
get() = _showLoading
val sportList: LiveData<Results>
get() = _sportList
fun loadNews() {
// Show progressBar during the operation on the MAIN (default) thread
_showLoading.value = true
// launch the Coroutine
launch {
// Switching from MAIN to IO thread for API operation
// Update our data list with the new one from API
val result = newsRepository.refresh()
_sportList.value = result
_showLoading.value = false
}
}
override fun onCleared() {
job.cancel()
}
}
below my KoinNetworkModule.kt
const val BASE_URL = "https://newsapi.org/"
val netModule = module {
single {
createWebService<SportNewsInterface>(
okHttpClient = createHttpClient(),
factory = RxJava2CallAdapterFactory.create(),
baseUrl = BASE_URL
)
}
}
/* Returns a custom OkHttpClient instance with interceptor. Used for building Retrofit service */
fun createHttpClient(): OkHttpClient {
val client = OkHttpClient.Builder()
client.readTimeout(5 * 60, TimeUnit.SECONDS)
return client.addInterceptor {
val original = it.request()
val requestBuilder = original.newBuilder()
requestBuilder.header("Content-Type", "application/json")
val request = requestBuilder.method(original.method, original.body).build()
return#addInterceptor it.proceed(request)
}.build()
}
/* function to build our Retrofit service */
inline fun <reified T> createWebService(
okHttpClient: OkHttpClient,
factory: CallAdapter.Factory, baseUrl: String
): T {
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addCallAdapterFactory(factory)
.client(okHttpClient)
.build()
return retrofit.create(T::class.java)
}

I fixed problem by changing my code followingly.
private fun initViewModel() {
viewModel.sportList.observe(this, Observer { result ->
when (result) {
is Results.Success -> {
val newList = result.data
if (newList != null) {
topHeadlinesAdapter.updateData(newList)
}
binding.recyclerView.adapter = topHeadlinesAdapter
topHeadlinesAdapter.notifyDataSetChanged()
}
}
})
viewModel.showLoading.observe(this, Observer { showLoading ->
pb.visibility = if (showLoading) View.VISIBLE else View.GONE
})
viewModel.loadNews()
}

Got your Problem. If you seriously want to show the response, whatever you are getting use this code in the Retrofit Instance. The role of Intercepter is used to show the request and response at the Log level. You can findout the URL of API, Request and resonse in the Log window.
Now modify KoinNetworkModule.kt like this
const val BASE_URL = "https://newsapi.org/"
val netModule = module {
single {
createWebService<SportNewsInterface>(
okHttpClient = createHttpClient(),
factory = RxJava2CallAdapterFactory.create(),
baseUrl = BASE_URL
)
}
}
/* Returns a custom OkHttpClient instance with interceptor. Used for building Retrofit service */
fun createHttpClient(): OkHttpClient {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val client1 = OkHttpClient.Builder()
.connectTimeout(2, TimeUnit.MINUTES)
.writeTimeout(2, TimeUnit.MINUTES) // write timeout
.readTimeout(2, TimeUnit.MINUTES) // read timeout
.addInterceptor(interceptor)
.build()
/* function to build our Retrofit service */
inline fun <reified T> createWebService(
okHttpClient: OkHttpClient,
factory: CallAdapter.Factory, baseUrl: String
): T {
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addCallAdapterFactory(factory)
.client(client1)
.build()
return retrofit.create(T::class.java)
}

Related

How can I mock Repository when it uses retrofit2 and room in it?

I am trying to write test code for a ViewModel.
and the ViewModel uses this repository, so I'd like to mock it.
class ServiceRepository #Inject constructor(
private val service: MyService,
private val dao: MyDao
) {
suspend fetchMenus(filter: FilterData) = flow{
val insertList = mutableListOf<MenuData>()
val deleteList = mutableListOf<MenuData>()
val response = try {
decodeMenus(filter)
} catch(e: CustomIOException) {
// catch
}
val result = response.getOrNull()?.results?.map{ menu ->
menu.isRead = isRead(menu)
}
}
private suspend fun decodeMenus(): Result<MenuListData> {
return try {
val response = service.fetchMenus()
when{
response.isSuccessful -> Result.success(resonse.body()!!.data)
else -> Result.failure(CustomException())
}
}catch (e: Exception){
Result.failure(e)
}
}
private fun isRead(menu: MenuData): Boolean {
val ret = if(menu.status == MenuType.yesterday.index){
true
}else{
val checkedList = dao.getChecked(menu.key)
if(checkedList.isEmpty()){
false
}else{
checkedList.first().isChecked
}
}
return ret
}
}
And here's my test code that I am trying to write.
#ExperimentalCoroutinesApi
#ExtendWith(CoroutinesTestRule::class)
class MainViewModelTest {
private val instantExecutorRule = InstantTaskExecutorRule()
private val mockWebServer = MockWebServer()
#get:Rule
val rule: RuleChain = RuleChain
.outerRule(instantExecutorRule)
.around(mockWebServer)
private val retrofit by lazy {
val client = OkHttpClient.Builder().apply {
connectTimeout(5, TimeUnit.SECONDS)
callTimeout(5, TimeUnit.SECONDS)
readTimeout(5, TimeUnit.SECONDS)
writeTimeout(5, TimeUnit.SECONDS)
retryOnConnectionFailure(true)
}
val gson = GsonBuilder()
.setLenient()
.setDateFormat("yyyy-MM-dd'T'hh:mm:ssZ")
.create()
Retrofit.Builder()
.baseUrl(mockWebServer.url("/"))
.client(client.build())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(NullOnEmptyConverterFactory())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
private val mService by lazy {
retrofit.create(MyService::class.java)
}
#MockK
private lateinit var mDao: MyDao
// no mock
private lateinit var serviceRepo: ServiceRepository
// should mock?
private lateinit var mainViewModel: MainViewModel
#Before
fun setUp() {
Dispatchers.setMain(Dispatchers.Unconfined)
MockKAnnotations.init(this, relaxed = true)
serviceRepo = ServiceRepository(mService, mDao)
mainViewModel = MainViewModel(serviceRepo, userRepo, prefRepo)
}
#Test
fun loadMenuList_returnMenuList(){
val fakeResJson = loadJson("/example_success.json")
val fakeFilter = FilterData("sunday")
val mockResponseBody = MockResponse().setBody(fakeResJson).setResponsCode(200)
val mockWebServer.enqueue(mockResponseBody)
val responseBody = mService.fetchMenus(FilterData("yesterday"))
coEvery { mService.fetchMenus("yesterday") } return responseBody
// coEvery { serviceRepo.fetchMenus("yesterday") } return ???
}
Since I need to get response from api, I used MockWebServer and created responseBody. And then I mock mService to return responseBody when fetchMenus() is called.
this is what I tired in other way:
#Mockk
private lateinit var mService: MyService
// TODO: without MockWebServer
#Test
fun loadReceptionList2_returnReceptionList() = runTest {
// given
coEvery {
mService.fetchMenus("yesterday")
} returns // How can I create return value...?
}
But I don't know how to create the return value there.
So, when serviceRepo calls fetchMenus("yesterday"), it must be called. However, since ServiceRepository is very complicated and it has flow. I don't know how to achieve this. How can I do that?

Infinite updating of the RecyclerView list

With the help of dagger and rxJava I update the list in a RecyclerView. Everything works well, the list is displayed. But the problem is that in the logs I see how this list is updated every second. What could be the problem? In a similar project but in Java everything works correctly, the list is updated once at startup.
My Network Module:
#Module(includes = [ViewModelModule::class])
class NetworkModule {
companion object {
const val KEY = "key"
const val BASE_URL = "base_url"
}
#Provides
#Singleton
fun provideOkHttp(): OkHttpClient {
val httpClient = OkHttpClient.Builder()
httpClient.addInterceptor(object : Interceptor {
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
val original = chain.request()
val originalHttpUrl = original.url
val url = originalHttpUrl.newBuilder()
//.addQueryParameter("apikey", KEY)
.build()
val requestBuilder = original.newBuilder()
.url(url)
.header("apikey", KEY)
val request = requestBuilder.build()
return chain.proceed(request)
}
})
// logging interceptor
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
httpClient.addInterceptor(logging)
return httpClient.build()
}
#Provides
#Singleton
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(provideOkHttp())
.build()
}
#Provides
#Singleton
fun provideContactsService(retrofit: Retrofit) : ContactsService{
return retrofit.create(ContactsService::class.java)
}
}
My ViewModel:
class ContactsViewModel #Inject constructor(private val contactsRepository: ContactsRepository) :
ViewModel() {
var mutableLiveData = MutableLiveData<List<ContactsModel>>()
private val disposable = CompositeDisposable()
fun getContactMutableLiveData(): MutableLiveData<List<ContactsModel>> {
loadData()
return mutableLiveData
}
fun loadData() {
disposable.add(contactsRepository.modelSingle()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<List<ContactsModel>>() {
override fun onSuccess(t: List<ContactsModel>) {
getContactMutableLiveData().value = t
}
override fun onError(e: Throwable) {
}
})
)
}
}
And my Activity:
contactsViewModel.getContactMutableLiveData().observe(this#ContactListActivity, Observer {
mAdapter = ContactsAdapter(this#ContactListActivity, it as ArrayList<ContactsModel>)
recycler_contacts.layoutManager =
LinearLayoutManager(applicationContext, OrientationHelper.VERTICAL, false)
recycler_contacts.adapter = mAdapter
recycler_contacts.setHasFixedSize(true)
mAdapter.sortByName()
})
Okay if you only want to update your data list only once... I would recommend you look into a single live event that would trigger the reloading of your recycler view
As such
//in repo
private SingleLiveEvent<Boolean> listHasBeenUpdate=new SingleLiveEvent<>();
//setItsGetterInTheRepo
public SingleLiveEvent<Boolean> getListHasBeenUpdated(){
return listHasBeenUpdated();
}
//uponSucessfuly fetching your list from retrofit
listHasBeenUpdated=true;
//pass list to viewmodel
then in the ViewModel, I would set the list to be an Observable Data which would be updated once it's fetched from retrofit (Consider using room db to store this)
//use a setter to set the list from Repo
ObservableField<List<Contacts>> list=new ObservableField<>();
public SingleLiveEvent<List<Contacts>> fetchContacts(){
return myRepo.getListHasBeenUpdated();
}
In your activity class now observe the single live event like so
viewModel.fetchContacts().observe(this,contacts->{
if(contacts){
//update Recycler
}
});
hope this helps you.
It was a logical error. You need to rewrite the loadData function as shown below
class ContactsViewModel #Inject constructor(private val contactsRepository: ContactsRepository) :
ViewModel() {
var mutableLiveData = MutableLiveData<List<ContactsModel>>()
private val disposable = CompositeDisposable()
fun getContactMutableLiveData(): MutableLiveData<List<ContactsModel>> {
disposable.add(contactsRepository.modelSingle()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<List<ContactsModel>>() {
override fun onSuccess(t: List<ContactsModel>) {
mutableLiveData.value = t
}
override fun onError(e: Throwable) {
}
}))
return mutableLiveData
}
}

extends java.util.List. Registering an InstanceCreator with Gson for this type may fix this problem.?

I am developing a new android app but I am getting the following exception Unable to invoke no-args constructor for kotlinx.coroutines.Deferred>. Registering an InstanceCreator with Gson for this type may fix this problem.
below my MainViewModel.kt
#Suppress("UNCHECKED_CAST")
class MainViewModel(val newsRepository: NewsRepository) : ViewModel(), CoroutineScope {
// Coroutine's background job
val job = Job()
// Define default thread for Coroutine as Main and add job
override val coroutineContext: CoroutineContext = Dispatchers.Main + job
val showLoading = MutableLiveData<Boolean>()
val sportList = MutableLiveData <List<Article>>()
val showError = SingleLiveEvent<String>()
fun loadNews() {
// Show progressBar during the operation on the MAIN (default) thread
showLoading.value = true
// launch the Coroutine
launch {
// Switching from MAIN to IO thread for API operation
// Update our data list with the new one from API
val result = withContext(Dispatchers.IO) {
newsRepository?.getNewsList()
}
// Hide progressBar once the operation is done on the MAIN (default) thread
showLoading.value = false
when (result) {
is UseCaseResult.Success<*> -> {
sportList.value = result.data as List<Article>
}
is Error -> showError.value = result.message
}
}
}
override fun onCleared() {
super.onCleared()
// Clear our job when the linked activity is destroyed to avoid memory leaks
job.cancel()
}
}
below appmodules.kt my network logic implementation
const val BASE_URL = "https://newsapi.org/"
val appModules = module {
// The Retrofit service using our custom HTTP client instance as a singleton
single {
createWebService<SportNewsInterface>(
okHttpClient = createHttpClient(),
factory = RxJava2CallAdapterFactory.create(),
baseUrl = BASE_URL
)
}
// Tells Koin how to create an instance of CatRepository
factory<NewsRepository> { (NewsRepositoryImpl(sportsNewsApi = get())) }
// Specific viewModel pattern to tell Koin how to build MainViewModel
viewModel { MainViewModel (newsRepository = get ()) }
}
/* Returns a custom OkHttpClient instance with interceptor. Used for building Retrofit service */
fun createHttpClient(): OkHttpClient {
val client = OkHttpClient.Builder()
client.readTimeout(5 * 60, TimeUnit.SECONDS)
return client.addInterceptor {
val original = it.request()
val requestBuilder = original.newBuilder()
requestBuilder.header("Content-Type", "application/json")
val request = requestBuilder.method(original.method, original.body).build()
return#addInterceptor it.proceed(request)
}.build()
}
/* function to build our Retrofit service */
inline fun <reified T> createWebService(
okHttpClient: OkHttpClient,
factory: CallAdapter.Factory, baseUrl: String
): T {
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addCallAdapterFactory(factory)
.client(okHttpClient)
.build()
return retrofit.create(T::class.java)
}
below SportNewsResponse.kt
data class SportNewsResponse(
val articles: List<Article>,
val status: String,
val totalResults: Int
)
below SportNewsInterface where I have implemented my ending points
interface SportNewsInterface {
#GET("v2/top-headlines?country=us&apiKey=da331087e3f3462bb534b3b0917cbee9")
suspend fun getNews(): Deferred<List<SportNewsResponse>>
#GET("/v2/top-headlines?sources=espn&apiKey=da331087e3f3462bb534b3b0917cbee9")
fun getEspn(): Deferred<List<SportNewsResponse>>
#GET("/v2/top-headlines?sources=football-italia&apiKey=da331087e3f3462bb534b3b0917cbee9")
fun getFootballItalia(): Deferred<List<SportNewsResponse>>
#GET("/v2/top-headlines?sources=bbc-sport&apiKey=da331087e3f3462bb534b3b0917cbee9")
fun getBBCSport(): Deferred<List<SportNewsResponse>>
}

Caused by: org.koin.core.error.InstanceCreationException: Could not create instance for [type:Factory,primary_type

I am developing news app I have implemented koin with viewmodel in fragment class but I am getting following error when I test code on emulator
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:557)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:922)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:922) 
Caused by: org.koin.core.error.InstanceCreationException: Could not create instance for [type:Factory,primary_type:'yodgorbek.komilov.musobaqayangiliklari.viewmodel.MainViewModel']
at org.koin.core.instance.DefinitionInstance.create(DefinitionInstance.kt:61)
at org.koin.core.instance.FactoryDefinitionInstance.get(FactoryDefinitionInstance.kt:37)
at org.koin.core.definition.BeanDefinition.resolveInstance(BeanDefinition.kt:70)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:165)
at org.koin.core.scope.Scope.get(Scope.kt:128)
at org.koin.androidx.viewmodel.ViewModelResolutionKt$createViewModelProvider$1.create(ViewModelResolution.kt:66)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:164)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:130)
at org.koin.androidx.viewmodel.ViewModelResolutionKt.getInstance(ViewModelResolution.kt:43)
at org.koin.androidx.viewmodel.ViewModelResolutionKt.getViewModel(ViewModelResolution.kt:23)
at org.koin.androidx.viewmodel.ext.android.LifecycleOwnerExtKt.getViewModel(LifecycleOwnerExt.kt:85)
at yodgorbek.komilov.musobaqayangiliklari.ui.TopHeadlinesFragment$$special$$inlined$viewModel$1.invoke(LifecycleOwnerExt.kt:95)
at yodgorbek.komilov.musobaqayangiliklari.ui.TopHeadlinesFragment$$special$$inlined$viewModel$1.invoke(Unknown Source:0)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at yodgorbek.komilov.musobaqayangiliklari.ui.TopHeadlinesFragment.getViewModel(Unknown Source:7)
at yodgorbek.komilov.musobaqayangiliklari.ui.TopHeadlinesFragment.initViewModel(TopHeadlinesFragment.kt:57)
at yodgorbek.komilov.musobaqayangiliklari.ui.TopHeadlinesFragment.onCreateView(TopHeadlinesFragment.kt:51)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2600)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:881)
at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2663)
at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManagerImpl.java:2613)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:246)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:542)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:201)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1392)
at android.app.Activity.performStart(Activity.java:7252)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3000)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:185)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:170)
2019-11-16 15:12:01.728 12995-12995/yodgorbek.komilov.musobaqayangiliklari E/AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:147)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:73)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1858)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:201)
at android.app.ActivityThread.main(ActivityThread.java:6820)
... 3 more
Caused by: org.koin.core.error.NoBeanDefFoundException: No definition found for 'java.lang.Object' has been found. Check your module definitions.
at org.koin.core.scope.Scope.findDefinition(Scope.kt:170)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:164)
at org.koin.core.scope.Scope.get(Scope.kt:128)
at yodgorbek.komilov.musobaqayangiliklari.di.application.module.AppModulesKt$appModules$1$3.invoke(appModules.kt:65)
at yodgorbek.komilov.musobaqayangiliklari.di.application.module.AppModulesKt$appModules$1$3.invoke(Unknown Source:4)
at org.koin.core.instance.DefinitionInstance.create(DefinitionInstance.kt:54)
... 44 more
below TopHeadlinesFragment.kt
class TopHeadlinesFragment : Fragment() {
private val viewModel: MainViewModel by viewModel()
private lateinit var topHeadlinesAdapter: TopHeadlinesAdapter
//3
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(
R.layout.fragment_top_headlines
, container, false
)
val recyclerView = view.findViewById(R.id.recyclerView) as RecyclerView
val pb = view.findViewById(R.id.pb) as ProgressBar
topHeadlinesAdapter = TopHeadlinesAdapter(recyclerView.context)
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = topHeadlinesAdapter
initViewModel()
return view
}
private fun initViewModel() {
viewModel?.sportList?.observe(this, Observer { newList ->
topHeadlinesAdapter.updateData(newList)
})
viewModel?.showLoading?.observe(this, Observer { showLoading ->
pb.visibility = if (showLoading) View.VISIBLE else View.GONE
})
viewModel?.showError?.observe(this, Observer { showError ->
(showError)
})
viewModel?.loadNews()
}
}
below MainViewModel.kt
Suppress("UNCHECKED_CAST")
class MainViewModel(newsRepository: Any?) : ViewModel(), CoroutineScope {
// Coroutine's background job
val job = Job()
val sportNewsInterface: SportNewsInterface? = null
// Define default thread for Coroutine as Main and add job
override val coroutineContext: CoroutineContext = Dispatchers.Main + job
val showLoading = MutableLiveData<Boolean>()
val sportList = MutableLiveData <List<Article>>()
val showError = SingleLiveEvent<String>()
fun loadNews(
) {
// Show progressBar during the operation on the MAIN (default) thread
showLoading.value = true
// launch the Coroutine
launch {
// Switching from MAIN to IO thread for API operation
// Update our data list with the new one from API
val result = withContext(Dispatchers.IO) {
sportNewsInterface?.getNews()
}
// Hide progressBar once the operation is done on the MAIN (default) thread
showLoading.value = false
when (result) {
is UseCaseResult.Success<*> -> {
sportList.value = result.data as List<Article>
}
is Error -> showError.value = result.message
}
}
}
override fun onCleared() {
super.onCleared()
// Clear our job when the linked activity is destroyed to avoid memory leaks
job.cancel()
}
}
below appModules.kt
const val BASE_URL = "https://newsapi.org/"
val appModules = module {
// The Retrofit service using our custom HTTP client instance as a singleton
single {
createWebService<SportNewsInterface>(
okHttpClient = createHttpClient(),
factory = RxJava2CallAdapterFactory.create(),
baseUrl = BASE_URL
)
}
// Tells Koin how to create an instance of CatRepository
factory<NewsRepository> { (NewsRepositoryImpl(sportNewsInterface = get())) }
// Specific viewModel pattern to tell Koin how to build MainViewModel
viewModel { MainViewModel (newsRepository = get ()) }
}
/* Returns a custom OkHttpClient instance with interceptor. Used for building Retrofit service */
fun createHttpClient(): OkHttpClient {
val client = OkHttpClient.Builder()
client.readTimeout(5 * 60, TimeUnit.SECONDS)
return client.addInterceptor {
val original = it.request()
val requestBuilder = original.newBuilder()
requestBuilder.header("Content-Type", "application/json")
val request = requestBuilder.method(original.method, original.body).build()
return#addInterceptor it.proceed(request)
}.build()
}
/* function to build our Retrofit service */
inline fun <reified T> createWebService(
okHttpClient: OkHttpClient,
factory: CallAdapter.Factory, baseUrl: String
): T {
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addCallAdapterFactory(factory)
.client(okHttpClient)
.build()
return retrofit.create(T::class.java)
}
below NewsRepository.kt where I have implemented NewsRepository
interface NewsRepository {
// Suspend is used to await the result from Deferred
suspend fun getNewsList(): UseCaseResult<Deferred<List<SportNewsResponse>>>
}
class NewsRepositoryImpl(private val sportNewsInterface: SportNewsInterface) : NewsRepository {
override suspend fun getNewsList(): UseCaseResult<Deferred<List<SportNewsResponse>>> {
/*
We try to return a list of cats from the API
Await the result from web service and then return it, catching any error from API
*/
return try {
val result = sportNewsInterface.getNews()
UseCaseResult.Success(result) as UseCaseResult<Deferred<List<SportNewsResponse>>>
} catch (ex: Exception) {
UseCaseResult.Error(ex)
}
}
}
class MainViewModel(newsRepository: Any?) : ViewModel(),
CoroutineScope {
Why your newsRepository is of Any? type make it of NewsRepository
class MainViewModel(newsRepository: NewsRepository) : ViewModel(), CoroutineScope {
In app module your NewsRepositoryImpl need's Implementation of SportNewsInterface.
I believe below is missing in your appModule.
factory<SportNewsInterface> { (SportNewsInterfaceImpl()) }
i.e:
val appModules = module {
// The Retrofit service using our custom HTTP client instance as a singleton
single {
createWebService<SportNewsInterface>(
okHttpClient = createHttpClient(),
factory = RxJava2CallAdapterFactory.create(),
baseUrl = BASE_URL
)
}
// Tells Koin how to create an instance of CatRepository
factory<NewsRepository> { (NewsRepositoryImpl(sportNewsInterface = get())) }
//Tells Koin how to create SportNewsInterface
factory<SportNewsInterface> { (SportNewsInterfaceImpl()) }
// Specific viewModel pattern to tell Koin how to build MainViewModel
viewModel { MainViewModel (newsRepository = get ()) }
}
Few other Tip's to clean up your code:
Use ViewModelScope instead of creating your own CoroutineScope in ViewModel
dependency
//Coroutine View Model Scope.
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0"
You need to use the "provide" convention just like with Dagger #Provides annotation like in the example below.
val appModules = module {
fun provideNewsRepository(sportNewsInterface: SportNewsInterface): NewsRepository {
return NewsRepositoryImpl(sportNewsInterface)
}
fun provideSportNewsInterface(): SportNewsInterface {
return createWebService < SportNewsInterface > (
okHttpClient = createHttpClient(),
factory = RxJava2CallAdapterFactory.create(),
baseUrl = BASE_URL
)
}
single {
provideNewsRepository(get())
}
single {
provideSportNewsInterface()
}
viewModel {
MainViewModel(newsRepository = get())
}
}
In my case, I have fixed it adding a /.
I changed from
single(named("baseUrl")) { "https://yourwebsite.net/api" }
to
single(named("baseUrl")) { "https://yourwebsite.net/api/" }
You may have forgotten to create an instance of the API interface in the networking module.
My networking module class looks like this,
val networkingModule = module {
single { GsonConverterFactory.create() as Converter.Factory }
single { HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY) as Interceptor }
single {
OkHttpClient.Builder().apply {
if (BuildConfig.DEBUG) addInterceptor(get())
.callTimeout(10, TimeUnit.SECONDS)
}.build()
}
single {
Retrofit.Builder()
.baseUrl(BuildConfig.HOST)
.client(get())
.addConverterFactory(get())
.build()
}
single { get<Retrofit>().create(LoginService::class.java) }
single { get<Retrofit>().create(BlockService::class.java) }
single { get<Retrofit>().create(ForgotPasswordService::class.java) }
}

android- architecture component return nothing on android

I'm new in architecture component and kotlin.
I've written and app that works fine one android pre orio but it returns nothing on android orio and above .
these are my codes, this is my code for retrofit connection
object ApiConnection {
val BaseUrl ="http://site.ir/"
val client: Retrofit
get() {
val gson = GsonBuilder()
.setLenient()
.create()
///for Logging
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient.Builder().addInterceptor(interceptor).build()
var retrofit = Retrofit.Builder()
.baseUrl(BaseUrl)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
.build()
return retrofit
}
}
this is my code for activity :
lateinit var rc_peyk:RecyclerView;
lateinit var peykViewModel:PeykHistoryViewModel
lateinit var peykHistAdapter: PeykHistoryAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.peyk_history)
declare();
peykViewModel= ViewModelProviders.of(this).get(PeykHistoryViewModel::class.java)
peykViewModel.getPeyks(Func.getUid(this)).observe(this
, Observer<MutableList<Peyk_item>>{
if(it==null){
MyToast.makeText(this#PeykHistory, "null 1");
}
if(it!=null && it.size==0){
MyToast.makeText(this#PeykHistory, "size 0");
}
if(it!=null && it.size>0){
peykHistAdapter= PeykHistoryAdapter(this#PeykHistory,it)
rc_peyk.adapter=peykHistAdapter
}
})
actionbar()
}
private fun declare() {
rc_peyk=findViewById(R.id.rc_peyk_history)
rc_peyk.layoutManager=LinearLayoutManager(this)
}
this is the code view viewModel :
class PeykHistoryViewModel(application:Application):AndroidViewModel(application){
private val peykRepository:PeykHistoryRepository= PeykHistoryRepository()
private lateinit var peykHistory:MutableLiveData<MutableList<Peyk_item>>
fun getPeyks(uid:String):MutableLiveData<MutableList<Peyk_item>>{
if(!::peykHistory.isInitialized){
peykHistory=peykRepository.getPeyks(uid)
}
return peykHistory
}
this is the code for repository :
class PeykHistoryRepository {
private lateinit var getPeykHistory: getPeykHistoryApi
private lateinit var peykList: MutableLiveData<MutableList<Peyk_item>>
fun getPeyks(uid: String): MutableLiveData<MutableList<Peyk_item>> {
if (!::peykList.isInitialized) {
peykList = MutableLiveData()
}
getPeykHistory = ApiConnection.client.create(getPeykHistoryApi::class.java)
getPeykHistory.getPeykHistory(uid)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ result ->
if(result.peyks!=null){
peykList+=result.peyks
}else{
Log.v("this","nothing");
}
}, { error ->
Log.v("this",error.localizedMessage.toString())
})
return peykList;
}
operator fun <T> MutableLiveData<MutableList<T>>.plusAssign(values: List<T>) {
val value = this.value ?: arrayListOf()
value.addAll(values)
this.value = value
}
there is no error and the api is fine and return the correct json .
could you help me ? I've tried too many things but doesn't help me

Categories

Resources