LoaderManager:

Loader出现的背景:

Activity是我们的前端页面展现,数据库是我们的数据持久化地址,那么正常的逻辑就是在展示页面的渲染页面的阶段进行数据库查询。拿到数据以后才展示页面。但是这个逻辑有一些缺点:

首先是查询数据的逻辑放在了UI生成的同个线程中,这个就意味着在查询数据的时候,UI页面生成的工作被阻塞住了。UI一旦被阻塞用户就会被感知出来了,因此就会出现各种无相应页面(Application Not Response),或者activity页面延迟的现象,这对用户体验来说是不可接受的。

其次是在渲染页面的时候需要固定需要进行一次数据查询,但是这个是很不节省资源的。假如一个Activity从一个停止状态回到前台,那么这个时候尽管数据并没有变化,但是也需要进行一次query操作。在浪费资源的同时也再次增加了页面渲染失败的风险。

还有就是当数据变化的时候如何通知页面进行修改呢?这个时候往往就又要创建一个monitor的角色,来当数据源变化的时候来让页面重新调用requery。

因此在Android的越来越提倡用户体验的今天,加载器和加载管理器(Loader,LoaderManager)就出现了。

在Android3.0中,Google引入了一种数据异步加载机制,该机制的核心,便是LoaderManager、Loader,顾名思义,LoaderManager是Loader的管理者,而Loader便是数据加载器,你可以根据自己的需要实现形形色色的数据加载器。

Google强烈建议在加载数据时,使用LoaderManager及其相关的机制。

每个Activity和Fragment中,都会有且只有一个LoaderManager,而LoaderManager中可以有多个Loader,也就是说,在一个Activity或者Fragment中,你可以同时异步加载N则不同的数据,具体加多少则,要看你那一亩三分地(Activity和Fragment就是你的地)有多大产。

Loaders机制在Android 3.0版本后引入。Loaders机制使一个Activity或者一个Fragment更加容易异步加载数据。Loaders有如下的特性:

Ø  它们适用于任何Activity和Fragment;

Ø  它们提供了异步加载数据的机制;

Ø  它们检测数据源,当数据源内容改变时它们能够传递新的结果;

Ø  当配置改变后需要重新创建时,它们会重新连接到最后一个loader的游标。这样,它们不需要重新查询它们的数据。

api

Class/Interface

描述
LoaderManager

一个与Activity和Fragment有关联的抽象类,用于管理一个或多个Loader实例。这有助于app管理长运行操作。

使用它的最显著的例子是CursorLoader。每个Activity或Fragment只能有一个LoaderManager。

而一个LoaderManager可以有多个loaders。

LoaderManager.LoaderCallbacks

提供给客户端的一个callback接口,用于和LoaderManager进行交互。

例如,你可以使用onCreateLoader() callback来创建一个新的loader。

AsyncTaskLoader 一个抽象Loader,提供一个AsyncTask进行工作。
CursorLoader

AsyncTaskLoader的子类,用于向ContentResover请求,返回一个Cursor。

这个类以标准的游标查询方式实现了Loader协议,建立了AsyncTaskLoader,使用一个后台线程来进行游标查询,不会阻塞app的UI。

因此,使用这个loader是从ContentProvider加载异步数据的最好的方式。

如何在Activity中使用Loader?

1.声明一个LoaderManager的实例。并在oncreate方法中实例化

2. 在oncreate方法中启动一个loader

(initLoader(0,null, this);参数分别表示:

Ø 一个标志loader的唯一ID。

Ø  提供给loader构造函数的参数,可选。

Ø  一个LoaderManager.LoaderCallbacks的实现。该回调参数不能为空)

3.实现回调:一个LoaderManager.LoaderCallbacks的实现。在这你创建新的loader,和管理已经存在的loaders。

onCreateLoader方法中完成一个CursorLoader的创建,需要从一个ContentProvider里加载数据。

 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// TODO Auto-generated method stub
/*CursorLoader loader=new CursorLoader(MainActivity.this);
Uri uri = Uri
.parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
loader.setUri(uri);*/
Uri uri = Uri.parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
CursorLoader loader=new CursorLoader(MainActivity.this, uri, null, null, null, null);
return loader;
}

