I was trying to get response from viewModel But, I am having hard time to use the response data on another activity to display it on a text view.
I have already setup the backend for the repo's and interfaces, data classes etc...
Thank you!!!
// View Model
class HomeActivityViewModel : ViewModel() {
lateinit var createPostLiveData: MutableLiveData<PostResponseData?>
init {
createPostLiveData = MutableLiveData()
}
fun getPostLiveObserver(): MutableLiveData<PostResponseData?> {
return createPostLiveData
}
fun createPostData(postdata: PostData) {
val retroService = RetrofitApiFactory.retroInstance().create(ChefApi::class.java)
val call = retroService.postData(postdata)
call.enqueue(object : Callback<PostResponseData> {
override fun onResponse(
call: Call<PostResponseData>,
response: Response<PostResponseData>
) {
if (response.isSuccessful) {
createPostLiveData.postValue(response.body())
var text = response.body()!!.choices[0].text
Log.d("response", text) // only shows the one in the viewModel
} else {
createPostLiveData.postValue(null)
Log.d("failed", response.errorBody().toString())
}
}
override fun onFailure(call: Call<PostResponseData>, t: Throwable) {
Log.d("failed", t.message.toString())
createPostLiveData.postValue(null)
}
})
}
}
Activity.kt
class HomeActivity : AppCompatActivity() {
lateinit var mAuth: FirebaseAuth
lateinit var viewModel: HomeActivityViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home_actvity)
mAuth = FirebaseAuth.getInstance()
initViewModel()
generate.setOnClickListener {
createPost()
}
logout.setOnClickListener {
logoutUser()
}
}
private fun createPost() {
// creating a post
val prompt = "Some string..."
val postdata = PostData(120, prompt, 0.3, 1.0, 0.0)
viewModel.createPostData(postdata)
}
private fun initViewModel() {
// initialize view model
viewModel = ViewModelProvider(this).get(HomeActivityViewModel::class.java)
viewModel.getPostLiveObserver().observe(this, Observer<PostResponseData?> {
if (it == null) {
Toast.makeText(this#HomeActivity, "Failed to post data", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this#HomeActivity, "Successfully posted data", Toast.LENGTH_SHORT)
.show()
}
})
}
private fun logoutUser() {
mAuth.signOut()
updateUI()
}
private fun updateUI() {
val intent = Intent(this#HomeActivity, MainActivity::class.java)
startActivity(intent)
finish()
}
Try to change your HomeActivityViewModel class and add a LiveData object to it:
class HomeActivityViewModel : ViewModel() {
var _createPostLiveData: MutableLiveData<PostResponseData?>()
// Live data instance
val createPostLiveData
get() = _createPostLiveData
fun createPostData(postdata: PostData) {
val retroService = RetrofitApiFactory.retroInstance().create(ChefApi::class.java)
val call = retroService.postData(postdata)
call.enqueue(object : Callback<PostResponseData> {
override fun onResponse(
call: Call<PostResponseData>,
response: Response<PostResponseData>
) {
if (response.isSuccessful) {
// Update live data value
_createPostLiveData.value = response.body()
var text = response.body()!!.choices[0].text
Log.d("response", text) // only shows the one in the viewModel
} else {
// Update live data value
_createPostLiveData.value = null
Log.d("failed", response.errorBody().toString())
}
}
override fun onFailure(call: Call<PostResponseData>, t: Throwable) {
Log.d("failed", t.message.toString())
// Update live data value
_createPostLiveData.value = null
}
})
}
}
You should then be able to observe the LiveData instance in your Activity:
class HomeActivity : AppCompatActivity() {
lateinit var mAuth: FirebaseAuth
// Initialize view model in declaration
private val viewModel: HomeActivityViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home_actvity)
mAuth = FirebaseAuth.getInstance()
// Observe for `createPostLiveData` changes
viewModel.createPostLiveData.observe(this) {
if (it == null) {
Toast.makeText(this#HomeActivity, "Failed to post data", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this#HomeActivity, "Successfully posted data", Toast.LENGTH_SHORT)
.show()
}
}
generate.setOnClickListener {
createPost()
}
logout.setOnClickListener {
logoutUser()
}
}
private fun createPost() {
// creating a post
val prompt = "Some string..."
val postdata = PostData(120, prompt, 0.3, 1.0, 0.0)
viewModel.createPostData(postdata)
}
...
Related
I am making a messenger app on Kotlin and encountered this problem,
2020-11-18 14:20: 41.163 18185-18185/com.example.vinco E/zzf: Failed to get reCAPTCHA token - calling backend without app verification
I'll leave the class codes below
class EnterPhoneNumberFragment : Fragment(R.layout.fragment_enter_phone_number) {
private lateinit var mPhoneNumber:String
private lateinit var mCallback:PhoneAuthProvider.OnVerificationStateChangedCallbacks
override fun onStart() {
super.onStart()
AUTH = FirebaseAuth.getInstance()
initFirebase() ////////////////////////////////////////////////////////////////////////////////
mCallback = object :PhoneAuthProvider.OnVerificationStateChangedCallbacks(){
override fun onVerificationCompleted(credential: PhoneAuthCredential) {
AUTH.signInWithCredential(credential)
.addOnCompleteListener { task ->
if(task.isSuccessful){
showToast("Добро пожаловать")
(activity as RegisterActivity).replaceActivity(MainActivity())
}else showToast(task.exception?.message.toString())
}
}
override fun onVerificationFailed(p0: FirebaseException) {
// showToast(p0.message.toString())
}
override fun onCodeSent(id: String, token: PhoneAuthProvider.ForceResendingToken) {
replaceFragment(EnterCodeFragment(mPhoneNumber,id))
}
}
register_btn_next.setOnClickListener{ sendCode()}
}
private fun sendCode(){
if (register_input_phone_number.text.toString().isEmpty()){
showToast(getString(R.string.register_toast_enter_phone))
}else{
authUser()
}
}
private fun authUser() {
mPhoneNumber = register_input_phone_number.text.toString()
PhoneAuthProvider.verifyPhoneNumber(PhoneAuthOptions.newBuilder(FirebaseAuth.getInstance())
.setPhoneNumber(mPhoneNumber)
.setTimeout(60L,
TimeUnit.SECONDS)
.setActivity(activity as RegisterActivity)
.setCallbacks(mCallback)
.build()
)
}
}
class EnterCodeFragment(private val phoneNumber: String, val id: String) : Fragment(R.layout.fragment_enter_code) {
override fun onStart() {
super.onStart()
(activity as RegisterActivity).title = phoneNumber
register_input_code.addTextChangedListener ( AppTextWatcher{
initFirebase()
val string = register_input_code.text.toString()
if(string.length==6) {
enterCode()
}
} )
}
private fun enterCode(){
val code = register_input_code.text.toString()
val credential:PhoneAuthCredential = PhoneAuthProvider.getCredential(id, code)
AUTH.signInWithCredential(credential).addOnCompleteListener { task ->
if(task.isSuccessful){
val uid:String = AUTH.currentUser?.uid.toString()
val dateMap = mutableMapOf<String, Any>()
dateMap[CHILD_ID] = uid
dateMap[CHILD_PHONE] = phoneNumber
dateMap[CHILD_USERNAME] = uid
REF_DATABASE_ROOT.child(NODE_USERS).child(uid).updateChildren(dateMap)
.addOnCompleteListener{ task2 ->
if (task2.isSuccessful) {
showToast("Добро пожаловать")
(activity as RegisterActivity).replaceActivity(MainActivity())
}else showToast(task2.exception?.message.toString())
}
}
}
}
}
I am calling the API using retrofit in ApiClient Class. Where I am trying to store the output of successful login either message or responseBody into the output string. I tried using the output string as part of ViewModel Observer type too
But I CANNOT pass the value of output from AplClient to MainActivity
APICLIENT
...
package com.example.services.api
lateinit var service: ApiInterface
object ApiClient {
#JvmStatic
private val BASE_URL = GlobalConstants.SWAGGER
//private val sharedPrefClass = SharedPrefClass()
#JvmStatic
private var mApiInterface: ApiInterface? = null
var output = "Initializing"
// val sharedPrefClass: SharedPrefClass? = null
// #JvmStatic
// fun getApiInterface(): ApiInterface {
// return setApiInterface()
// }
#JvmStatic
fun setApiInterface() : String {
val platform = GlobalConstants.PLATFORM
var mAuthToken = GlobalConstants.SESSION_TOKEN
var companyId = GlobalConstants.COMPANY_ID
var phone = phone
var cCode = cCode
//Here a logging interceptor is created
//Here a logging interceptor is created
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
//The logging interceptor will be added to the Http client
//The logging interceptor will be added to the http client
val httpClient = OkHttpClient.Builder()
httpClient.addInterceptor(logging)
//The Retrofit builder will have the client attached, in order to get connection logs
//The Retrofit builder will have the client attached, in order to get connection logs
val retrofit: Retrofit = Retrofit.Builder()
.client(httpClient.build())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
service = retrofit.create<ApiInterface>(ApiInterface::class.java)
val call: Call<Tree> = service.post(phone, cCode, companyId, platform)
var model: Model
call.enqueue(object : Callback<Tree> {
override fun onResponse(
call: Call<Tree>, response: Response<Tree>)
{
Log.e(TAG, "Success")
if (!response.isSuccessful()) {
model = Model("Success", "91", "8884340404")
model.output += response.body().toString()
return;
}
}
override fun onFailure(
call: Call<Tree>,
t: Throwable
) {
Log.e(TAG, "Json/Network Error")
model = Model("Json/Network Error", "91", "8884340404")
model.output = "Json/Network Error"
// handle execution failures like no internet connectivity
}
})
return output
}
}
VIEWMODEL
package com.example.kotlinloginapi.viewModel
import androidx.databinding.Observable
import androidx.databinding.ObservableField
import androidx.lifecycle.MutableLiveData
class Model {
var output:String? = null
var cCode:String? = null
var phone: String? = null
constructor(output: String?, cCode: String?, phone: String?) {
this.output = output
this.cCode = cCode
this.phone = phone
}
}
...
MAINACTIVTY
...
package com.example.kotlinloginapi.ui.login
lateinit var textView: TextView
class MainActivity : AppCompatActivity() {
lateinit var mainBinding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
textView = findViewById<TextView>(R.id.textView)
val mApiService: ApiService<Tree>
val mApiClient = ApiClient
var model: Model
model = Model("Creating View", "91", "8884340404")
model.output = "Creating View"
Observable.just(setApiInterface())
.subscribe {
println(it)
textView!!.append(setApiInterface())}
// mainBinding.viewModel!!.output = "Creating View"
textView.append("\n" + model.output)
/*fun getLoginData(jsonObject : JsonObject?) {
if (jsonObject != null) {
val mApiService = ApiService<JsonObject>()
mApiService.get(
object : ApiResponse<JsonObject> {
override fun onResponse(mResponse : Response<JsonObject>) {
val loginResponse = if (mResponse.body() != null)
finish()
else {
output = mResponse.body().toString()
}
}
override fun onError(mKey : String) {
Toast.makeText(
applicationContext,
"Error",
Toast.LENGTH_LONG
).show()
}
}, ApiClient.getApiInterface().callLogin(jsonObject)
)
}
}*/
}
}
LOGINACTIVITY
package com.example.kotlinloginapi.ui.login
lateinit var cCode: String
lateinit var phone: String
class LoginActivity : AppCompatActivity() {
lateinit var loginBinding : ActivityLoginBinding
lateinit var eTcCode: EditText
lateinit var eTphone: EditText
private lateinit var loginViewModel: LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loginBinding = DataBindingUtil.setContentView(this, R.layout.activity_login)
eTcCode = findViewById<EditText>(R.id.etcCode)
eTphone = findViewById<EditText>(R.id.etPhone)
val login = findViewById<Button>(R.id.login)
val loading = findViewById<ProgressBar>(R.id.loading)
var model = Model("Binding Model", "91", "8884340404")
loginBinding.model = model
loginBinding.lifecycleOwner
loginViewModel = ViewModelProviders.of(this, LoginViewModelFactory())
.get(LoginViewModel::class.java)
loginViewModel.loginFormState.observe(this#LoginActivity, Observer {
val loginState = it ?: return#Observer
// disable login button unless both username / password is valid
login.isEnabled = loginState.isDataValid
if (loginState.usernameError != null) {
eTcCode.error = getString(loginState.usernameError)
}
if (loginState.passwordError != null) {
eTphone.error = getString(loginState.passwordError)
}
})
loginViewModel.loginResult.observe(this#LoginActivity, Observer {
val loginResult = it ?: return#Observer
loading.visibility = View.VISIBLE
if (loginResult.error != null) {
showLoginFailed(loginResult.error)
}
if (loginResult.success != null) {
updateUiWithUser(loginResult.success)
}
setResult(Activity.RESULT_OK)
//Complete and destroy login activity once successful
finish()
})
eTcCode.afterTextChanged {
loginViewModel.loginDataChanged(
eTcCode.text.toString(),
eTphone.text.toString()
)
}
eTphone.apply {
afterTextChanged {
loginViewModel.loginDataChanged(
eTcCode.text.toString(),
eTphone.text.toString()
)
}
setOnEditorActionListener { _, actionId, _ ->
when (actionId) {
EditorInfo.IME_ACTION_DONE ->
loginViewModel.login(
eTcCode.text.toString(),
eTphone.text.toString()
)
}
false
}
login.setOnClickListener {
loading.visibility = View.VISIBLE
loginViewModel.login(eTcCode.text.toString(), eTphone.text.toString())
}
}
}
private fun updateUiWithUser(model: LoggedInUserView) {
val welcome = getString(R.string.welcome)
val displayName = model.displayName
// TODO : initiate successful logged in experience
cCode = eTcCode.text.toString()
phone = eTphone.text.toString()
var intent = Intent(this, MainActivity::class.java)
startActivity(intent)
Toast.makeText(
applicationContext,
"$welcome $displayName",
Toast.LENGTH_LONG
).show()
}
private fun showLoginFailed(#StringRes errorString: Int) {
Toast.makeText(applicationContext, errorString, Toast.LENGTH_SHORT).show()
}
}
/**
* Extension function to simplify setting an afterTextChanged action to EditText components.
*/
fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
this.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable?) {
afterTextChanged.invoke(editable.toString())
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
})
}
...
Ideally, I want to use binding observers but I am unable to pass the output of retrofit to MainActivity even without that.
I moved the enqueue call to main activity which solved the problem. Now I can call text views. Will try to achieve this without moving and live data in future.
I am trying to show json data using retrofit library in kotlin
This is my Json:
[
{
"login": "mojombo",
"id": 1,
},
{
"login": "defunkt",
"id": 2,
}
]
My Main activity
call.enqueue(object : Callback<UserResponse> {
override fun onResponse(call: Call<UserResponse>, response: Response<UserResponse>) {
Log.e("list","list")
val countrylist = response.body()
for (size in response.body()) {
System.out.println(size.toString())
}
// var listOfMovies: List<UserResponse> = response.body()?.results!!
// myCustomAdapter = UserListAdapter(applicationContext, listOfMovies)
// recyclerView.setAdapter(myCustomAdapter)
progressBar.visibility = View.GONE
}
override fun onFailure(call: Call<UserResponse>?, t: Throwable?) {
progressBar.visibility = View.GONE
Log.e("list", t.toString())
}
})
This an app that I build in kotlin using retrofit and rxjava in the best way possible using a test API.
Model
data class Post( val userID:Int, val title:String, val body: String)
Retrofit Package
IMyApi interface
interface IMyApi {
#get:GET("posts")
val posts: Observable<List<Post>>
}
RetrofitClient Object class
object RetrofitClient {
val instance: Retrofit by lazy {
Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
Adapter Package
PostAdapter class
class PostAdapter(private val context: Context, private val postList: List<Post>)
:RecyclerView.Adapter<PostViewHolder>()
{
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
PostViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.post_item, parent, false)
return PostViewHolder(itemView)
}
override fun getItemCount(): Int {
return postList.size
}
override fun onBindViewHolder(holder: PostViewHolder, position: Int)
{
holder.userId.text = postList[position].userID.toString()
holder.title.text = postList[position].title
holder.body.text = StringBuilder(postList[position].body.substring(0,20))
.append("...").toString()
}
}
PostViewHolder class
class PostViewHolder(itemView: View):RecyclerView.ViewHolder(itemView) {
var userId = itemView.txtID
var title = itemView.txtTitle
var body = itemView.txtBody
}
MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var jsonApi: IMyApi
private var compositeDisposable: CompositeDisposable = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Init api
val retrofit = RetrofitClient.instance
jsonApi = retrofit.create(IMyApi::class.java)
// View
recycler_posts.layoutManager = LinearLayoutManager(this)
recycler_posts.setHasFixedSize(true)
fetchData()
}
private fun fetchData() {
compositeDisposable.add(jsonApi.posts
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { posts->displayData(posts)})
}
private fun displayData(posts: List<Post>?) {
val adapter = PostAdapter(this, posts!!)
recycler_posts.adapter = adapter
}
}
Using this as displayed above should help you solve your issue hopefully. Also when in the code you come across "recycler_posts". This is a id to the recycler added in activity_main. If you need me to include that let me know
That's what we have on our app
object GetFAQsAPI {
private val LOG_TAG = GetFAQsAPI.javaClass.simpleName
interface ThisCallback {
fun onSuccess(getFAQs: GetFAQs)
fun onFailure(failureMessage: String)
fun onError(errorMessage: String)
}
/* POST */
fun postData(jo: JsonObject, callback: GetFAQsAPI.ThisCallback) {
val call = Service.getService().get_faqs(jo)
call.enqueue(object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
//Log.e(LOG_TAG, response.body().toString());
try {
if (response.body()?.get("success")!!.asBoolean) {
val gson = GsonBuilder().setPrettyPrinting().create()
val getFAQs = gson.fromJson(response.body(), GetFAQs::class.java)
callback.onSuccess(getFAQs)
} else {
Log.e(LOG_TAG, "else")
val error = response.body()!!.get("err").asString
callback.onError(error)
}
} catch (e: Exception) {
Log.e(LOG_TAG, "exception" + e.localizedMessage)
callback.onFailure(e.message!!)
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
Log.e(LOG_TAG, "onFailure: " + t.message)
callback.onFailure(t.message!!)
}
})
}
}
That's how we call it from our fragment - getFAQs is the object parsed.
private fun getFAQsAPI() {
showLoading(true)
val jo = JsonObject().apply {
addProperty("faq_category", "admin")
}
GetFAQsAPI.postData(jo, object : GetFAQsAPI.ThisCallback {
override fun onSuccess(getFAQs: GetFAQs) {
Log.i(LOG_TAG, "onSuccess")
showLoading(false)
updateUI(getFAQs)
}
override fun onFailure(failureMessage: String) {
Log.e(LOG_TAG, failureMessage)
}
override fun onError(errorMessage: String) {
Log.e(LOG_TAG, errorMessage)
}
})
}
Hope that helps.
I want to show ProgressDialog while fetching data from ViewModel and it works fine when I fetch data for the first time, but when I want to refresh the data from API the ProgressDialog starts and does not stops
I create MutableLiveData<Boolean>() and try to manage the visibility but it's not working
This is how i refresh my data from my Activity
private fun loadNorthTram() {
val model =
ViewModelProviders.of(this#MainActivity).get(MyViewModelNorth::class.java)
model.isNorthUpdating.observe(
this#MainActivity,
Observer { b ->
if (b!!)
AppUtil.showProgressSpinner(this#MainActivity)
else
AppUtil.dismissProgressDialog()
})
model.getNorthTrams().observe(this#MainActivity, Observer
{
if (it != null) {
setData(it)
}
})
}
Below is my ViewModel class
class MyViewModelNorth : ViewModel() {
private lateinit var mtoken: String
private val apiService: ApiInterface = ApiClient.client.create(ApiInterface::class.java)
private lateinit var trams: MutableLiveData<TramModel>
val isNorthUpdating = MutableLiveData<Boolean>().default(false)
fun getNorthTrams(): MutableLiveData<TramModel> {
isNorthUpdating.value = true
if (!::trams.isInitialized) {
trams = MutableLiveData()
callTokenAPI()
}
return trams
}
private fun callTokenAPI() {
val tokenObservable: Observable<TokenModel> = apiService.fetchToken()
tokenObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { self ->
mtoken = self.responseObject[0].DeviceToken
callTramAPI()
}
.subscribe(getTokenObserver())
}
private fun callTramAPI() {
val apiService: ApiInterface = ApiClient.client.create(ApiInterface::class.java)
val observable: Observable<TramModel> = apiService.fetchTrams(AppUtil.NORTH_TRAMS, mtoken)
observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getTramObserver())
}
private fun getTokenObserver(): Observer<TokenModel> {
return object : Observer<TokenModel> {
override fun onComplete() {}
override fun onSubscribe(d: Disposable) {}
override fun onNext(tokenModel: TokenModel) {}
override fun onError(e: Throwable) {
if (e is HttpException) {
val errorBody = e.response().errorBody()
HttpErrorUtil(e.code()).handelError()
}
}
}
}
private fun getTramObserver(): Observer<TramModel> {
return object : Observer<TramModel> {
override fun onComplete() {
isNorthUpdating.value = false
}
override fun onSubscribe(d: Disposable) {}
override fun onNext(t: TramModel) {
if (!t.hasError && t.hasResponse)
trams.value = t
else if (t.errorMessage.isBlank())
applicationContext().showToast(t.errorMessage)
else
applicationContext().showToast(applicationContext().getString(R.string.something_wrong))
}
override fun onError(e: Throwable) {
if (e is HttpException) {
val errorBody = e.response().errorBody()
HttpErrorUtil(e.code()).handelError()
}
}
}
}
public fun getIsNothUpdating(): LiveData<Boolean> {
return isNorthUpdating
}
fun <T : Any?> MutableLiveData<T>.default(initialValue: T) = apply { setValue(initialValue) }
}
I have not tested your code but I think your problem is in function getNorthTrams() in viewmodel.
First time when you fetch data, trams is not initialized, your api call is happening and at only onCompleted, you are setting isNorthUpdating.value = false. This code works.
But when you refresh data, trams is already initialized. So, there is no case for isNorthUpdating.value = false, which is causing progress dialog to not dismiss.
So I think you should handle else case in your viewmodel.
fun getNorthTrams(): MutableLiveData<TramModel> {
isNorthUpdating.value = true
if (!::trams.isInitialized) {
trams = MutableLiveData()
callTokenAPI()
}else{
//do your thing for refresh
isNorthUpdating.value = false
}
return trams
}
Also, in api call, if error occur, you should set isNorthUpdating to false and show some error message. Otherwise, progress dialog will always be showing even if some error occur in api call.
I`m developing a gallery app in kotlin using pixabay api, implementing Android Architecture components and retrofit to interact with the server.
the app works fine but when i change the configuration the view model returns null! what is the problem ?
interface PhotoApi {
#GET(context.getString(R.string.api_key))
fun getPhotos(): Call<PhotoList>
}
Retrofit setup
object PhotoRetriever {
val BASE_URL = "https://pixabay.com/api/"
val service: PhotoApi
init {
val retrofit = Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()).build()
service = retrofit.create(PhotoApi::class.java)
}
}
class MainActivity : AppCompatActivity() {
//var photoList: List<Photo>? = null
lateinit var viewModel: MainActivityViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProviders.of(this#MainActivity).get(MainActivityViewModel::class.java)
viewModel.getList().observe(this, object :Observer<List<Photo>> {
override fun onChanged(t: List<Photo>?) {
textview.setText(t?.get(1)?.webFormatUrl)
}
})
the view model class
class MainActivityViewModel : ViewModel() {
private val repository: Repository
lateinit var photoList: LiveData<List<Photo>>
val itemsListObservable: MediatorLiveData<List<Photo>>
init {
repository = Repository()
itemsListObservable = MediatorLiveData()
}
fun getList(): LiveData<List<Photo>> {
photoList = repository.getNetworkData()
Log.i("RECEIVED","size is ${photoList.value?.size} from viewModel ")
return photoList
}
}
the Repository class
class Repository {
val photoApi: PhotoApi
var photoList: List<Photo>?
val retlist = MutableLiveData<List<Photo>>()
init {
photoApi = PhotoRetriever.service
photoList = ArrayList<Photo>()
}
fun getItemsListFromWeb() {
photoApi.getPhotos().enqueue(object : Callback<PhotoList> {
override fun onFailure(call: Call<PhotoList>?, t: Throwable?) {
// Log.i("RECEIVED","${t?.message} from repository ")
// retlist.value = null
}
override fun onResponse(call: Call<PhotoList>?, response: Response<PhotoList>?) {
if (response!!.isSuccessful) {
retlist.value = response.body()?.hits
} else {
retlist.value = null
}
Log.i("RECEIVED", "size is ${retlist.value?.size} from repository ")
}
})
}
fun getNetworkData(): LiveData<List<Photo>> {
getItemsListFromWeb()
return retlist
}
}
i have solved the problem, the problem was in retrofit onResponse callback method
i remove the else block and everything works fine
before
override fun onResponse(call: Call<PhotoList>?, response: Response<PhotoList>?) {
if (response!!.isSuccessful) {
retlist.value = response.body()?.hits
} else {
retlist.value = null
}
Log.i("RECEIVED", "size is ${retlist.value?.size} from repository ")
}
after
override fun onResponse(call: Call<PhotoList>?, response: Response<PhotoList>?) {
if (response!!.isSuccessful) {
retlist.value = response.body()?.hits
}
Log.i("RECEIVED", "size is ${retlist.value?.size} from repository ")
}