loadermanager使用解析之个人见解
来源:互联网 发布:pdf.js 获取当前页数 编辑:程序博客网 时间:2024/05/17 03:22
在学习loader之前,先谈谈为什么使用这个。以下是官方对于loader的介绍:
1、They are available to every Activity and Fragment.
2、They provide asynchronous loading of data.
3、They monitor the source of their data and deliver new results when the content changes.
4、They automatically reconnect to the last loader's cursor when being recreated after a configuration change. Thus, they don't need to re-query their data.
看看我做的一个demo,结合这个demo来讲讲哪些地方loader,以及关于loader的一般用法。
首先先看看loader的简单用法:
第一,我需要知道requestLoader在哪里创建?
第二,何时会被促发loader的创建?
第三,我们创建的loader,什么时候会被启动去获取数据呢?
第四,当服务将数据返回之后,客户端怎么拿到这个数据?
一、哪里创建loader
public
class
SearchBookResultFragment
extends
BaseFragment
implements
LoaderManager.LoaderCallbacks<DoubanSearchBook>{
@Override
public
Loader<DoubanSearchBook> onCreateLoader(
int
id, Bundle bundle) {
//该方法会被LoaderManager的内部实现类LoaderMangerImp回调。该函数会根据传入的ID,初始化并返回一个自定义的loader(
//负责处理耗时的操作,当异步线程操作完成之后,就会返回请求的数据).
如:
return
new
RequestLoader(getActivity(),
new
GuessBookNameRuquest(
"莫言"
), Request.Origin.NET);
//自定义的requestLoader
}
@Override
public
void
onLoadFinished(Loader<DoubanSearchBook> resultLoader, DoubanSearchBook result) {
//接收请求得到的数据,并进行相应的处理。
}
@Override
public
void
onLoaderReset(Loader<DoubanSearchBook> resultLoader) {
//当loader被重置时,使得数据无效.
}
};
二、何时启动创建loader方法
那么问题来了,我们创建loader的这个方法什么时候会被调用呢?看以下一块代码:
@Override
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
}
@Override
public
View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return
super
.onCreateView(inflater, container, savedInstanceState);
}
@Override
public
void
onActivityCreate(Bundle savedInstanceState) {
super
.onActivityCreated(savedInstanceState);
getListView().setSelector(android.R.color.transparent);
getListView().setDivider(
null
);
if
(getArguments() !=
null
) {
mSearchText = getArguments().getString(TEXT);
}
//获得loadermanager中的initloader方法,并将loaderCallBack接口实现类传入
getLoaderManager().initLoader(PAGE_LOADER_ID,
null
,
this
);
//init方法如何调用创建loader,就需要了解一下LoaderManger这个抽象类
}
private
Exception exception;
@Override
public
void
onLoadFinished(Loader<List<DoubanBookInfo>> loader, List<DoubanBookInfo> data, Exception exception) {
super
.onLoadFinished(loader, data, exception);
this
.exception = exception;
}
@Override
protected
List<DoubanBookInfo> getList(List<DoubanBookInfo> doubanBookInfos) {
return
doubanBookInfos;
}
@Override
protected
void
refresh() {
Bundle args =
new
Bundle();
args.putBoolean(
"refresh"
,
true
);
getLoaderManager().restartLoader(
0
, args,
this
);
}
LoaderManger干嘛用的
initLoader方法源码:
public
<D> Loader<D> initLoader(
int
id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
if
(mCreatingLoader) {
throw
new
IllegalStateException(
"Called while creating a loader"
);
}
//判断SparseArrayCompat中loaderInfo中的 loader(loaderInfo.mloader)是否存在,如果不存在去createAndInstallLoader(createLoader,installLoader))
创建loader
LoaderInfo info = mLoaders.get(id);
if
(DEBUG) Log.v(TAG,
"initLoader in "
+
this
+
": args="
+ args);
if
(info ==
null
) {
// 如果loader不存在,则重新创建(createAndInstallLoader) 找到了第二个问题的答案!!!
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
if
(DEBUG) Log.v(TAG,
" Created new loader "
+ info);
}
else
{
//如果loader存在的话,那么只要info对象回调LoaderCallback替换为我们需要被回调的fragment或Activity
if
(DEBUG) Log.v(TAG,
" Re-using existing loader "
+ info);
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
}
//如果已经有数据,且启用onstart方法(mStarted标记是否调用fragment或者Activity的onStart方法(注意)
if
(info.mHaveData && mStarted) {
// If the loader has already generated its data, report it now.
info.callOnLoadFinished(info.mLoader, info.mData);
//回调callback的onLoadFinished方法;
}
return
(Loader<D>)info.mLoader;
}
createAndInstallLoader()方法中的createLoader方法调用了callback.onCreateLoader(id, args);//调用回调方法onCreateLoader时机(第二个问题的答案)
private
LoaderInfo createAndInstallLoader(
int
id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
try
{
mCreatingLoader =
true
;
LoaderInfo info = createLoader(id, args, callback);
//createloader中会去调用客户端的回调createLoader方法(第二个问题答案)
installLoader(info);(第三个问题答案...后续详谈)
return
info;
}
finally
{
mCreatingLoader =
false
;
}
}
private
LoaderInfo createLoader(
int
id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
LoaderInfo info =
new
LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
Loader<Object> loader = callback.onCreateLoader(id, args);
//调用回调方法onCreateLoader时机
info.mLoader = (Loader<Object>)loader;
//将loader存入SparseArrayCompat数组中
return
info;
}
}
综上所述,简单的理解是,LoaderManager用来负责管理与Activity或者Fragment联系起来的一个或多个Loaders对象。而真正起作用的,用来启动、停止、保持、重启、关闭它的Loaders的是LoaderManagerImpl中的LoaderInfo这个内部类。
getLoaderManger()方法解析
那么了解完这个loaderManager这个类大概干嘛用之后,我们就需要知道我们怎么获取这个LoaderManager类型的实例呢;以上方法中提到了一个getLoaderManger方法来获得一个LoadermangerImple实例;接下来谈谈这个方法。看源码:
/**
* Return the LoaderManager for this fragment, creating it if needed.
*/
public
LoaderManager getLoaderManager() {
if
(mLoaderManager !=
null
) {
//如果存在loadermanager对象则直接返回
return
mLoaderManager;
}
if
(mActivity ==
null
) {
throw
new
IllegalStateException(
"Fragment "
+
this
+
" not attached to Activity"
);
}
mCheckedForLoaderManager =
true
;
mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted,
true
);
//不存在则会调用Activity的getLoaderManger方法,其中有个参数mWho是用来唯一标示这个fragment的
return
mLoaderManager;
}
那么我们就来看看Activity中的这个方法;
LoaderManagerImpl getLoaderManager(String who,
boolean
started,
boolean
create) {
if
(mAllLoaderManagers ==
null
) {
mAllLoaderManagers =
new
SimpleArrayMap<String, LoaderManagerImpl>();
//创建一个存放Loadermanager的一个map;
}
LoaderManagerImpl lm = mAllLoaderManagers.get(who);
//如果在map中有存在与这个fragment对应的LoaderManagerImpl,那么直接从map中取就可以咯。(PS:每个fragment只持有一个loadermanager对象的)
if
(lm ==
null
) {
//如果之前不存在
if
(create) {
lm =
new
LoaderManagerImpl(who,
this
, started);
//答案来了,这个时候就会去创建一个LoaderManagerImpl对象。(Magic)
mAllLoaderManagers.put(who, lm);
//然后再把它放到map中,以便下次直接从map中取,而不需要去创建。(以who这个key存储)
}
}
else
{
lm.updateActivity(
this
);
}
return
lm;
}
那么这个mWho这个key这么重要,它是怎么来的呢;看源码;可以知道由setIndex方法来设置这个值的。
final
void
setIndex(
int
index, Fragment parent) {
mIndex = index;
//magic哈哈
if
(parent !=
null
) {
mWho = parent.mWho +
":"
+ mIndex;
}
else
{
mWho =
"android:fragment:"
+ mIndex;
}
}
那方法是啥时候被调用的呢,继续看源码:
public
Fragment instantiate(FragmentActivity activity, Fragment parent) {
if
(mInstance !=
null
) {
return
mInstance;
}
if
(mArguments !=
null
) {
mArguments.setClassLoader(activity.getClassLoader());
}
mInstance = Fragment.instantiate(activity, mClassName, mArguments);
if
(mSavedFragmentState !=
null
) {
mSavedFragmentState.setClassLoader(activity.getClassLoader());
mInstance.mSavedFragmentState = mSavedFragmentState;
}
mInstance.setIndex(mIndex, parent);
//答案终于找到了,就在这个,其中mIndex初始化设置为-1
mInstance.mFromLayout = mFromLayout;
mInstance.mRestored =
true
;
mInstance.mFragmentId = mFragmentId;
mInstance.mContainerId = mContainerId;
mInstance.mTag = mTag;
mInstance.mRetainInstance = mRetainInstance;
mInstance.mDetached = mDetached;
mInstance.mFragmentManager = activity.mFragments;
if
(FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
"Instantiated fragment "
+ mInstance);
return
mInstance;
}
三、何时去获取数据
前面我们看到的createAndInstallLoader中会调用installLoader方法,第一次initLoader时只是创建,没有启动loader去取数据,因为mStarted为false
(注意点:LoaderInfo里的mStart指的是该Loader有没有启动,LoaderManager里的mStart指的是Activity或Fragment有没有调用onStart()。所以,loader的执行一定是在Activity或Fragment的onStart()调用时。)该方法源码如下:
void
installLoader(LoaderInfo info) { 【LoaderManager中的方法】
mLoaders.put(info.mId, info);
if
(mStarted) {
//mStarted值由Fragment中生命周期的onstart方法决定的。(注意这个时候onstart方法还没有被调用,所以mStart为false,不执行info.start)
// The activity will start all existing loaders in it's onStart(),
// so only start them here if we're past that point of the activitiy's
// life cycle
info.start();
//最终调用的是star方法中的Loader的startLoading方法,中的onStartLoading方法(空实现),所以自定义Loader时,需要override该方法
}
Fragment的生命周期onStart方法,会调用loadermanager的doStart方法,
public
void
onStart() {
mCalled =
true
;
if
(!mLoadersStarted) {
mLoadersStarted =
true
;
if
(!mCheckedForLoaderManager) {
mCheckedForLoaderManager =
true
;
mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted,
false
);
}
if
(mLoaderManager !=
null
) {
mLoaderManager.doStart();
}
}
}
dostart方法,就会去调用LoaderInfo的start方法了。
void
doStart() {
if
(DEBUG) Log.v(TAG,
"Starting in "
+
this
);
if
(mStarted) {
RuntimeException e =
new
RuntimeException(
"here"
);
e.fillInStackTrace();
Log.w(TAG,
"Called doStart when already started: "
+
this
, e);
return
;
}
mStarted =
true
;
//注意,这个时候mStarted未设置为true了
// Call out to sub classes so they can start their loaders
// Let the existing loaders know that we want to be notified when a load is complete
for
(
int
i = mLoaders.size()-
1
; i >=
0
; i--) {
mLoaders.valueAt(i).start();
}
}
接着看start方法是怎么做的:
void
start() {
//重启动前,已经在执行了,只需要恢复状态即可
if
(mRetaining && mRetainingStarted) {
// Our owner is started, but we were being retained from a
// previous instance in the started state... so there is really
// nothing to do here, since the loaders are still started.
mStarted =
true
;
return
;
}
if
(mStarted) {
// If loader already started, don't restart.
return
;
}
mStarted =
true
;
if
(DEBUG) Log.v(TAG,
" Starting: "
+
this
);
if
(mLoader ==
null
&& mCallbacks !=
null
) {
mLoader = mCallbacks.onCreateLoader(mId, mArgs);
}
if
(mLoader !=
null
) {
if
(mLoader.getClass().isMemberClass()
&& !Modifier.isStatic(mLoader.getClass().getModifiers())) {
throw
new
IllegalArgumentException(
"Object returned from onCreateLoader must not be a non-static inner member class: "
+ mLoader);
}
if
(!mListenerRegistered) {
mLoader.registerListener(mId,
this
);
mListenerRegistered =
true
;
}
mLoader.startLoading();
}
}
四、客户端如何获取数据
public
final
void
startLoading() {
mStarted =
true
;
mReset =
false
;
mAbandoned =
false
;
onStartLoading();
}
@Override
protected
void
onStartLoading() {
if
(data !=
null
) {
deliverResult(data);
}
if
(takeContentChanged() || data ==
null
) {
forceLoad();
}
}
final
class
LoadTask
extends
ConcurrentTask<Void, Void, D>
implements
Runnable {
D result;
boolean
waiting;
private
CountDownLatch done =
new
CountDownLatch(
1
);
LoadTask() {
}
LoadTask(Executor executor) {
super
(executor);
}
/* Runs on a worker thread */
@Override
protected
D doInBackground(Void... params) {
if
(DEBUG) Log.v(TAG,
this
+
" >>> doInBackground"
);
result = ConcurrentTaskLoader.
this
.onLoadInBackground();
//调用loadInBackground()方法,这个就是我们需要操作的异步请求的任务!!!!所以客户端需要实现这个方法
if
(DEBUG) Log.v(TAG,
this
+
" <<< doInBackground"
);
return
result;
}
/* Runs on the UI thread */
@Override
protected
void
onPostExecute(D data) {
//获取到数据之后,就会调用dispatchOnLoadComplete()返回给客户端
if
(DEBUG) Log.v(TAG,
this
+
" onPostExecute"
);
try
{
ConcurrentTaskLoader.
this
.dispatchOnLoadComplete(
this
, data); !!!
}
finally
{
done.countDown();
}
}
@Override
protected
void
onCancelled() {
if
(DEBUG) Log.v(TAG,
this
+
" onCancelled"
);
try
{
ConcurrentTaskLoader.
this
.dispatchOnCancelled(
this
, result);
}
finally
{
done.countDown();
}
}
@Override
public
void
run() {
waiting =
false
;
ConcurrentTaskLoader.
this
.executePendingTask();
}
}
void
dispatchOnLoadComplete(LoadTask task, D data) {
if
(mTask != task) {
if
(DEBUG) Slog.v(TAG,
"Load complete of old task, trying to cancel"
);
dispatchOnCancelled(task, data);
}
else
{
if
(isAbandoned()) {
// This cursor has been abandoned; just cancel the new data.
onCanceled(data);
}
else
{
commitContentChanged();
mLastLoadCompleteTime = SystemClock.uptimeMillis();
mTask =
null
;
if
(DEBUG) Slog.v(TAG,
"Delivering result"
);
deliverResult(data);
//调用deleverResult返回数据
}
}
}
看看deliverResult方法:
public
void
deliverResult(D data) {
if
(mListener !=
null
) {
mListener.onLoadComplete(
this
, data);
//Loadermanager中的内部类LoaderInfo类实现OnLoadCompleteListener接口,回调onLoadComplete
}
//loader是在registerListener中注册的
}
public
class
RequestLoader<D>
extends
AbstractModelLoader<D> {
protected
final
Request.Origin origin;
private
final
Request<D> request;
public
RequestLoader(Context context, Request<D> request, Request.Origin origin) {
super
(context);
this
.request = request;
this
.origin = origin;
}
@Override
protected
void
onStartLoading() {
super
.onStartLoading();
}
@Override
protected
D doLoadData()
throws
IOException {
return
getRequest().execute(origin);
}
@Override
protected
Executor dispatchExecutor() {
switch
(origin) {
case
LOCAL:
return
ConcurrentTask.SERIAL_EXECUTOR;
case
NET:
return
ConcurrentTask.THREAD_POOL_EXECUTOR;
case
UNSPECIFIED:
default
:
return
getRequest().isLocalValid() ? ConcurrentTask.SERIAL_EXECUTOR : ConcurrentTask.THREAD_POOL_EXECUTOR;
}
}
public
Request<D> getRequest() {
return
request;
}
}
public
class
SearchBookResultRequest
extends
AbstractBookListRequest {
private
static
final
String URL_PATH =
"v2/book/search"
;
private
final
String keyword;
public
SearchBookResultRequest(String keyword) {
this
.keyword = keyword;
}
@Override
protected
List<DoubanBookInfo> local()
throws
IOException {
return
null
;
//暂不做本地化
}
@Override
public
List<DoubanBookInfo> execute(Origin origin)
throws
IOException {
return
super
.execute(Origin.NET);
}
@Override
public
boolean
isLocalValid() {
return
false
;
}
@Override
protected
String getBaseUrl() {
Uri.Builder builder = Uri.parse(ApiConfig.baseDoubanApiUrl).buildUpon();
builder.appendEncodedPath(URL_PATH)
.appendQueryParameter(
"q"
, keyword)
.toString();
return
builder.toString();
}
@Override
public
List<DoubanBookInfo> convert(JsonElement rootElement)
throws
IOException {
JsonObject rootObject = rootElement.getAsJsonObject();
int
total = rootObject.has(
"total"
) ? rootObject.get(
"total"
)
.getAsInt() :
0
;
setTotal(total);
return
super
.convert(rootElement);
}
@Override
protected
String getUrl() {
Uri.Builder builder = Uri.parse(getBaseUrl()).buildUpon();
if
(limit !=
0
) {
builder.appendQueryParameter(
"start"
, String.valueOf(offset));
builder.appendQueryParameter(
"count"
, String.valueOf(limit));
}
return
builder.toString();
}
}
- loadermanager使用解析之个人见解
- Android之LoaderManager的使用
- Android之Activity个人见解
- 性能优化之个人见解
- 面向对象之个人见解
- ssl/tls之个人见解
- 消元法求主元素之个人见解
- 设计模式之个人见解
- 快包之个人见解
- 有关XML解析文档的个人见解
- 我的注入方法之个人见解!
- 软件开发技术之个人见解
- 凤凰传奇情侣说之个人见解
- Webservice soap wsdl区别之个人见解
- 面向对象的特征之个人见解
- Webservice soap wsdl区别之个人见解
- Android和iOS系统个人之见解
- Android和iOS系统个人之见解
- JavaScript中,关于new的那些事
- json和ajax基础运用
- BNUOJ44586(栈模拟)
- UVA 10881 Piotr's Ants
- Ubuntu中compiz 安装与设置
- loadermanager使用解析之个人见解
- 电脑任务管理器里的进程
- Reverse Words in a String
- [2014-12-28]Java笔记_Junit 3.8
- SQLServer 中dm_os_ring_buffers(ring_buffer_connectivity)读取XML
- FragmentpagerAdapter中的getItem方法,在第二次进入承载viewPager的Fragment时不会被调用原因
- 手机发送验证码—.net代码
- servlet中获得某文件的绝对路径(摘)
- String与StringBuffer的区别