Snippets

Nemanja B Sample LiveData with features like loaders. Content observer, load queuing, throttling. Cancellation not implemented.

Created by Nemanja B
/*
 * Copyright (C) 2018 Sony Mobile Communications Inc.
 * All rights, including trade secret rights, reserved.
 */

package com.sonymobile.emailcommon.provider;

import android.annotation.SuppressLint;
import android.arch.lifecycle.LiveData;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v4.util.Pair;

import com.sonymobile.mail.log.Elog;

import java.lang.ref.WeakReference;
import java.util.concurrent.Executor;

/**
 * TODO Write doc
 */
public class EmailContentLiveData<T extends EmailContent> extends LiveData<T> {
    static Executor sExecutor = AsyncTask.THREAD_POOL_EXECUTOR;

    private final Executor mExecutor = sExecutor;
    private final Context mContext;
    private final Uri mContentUri;
    private final long mId;
    private final Class<T> mEmailContentClass;
    private final Throttle mThrottle;
    private final String[] mProjection;

    private ForceLoadContentObserver mForceLoadContentObserver;
    private boolean mPending = true;
    private ContentResolver mContentResolver;
    private Task mExecutingTask;
    private boolean mIsActive = false;

    public EmailContentLiveData(Context context, Uri contentUri, long id,
            Class<T> emailContentClass, String[] projection) {
        mContext = context.getApplicationContext();
        mContentUri = contentUri;
        mId = id;
        mEmailContentClass = emailContentClass;
        mThrottle = new Throttle("LiveData:" + emailContentClass.getName(),
                this::executePendingTask, new Handler());
        mProjection = projection;
    }

    @Override
    protected void onActive() {
        super.onActive();
        if (mForceLoadContentObserver == null) {
            mForceLoadContentObserver = new ForceLoadContentObserver(this);
            mContentResolver = mContext.getContentResolver();
            mContentResolver
                    .registerContentObserver(mContentUri, true, mForceLoadContentObserver);
        }
        mIsActive = true;
        executePendingTask();
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        mIsActive = false;
    }

    @Override
    protected void finalize() throws Throwable {
        // This might be the only way
        try {
            if (mForceLoadContentObserver != null) {
                Elog.w(EmailContentLiveData.class, "finalize: ");
                mContentResolver.unregisterContentObserver(mForceLoadContentObserver);
                mForceLoadContentObserver = null;
            }
        } finally {
            super.finalize();
        }
    }

    private void executePendingTask() {
        boolean execute = mPending && mExecutingTask == null && mIsActive;
        if (execute) {
            // no cancellation yet
            mExecutingTask = new Task(getValue());
            mExecutingTask.executeOnExecutor(mExecutor);
            mPending = false;
        }
    }

    private T executeInBackground() {
        return EmailContent.restoreContentWithId(mContext, mEmailContentClass, mContentUri,
                mProjection, mId, null);
    }

    static class ForceLoadContentObserver extends ContentObserver {
        private final WeakReference<EmailContentLiveData> mWeakReference;

        public ForceLoadContentObserver(EmailContentLiveData liveData) {
            super(new Handler());
            mWeakReference = new WeakReference<>(liveData);
        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            EmailContentLiveData liveData = mWeakReference.get();
            if (liveData != null) {
                liveData.mPending = true;
                liveData.mThrottle.onEvent();
            }
        }
    }

    @SuppressLint("StaticFieldLeak")
    class Task extends AsyncTask<Void, Void, Pair<T, Boolean>> {

        @Nullable
        private final T mValue;

        public Task(@Nullable T value) {
            mValue = value;
        }

        @Override
        protected final Pair<T, Boolean> doInBackground(Void... voids) {
            T t = executeInBackground();
            if (mValue != null && t != null) {
                return Pair.create(t, t.toContentValues().equals(mValue.toContentValues()));
            }
            return Pair.create(t, Boolean.FALSE);
        }

        @Override
        protected void onPostExecute(Pair<T, Boolean> pair) {
            super.onPostExecute(pair);
            if (!pair.second) {
                setValue(pair.first);
            }
            mExecutingTask = null;
            // If changed in the mean time
            executePendingTask();
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
            mExecutingTask = null;
        }
    }
}

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.