package droidkit.sqlite; import android.database.Cursor; import android.os.Process; import android.support.annotation.NonNull; import android.util.Log; import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicReference; import droidkit.concurrent.AsyncQueue; import droidkit.io.IOUtils; /** * @author Daniel Serdyukov */ @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") class SQLiteGuard extends PhantomReference<SQLiteResult<?>> { private static final Set<Reference<?>> REFERENCES = new CopyOnWriteArraySet<>(); private static final BlockingQueue<ReferenceQueue<SQLiteResult<?>>> QUEUE = new LinkedBlockingQueue<>(); static { AsyncQueue.get().invoke(new Runnable() { @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); while (!Thread.currentThread().isInterrupted()) { try { final ReferenceQueue<SQLiteResult<?>> referenceQueue = QUEUE.take(); final Reference<? extends SQLiteResult<?>> reference = referenceQueue.remove(); if (reference != null) { ((SQLiteGuard) reference).finalizeReferent(); REFERENCES.remove(reference); } else { QUEUE.put(referenceQueue); } } catch (InterruptedException e) { Log.e("SQLiteGuard", e.getMessage(), e); Thread.interrupted(); } } } }); } private final AtomicReference<Cursor> mCursorRef; private SQLiteGuard(@NonNull SQLiteResult<?> referent, @NonNull AtomicReference<Cursor> cursorRef) { super(referent, createReferenceQueue()); mCursorRef = cursorRef; REFERENCES.add(this); } static SQLiteGuard guard(@NonNull SQLiteResult<?> referent) { return new SQLiteGuard(referent, referent.getCursorReference()); } private static ReferenceQueue<SQLiteResult<?>> createReferenceQueue() { final ReferenceQueue<SQLiteResult<?>> referenceQueue = new ReferenceQueue<>(); QUEUE.offer(referenceQueue); return referenceQueue; } public void finalizeReferent() { final Cursor cursor = mCursorRef.get(); if (cursor != null) { IOUtils.closeQuietly(cursor); mCursorRef.set(null); } } }