I am working on Android Application in Which I am getting specific Data from Room Database by specific path in the Storage. My App Got Crashes as It does not have Any Data in the Storage and the Logcat gives me this..
java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
at androidx.room.RoomOpenHelper.checkIdentity(RoomOpenHelper.java:154)
at androidx.room.RoomOpenHelper.onOpen(RoomOpenHelper.java:135)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onOpen(FrameworkSQLiteOpenHelper.java:195)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:428)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:317)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:145)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:106)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
at com.maximus.technologies.views.activities.scanneddatabase.TodoDaoScanned_Impl.getAllScan(TodoDaoScanned_Impl.java:152)
at com.maximus.technologies.views.fragments.scanhistorypackage.QRRetrievingScanClassPresenter$getAllDatFromDatabase$1.invokeSuspend(QRRetrievingScanClassPresenter.kt:29)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
The Above Error or crash Only occurs as the app dont have any data in Storage. But as I put a Data the Crash Problem Get Resolved.
I am not able to Understand what the Problem actually is...
Here is My Room Database Class..
#Database(
entities = [TodoEntity::class,TodoEntityScanned::class],
version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun TodoDao(): TodoDao
abstract fun TodoDaoScanned(): TodoDaoScanned
object DatabaseBuilder {
private var INSTANCE: AppDatabase? = null
fun getInstance(context: Context): AppDatabase {
if (INSTANCE == null) {
synchronized(AppDatabase::class) {
INSTANCE = buildRoomDB(context)
}
}
return INSTANCE!!
}
private fun buildRoomDB(context: Context) =
Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"mindorks-example-coroutines"
).build()
}
}
Room Database Retrieving Interface where app Crashes on getall()
override fun getAllDatFromDatabase(appDatabasescanned: AppDatabase) {
var list = listOf<TodoEntityScanned>()
try {
GlobalScope.launch(Dispatchers.Default) {
list = appDatabasescanned.TodoDaoScanned().getAllScan()
Log.d("hello","hello")
mView.showAllData(list)
}
}
catch (e:Exception){
Log.d("get hello",e.toString())
}
}
The getAll lies in Dao Class
interface TodoDao {
#Query("SELECT * FROM tablefilepaths")
fun getAll(): List<TodoEntity>
#Query("SELECT * FROM tablefilepaths WHERE imagespath LIKE :title")
fun findByTitle(title: String): TodoEntity
#Insert
fun insertpaths(todo: TodoEntity)
#Delete
fun deletepaths(todo: TodoEntity)
#Query("DELETE FROM tablefilepaths WHERE id = :noteId")
fun deleteNoteById(noteId: Int)
#Update
fun updateTodo(vararg todos: TodoEntity)}
Here is My Fragment Class Where I am Setting data in RecyclerView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerviewcreatehistory?.layoutManager = LinearLayoutManager(context)
recyclerviewcreatehistory?.setHasFixedSize(true)
filefetch()
customAdaptercreatehistory = CustomAdapterCreateHistory(this.context ?: return, charItemcreate!!,this)
recyclerviewcreatehistory?.adapter = customAdaptercreatehistory
}
fun filefetch() {
val noteDatabase: AppDatabase = AppDatabase.DatabaseBuilder.getInstance(requireContext())
retrivingpresenter = QRRetrievingClassPresenter(this)
retrivingpresenter!!.getAllDatFromDatabase(noteDatabase)
}
override fun showAllData(note_list: List<TodoEntity>) {
if (note_list is ArrayList<*>) {
val arraylist = note_list as ArrayList<TodoEntity>
charItemcreate=arraylist
}
if (charItemcreate.isEmpty()){
}else{
customAdaptercreatehistory?.updateUsers(note_list as ArrayList<TodoEntity>)
customAdaptercreatehistory?.notifyDataSetChanged()
// Log.d("hello", note_list[0].imagesPathData)
}
}
You have to do some checks in your getAllDatFromDatabase() inside your coroutine. I guess list variable equals null or something like that. You should check if there are any files and if not you need to put there something else.
Related
I have two Activities: ActivityOne.kt and ActivityTwo.kt. Both of them use Jetpack Compose to display an UI. In the first one, an item (let's say, a Text) has to be shown with variable showtText is true, and hidden when it's false. I achieve this by using:
#Composable
fun MyUI(){
AnimatedVisibility(visible = viewModel.showText) {
Text("Some text")
}
}
My variable showText is defined in a ViewModel such as:
val showText by mutableStateOf(false)
That way, anytime I change the value of showText within my ViewModel when ActivityOne.kt is visible, it appears or disaappears. What I want to achieve is the following:
From ActivityOne.kt the user can navigate to ActivityTwo.kt (with the first one running in background, it's not cleared from the stack).
There, there's a Switch that can toggle showText value.
When the user press "back", ActivityTwo.kt calls finish() and it dissapears, showing again ActivityOne.kt (it was in the stack).
If user has toggled showText value in ActivityTwo.kt, the text should be hidden or shown automatically, as the state of showText has changed.
The problem is that, although the value of showText does change, the UI in ActivityOne.kt does not respond to those changes. I've checked that recomposition of ActivityOne.kt's UI is not taking place after ActivityTwo.kt is finished, because it is not remembering the previous state of showText.
How could I achieve that? Thanks in advance!
EDIT 1
The problem is a little more difficult as I explained, but the basis are the same. Here is my complete code:
ActivityOne.kt:
class ActivityOne : ComponentActivity() {
private lateinit var vm: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vmFactory = MainViewModelFactory()
vm = ViewModelProvider(this, vmFactory).get(MainViewModel::class.java)
setContent {
MyTheme {
Surface {
MyUI()
}
}
}
}
#Composable
fun MyUI() {
AnimatedVisibility(visible = myPrefs.showText) {
Text("Some text")
}
}
}
MainViewModel.kt:
class MainViewModel : ViewModel() {
companion object {
val myPrefs by mutableStateOf( AppPrefs() )
}
}
ActivityTwo.kt:
class ActivityTwo : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyTheme {
Surface {
MyUI2()
}
}
}
}
#Composable
fun MyUI2() {
val showText = remember {
mutableStateOf(MainViewModel.myPrefs.showText)
}
Switch(
checked = showText.value,
onCheckedChange = {
showText.value = it
MainViewModel.myPrefs.showText = it
}
)
}
}
AppPrefs.kt:
class AppPrefs {
var showText: Boolean = false
}
The recomposition is not taking place because mutableStateOf is tracking the state of myPrefs which is never changed (the reference itself never changes).
You can achieve the recomposition in a few ways.
Here I will be assuming that your AppPrefs class either already contains more than one member/field, or that you would want to add more members to it in the future easily. That is why I added 2 more properties to it in the samples below and I only suggested solutions where adding more members won't affect the existing code.
Option 1
If you can change the AppPrefs class to track the state of each property individually, then make these changes
class MainViewModel : ViewModel() {
companion object {
val myPrefs = AppPrefs()
}
}
class AppPrefs {
var showText by mutableStateOf(false)
var prop2 by mutableStateOf(0)
var prop3: String? by mutableStateOf(null)
}
Everything else stays the same. Here the recomposition works again because now the state is tracked on members that are actually being changed.
Option 2
If you can't (or don't want to) have mutableStateOf inside AppPrefs you can change AppPrefs from a normal class to a data class. In that way you get the copy function automatically implemented (alongside equals, hashCode, toString and componentN for destructuring support).
class MainViewModel : ViewModel() {
companion object {
// the only change here is val -> var
var myPrefs by mutableStateOf(AppPrefs())
}
}
// data class instead of a normal class
data class AppPrefs(
val showText: Boolean = false,
val prop2: Int = 0,
val prop3: String? = null,
)
In this case you have to also change how you change the value of myPrefs. This is what makes the recomposition work correctly again
onCheckedChange = {
showText.value = it
MainViewModel.myPrefs = MainViewModel.myPrefs.copy(showText = it)
}
In case you cannot use a data class, then you can still go with option 2, but you implement the copy function on your existing class
// normal class with a copy function
class AppPrefs(
val showText: Boolean = false,
val prop2: Int = 0,
val prop3: String? = null,
) {
fun copy(
showText: Boolean = this.showText,
prop2: Int = this.prop2,
prop3: String? = this.prop3,
) = AppPrefs(showText, prop2, prop3)
}
As a bonus, if you go with any of the above solutions, you can even simplify your existing code for the MyUI2 composable and the recomposition will still work.
Example:
#Composable
fun MyUI2() {
val showText = MainViewModel.myPrefs.showText
Switch(
checked = showText,
onCheckedChange = {
// for Option 1
MainViewModel.myPrefs.showText = it
// for Option 2
MainViewModel.myPrefs = MainViewModel.myPrefs.copy(showText = it)
}
)
}
I am struggling to use Live data on an MVVM pattern. The app is supposed to:
Fetch data from an API (which it does correctly)
Store that data in the Live data object from the ViewModel
Then the fragment calls the Observer method to fill the recyclerView.
The problem comes in point 3, it does nothing, and I cannot find the solution.
Here is the relevant code. (If I'm missing something, I will try to answer as quickly as possible)
Main Activity:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val viewModel: SharedViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// Custom button to fetch data from api and log the Live Data value.
binding.refreshFab.setOnClickListener {
viewModel.fetchPlayerData()
Log.d("gabs", "${viewModel.livePlayerlist.value}")
}
}
}
ViewModel:
class SharedViewModel(app: Application): AndroidViewModel(app) {
// val playerDao = LaRojaDB.getDatabase(app).playerDao()
lateinit var playerList: Players
val livePlayerlist: MutableLiveData<MutableList<Players.PlayersItem>> by lazy {
MutableLiveData<MutableList<Players.PlayersItem>>()
}
fun fetchPlayerData() {
CoroutineScope(Dispatchers.IO).launch {
val response = MyService.getLaRojaService().getAllPlayers()
withContext(Dispatchers.Main) {
if (response.isSuccessful) {
val body = response.body()
if(!body.isNullOrEmpty()){
playerList = body
val playerArrayList = mutableListOf<Players.PlayersItem>()
playerList.forEach {
playerArrayList.add(it)
}
livePlayerlist.value = playerList
}
}
}
}
}
}
The fragment that displays the recycler view: (Fragment is already showing, I set up a textView as a title to make sure since I'm new using fragments as well.)
class PlayerListFragment : Fragment() {
private var _binding: FragmentPlayerListBinding? = null
private val binding get() = _binding!!
private val model: SharedViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentPlayerListBinding.inflate(inflater, container, false)
binding.rvPlayerList.layoutManager = LinearLayoutManager(activity)
----> // This is the observer that does not update the UI** <----
model.livePlayerlist.observe( viewLifecycleOwner, {
binding.rvPlayerList.adapter = PlayerAdapter(it)
})
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_player_list, container, false)
}
}
Thank you all in advance, hope I can finally learn what is causing the issue!
I think you don't need to switch Coroutine contexts. A few changes I'd expect if I were reviewing this code:
This should all be in the same IO context. You then postValue to your liveData.
fun fetchPlayerData() {
viewModelScope.launch(Dispatchers.IO) {
val xx = api.fetch()
...
_playerState.postValue(xx) //see below
}
}
Additionally, it's preferred not to expose mutable state, so your ViewModel should not expose the MutableLiveData (which shouldn't really be lazy). But it's also better to encapsulate the state in a sealed class:
//delete this
val livePlayerlist: MutableLiveData<MutableList<Players.PlayersItem>> by lazy {
MutableLiveData<MutableList<Players.PlayersItem>>()
}
Should be: (names are just pseudo code, I have no idea what this code is about)
sealed class PlayerDataState {
data class ListAvailable(data: List<Players.PlayersItem>>): PlayerDataState
object Loading(): PlayerDataState
}
And your new LiveData:
private val _playerState = MutableLiveData<PlayerDataState>()
val playerState: LiveData<PlayerDataState>() get() = _playerState
Finally when observing from the UI, you just...
model.playerState.observe(viewLifecycleOwner, {
when (it) {
is Loading -> ...
is ListAvailable -> binding.rvPlayerList.adapter = PlayerAdapter(it.data)
}
}
I am new to kotlin and android studio and currently I am trying to build an todo list applications with my own ideas. Its mostly done but I have to add edit and delete functionality to the tasks that user adds. The tasks that user adds are stored on the device using SQLiteDatabase. This is the base swipe to delete class that I wrote:
abstract class SwipeToDelete(context: Context, dragDir: Int, swipeDir: Int): ItemTouchHelper.SimpleCallback(dragDir, swipeDir) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
TODO("Not yet implemented")
}
}
This is the delete functionality that I have added which works great with a button:
fun deleteToDo(todoId: Long){
val db = writableDatabase
db.delete(TABLE_TODO_ITEM,"$COL_TODO_ID=?", arrayOf(todoId.toString()))
db.delete(TABLE_TODO,"$COL_ID=?", arrayOf(todoId.toString()))
}
This is the recyclerview adapter that I am using:
class ItemAdapter(val context: Context,val dbHandler: DBHandler, val list: MutableList<ToDoItem>) :
RecyclerView.Adapter<ItemAdapter.ViewHolder>(){
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(context).inflate(R.layout.rv_child_item,p0,false))
}
override fun onBindViewHolder(holder: ViewHolder, p1: Int) {
holder.itemName.text = list[p1].itemName
holder.itemName.isChecked = list[p1].isCompleted
holder.itemName.setOnClickListener{
list[p1].isCompleted = !list[p1].isCompleted
dbHandler.updateToDoItem(list[p1])
}
}
override fun getItemCount(): Int {
return list.size
}
class ViewHolder(v : View) : RecyclerView.ViewHolder(v){
val itemName : CheckBox = v.findViewById(R.id.cb_item)
}
}
but for some reason I cannot make it work when I try to call the delete function in this Swipetodelete object:
val item = object : SwipeToDelete(this,0,ItemTouchHelper.LEFT){
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
}
I also want to add the edit functionality but if i can get this delete functionality to work i can add it.
Since the SwipeToDelete is an abstract class, you can override the onSwiped function on your Fragment/Activity Class.
You can modify your SwipeToDelete class to:
abstract class SwipeToDelete(context: Context, dragDir: Int, swipeDir: Int): ItemTouchHelper.SimpleCallback(dragDir, swipeDir) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
}
And then override the onSwiped function inside your fragment/activity and attach it to your recyclerview:
val item = object : SwipeToDelete(this,0,ItemTouchHelper.LEFT){
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
deleteToDo(todoId)
}
}
ItemTouchHelper(item).attachToRecyclerView(recycler)
I would like to get your feedback on the code below.
I'm wondering if it's safe to call currentSession.onNext(result.session)
from inside the SessionManager.signIn stream.
My first intuition is to say NO because of multithreading and synchronization issues, meaning, based on this code, I could be calling currentSession.onNext(result.session) from different threads.
Here is the code, please let me know what you think! Thanks
SessionManager which is a singleton
#Singleton
class SessionManager #Inject constructor(
private val sessionService: SessionService,
){
val currentSession = BehaviorProcessor.create<Session>()
fun signIn(login: String, password: String): Single<Boolean> =
sessionService.signIn(login, password)
.doOnNext(result ->
if (session is Success) {
currentSession.onNext(result.session)
}
).map { result ->
when (result) {
is Success -> true
else -> false
}
}
.subscribeOn(Schedulers.io())
}
HomeView which is a random View subscribing to the SessionManager's signIn stream
class HomeView(val context: Context) : View(context) {
#Inject
lateinit var sessionManager: SessionManager
private val disposables = CompositeDisposable()
override fun onAttachedToWindow() {
super.onAttachedToWindow()
disposables.add(sessionManager.signIn("username", "password")
.distinctUntilChanged()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { result ->
textView.text = if (result) "Success" else "Fail"
})
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
disposables.clear()
}
}
A random View observing the currentSession from SessionManager
class RandomView(val context: Context) : View(context) {
#Inject
lateinit var sessionManager: SessionManager
private val disposables = CompositeDisposable()
override fun onAttachedToWindow() {
super.onAttachedToWindow()
disposables.add(sessionManager.currentSession
.distinctUntilChanged()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { session -> userTextView.text = session.userName })
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
disposables.clear()
}
}
The documentation of BehaviorProcessor says:
Calling onNext(Object), offer(Object), onError(Throwable) and onComplete() is required to be serialized (called from the same thread or called non-overlappingly from different threads through external means of serialization). The FlowableProcessor.toSerialized() method available to all FlowableProcessors provides such serialization and also protects against reentrance (i.e., when a downstream Subscriber consuming this processor also wants to call onNext(Object) on this processor recursively).
So if you define it like this:
val currentSession = BehaviorProcessor.create<Session>().toSerialized()
then you can safely call onNext from any thread, it will not cause any synchronisation problems.
Notes:
I agree that the update of the processor should be in a doOnNext instead of the map.
I think it would be better to use a Completable instead of a Single<Boolean>, and use Rx errors to indicate what prevented signing in. You should also define the error handlers in the subscribe methods.
I'm trying to create a simple schema using ReflectiveSchema and then trying to project an Employee "table" using Groovy as my programming language. Code below.
class CalciteDemo {
String doDemo() {
RelNode node = new CalciteAlgebraBuilder().build()
return RelOptUtil.toString(node)
}
class DummySchema {
public final Employee[] emp = [new Employee(1, "Ting"), new Employee(2, "Tong")]
#Override
String toString() {
return "DummySchema"
}
class Employee {
Employee(int id, String name) {
this.id = id
this.name = name
}
public final int id
public final String name
}
}
class CalciteAlgebraBuilder {
FrameworkConfig config
CalciteAlgebraBuilder() {
SchemaPlus rootSchema = Frameworks.createRootSchema(true)
Schema schema = new ReflectiveSchema(new DummySchema())
SchemaPlus rootPlusDummy = rootSchema.add("dummySchema", schema)
this.config = Frameworks.newConfigBuilder().parserConfig(SqlParser.Config.DEFAULT).defaultSchema(rootPlusDummy).traitDefs((List<RelTraitDef>)null).build()
}
RelNode build() {
RelBuilder.create(config).scan("emp").build()
}
}
}
I seem to be correctly passing in the "schema" object to the constructor of the ReflectiveSchema class, but I think its failing while trying to get the fields of the Employee class.
Here's the error
java.lang.StackOverflowError
at java.lang.Class.copyFields(Class.java:3115)
at java.lang.Class.getFields(Class.java:1557)
at org.apache.calcite.jdbc.JavaTypeFactoryImpl.createStructType(JavaTypeFactoryImpl.java:76)
at org.apache.calcite.jdbc.JavaTypeFactoryImpl.createType(JavaTypeFactoryImpl.java:160)
at org.apache.calcite.jdbc.JavaTypeFactoryImpl.createType(JavaTypeFactoryImpl.java:151)
at org.apache.calcite.jdbc.JavaTypeFactoryImpl.createStructType(JavaTypeFactoryImpl.java:84)
at org.apache.calcite.jdbc.JavaTypeFactoryImpl.createType(JavaTypeFactoryImpl.java:160)
at org.apache.calcite.jdbc.JavaTypeFactoryImpl.createStructType(JavaTypeFactoryImpl.java:84)
What is wrong with this example?
Seems that by just moving the Employee class a level above, ie. making it a sibling of the DummySchema class, makes the problem go away.
I think the way the org.apache.calcite.jdbc.JavaTypeFactoryImpl of Calcite is written doesn't handle Groovy's internal fields well.