Android Fragment - RuntimeException: Parcelable encountered IOException writing serializable object - android

I have this interface:
interface MyListener : Serializable {
fun onClick()
}
In my Fragment I have a lazy instance of it which I attach it to the fragment's arguments. Something like:
class MyFragment: Fragment() {
private val listener: MyListener by lazy {
object : MyListener {
override fun onClick() {
// Do something
}
}
}
fun testFun1() {
arguments = bundleOf(
"listener" to listener
)
}
fun onAcceptClick() {
val listener = arguments?.getSerializable("listener")
}
}
Everything works fine, until the moment when I press home button, or recents button and the app throughs this exception:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.test.me, PID: 8501
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = com.test.me.MyFragment$listener$2$1)
at android.os.Parcel.writeSerializable(Parcel.java:1833)
at android.os.Parcel.writeValue(Parcel.java:1780)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584)
at android.os.Bundle.writeToParcel(Bundle.java:1253)
at android.os.Parcel.writeBundle(Parcel.java:997)
at androidx.fragment.app.FragmentState.writeToParcel(FragmentState.java:159)
at android.os.Parcel.writeTypedObject(Parcel.java:1634)
at android.os.Parcel.writeTypedList(Parcel.java:1513)
at android.os.Parcel.writeTypedList(Parcel.java:1470)
at androidx.fragment.app.FragmentManagerState.writeToParcel(FragmentManagerState.java:64)
at android.os.Parcel.writeParcelable(Parcel.java:1801)
at android.os.Parcel.writeValue(Parcel.java:1707)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584)
at android.os.Bundle.writeToParcel(Bundle.java:1253)
at android.os.Parcel.writeBundle(Parcel.java:997)
at androidx.fragment.app.FragmentState.writeToParcel(FragmentState.java:161)
at android.os.Parcel.writeTypedObject(Parcel.java:1634)
at android.os.Parcel.writeTypedList(Parcel.java:1513)
at android.os.Parcel.writeTypedList(Parcel.java:1470)
at androidx.fragment.app.FragmentManagerState.writeToParcel(FragmentManagerState.java:64)
at android.os.Parcel.writeParcelable(Parcel.java:1801)
at android.os.Parcel.writeValue(Parcel.java:1707)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584)
at android.os.Bundle.writeToParcel(Bundle.java:1253)
at android.os.Parcel.writeBundle(Parcel.java:997)
at android.os.Parcel.writeValue(Parcel.java:1698)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584)
at android.os.Bundle.writeToParcel(Bundle.java:1253)
at android.os.Parcel.writeBundle(Parcel.java:997)
at android.os.Parcel.writeValue(Parcel.java:1698)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584)
at android.os.Bundle.writeToParcel(Bundle.java:1253)
at android.app.IActivityTaskManager$Stub$Proxy.activityStopped(IActivityTaskManager.java:4505)
at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:145)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Caused by: java.io.NotSerializableException: com.test.me.MyFragment
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1240)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)
at android.os.Parcel.writeSerializable(Parcel.java:1828)
E/AndroidRuntime: ... 45 more
I know that I can solve the problem by using Parcelable instead of Serializable but I would like to understand why this is happening. Also, if I want to use Parcelable in combination with #Parcelize annotation then I will have to convert MyListener from an interface to a class.
So, what would be the solution or workaround here while keep using an interface?

Related

How to request API using Retrofit in a Foreground Service in Android 12(Api 31)?

My app has a specific requirement of uploading files as multipart using retrofit and LiveData in the background(App Navigation works while uploading data to the server.). The app requires chained network requests on the bases of the response it gets. I've used the Foreground service for network requests. this procedure works well in Android 9,10,11.
UploadFragment.kt
class UploadFragment : BaseFragment() {
....
private fun uploadFilesToServer() {
val intent = Intent(requireActivity(), MyFileUploadingService::class.java)
ContextCompat.startForegroundService(requireActivity(), intent)
}
}
FileUploadService.kt
class FileUploadService : LifecycleService() {
....
private fun registerObservers() {
viewModel?.uploadLiveData!!.observe(this) { response ->
val dataResponse: DataUploadResponse = response.responseBody!!.data!!
if (dataResponse.error == null) {
....
// Repeat
} else {
stopSelf()
}
}
}
}
I'm getting an error while observing response using LiveData.
val dataResponse: DataUploadResponse = response.responseBody!!.data!!
Error Log
2022-08-24 13:08:59.792 26140-26140/com.myapp.demo E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.myapp.demo, PID: 26140
java.lang.NullPointerException
at com.myapp.demo.service.FileUploadService$registerObservers$1.onChanged(FileUploadService.kt:242)
at com.myapp.demo.service.FileUploadService$registerObservers$1.onChanged(FileUploadService.kt:54)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:133)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:151)
at androidx.lifecycle.LiveData.setValue(LiveData.java:309)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at androidx.lifecycle.LiveData$1.run(LiveData.java:93)
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:7870)
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)