onLoadFinished方法完成提取 放到适配器(自定义)中 更新UI

     public class myAdapter extends BaseAdapter{

         private Context context;
private List<String> list;
private myAdapter(){ }
private myAdapter(Context context){
this.context=context;
} private void setData(List<String> list){
this.list=list;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
} @Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return list.get(position);
} @Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
TextView view=null;
if(convertView==null){
view=new TextView(context);
}
else
view=(TextView)convertView;
view.setText(list.get(position));
return view;
} }
 @Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
// TODO Auto-generated method stub
List<String> list=new ArrayList<String>();
while(cursor.moveToNext()){
String name=cursor.getString(cursor.getColumnIndex("name"));
list.add(name);
}
myAdapter adapter=new myAdapter(MainActivity.this);
adapter.setData(list);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}

这样就可以异步加载数据库中的数据了,接下来需要借助上下文菜单ContextMenu给listview中的选项添加两个操作:添加和删除

首先在activity的oncreate方法中为listView注册一个上下文菜单:registerForContextMenu(listView);

第二步:重写onCreateContextMenu方法处理上下文菜单,需要定义一个菜单,也可编辑使用menu菜单下已有的菜单main.xml,加载菜单布局

第三步:重写onMenuItemSelected方法,其中需要自定义对话框(还要自定义一个布局)处理用户的选择

最终源码:

项目目录:

MainActivity.java

 package com.example.android_07loader_manager;

 import java.util.ArrayList;
import java.util.List; import android.app.Activity;
import android.app.Dialog;
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.AdapterContextMenuInfo; public class MainActivity extends Activity { private LoaderManager manager;
private ListView listView;
myAdapter adapter=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView=(ListView)this.findViewById(R.id.listView1);
adapter=new myAdapter(MainActivity.this);
manager=getLoaderManager();//LoaderManager已经集成在activity中,用来完成异步加载
manager.initLoader(, null, callback);//启动loader
registerForContextMenu(listView);
} private LoaderManager.LoaderCallbacks<Cursor> callback=new LoaderCallbacks<Cursor>() { @Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// TODO Auto-generated method stub
/*CursorLoader loader=new CursorLoader(MainActivity.this);
Uri uri = Uri
.parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
loader.setUri(uri);*/
Uri uri = Uri.parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
CursorLoader loader=new CursorLoader(MainActivity.this, uri, null, null, null, null);
return loader;
} //完成提取 放到适配器中 更新UI
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
// TODO Auto-generated method stub
List<String> list=new ArrayList<String>();
while(cursor.moveToNext()){
String name=cursor.getString(cursor.getColumnIndex("name"));
list.add(name);
} adapter.setData(list);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
} @Override
public void onLoaderReset(Loader<Cursor> loader) {
// TODO Auto-generated method stub }
};
public class myAdapter extends BaseAdapter{ private Context context;
private List<String> list;
private myAdapter(){ }
private myAdapter(Context context){
this.context=context;
} private void setData(List<String> list){
this.list=list;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
} @Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return list.get(position);
} @Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
TextView view=null;
if(convertView==null){
view=new TextView(context);
}
else
view=(TextView)convertView;
view.setText(list.get(position));
return view;
} }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
} @Override
public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {
// TODO Auto-generated method stub
super.onCreateContextMenu(menu, v, menuInfo);
getMenuInflater().inflate(R.menu.main, menu);
}
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
// TODO Auto-generated method stub
switch (item.getItemId()) {
case R.id.add:
//Toast.makeText(MainActivity.this, "add", 1).show();
final Dialog dialog=createAddDialog(MainActivity.this);
Button button1=(Button)dialog.findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
final EditText editText=(EditText)dialog.findViewById(R.id.editText1);
String name=editText.toString();
ContentResolver resolver=getContentResolver();
Uri uri = Uri.parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
ContentValues values = new ContentValues();
values.put("name", name);
Uri res_uri=resolver.insert(uri, values);
if(res_uri!=null)//添加成功的时候 刷新数据
{
manager.restartLoader(, null, callback);//重新启动查询器
dialog.dismiss();
}
}
});
dialog.show();
break; case R.id.del:
//AdapterContextMenuInfo对象包含了对话框选项的所有信息 AdapterContextMenuInfo into=item.getMenuInfo();
AdapterContextMenuInfo info=(AdapterContextMenuInfo) item.getMenuInfo();
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
int position=info.position;
String name=adapter.getItem(position).toString();
int count = resolver.delete(uri, "name=?", new String[]{name});
//Toast.makeText(MainActivity.this, "del", 1).show();
if(count>){
manager.restartLoader(, null, callback);//重新启动查询器
} break;
}
return super.onMenuItemSelected(featureId, item);
} public Dialog createAddDialog(Context context){
Dialog dialog=new Dialog(context);
dialog.setContentView(R.layout.add);
return dialog; }
/* @Override
public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
} return super.onOptionsItemSelected(item);
}*/
}

