package tv.danmaku.android.loader; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import tv.danmaku.android.util.Assure; import tv.danmaku.android.util.DebugLog; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.Loader; public class LoaderLauncher<D> implements LoaderCallbacks<D> { private static final String TAG = LoaderLauncher.class.getSimpleName(); private boolean ENABLE_VERBOSE; /* 使用loader pool的id的任务也会统一以 mLoaderId回调给外部 */ private WeakReference<LoaderCallbacks<D>> mWeakCallbacks; private WeakReference<Fragment> mWeakFragment; /* 如果没有足够的loader, 请求会在这里暂存 */ private ArrayList<Integer> mLoaderPool = new ArrayList<Integer>(); private HashSet<Integer> mBusyLoaders = new HashSet<Integer>(); private LinkedList<LoaderRequest<D>> mRequestQueue = new LinkedList<LoaderRequest<D>>(); private Handler mHandler = new Handler(); private int mQueueSize; // <=0 for no limit public LoaderLauncher(int id, Fragment fragment, LoaderCallbacks<D> callbacks) { mWeakFragment = new WeakReference<Fragment>(fragment); mWeakCallbacks = new WeakReference<LoaderCallbacks<D>>(callbacks); addExtraLoader(id); } public LoaderLauncher(int idBegin, int idEnd, Fragment fragment, LoaderCallbacks<D> callbacks) { mWeakFragment = new WeakReference<Fragment>(fragment); mWeakCallbacks = new WeakReference<LoaderCallbacks<D>>(callbacks); addExtraLoaderRange(idBegin, idEnd); } public void setQueueSize(int queueSize) { mQueueSize = queueSize; } private void addExtraLoader(int id) { mLoaderPool.add(id); } private void addExtraLoaderRange(int begin, int end) { for (int i = begin; i < end; ++i) addExtraLoader(i); } public void pushHead(Bundle args) { LoaderRequest<D> request = new LoaderRequest<D>(0, args, this); mRequestQueue.addFirst(request); if (mQueueSize > 0) { while (mRequestQueue.size() > mQueueSize) { mRequestQueue.removeLast(); } } launchHeadLoader(); } public void pushTail(Bundle args) { LoaderRequest<D> request = new LoaderRequest<D>(0, args, this); mRequestQueue.addLast(request); if (mQueueSize > 0) { while (mRequestQueue.size() > mQueueSize) { mRequestQueue.removeFirst(); } } launchHeadLoader(); } private void launchHeadLoader() { if (mRequestQueue.isEmpty()) return; LoaderRequest<D> request = mRequestQueue.removeFirst(); if (request == null) return; if (launchLoader(request)) return; // not launched, push back if (ENABLE_VERBOSE) DebugLog.wfmt( TAG, "queueLoader x%x, %s-%d", request.hashCode(), new String(request.mArgs .getString(LoaderBundle._BUNDLE_NAME)), request.mArgs.getInt(LoaderBundle._BUNDLE_ID)); mRequestQueue.addFirst(request); } private boolean launchLoader(LoaderRequest<D> request) { return launchLoader(request.mArgs); } private boolean launchLoader(Bundle args) { Assure.checkNotEmptyCollection(mLoaderPool); LoaderManager loaderManager = getLoaderManager(); if (loaderManager == null) return false; /* find a loader */ for (Integer id : mLoaderPool) { Loader<D> loader = loaderManager.getLoader(id); if (loader == null) { if (ENABLE_VERBOSE) DebugLog.vfmt(TAG, "initLoader %d", id); mBusyLoaders.add(id); loaderManager.initLoader(id, args, this); return true; } else if (!mBusyLoaders.contains(id)) { if (ENABLE_VERBOSE) DebugLog.vfmt(TAG, "restartLoader %d", id); mBusyLoaders.add(id); loaderManager.restartLoader(id, args, this); return true; } } return false; } /*-------------------------------------- * LoaderManager.Callbacks */ @Override public Loader<D> onCreateLoader(int id, Bundle args) { LoaderCallbacks<D> callbacks = getLoaderCallbacks(); if (callbacks == null) return null; return callbacks.onCreateLoader(id, args); } @Override public void onLoadFinished(Loader<D> loader, D data) { int id = loader.getId(); mBusyLoaders.remove(id); if (ENABLE_VERBOSE) DebugLog.vfmt(TAG, "finish %d", id); LoaderCallbacks<D> callbacks = getLoaderCallbacks(); if (callbacks == null) return; callbacks.onLoadFinished(loader, data); /* avoid unlimited recursive */ mHandler.post(new Runnable() { @Override public void run() { /* launch next loader */ launchHeadLoader(); } }); } @Override public void onLoaderReset(Loader<D> loader) { int id = loader.getId(); mBusyLoaders.remove(id); if (ENABLE_VERBOSE) DebugLog.vfmt(TAG, "reset %d", id); LoaderCallbacks<D> callbacks = getLoaderCallbacks(); mBusyLoaders.add(loader.getId()); if (callbacks == null) return; callbacks.onLoaderReset(loader); } /*-------------------------------------- * getter */ private LoaderManager getLoaderManager() { Fragment fragment = mWeakFragment.get(); if (fragment == null) return null; if (!fragment.isAdded() || fragment.isDetached()) return null; try { LoaderManager loaderManager = fragment.getLoaderManager(); return loaderManager; } catch (IllegalStateException e) { } return null; } private LoaderCallbacks<D> getLoaderCallbacks() { return mWeakCallbacks.get(); } public void setEnableVerbose(boolean enableVerbose) { ENABLE_VERBOSE = enableVerbose; } }