Android Studio kotlin app "keep stopping" error

package com.example.absolutelydumb
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.InputType
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*
//sets var to some random number and asks for a guess, giving feedback on proximity to answer
class MainActivity : AppCompatActivity() {
private val startButton: Button = findViewById(R.id.startButton)
private val submitButton: Button = findViewById(R.id.submit)
private val instructionsText: TextView = findViewById(R.id.instructionView)
private val response: EditText = findViewById(R.id.answerInput)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val startText: String = getString(R.string.startText)
startButton.text = startText
//TODO("Add autofillHints")
startButton.setOnClickListener {
numberGuess()
}
//TODO("Set text back to start after guessing is done")
}
fun numberGuess() {
response.inputType = InputType.TYPE_CLASS_NUMBER
val clickText: String = getString(R.string.clickText)
startButton.text = clickText
//TODO("Add autofillHints")
instructionsText.text = getString(R.string.instructions)
val randomNumber: Int = rand(0,10)
submitButton.setOnClickListener {
val responseText = Integer.parseInt(answerInput.text.toString())
if(responseText == randomNumber) {
instructionsText.text = getString(R.string.correctGuess)
} else {
instructionsText.text = getString(R.string.badGuess)
}
}
}
fun rand(start: Int, end: Int): Int {
//require(start <= end) { "Illegal Argument" }
return (start..end).random()
}
}
Above is the code that I've been writing. This is my first project, so I don't know what went wrong. I fixed a variable type problem in the numberGuess function but when I finished fixing it the app wouldn't open and the virtual device stated a "app keeps stopping" error.
Here is the Run log that lists off errors. The build log didn't have any errors.
07/09 19:58:05: Launching 'app' on phone1.
Install successfully finished in 1 s 266 ms.
$ adb shell am start -n "com.example.absolutelydumb/com.example.absolutelydumb.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Connected to process 5935 on device 'phone1 [emulator-5554]'.
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.absolutelydumb, PID: 5935
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.absolutelydumb/com.example.absolutelydumb.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2843)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
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)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
at android.content.ContextWrapper.getApplicationInfo(ContextWrapper.java:159)
at android.view.ContextThemeWrapper.getTheme(ContextThemeWrapper.java:157)
at android.content.Context.obtainStyledAttributes(Context.java:675)
at androidx.appcompat.app.AppCompatDelegateImpl.createSubDecor(AppCompatDelegateImpl.java:692)
at androidx.appcompat.app.AppCompatDelegateImpl.ensureSubDecor(AppCompatDelegateImpl.java:659)
at androidx.appcompat.app.AppCompatDelegateImpl.findViewById(AppCompatDelegateImpl.java:479)
at androidx.appcompat.app.AppCompatActivity.findViewById(AppCompatActivity.java:214)
at com.example.absolutelydumb.MainActivity.<init>(MainActivity.kt:13)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:69)
at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:45)
at android.app.Instrumentation.newActivity(Instrumentation.java:1215)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2831)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048) 
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808) 
at android.os.Handler.dispatchMessage(Handler.java:106) 
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) 
You cannot call findViewById before onCreate() or setContentView() is called. Since you are using findViewById at the declaration site of your four properties, it is being called at the time your Activity class is being instantiated, which is before onCreate is called.
There are two ways to handle this.
Use lateinit var and set the properties right after your call to setContentView():
private lateinit var startButton: Button
//...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button = findViewById(R.id.startButton)
//...
Use by lazy so it is only called when the property is first accessed, which will be after setContentView() is called:
private val startButton: Button by lazy { findViewById(R.id.startButton) }
Or you can use view binding, which gets rid of most of this boilerplate and is less error-prone. By error-prone, I mean you can call findViewById and accidentally search for a view that isn't present in the current layout and it will crash at runtime. View binding makes this impossible.