MyTest.java

 package com.example.android_07loader_manager;

 import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.test.AndroidTestCase; public class MyTest extends AndroidTestCase { public MyTest() {
// TODO Auto-generated constructor stub
} public void add() {
ContentResolver contentResolver = getContext().getContentResolver();
ContentValues values = new ContentValues();
values.put("name", "mlj");
Uri url = Uri
.parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
contentResolver.insert(url, values);
} public void delete() {
ContentResolver contentResolver = getContext().getContentResolver(); Uri url = Uri
.parse("content://com.example.android_07loader_manager.StudentContentProvider/student/1");
int count = contentResolver.delete(url, null, null);
System.out.println("---count-->>" + count);
} public void query() {
ContentResolver contentResolver = getContext().getContentResolver();
Uri url = Uri
.parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
Uri uri = ContentUris.withAppendedId(url, ); Cursor cursor = contentResolver.query(uri, null, null, null, null);
while (cursor.moveToNext()) {
System.out.println("--->>"
+ cursor.getString(cursor.getColumnIndex("name")));
}
}
}

StudentContentProvider.java

 package com.example.android_07loader_manager;

 import com.example.android_07loader_manager.dbhelp.DBHelper;

 import android.R.integer;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri; public class StudentContentProvider extends ContentProvider { private final static UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
private final static int STUDENT = ;
private final static int STUDENTS = ;
private DBHelper helper;
static {
URI_MATCHER.addURI(
"com.example.android_07loader_manager.StudentContentProvider","student", STUDENTS);
URI_MATCHER.addURI(
"com.example.android_07loader_manager.StudentContentProvider","student/#", STUDENT);
} public StudentContentProvider() {
// TODO Auto-generated constructor stub
} @Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
int count = ;// 影响数据库的行数
int flag = URI_MATCHER.match(uri);
SQLiteDatabase database = helper.getWritableDatabase(); switch (flag) {
case STUDENT:
long stuid = ContentUris.parseId(uri);
String where_value = " stuid = " + stuid;
if (selection != null && !selection.equals("")) {
where_value += selection;
}
count = database.delete("student", where_value, selectionArgs);
break; case STUDENTS:
count = database.delete("student", selection, selectionArgs);
break;
}
return count;
} @Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
int flag = URI_MATCHER.match(uri);
switch (flag) {
case STUDENT:
return "vnd.android.cursor.item/student";
case STUDENTS:
return "vnd.android.cursor.dir/studens";
}
return null;
} @Override
public Uri insert(Uri uri, ContentValues contentValues) {
// TODO Auto-generated method stub
int flag = URI_MATCHER.match(uri);
SQLiteDatabase database = helper.getWritableDatabase();
Uri uri2 = null;
switch (flag) {
case STUDENTS:
long id = database.insert("student", null, contentValues);
uri2 = ContentUris.withAppendedId(uri, id);
break;
}
System.out.println("-->>" + uri2.toString());
return uri2;
} @Override
public boolean onCreate() {
// TODO Auto-generated method stub
helper = new DBHelper(getContext());
return false;
} @Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
Cursor cursor = null;
int flag = URI_MATCHER.match(uri);
SQLiteDatabase database = helper.getReadableDatabase();
switch (flag) {
case STUDENT:
long stuid = ContentUris.parseId(uri);
String where_value = " stuid = " + stuid;
if (selection != null && !"".equals(selection)) {
where_value += selection;
}
cursor = database.query("student", projection, where_value,
selectionArgs, null, null, null); break; case STUDENTS:
cursor = database.query("student", projection, selection,
selectionArgs, null, null, null);
break;
}
return cursor;
} @Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
int count = ;
int flag = URI_MATCHER.match(uri);
SQLiteDatabase database = helper.getWritableDatabase();
switch (flag) {
case STUDENT:
long stuid = ContentUris.parseId(uri);
String where_value = " stuid = " + stuid;
if (selection != null && !selection.equals("")) {
where_value += selection;
}
count = database.update("student", values, where_value,
selectionArgs);
break; case STUDENTS:
count = database
.update("student", values, selection, selectionArgs);
break;
}
return count;
} }

