Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LiveData in Background Threads or non-UI components

Dears. I used to develop Android apps using MVP pattern and now I'm trying the MVVM with Architecture components like DataBind and LiveData.

I wrote my Repository class that provides a LiveData:

LiveData<MyEntity> getById(long id);

For Activity/Fragments I observe the LiveData exposed by ViewModel (that uses my Repository) and everything works fine.

The problem is I have to schedule an Alarm to display a Notification with a text related to MyEntity, so I created an Intent containing my MyEntityId as an Extra.

When the AlarmManager calls my BroadcastReceiver, I need to use the Repository to get MyEntity. The point is how to "Observe" the LiveData inside a non-UI component.

Also, I can start an IntentService (background thread) to avoid accessing the Repository in the Main Thread, and use something like "blockingNext" from RxJava, but I still could not figure a way to wait for LiveData.

What is the correct way of doing this? Note that my Repository may not be implemented using Room, due to legacy issues.

Thanks

The only solution I figured so far was have methods like this in the Repository:

LiveData<MyEntity> getByIdLive(long id);
MyEntity getById(long id);

But this does not look good for me. So I'd like to ask how is the correct way of implement this.

Best Regards

like image 225
William Miranda Avatar asked Nov 07 '22 21:11

William Miranda


1 Answers

It's better to avoid such things, but if you really need it and really know what you're doing you can try the following code:

public static <T> void observeOnce(final LiveData<T> liveData, final Observer<T> observer) {
    liveData.observeForever(new Observer<T>() {
        @Override
        public void onChanged(T t) {
            liveData.removeObserver(this);
            observer.onChanged(t);
        }
    });
}

@WorkerThread
public static <T> T waitSync(final LiveData<T> liveData) {
    final Object lock = new Object();
    final Object[] result = new Object[1];
    final boolean[] resultReady = new boolean[] {false};
    (new Handler(Looper.getMainLooper())).post(new Runnable() {
        @Override
        public void run() {
            observeOnce(liveData, new Observer<T>() {
                @Override
                public void onChanged(T t) {
                    synchronized (lock) {
                        result[0] = t;
                        resultReady[0] = true;
                        lock.notify();
                    }
                }
            });
        }
    });
    synchronized (lock) {
        try {
            while (!resultReady[0]) {
                lock.wait();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        }
    }
    return (T) result[0];
}
like image 138
algrid Avatar answered Nov 14 '22 22:11

algrid