Trying to extend LifecycleService to my JobIntentService so I can observe LivaData, but can only have one class appear in the supertype list

SO I am trying to observe some LiveData in my repository, so I can work with this data and update it's items according to my operations. But I can't figure out how to access the data. When I try to observe it, the ~Observeris asking for a LifecycleOwner. Reading about similar problems I've seen a suggestion to extendLifecycleService, but when I do I get the error sayingOnly one class may appear in a supertype list`.
How do I go around this?
This is my service:
class DetectJobIntentService : JobIntentService() {
private val TAG = "DetectJobIntentServi22"
fun enqueueWork(context: Context, work: Intent) {
enqueueWork(context, DetectJobIntentService::class.java, 12, work)
}
override fun onHandleWork(intent: Intent) {
Log.d(TAG, "onHandleWork")
val options = FirebaseVisionFaceDetectorOptions.Builder()
.setClassificationMode(FirebaseVisionFaceDetectorOptions.ACCURATE)
.setClassificationMode(FirebaseVisionFaceDetectorOptions.ALL_CLASSIFICATIONS)
.setMinFaceSize(0.15f)
.build()
val detector = FirebaseVision.getInstance()
.getVisionFaceDetector(options)
val repo = PhotoRepository(application)
val allPhotos = repo.getAllPhotos()
allPhotos.observe(SomeLifeCycleOwner, Observer {
for (file in it) {
val image = FirebaseVisionImage.fromFilePath(application, Uri.parse(file.uri))
detector.detectInImage(image).addOnSuccessListener { list ->
if (list.isNotEmpty()) {
file.hasFaces = 1
repo.update(file)
} else {
file.hasFaces = 2
repo.update(file)
}
}
}
})
}
}
EDIT: Following Sina's suggestion which sounds right, I've tried implementing a query that fetches the data into a non-LiveData object my app crashes.
This is the quesry I've added in my Dow:
#Query("SELECT * FROM photos_table")
fun getAllPhotosStatic(): MutableList<Photo>
If I run at at this point it's all good.
Then in my repository I've added this:
val allPhotosStatic = photoDao.getAllPhotosStatic()
And as soon as I've done that and try to run the app it crashes, and I get this stack:
2019-11-13 23:49:37.079 20720-20720/tech.levanter.anyvision E/AndroidRuntime: FATAL EXCEPTION: main
Process: tech.levanter.anyvision, PID: 20720
java.lang.RuntimeException: Unable to start activity ComponentInfo{tech.levanter.anyvision/tech.levanter.anyvision.MainActivity}: java.lang.RuntimeException: Cannot create an instance of class tech.levanter.anyvision.viewModels.AllPhotosViewModel
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2991)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3126)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1846)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:201)
at android.app.ActivityThread.main(ActivityThread.java:6882)
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:873)
Caused by: java.lang.RuntimeException: Cannot create an instance of class tech.levanter.anyvision.viewModels.AllPhotosViewModel
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:238)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:164)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:130)
at tech.levanter.anyvision.MainActivity.onCreate(MainActivity.kt:66)
at android.app.Activity.performCreate(Activity.java:7232)
at android.app.Activity.performCreate(Activity.java:7221)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1272)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2971)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3126) 
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1846) 
at android.os.Handler.dispatchMessage(Handler.java:106) 
at android.os.Looper.loop(Looper.java:201) 
at android.app.ActivityThread.main(ActivityThread.java:6882) 
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:873) 
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:230)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:164) 
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:130) 
at tech.levanter.anyvision.MainActivity.onCreate(MainActivity.kt:66) 
at android.app.Activity.performCreate(Activity.java:7232) 
at android.app.Activity.performCreate(Activity.java:7221) 
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1272) 
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2971) 
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3126) 
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1846) 
at android.os.Handler.dispatchMessage(Handler.java:106) 
at android.os.Looper.loop(Looper.java:201) 
at android.app.ActivityThread.main(ActivityThread.java:6882) 
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:873) 
Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
at androidx.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:267)
at androidx.room.RoomDatabase.query(RoomDatabase.java:323)
at androidx.room.util.DBUtil.query(DBUtil.java:83)
at tech.levanter.anyvision.room.PhotoDao_Impl.getAllPhotosStatic(PhotoDao_Impl.java:154)
at tech.levanter.anyvision.room.PhotoRepository.<init>(PhotoRepository.kt:37)
at tech.levanter.anyvision.viewModels.AllPhotosViewModel.<init>(AllPhotosViewModel.kt:12)
at java.lang.reflect.Constructor.newInstance0(Native Method) 
at java.lang.reflect.Constructor.newInstance(Constructor.java:343) 
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:230) 
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:164) 
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:130) 
at tech.levanter.anyvision.MainActivity.onCreate(MainActivity.kt:66) 
at android.app.Activity.performCreate(Activity.java:7232) 
at android.app.Activity.performCreate(Activity.java:7221) 
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1272) 
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2971) 
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3126) 
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1846) 
at android.os.Handler.dispatchMessage(Handler.java:106) 
at android.os.Looper.loop(Looper.java:201) 
at android.app.ActivityThread.main(ActivityThread.java:6882) 
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:873) 
I thought maybe I can just get a copy of the data into a static list in my repository, but for that I'd need to observe it in the repository and I'm stuck at the same place again because I don't have a lifecycleOwner.
The idea in MVVM is to observe from view if you are doing an action that you want to be sure is finished do not wrap it in a livedata. Basically you don't need any life cycle aware component. Your query should return list of photos without any need to observe it. I mean in your dao if you don't wrap data in a livedata you don't need to observe anything and getAllPhotos return photos. You can do this kind of thing anywhrere just do everything from background, in a thread or something. For both kind of designs you can have 1 livedata wraped method in dao and 1 method without livedata for jobs that need to be done serially.
Update: put your code in a background thread:
new Thread(new Runnable() {
#Override
public void run() {
val allPhotosStatic = photoDao.getAllPhotosStatic()
for (file in it) {
val image = FirebaseVisionImage.fromFilePath(application, Uri.parse(file.uri))
detector.detectInImage(image).addOnSuccessListener { list ->
if (list.isNotEmpty()) {
file.hasFaces = 1
repo.update(file)
} else {
file.hasFaces = 2
repo.update(file)
}
}
}
}
}).start();
or:
AsyncTask.execute(new Runnable() {
#Override
public void run() {
val allPhotosStatic = photoDao.getAllPhotosStatic()
for (file in it) {
val image = FirebaseVisionImage.fromFilePath(application, Uri.parse(file.uri))
detector.detectInImage(image).addOnSuccessListener { list ->
if (list.isNotEmpty()) {
file.hasFaces = 1
repo.update(file)
} else {
file.hasFaces = 2
repo.update(file)
}
}
}
}
});