DBHelper.java

 package com.example.android_07loader_manager.dbhelp;

 import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper; public class DBHelper extends SQLiteOpenHelper{ private static String name = "mydb.db";
private static int version = ; public DBHelper(Context context) {
super(context, name, null, version);
// TODO Auto-generated constructor stub
} public DBHelper(Context context, String name, CursorFactory factory,
int version, DatabaseErrorHandler errorHandler) {
super(context, name, factory, version, errorHandler);
// TODO Auto-generated constructor stub
} @Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
String sql = "create table student (stuid integer primary key autoincrement,name varchar(64))";
db.execSQL(sql);
} @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub } }

Google倒是提供了一个标准的Loader,即CursorLoader,它是Loader的标准实现,如果你的数据能够用Cursor表示,比如来自SQLiteDatabase的数据就是标准的Cursor,那么这个类对你而言就够用了,具体如何使用CursorLoader,请参看如下例子:

首先添加单元测试授权和包

第二步建内容提供者类

1.DBHelper extends SQLiteOpenHelper

2.class StudentContentProvider extends ContentProvider

2.1 初始化操作匹配器,标识位,DBHelper声明及实例化,匹配规则,getType ,注册内容提供者

  匹配器:private final static UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);

  标识位private final static int STUDENT = 1; private final static int STUDENTS = 2;

  初始化sqliteDatabase示例并在oncreate方法中初始化;

  private DBHelper helper;

 @Override
public boolean onCreate() {
// TODO Auto-generated method stub
helper = new DBHelper(getContext());
return false;
}

  匹配规则(静态代码块)

 static {
URI_MATCHER.addURI(
"com.example.android_07loader_manager.StudentContentProvider","student", STUDENTS);
URI_MATCHER.addURI(
"com.example.android_07loader_manager.StudentContentProvider","student/#", STUDENT);
}

重写getType方法

 @Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
int flag = URI_MATCHER.match(uri);
switch (flag) {
case STUDENT:
return "vnd.android.cursor.item/student";
case STUDENTS:
return "vnd.android.cursor.dir/studens";
}
return null;
}

.在清单文件中注册内容提供者: <provider android:name=".StudentContentProvider" android:authorities="com.example.android_07loader_manager.StudentContentProvider"></provider>

2.2 完成增删改查

2.3建测试类 

第三步:

