My Firebase callable function needs to notify the client when invalid values are passed. According to documentation this should be done using functions.https.HttpsError-
if (!condition) {
throw new functions.https.HttpsError(
'invalid-argument',
'Cheating will incur ban'
);
}
Adding the client side code to call the function as given in the docs causes the app to crash.
fun addPlayTime(playTime: Int): Task<String> {
val data = hashMapOf(
"playTime" to playTime
)
return functions
.getHttpsCallable("addPlayTime")
.call(data)
.continueWith { task ->
val result = task.result?.data as String
result
}
}
viewModel.addPlayTime(10000)
.addOnCompleteListener { task ->
Timber.d("API response: ${task.result}")
if (!task.isSuccessful) {
val e = task.exception
if (e is FirebaseFunctionsException) {
val code = e.code
val details = e.details
Timber.d("API call failed: $details")
}
}
}
I'm able to see the error in logcat. How do I handle this exception without my app crashing? Wrapping the above code in try-catch didn't help.
2020-07-31 19:04:28.722 17502-17502/com.teamvanar.gcharge E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.teamvanar.gcharge, PID: 17502
com.google.android.gms.tasks.RuntimeExecutionException: com.google.firebase.functions.FirebaseFunctionsException: Cheating will incur ban
at com.google.android.gms.tasks.zzu.getResult(Unknown Source:15)
at com.teamvanar.gcharge.MainActivity$onCreate$2.onComplete(MainActivity.kt:67)
at com.google.android.gms.tasks.zzj.run(Unknown Source:4)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6718)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:491)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: com.google.firebase.functions.FirebaseFunctionsException: Cheating will incur ban
In my experience, Firebase Http Callable has some type-transform (reflection?) issues when it uses CompleteListener in Kotlin, and those FATALs cannot be caught by try-catch.
Replace it with SuccessListener and FailureListener solve that in my case:
// from this
someFirebaseFunction.addOnCompleteListener { task ->
if (task.isSuccessful) {
task.result?.let { /* do something */ }
} else {
println("Error: ${task.exception}")
}
}
// to this
someFirebaseFunction.addOnSuccessListener { result ->
/* do something */
}.addOnFailureListener { exception ->
println("Error: $exception")
}
}
In your case, this might work:
viewModel.addPlayTime(10000).addOnSuccessListener { result ->
Timber.d("API response: $result")
}.addOnFailureListener { e ->
if (e is FirebaseFunctionsException) {
val code = e.code
val details = e.details
Timber.d("API call failed: $details")
}
}
Related
So, my roommate and I are trying to develop an app to help students living on campus at our school keep track of their laundry. However, we are having trouble creating new laundry loads.
Our addLoad function is supposed to add a LaundryHolder object to Firebase (containing the machine number, whether it is a washer or dryer, who owns the load, and how many seconds are left for the load), whereas the LaundryLoad object contains a LaundryHolder, observer function (notifyDataSetChanged() for the LaundryLoadFragment), and timer (with time form LaundryHolder).
In Firebase, each clothingItem has a load ID with which to identify which load it is in on the user side. For our implementation to work, we need to fetch the ID which Firebase gives our LaundryHolder, which is why we are adding an onSuccessListener to a temporary query. The issue arises, however, when the query doesn't succeed or fail, and we can't figure out what is going on here.
This is the error we get:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: edu.rosehulman.roselaundrytracker, PID: 11847
kotlin.UninitializedPropertyAccessException: lateinit property load has not been initialized
at edu.rosehulman.roselaundrytracker.model.LaundryLoadViewModel.addLoad(LaundryLoadViewModel.kt:42)
at edu.rosehulman.roselaundrytracker.adapter.AddLoadAdapter.addLoad(AddLoadAdapter.kt:67)
at edu.rosehulman.roselaundrytracker.fragment.AddLoadFragment.onCreateView$lambda-1(AddLoadFragment.kt:32)
at edu.rosehulman.roselaundrytracker.fragment.AddLoadFragment.$r8$lambda$lIyFvxsLH_bCt-kHzadMjy2Ls_Y(Unknown Source:0)
at edu.rosehulman.roselaundrytracker.fragment.AddLoadFragment$$ExternalSyntheticLambda0.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:7455)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
at android.view.View.performClickInternal(View.java:7432)
at android.view.View.access$3700(View.java:835)
at android.view.View$PerformClick.run(View.java:28810)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7842)
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:1003)
Does anyone have any idea?
class LaundryLoadViewModel: ViewModel() {
private var loads = ArrayList<LaundryLoad>()
private var curPos = 0
lateinit var ref: CollectionReference
lateinit var uid: String
private var onlyOwned = true
private val subscriptions = HashMap<String, ListenerRegistration>()
fun getPreference() = onlyOwned
fun addLoad(machineNumber: Int, machineType: String, contents: ArrayList<ClothingItem>, time: Long, observer: () -> Unit){
val holder = LaundryHolder(machineNumber, machineType.lowercase(Locale.getDefault()) == "dryer", time * LaundryLoadFragment.SEC_TO_MIN, uid)
// val load = LaundryLoad(holder, observer)
// loads.add(load)
ref.add(holder)
lateinit var load: LaundryLoad
val query = ref
.whereEqualTo("machineNumber",machineNumber)
.whereEqualTo("owner",uid)
query.get().addOnSuccessListener { snapshot ->
snapshot.documents.forEach {
Log.d(Constants.TAG,"Retrieving load from Firebase")
load = LaundryLoad.from(it, observer)
}
}
query.get().addOnFailureListener {
Log.d(Constants.TAG,"Retrieval failed due to $it")
}
// val query = ref.whereEqualTo("machineNumber",machineNumber).whereEqualTo("dryer",machineType.lowercase(Locale.getDefault())=="dryer")
load.addMany(contents)
loads.add(load)
}
fun addListener(fragmentName: String, observer: () -> Unit) {
lateinit var subscription: ListenerRegistration
loads.clear()
val auth = Firebase.auth
val user = auth.currentUser!!
val clothes = ArrayList<ClothingItem>()
uid = user.uid
ref = Firebase.firestore.collection(LaundryLoad.COLLECTION_PATH)
val ref2 = Firebase.firestore.collection(ClothingItem.COLLECTION_PATH)
val inLoadQuery = ref2.whereNotEqualTo("load","")
inLoadQuery.addSnapshotListener { snapshot: QuerySnapshot?, error: FirebaseFirestoreException? ->
error?.let {
Log.d(Constants.TAG, "Error: $it")
return#addSnapshotListener
}
snapshot?.documents?.forEach {
clothes.add(ClothingItem.from(it))
}
}
if(onlyOwned) {
val query = ref.whereEqualTo("owner",uid)
subscription = query
.addSnapshotListener { snapshot: QuerySnapshot?, error: FirebaseFirestoreException? ->
error?.let {
Log.d(Constants.TAG, "Error: $it")
return#addSnapshotListener
}
retrieveLoads(snapshot, clothes, observer)
}
} else {
subscription = ref
.addSnapshotListener { snapshot: QuerySnapshot?, error: FirebaseFirestoreException? ->
error?.let {
Log.d(Constants.TAG, "Error: $it")
return#addSnapshotListener
}
retrieveLoads(snapshot, clothes, observer)
}
}
subscriptions[fragmentName] = subscription
observer()
}
private fun retrieveLoads(snapshot: QuerySnapshot?, clothes: ArrayList<ClothingItem>, observer: () -> Unit) {
snapshot?.documents?.forEach {
loads.add(LaundryLoad.from(it, observer))
}
for (load in loads) {
for (item in clothes) {
if (item.load == load.getId()) {
load.addToLoad(item)
}
}
}
}
fun removeListener(fragmentName: String) {
for(load in loads) {
ref.document(load.getId()).set(load.laundryHolder)
}
subscriptions[fragmentName]?.remove()
subscriptions.remove(fragmentName)
}
fun togglePreference() {
onlyOwned = !onlyOwned
}
}
It looks like ref has not been initialized when you ref.add(holder) in addLoad. It's impossible for us to say why that is, as the code that calls addLoad seems to be missing, but the stack trace should point you pretty directly to where the problem is.
I have a retrofit request:
try {
val response = service.getItems()
when {
response.isSuccessful -> handleResponse()
else -> handleError(Throwable(
response.errorBody()?.charStream()?.readText()
))
}
} catch (e: Exception) {
handleError(e)
}
and sometimes I get this exception:
ItemsViewModel$fetchImages$1.invokeSuspend(Unknown Source:100)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(Unknown Source:33)
at kotlinx.coroutines.DispatchedTask.run(Unknown Source:129)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(Unknown Source:2)
at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(Unknown Source:2)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(Unknown Source:34)
It's not consistent and I don't have steps to reproduce. What can be the reason?
I am developing new app but when I run project in debug mode I am getting the following exception
java.lang.RuntimeException: Unable to start activity ComponentInfo{at.mksquad.swapit/at.mksquad.swapit.ui.screenflow.main.MainActivity}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2976)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3113)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:113)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:71)
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)
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: java.lang.NullPointerException
at at.mksquad.swapit.data.sources.auth.AuthSourceFirebase.getUidOfLoggedInUser(AuthSourceFirebase.kt:18)
at at.mksquad.swapit.features.auth.AuthManager.getUidOfLoggedInUser(AuthManager.kt:28)
at at.mksquad.swapit.data.repositories.UserRepository.getUid(UserRepository.kt:30)
at at.mksquad.swapit.utils.UserUtil.createNewUser(UserUtil.kt:17)
at at.mksquad.swapit.data.repositories.UserRepository.loadUser(UserRepository.kt:90)
at at.mksquad.swapit.ui.screenflow.main.SharedViewModel.<init>(SharedViewModel.kt:12)
at at.mksquad.swapit.ui.screenflow.main.SharedViewModelFactory.create(SharedViewModelFactory.kt:14)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:187)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:54)
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:41)
at at.mksquad.swapit.ui.screenflow.main.MainActivity.getSharedViewModel(Unknown Source:2)
at at.mksquad.swapit.ui.screenflow.main.MainActivity.prepareViews(MainActivity.kt:765)
at at.mksquad.swapit.ui.screenflow.main.MainActivity.onCreate(MainActivity.kt:194)
at android.app.Activity.performCreate(Activity.java:7224)
at android.app.Activity.performCreate(Activity.java:7213)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1272)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2956)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3113)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:113)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:71)
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)
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)
below following My MainActivity.kt https://gist.github.com/kyodgorbek/2a369fcbb2138cb9e25c0855514dc162 code
I want to know where exactly I am making mistake I have run debug mode but run step by step but it did not show anything but exception point out in following code in AuthSourceFirebase.kt
object AuthSourceFirebase {
fun isUserLoggedIn(): Boolean {
return FirebaseAuth.getInstance().currentUser != null
}
fun getUidOfLoggedInUser(): String {
return FirebaseAuth.getInstance().currentUser!!.uid // this line cause exception
}
fun loginUser(
activity: Activity,
mail: String,
password: String,
loginCallback: AuthManager.UserLoginCallback
) {
Timber.d("Starting process to login..")
FirebaseAuth.getInstance().signInWithEmailAndPassword(
mail,
password
).addOnCompleteListener(activity) {
if (it.isSuccessful) {
loginCallback.onCallback(null)
}
}
.addOnFailureListener { e ->
val error = e as FirebaseAuthException
Timber.d("Error logging in, code: %s", error.errorCode)
loginCallback.onCallback(
LibFirebaseErrors.getErrorFromErrorCode(
activity,
error.errorCode
)
)
}
}
fun registerUser(
activity: Activity,
mail: String,
password: String,
registrationCallback: AuthManager.UserRegistrationCallback
) {
Timber.d("Starting process to register..")
FirebaseAuth.getInstance().createUserWithEmailAndPassword(
mail,
password
).addOnCompleteListener(activity) {
if (it.isSuccessful) {
registrationCallback.onCallback(null)
}
}
.addOnFailureListener { e ->
val error = e as FirebaseAuthException
Timber.d("Error logging in, code: %s", error.errorCode)
registrationCallback.onCallback(
LibFirebaseErrors.getErrorFromErrorCode(activity, error.errorCode)
)
}
}
fun sendPasswordResetMail(
activity: Activity,
mail: String,
resetPwMailCallback: AuthManager.UserResetPwMailCallback
) {
Timber.d("Starting process to reset password..")
FirebaseAuth.getInstance().sendPasswordResetEmail(mail)
.addOnCompleteListener(activity) {
if (it.isSuccessful) {
resetPwMailCallback.onCallback(null)
}
}
.addOnFailureListener { e ->
val error = e as FirebaseAuthException
Timber.d("Error sending mail, code: %s", error.errorCode)
resetPwMailCallback.onCallback(
LibFirebaseErrors.getErrorFromErrorCode(
activity,
error.errorCode
)
)
}
}
fun logUserOut(context: Context){
UserRepository.deleteUserLocal(context)
FirebaseAuth.getInstance().signOut()
}
}
I have tried my in order to avoid nullpointer exception following way it did not work
fun getUidOfLoggedInUser(): String {
if(FirebaseAuth.getInstance().currentUser?.uid == null){
FirebaseAuth.getInstance().signOut()
}
return FirebaseAuth.getInstance().currentUser!!.uid
}
The error you are getting looks like a regular Null Pointer exception. According to the exception stack trace here:
Caused by: java.lang.NullPointerException
at at.mksquad.swapit.data.sources.auth.AuthSourceFirebase.**getUidOfLoggedInUser(AuthSourceFirebase.kt:18)**
at at.mksquad.swapit.features.auth.AuthManager.getUidOfLoggedInUser(AuthManager.kt:28)
at at.mksquad.swapit.data.repositories.UserRepository.getUid(UserRepository.kt:30)
and
fun getUidOfLoggedInUser(): String {
return FirebaseAuth.getInstance().currentUser!!.uid // this line cause exception
}
Make sure your currentUser is not null. Please check if Firebase authentication is processed and current user session is active.
Update your code like this:
**fun getUidOfLoggedInUser(): String {
if(FirebaseAuth.getInstance().currentUser == null || FirebaseAuth.getInstance().currentUser?.uid == null){
FirebaseAuth.getInstance().signOut() return
}
return FirebaseAuth.getInstance().currentUser!!.uid
}**
I'm trying to build a firebase login function, but the code crashes even though the exception thrown is caught.
When I output the exception as a log the code works, but when I pass it on as an exception it crashes.
I would like to pass the exception on to the MainActivity in order to process it correctly there, i.e. to have the exception.message output there as toast.
The login process works with the use of valid login data without problems only if firebase reports an exception it crashes.
internal var loginResult = MutableLiveData<LoginResult>()
inner class LoginResult {
var authResult: AuthResult? = null
var exception: Exception? = null
var isSuccessful = false
constructor(authResult: AuthResult?, isSuccessful: Boolean) {
this.authResult = authResult
this.isSuccessful = isSuccessful
}
constructor(authResult: AuthResult?, exception: Exception?, isSuccessful: Boolean) {
this.authResult = authResult
this.exception = exception
this.isSuccessful = isSuccessful
}
}
fun login(context: Context, email: String, password: String) {
FirebaseAuth.getInstance().signInWithEmailAndPassword(email, password)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
loginResult.value = LoginResult(task.result, task.isSuccessful)
startSession(context, LoginType.CREDENTIAL)
saveCredentialsEncrypted(context, email, password)
} else {
try {
loginResult.value = LoginResult(task.result, task.exception, task.isSuccessful)
} catch (e: FirebaseAuthInvalidCredentialsException) {
loginResult.value = LoginResult(task.result, e, task.isSuccessful)
} catch (e: Exception) {
loginResult.value = LoginResult(task.result, e, task.isSuccessful)
}
}
}
}
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.simplesaakotlin, PID: 23984
com.google.android.gms.tasks.RuntimeExecutionException: com.google.firebase.auth.FirebaseAuthInvalidCredentialsException: The password is invalid or the user does not have a password.
at com.google.android.gms.tasks.zzu.getResult(com.google.android.gms:play-services-tasks##17.1.0:15)
at com.example.simplesaakotlin.auth.AuthManager$login$1.onComplete(AuthManager.kt:84)
at com.google.android.gms.tasks.zzj.run(com.google.android.gms:play-services-tasks##17.1.0:4)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at com.google.android.gms.internal.tasks.zzb.dispatchMessage(com.google.android.gms:play-services-tasks##17.1.0:6)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7660)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: com.google.firebase.auth.FirebaseAuthInvalidCredentialsException: The password is invalid or the user does not have a password.
at com.google.android.gms.internal.firebase-auth-api.zztt.zza(com.google.firebase:firebase-auth##20.0.2:28)
at com.google.android.gms.internal.firebase-auth-api.zzvb.zza(com.google.firebase:firebase-auth##20.0.2:9)
at com.google.android.gms.internal.firebase-auth-api.zzvc.zzk(com.google.firebase:firebase-auth##20.0.2:1)
at com.google.android.gms.internal.firebase-auth-api.zzuz.zzh(com.google.firebase:firebase-auth##20.0.2:25)
at com.google.android.gms.internal.firebase-auth-api.zztq.zzk(com.google.firebase:firebase-auth##20.0.2:1)
at com.google.android.gms.internal.firebase-auth-api.zzoi.zza(com.google.firebase:firebase-auth##20.0.2:2)
at com.google.android.gms.internal.firebase-auth-api.zzvg.zza(com.google.firebase:firebase-auth##20.0.2:25)
at com.google.android.gms.internal.firebase-auth-api.zzuq.zzf(com.google.firebase:firebase-auth##20.0.2:4)
at com.google.android.gms.internal.firebase-auth-api.zzpy.zzj(com.google.firebase:firebase-auth##20.0.2:5)
at com.google.android.gms.internal.firebase-auth-api.zztu.zzi(com.google.firebase:firebase-auth##20.0.2:8)
at com.google.android.gms.internal.firebase-auth-api.zzsk.zzd(Unknown Source:15)
at com.google.android.gms.internal.firebase-auth-api.zzsj.accept(Unknown Source:6)
at com.google.android.gms.common.api.internal.zach.doExecute(com.google.android.gms:play-services-base##17.3.0:2)
at com.google.android.gms.common.api.internal.zah.zaa(com.google.android.gms:play-services-base##17.3.0:9)
at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.zac(com.google.android.gms:play-services-base##17.3.0:192)
at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.zab(com.google.android.gms:play-services-base##17.3.0:157)
at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.zaa(com.google.android.gms:play-services-base##17.3.0:125)
at com.google.android.gms.common.api.internal.GoogleApiManager.handleMessage(com.google.android.gms:play-services-base##17.3.0:144)
at android.os.Handler.dispatchMessage(Handler.java:102)
at com.google.android.gms.internal.base.zap.dispatchMessage(com.google.android.gms:play-services-base##17.3.0:8)
at android.os.Looper.loop(Looper.java:223)
at android.os.HandlerThread.run(HandlerThread.java:67)
UPDATE:
the error was thrown because i tried to access the task.result when there was no result, see the try catch block.
To fix this error i passed null as authResult
fun login(context: Context, email: String, password: String) {
FirebaseAuth.getInstance().signInWithEmailAndPassword(email, password)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
loginResult.value = LoginResult(task.result, task.isSuccessful)
startSession(context, LoginType.CREDENTIAL)
saveCredentialsEncrypted(context, email, password)
} else {
loginResult.value = LoginResult(null, task.exception, task.isSuccessful)
}
}
}
I am new to Kotlin MVVM, I have use concept of Executor to create a thread-pool for fetching the resources for an Endpoint but I am continuously getting an exception which eventually lead to crash the application.
Crash Log:
2020-11-12 11:43:39.768 3018-3043/com.ankittlabs.rasodarecipe E/TAG: run:
java.io.InterruptedIOException: interrupted
at okio.Timeout.throwIfReached(Timeout.java:146)
at okio.Okio$1.write(Okio.java:76)
at okio.AsyncTimeout$1.write(AsyncTimeout.java:180)
at okio.RealBufferedSink.flush(RealBufferedSink.java:224)
at okhttp3.internal.http1.Http1ExchangeCodec.finishRequest(Http1ExchangeCodec.java:190)
at okhttp3.internal.connection.Exchange.finishRequest(Exchange.java:101)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:86)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:43)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:94)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:88)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:229)
at okhttp3.RealCall.execute(RealCall.java:81)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:204)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall.execute(DefaultCallAdapterFactory.java:108)
at com.ankittlabs.rasodarecipe.request.RecipeApiClient$RetrieveRecipeRunnable.run(RecipeApiClient.kt:37)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:458)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
2020-11-12 11:43:39.769 3018-3018/com.ankittlabs.rasodarecipe D/AndroidRuntime: Shutting down VM
--------- beginning of crash
2020-11-12 11:43:39.770 3018-3018/com.ankittlabs.rasodarecipe E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.ankittlabs.rasodarecipe, PID: 3018
java.lang.NullPointerException: recipe must not be null
at com.ankittlabs.rasodarecipe.RecipeActivity$subscribeObserver$1.onChanged(RecipeListActivity.kt:48)
at com.ankittlabs.rasodarecipe.RecipeActivity$subscribeObserver$1.onChanged(RecipeListActivity.kt:26)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:131)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:149)
at androidx.lifecycle.LiveData.setValue(LiveData.java:307)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at androidx.lifecycle.LiveData$1.run(LiveData.java:91)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
2020-11-12 11:43:39.777 3018-3018/com.ankittlabs.rasodarecipe I/Process: Sending signal. PID: 3018 SIG: 9
The code for fetching the API is based on Retrofit
private class RetrieveRecipeRunnable(private val query: String, private val pageNumber: Int) : Runnable {
var cancelRequest = false
override fun run() {
try {
val response = getRecipes(query, pageNumber)?.execute()
if (cancelRequest) {
return
}
if (response?.code() == 200) {
val list: MutableList<Recipe> = ArrayList((response?.body() as RecipeSearchResponse).recipes)
if (pageNumber == 1) {
mRecipes?.postValue(list)
} else {
val currentRecipe: MutableList<Recipe> = mRecipes?.value as MutableList<Recipe>
currentRecipe.addAll(list)
mRecipes?.postValue(currentRecipe)
}
} else {
val error: String = response?.errorBody().toString()
Log.i("TAG", "run: $error")
mRecipes?.postValue(null)
}
} catch (ex: IOException) {
Log.e("TAG", "run: ", ex)
mRecipes?.postValue(null)
}
}
And I am trying observe recipe which is null and causing due to first error of Exception
recipeListViewModel?.getRecipes()?.observe(this, { recipe ->
mAdapter?.setRecipes(recipe)
})
It is due to timeout. You need to expand the time for API's request and response.
Problem is mRecipes?.postValue(null) for non-null reciepes. Just use mRecipes?.postValue(emptyList()) instead of mRecipes?.postValue(null)
And it should be:
private class RetrieveRecipeRunnable(private val query: String, private val pageNumber: Int) : Runnable {
var cancelRequest = false
override fun run() {
try {
val response = getRecipes(query, pageNumber)?.execute()
if (cancelRequest) {
return
}
if (response?.code() == 200) {
val list: MutableList<Recipe> = ArrayList((response?.body() as RecipeSearchResponse).recipes)
if (pageNumber == 1) {
mRecipes?.postValue(list)
} else {
val currentRecipe: MutableList<Recipe> = mRecipes?.value as MutableList<Recipe>
currentRecipe.addAll(list)
mRecipes?.postValue(currentRecipe)
}
} else {
val error: String = response?.errorBody().toString()
Log.i("TAG", "run: $error")
mRecipes?.postValue(emptyList()) //this line
}
} catch (ex: IOException) {
Log.e("TAG", "run: ", ex)
mRecipes?.postValue(emptyList())//this line
}
}