App Crash once replace Fragment for scoped ViewModel

Bug
I was found an issue when I try to open fragment and then replace with the same fragment.
In our prod application, it's a popular case.
java.lang.IllegalStateException: Definition without any InstanceContext - [type:Scope,scope:'com.abc.view.fragment.BrowseTaskFragment', primary_type:'com.abc.viewModel.BrowseTaskVM']
at org.koin.core.definition.BeanDefinition.resolveInstance(BeanDefinition.kt:72)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:141)
at org.koin.core.scope.Scope.get(Scope.kt:131)
at com.abc.view.fragment.BrowseTaskFragment$$special$$inlined$inject$1.invoke(Scope.kt:274)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at com.abc.view.fragment.BrowseTaskFragment.getMViewModel(Unknown Source:25)
at com.abc.view.fragment.BrowseTaskFragment.getMViewModel(BrowseTaskFragment.kt:37)
at com.abc.base.BaseFragment.performViewModelBinding(BaseFragment.kt:55)
at com.abc.base.BaseFragment.onViewCreated(BaseFragment.kt:31)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManager.java:1471)
at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1784)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManager.java:1852)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:802)
at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManager.java:2625)
at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2411)
at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366)
at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2273)
at androidx.fragment.app.FragmentManagerImpl$1.run(FragmentManager.java:733)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:201)
at android.app.ActivityThread.main(ActivityThread.java:6810)
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:873)
Steps to reproduce the behavior:
I have BottomNavigationView once I tap on existing fragment which is visible, getting above crash.
Koin version: 2.0.1
Module
val viewModelModule = module {
scope(named<BrowseTaskFragment>()) {
scoped { BrowseTaskVM() }
}
}
Application class
startKoin {
androidContext(this#AbcApplication)
modules(listOf(appModule, stateModule, apiModule, viewModelModule))
}
You can do this by create scope.
First in your module class create scope by named koin method
val viewModelModule = module {
scope(named<BrowseTaskFragment>()) {
scoped { BrowseTaskVM() }
}
}
Second in your fragment
private val viewModelScope = getKoin().getOrCreateScope("Scope1",named<BrowseTaskFragment>())
private val browseTaskVM: BrowseTaskVM= viewModelScope.get()
Last in an onDestoy method close your scope.
override fun onDestroy() {
super.onDestroy()
viewModelScope.close()
}
define in application class like this
startKoin {
androidContext(this#ApplicationContext)
// your modules
modules(listOf(your modules))
}
and pass context in you modules

Error accessing Room Database using RxJava, with Use Cases and Clean Architecure

I'm building an app using clean architecture.
I'm getting an error when calling an use case from my ViewModel that needs to run a query on my Room Database, telling me I'm accessing it in the main thread.
Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
at android.arch.persistence.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:204)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:232)
at com.something.data.db.PatientDAO_Impl.getAllPatients(PatientDAO_Impl.java:229)
at com.something.data.repositories.dataSource.PatientDataStoreFactory.create(PatientDataStoreFactory.kt:18)
at com.something.data.repositories.PatientRepositoryImplementation.getPatient(PatientRepositoryImplementation.kt:97)
at com.something.domain.useCases.GetPatientUseCase.execute(GetPatientUseCase.kt:10)
at com.something.some_thing.searchForm.SearchFormViewModel.getPatientFromDb(SearchFormViewModel.kt:49)
at com.something.some_thing.searchForm.SearchFormFragment.onCreateView(SearchFormFragment.kt:99)
at android.support.v4.app.Fragment.performCreateView(Fragment.java:2439)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1460)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1784)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1852)
at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:802)
at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2625)
at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2411)
at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2273)
at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3273)
at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:3229)
at android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:201)
at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:620)
at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:178)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1334)
at android.app.Activity.performStart(Activity.java:7057)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2819)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2931) 
at android.app.ActivityThread.-wrap11(Unknown Source:0) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1620) 
at android.os.Handler.dispatchMessage(Handler.java:105) 
at android.os.Looper.loop(Looper.java:173) 
at android.app.ActivityThread.main(ActivityThread.java:6698) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:782) 
Here is some code
My ViewModel call:
fun getPatient(){
val disposable = getPatientUseCase.execute()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::getPatientSuccess, this::onError)
compositeDisposable.add(disposable)
}
My Use Case:
class GetPatientUseCase(private val patientRepository: PatientRepository){
fun execute(): Single<Patient> {
return patientRepository.getPatient()
}
My repository method implementation
override fun getPatient(): Single<Patient> {
val patientDataStore = PatientDataStoreFactory().create()
return patientDataStore.patient()
}
This is where the crash happens, in My PatientDataStoreFactory, when running db.patientDAO().getAllPatients().isNotEmpty():
#Singleton
class PatientDataStoreFactory {
private val db = DaggerWrapper.getInstance().database()
fun create(): PatientDataStore {
return if (db.patientDAO().getAllPatients().isNotEmpty()){
LocalPatientDataStore()
} else {
CloudPatientDataStore()
}
}
}
In my ViewModel I'm subscribing the use case execution so I dont understand this crash...
Any ideas?
The problem is here: db.patientDAO().getAllPatients(). You are calling getAllPatients() on the UI Thread. You should wrap in in the Single and subscribe on Schedulers.io().
One of the possible solutions is to rewrite the following code:
val patientDataStore = PatientDataStoreFactory().create()
return patientDataStore.patient()
to something like this:
return Single.fromCallable { PatientDataStoreFactory().create() }
.flatMap { store -> store.patient() }

Categories

Resources