Android 之异步加载LoaderManager的更多相关文章

  1. [Android] Android 用于异步加载 ContentProvider 中的内容的机制 -- Loader 机制 (LoaderManager + CursorLoader + LoaderManager.LoaderCallbacks)

    Android 用于异步加载 ContentProvider 中的内容的机制 -- Loader 机制 (LoaderManager + CursorLoader + LoaderManager.Lo ...

  2. Android 图片异步加载的体会,SoftReference已经不再适用

      在网络上搜索Android图片异步加载的相关文章,目前大部分提到的解决方案,都是采用Map<String, SoftReference<Drawable>>  这样软引用的 ...

  3. Android图片异步加载之Android-Universal-Image-Loader

    将近一个月没有更新博客了,由于这段时间以来准备毕业论文等各种事务缠身,一直没有时间和精力沉下来继续学习和整理一些东西.最近刚刚恢复到正轨,正好这两天看了下Android上关于图片异步加载的开源项目,就 ...

  4. Android图片异步加载之Android-Universal-Image-Loader(转)

    今天要介绍的是Github上一个使用非常广泛的图片异步加载库Android-Universal-Image-Loader,该项目的功能十分强大,可以说是我见过的目前功能最全.性能最优的图片异步加载解决 ...

  5. android 网络异步加载数据进度条

    ProgressDialog progressDialog = null; public static final int MESSAGETYPE = 0; private void execute( ...

  6. Android开发--异步加载

    因为移动端软件开发思维模式或者说是开发的架构其实是不分平台和编程语言的,就拿安卓和IOS来说,他们都是移动前端app开发展示数据和用户交互数据的数据终端,移动架构的几个大模块:UI界面展示.本地数据可 ...

  7. Android图片异步加载框架Android-Universal-Image-Loader

    版权声明:本文为博主原创文章,未经博主允许不得转载. Android-Universal-Image-Loader是一个图片异步加载,缓存和显示的框架.这个框架已经被很多开发者所使用,是最常用的几个 ...

  8. Android 多线程 异步加载

    Android 应用中需要显示网络图片时,图片的加载过程较为耗时,因此加载过程使用线程池进行管理, 同时使用本地缓存保存图片(当来回滚动ListView时,调用缓存的图片),这样加载和显示图片较为友好 ...

  9. android ListView异步加载图片(双缓存)

    首先声明,参考博客地址:http://www.iteye.com/topic/685986 对于ListView,相信很多人都很熟悉,因为确实太常见了,所以,做的用户体验更好,就成了我们的追求... ...

随机推荐

  1. SQL练习题完整(做完你就是高手)

    SQL 练习题答案 一.补充作业一.   设有三个关系:                S(SNO, SNAME, AGE, SEX,Sdept)                SC(SNO, CNO ...

  2. Android 开源可缩放平移的绘画板

    ScaleSketchPadDemo 此项目包含两个模块 app1 为普通绘画板 app2 为可所发的绘画板 方便各位Android 开发者理解和使用 用法: 进入项目根目录:https://gith ...

  3. 【Tomcat】Invalid character found in the request target

    Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC ...

  4. Java 垃圾回收机制(早期版本)

    Java 垃圾回收机制在我们普通理解来看,应该视为一种低优先级的后台进程来实现的,其实早期版本的Java虚拟机并非以这种方式实现的. 先从一种很简单的垃圾回收方式开始. 引用计数 引用计数是一种简单但 ...

  5. Myeclipse详细使用教程

    Myeclipse详细使用教程.. /*+Shift+Enter(生成多行注释) /**+Shift+Enter(生成文档注释)-----------------问题:在编辑jsp的时候,如果光标移动 ...

  6. Vim 命令图解-Gvim使用笔记-2017-5-9

    Vim 命令图解-Gvim使用笔记... 图片要是看的不太清楚推荐使用:鼠标右键在新标签中打开或是全屏看图 Vim中常用的一些速查命令: Vim发展历史:1976年Bill Joy开发了vi1988 ...

  7. 用户管理-linux基础

    用户管理 useradd -u -g -G(附加组) ,-c -d -s -r(创建系统用户)   -M (不创建家目录)    思考:删除user1用户家目录,如何还原.        可以#cp ...

  8. node.js后台快速搭建在阿里云(二)(pm2和nginx篇)

    前期准备 阿里云服务器 node.js pm2 express nginx linux(推荐教程:鸟哥的私房菜) 简介 嗯……我只是个前端而已 在第一部分说完了express篇. 后面继续项目的部署, ...

  9. poj 3683 2-sat建图+拓扑排序输出结果

    发现建图的方法各有不同,前面一题连边和这一题连边建图的点就不同,感觉这题的建图方案更好. 题意:给出每个婚礼的2个主持时间,每个婚礼的可能能会冲突,输出方案. 思路:n个婚礼,2*n个点,每组点是对称 ...

  10. (四)Lua脚本语言入门

    这篇文章就当成铺垫型的文章,写着写着发现有好多想写的,,关于C#与Java,当然作为铺垫肯定与Lua的下部分介绍有关..... 对于"泛型",先看C#中"泛型" ...