[Android] Android 用于异步加载 ContentProvider 中的内容的机制 -- Loader 机制 (LoaderManager + CursorLoader + LoaderManager.LoaderCallbacks)
Android 用于异步加载 ContentProvider 中的内容的机制 -- Loader 机制 (LoaderManager + CursorLoader + LoaderManager.LoaderCallbacks)
关于Android Loader 的文章,百度一搜搜出了一大把。笔者看了好多篇,都吧唧吧唧讲了很多 异步 的好处。但笔者看完后,还是一头雾水,实现异步加载的方式
不是已经有了 Thread + Handle 或者 AsyncTask 等很多机制了吗?(可参考: https://www.cnblogs.com/wukong1688/p/10657659.html )
为啥又要搞出一个新的东东出来???
后来终于查阅了很多资料,终于找到 Loader 机制 相比其他 异步加载更适合使用的场景:
Loader 机制一般用于数据加载,特别是用于加载 ContentProvider 中的内容,比起 Handler + Thread 或者 AsyncTask 的实现方式,Loader 机制能让代码更加的简洁易懂,而且是 Android 3.0 之后最推荐的加载方式。
Loader 机制的 使用场景 有:
展现某个 Android 手机有多少应用程序
加载手机中的图片和视频资源
访问用户联系人
好了,既然明白了 Loader 机制使用的场景,
下面用一个加载手机中的图片文件夹的例子,看看在实际开发中如何运用 Loader 机制进行高效加载。
我们接下来就来看如何实现吧!
一、实现自己的CursorLoader 加载器
加载器是我们加载数据的工具,通过将对应的 URI 以及其他的查询条件传递给加载器,便可让加载器在后台高效地加载数据,等数据加载完成了便会返回一个 Cursor.
AlbumLoader.java
package com.jack.testmd.loader; import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v4.content.CursorLoader; public class AlbumLoader extends CursorLoader {
public static final String COLUMN_COUNT = "count"; /**
* content://media/external/file
*/
private static final Uri QUERY_URI = MediaStore.Files.getContentUri("external"); private static final String[] COLUMNS = {
MediaStore.Files.FileColumns._ID,
"bucket_id",
"bucket_display_name",
MediaStore.MediaColumns.DATA,
COLUMN_COUNT}; private static final String[] PROJECTION = {
MediaStore.Files.FileColumns._ID,
"bucket_id",
"bucket_display_name",
MediaStore.MediaColumns.DATA,
"COUNT(*) AS " + COLUMN_COUNT}; /**
* (media_type=? OR media_type =?) AND _size>0) GROUP BY (bucket_id
*/
private static final String SELECTION =
"(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ " OR "
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)"
+ " AND " + MediaStore.MediaColumns.SIZE + ">0"
+ ") GROUP BY (bucket_id"; private static final String[] SELECTION_ARGS = {
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE),
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO)
}; private static final String BUCKET_ORDER_BY = "datetaken DESC"; private AlbumLoader(Context context, String selection, String[] selectionArgs) {
super(context, QUERY_URI, PROJECTION, SELECTION, SELECTION_ARGS, BUCKET_ORDER_BY);
} public static CursorLoader newInstance(Context context) {
String selection = SELECTION;
String[] selectionArgs = SELECTION_ARGS;
return new AlbumLoader(context, selection, selectionArgs);
} @Override
public Cursor loadInBackground() {
return super.loadInBackground();
} }
二、实现 LoaderCallbacks 进行客户端的交互
为了降低代码的耦合度,继承 LoaderManager.Loadercallbacks 实现 AlbumLoader 的管理类,将 Loader 的各种状态进行管理。
通过外部传入 Context,采用弱引用的方式防止内存泄露,获取 LoaderManager,并在 AlbumCollection 内部定义了相应的接口,将加载完成后返回的 Cursor 回调出去,让外部的 Activity 或 Fragment 进行相应的处理。
AlbumCollection.java
package com.jack.testmd.loader; import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader; import java.lang.ref.WeakReference; public class AlbumCollection implements LoaderManager.LoaderCallbacks<Cursor> {
private static final int LOADER_ID = ;
private WeakReference<Context> mContext;
private LoaderManager mLoaderManager;
private AlbumCallbacks mCallbacks; @Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Context context = mContext.get();
if(context == null){
return null;
} return AlbumLoader.newInstance(context);
} @Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Context context = mContext.get();
if(context == null){
return;
} mCallbacks.onAlbumLoad(data);
} @Override
public void onLoaderReset(Loader<Cursor> loader) {
Context context = mContext.get();
if(context == null){
return;
}
mCallbacks.onAlbumReset();
} public void onCreate(FragmentActivity activity, AlbumCallbacks callbacks){
mContext = new WeakReference<Context>(activity);
mLoaderManager = activity.getSupportLoaderManager();
mCallbacks = callbacks;
} public void loadAlbums(){
mLoaderManager.initLoader(LOADER_ID, null, this);
} public interface AlbumCallbacks{ void onAlbumLoad(Cursor cursor); void onAlbumReset();
} }
三、填充数据到 RecyclerList 中
AlbumAdapter.java
package com.jack.testmd.loader; import android.database.Cursor;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView; import com.bumptech.glide.Glide;
import com.jack.testmd.R; public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.AlbumViewHolder> {
private Cursor mCursor; public AlbumAdapter(Cursor cursor) {
this.mCursor = cursor;
} @Override
public AlbumAdapter.AlbumViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_rv_album,null);
return new AlbumViewHolder(view);
} @Override
public void onBindViewHolder(AlbumAdapter.AlbumViewHolder holder, int position) {
if(mCursor != null && mCursor.moveToNext()){
String albumCoverPath = mCursor.getString(mCursor.getColumnIndex("_data"));
String albumName = mCursor.getString(mCursor.getColumnIndex("bucket_display_name"));
String amount = mCursor.getString(mCursor.getColumnIndex("count")); holder.tvAlbumName.setText(albumName);
holder.tvAlbumAmount.setText(amount);
Glide.with(holder.ivAlbum.getContext())
.load(albumCoverPath)
.centerCrop()
.into(holder.ivAlbum);
}
} @Override
public int getItemCount() {
return mCursor.getCount();
} public static class AlbumViewHolder extends RecyclerView.ViewHolder { private ImageView ivAlbum;
private TextView tvAlbumName;
private TextView tvAlbumAmount; public AlbumViewHolder(View itemView) {
super(itemView);
ivAlbum = (ImageView) itemView.findViewById(R.id.album_iv_album);
tvAlbumName = (TextView) itemView.findViewById(R.id.album_tv_album_name);
tvAlbumAmount = (TextView) itemView.findViewById(R.id.album_tv_amount);
}
}
}
四、主界面中的逻辑
看到这代码是不是觉得特别简洁,让 MainActivity 中继承了 AlbumCollection 中的 AlbumCallback 接口,接着 onCreate() 中实例化了 AlbumCollection,然后让 AlbumCollection 开始加载数据。
等数据加载完成后,便将包含数据的 Cursor 回调在 onAlbumLoad() 方法中,我们便可以进行 UI 的更新。
可以看到采用 Loader 机制,可以让我们的 Activity 或 Fragment 中的代码变得相当的简洁、清晰,而且代码耦合程度也相当低。
package com.jack.testmd; import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button; import com.jack.testmd.loader.AlbumAdapter;
import com.jack.testmd.loader.AlbumCollection; public class TestLoaderActivity extends AppCompatActivity implements AlbumCollection.AlbumCallbacks {
private AlbumCollection mCollection;
private AlbumAdapter mAdapter;
private RecyclerView mRvAlbum; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_loader); initLoad();
} private void initLoad() {
if (ContextCompat.checkSelfPermission(TestLoaderActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
mCollection = new AlbumCollection();
mCollection.onCreate(this, this);
mCollection.loadAlbums();
} else {
ActivityCompat.requestPermissions(TestLoaderActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
}
} @Override
public void onAlbumLoad(Cursor cursor) {
mRvAlbum = (RecyclerView) findViewById(R.id.main_rv_album);
mRvAlbum.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new AlbumAdapter(cursor);
mRvAlbum.setAdapter(mAdapter); } @Override
public void onAlbumReset() { } @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case :
if (grantResults.length > && grantResults[] == PackageManager.PERMISSION_GRANTED) {
mCollection = new AlbumCollection();
mCollection.onCreate(this, this);
mCollection.loadAlbums();
}
break;
default:
break;
}
} }
附:
笔者在测试的时候,还以为自动在相册中添加图片文件后,需要我们手动刷新。后来再次测试,发现确实是可以自动刷新数据,而不是我们手动去维护刷新!
本博客地址: wukong1688
本文原文地址:https://www.cnblogs.com/wukong1688/p/10702858.html
转载请著名出处!谢谢~~
[Android] Android 用于异步加载 ContentProvider 中的内容的机制 -- Loader 机制 (LoaderManager + CursorLoader + LoaderManager.LoaderCallbacks)的更多相关文章
- wemall app商城源码Android之ListView异步加载网络图片(优化缓存机制)
wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享wemall app商城源码Android之L ...
- Android必学-异步加载+Android自定义View源码【申明:来源于网络】
Android必学-异步加载+Android自定义View源码[申明:来源于网络] 异步加载地址:http://download.csdn.net/detail/u013792369/8867609 ...
- Android之ListView异步加载图片且仅显示可见子项中的图片
折腾了好多天,遇到 N 多让人崩溃无语的问题,不过今天终于有些收获了,这是实验的第一版,有些混乱,下一步进行改造细分,先把代码记录在这儿吧. 网上查了很多资料,发现都千篇一律,抄来抄去,很多细节和完整 ...
- android 之图片异步加载
一.概述 本文来自"慕课网" 的学习,只是对代码做一下分析 图片异步加载有2种方式: (多线程/线程池) 或者 用其实AsyncTask , 其实AsyncTask底层也是用的多 ...
- Android ListView 图片异步加载和图片内存缓存
开发Android应用经常需要处理图片的加载问题.因为图片一般都是存放在服务器端,需要联网去加载,而这又是一个比较耗时的过程,所以Android中都是通过开启一个异步线程去加载.为了增加用户体验,给用 ...
- Android的ListView异步加载图片时,错位、重复、闪烁问题的分析及解决方法
Android ListView异步加载图片错位.重复.闪烁分析以及解决方案,具体问题分析以及解决方案请看下文. 我们在使用ListView异步加载图片的时候,在快速滑动或者网络不好的情况下,会出现图 ...
- [android] 数据的异步加载和图片保存
把从网络获取的图片数据保存在SD卡上, 先把权限都加上 网络权限 android.permission.INTERNET SD卡读写权限 android.permission.MOUNT_UNMOUN ...
- Android 实现ListView异步加载图片
ListView异步加载图片是非常实用的方法,凡是是要通过网络获取图片资源一般使用这种方法比较好,用户体验好,下面就说实现方法,先贴上主方法的代码: package cn.wangmeng.test; ...
- axios 异步加载 导致 {{}} 中变量为 undefined 报错 的 解决方案
情景:axios 异步加载数据,当返回数据为一个 数组 时,双花括号中 这样写 会报错 {{informationDetail[0].img}} 解决方案一:通过 v-if 进行判断 解决方案二:单独 ...
随机推荐
- NOIP2018联赛总结
NOIP2018联赛总结 Day 0 打了几个模板,看了一下别人的博客,背了一下vimrc Day 1 到了考场,先把vimrc配好 打开题目一先把三道题瞄了一眼,\(T1\)似乎是NOIP原题,\( ...
- 扩展资源服务器解决oauth2 性能瓶颈
OAuth用户携带token 请求资源服务器资源服务器拦截器 携带token 去认证服务器 调用tokenstore 对token 合法性校验资源服务器拿到token,默认只会含有用户名信息通过用户名 ...
- zabbix3.4.6之监控Oracle
新zabbix搭建配置完后,公司所有的主机是通过自动注册完成了添加,网络设备及其Templates是从旧zabbix中Export出模板,然后Import入新zabbix系统中.一些应用的监控就需要自 ...
- 【BZOJ5314】[JSOI2018]潜入行动(动态规划)
[BZOJ5314][JSOI2018]潜入行动(动态规划) 题面 BZOJ 洛谷 题解 不难想到一个沙雕\(dp\),设\(f[i][j][0/1][0/1]\)表示当前点\(i\),子树中一共放了 ...
- 「SCOI2014」方伯伯的商场之旅 解题报告
「SCOI2014」方伯伯的商场之旅 我一开始的想法会被两个相同的集合位置去重给搞死,不过应该还是可以写的,讨论起来老麻烦. 可以先钦定在\(1\)号点集合,然后往后调整一部分. 具体一点,通过前缀和 ...
- 利用httpd配置虚拟目录创建下载站点
应用环境:通常放置一些文件来提供下载. 配置环境:centos7 //已经关闭Selinux和Firewall 需求假设:在网页输入主机IP并进入,会显示主机目录/home/share/的文件以提供下 ...
- ava EE 7 - Injection into Runnable/Callable object ejb entityManager Concurrency ManagedExecutorService 异步调用如何获取context
或者直接把 MyTask类内嵌如MyBean中,这样可以在myBean中inject 数据库连接,在内嵌类内访问. java ee 引入了并发执行.因为是在服务器执行并发,所以要用java ee包里面 ...
- 初入webform的杂七杂八
客户端---IIS(Internet Information Services的缩写,意为互联网信息服务管理器)---.NET framework---数据库 1.Repeater控件:对应的集合有5 ...
- Flask flask_script扩展库
flask_script 1.安装:进入到虚拟环境中,pip install flask_script 2.flask_script 作用:可以通过命令行的形式来操作Flask,例如通过命令跑一个开发 ...
- TODO java疑问
TODOjava 疑惑-关于方法调用的参数是基本类型和引用类型的差别 class DataWrap { int a; int b; } public class ReferenceTransferTe ...