简体   繁体   中英

Can you use room and not use a LiveData object?

My usage currently is to store 5 specific button's info into a database (room) to persist it across reboots. My current usage doesn't rely on changes of the data because the only one changing the data is the user upon long press of the button (then i update the database). Hence I do not need a LiveData variable, and this is making it difficult for me to initialize my ViewModel.

Essentially, since the LiveData objects only update on change, my data never gets initialized. Thus the app always will cause a null-pointer on startup.

I'll share a gist of my setup so far bellow. I'm hoping there is some way to make this work where I don't have to observe any LiveData object, and I can just grab data when I instantiate the Model.

Entity:

@Entity(tableName = "myEntity")
public class MyEntity {
    @PrimaryKey
    public int buttonID;

    // other fields...
}

DAO:

@Dao
interface MyDAO {
    @Query("Select * from myDB")
    LiveData<List<MyEntity>> getEntityList();
    // I think this needs to change to just List<MyEntity>?

    // also insert and update here...
}

Repository:

class MyRepository {
    private MyDAO myDAO;
    private LiveData<List<MyEntity>> allEntities;

    MyRepository(Application application) {
        MyDatabase db = MyDatabase.getInstance(application);
        myDAO = db.myDAO();
        allEntities = myDAO.getAllEntities();
    }

    LiveData<List<MyEntity>> getAllEntities() { return allEntities; }

    // Update entity...
}

ViewModel:

public class ViewModel extends AndroidViewModel {
    private MyRepository repository;
    private List<MyEntity> tempList;
    private HashMap<MyEntity> allEntities;

    public ViewModel (Application application) {
        super(application);
        repository = new MyRepository(application);

        Observer<List<MyEntity>> observer = data -> tempList = data;
        ObserveOnce(repository.getAllEntities(), observer); // ObserveOnce implementation found in this answer: https://stackoverflow.com/a/59845763/10013384
        allEntities = new HashMap<>();

        for (int i = 0; i < tempList.size(); i++) { // Nullpointer here, as tempList doesn't have any items yet.
            allEntities.put(tempList.get(i).buttonID, templist.get(i));
        }
    }

    // getter and update methods...
}

Activity:

// ...
protected void onCreate(Bundle savedInstanceState) {
    // ...
    viewModel = new ViewModelProvider(this).get(ViewModel.class);

    // Initialize UI views with data from ViewModel
}

Then in the respective listeners:

@Override
public boolean onLongClick(View v) {
    int index = (Integer) v.getTag();

    data.get(index).foo = fooNewUIData;
    ButtonArray[index].setText(fooNewUIData);
    ViewModel.update(data.get(index));  // if updated, update the ViewModel and the database
}

since the LiveData objects only update on change, my data never gets initialized

That is not your problem. Your problem is that you think that ObserveOnce() is a blocking call, and that the results will be ready immediately when it returns. In reality, LiveData from Room does work on a background thread. You need to react to when the data is available in your Observer , not assume that it will be available in the next statement.

OfCourse you can, you can simply return the normal object class. LiveData needs to be used only if you want observe the changes to those rows.

You can also use to kotlin flows to still listen to the changes and not use LiveData

Without LiveData:

List<MyEntity> getAllEntities();

With Kotlin Flows:

fun getAllEntities(): Flow<List<MyEntity>>

Hope this helps !!

Yes, of course you can. Just change the return type of the specific function in your DAO from LiveData<MyDataClass> to MyDataClass .

See this Codelab for further tutorial about Room .

Okay, thanks all to provided answers and feedback, I appreciate it!

Ultimately I agree with @CommonsWare that the easiest way to handle this is probably not to have the ViewModel consume the LiveData . I'm opting to setup an Observer as normal in my Activity, and convert it into the format that I want ( ArrayList ) when I save that data to my Activity class.

The issue I was originally concerned about with data not being sorted to my liking could otherwise be solved with a simple sort:

Collections.sort(this.data, (o1, o2) -> Integer.compare(o1.buttonID, o2.buttonID));

As for the async issue, I'm just falling back to allowing the observer's callback update the data as it gets it (and update the UI accordingly). If this situation involves a lot more data, then perhaps I'll need some sort of a splash screen while the app loads data. But luckily I don't have to do any of that quite yet.

Activity:

// onCreate() {
    ViewModel = new ViewModelProvider(this).get(ViewModel.class);
            Observer<List<MyEntity>> observer = this::setData;
            observeOnce(ViewModel.getAllEntities(), observer);

    if (data.size() > 0) {
        // Initialize UI views
    }
// }
//...

public void setData(List<MyEntity> entities) {
    this.data = entities
    Collections.sort(this.data, (o1, o2) -> Integer.compare(o1.buttonID, o2.buttonID));

    // also update UI if need-be
}

The only thing left is to solve the issue of my Database not persisting across reboots, but that is out of the scope of this question.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM