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. RoutePrefix和Route 路由前缀

    使用应用到某个控制器中所有操作的路由前缀来批注该控制器. web api /// <summary> ////// </summary> [RoutePrefix(" ...

  2. Git实用记录

    一.git命令名词解释 1.添加/跟踪/暂存:添加到本地索引 git add 文件名 2.提交:提交到本地仓库 git commit -m '注释' 3.推送:将提交到本地仓库的所有更新提交到服务器 ...

  3. java 面试,java 后端面试,数据库方面对初级和高级程序员的要求

    本内容摘自 java web轻量级开发面试教程 对于合格的程序员,需要有基本的数据库操作技能,具体体现在以下三个方面. l  第一,针对一类数据库(比如MySQL.Oracle.SQL Server等 ...

  4. HTTP协议初级入门

    Http = HyperText Transfer Protocol即超文本传输协议 HTTPS = Hyper Text Transfer Protocol over Secure Socket L ...

  5. Java入门(3)—— 数组

    数组 概述:数组就是一个容器可以存放固定个数的单一数据类型 特点: 容器 长度是固定的 数组里面的元素的数据类型要一致 定义数组: 1.先声明,后初始化 声明: 数据类型[] 数组名; // 标识符命 ...

  6. amoeba

    Amoeba 原理:amoeba相当于业务员,处理client的读写请求,并将读写请求分开处理.amoeba和master以及slave都有联系,如果是读的请求,amoeba就从slave读取信息反馈 ...

  7. HTML特殊符号、常用字符实体

    HTML特殊符号对照表.常用的字符实体 最常用的字符实体 显示结果 描述 实体名称 实体编号   空格     <</td> 小于号 < < > 大于号 > ...

  8. python--对配置文件进行搜索,增加新的内容

    要求: 文件haproxy1.查 输入:www.oldboy.org 获取当前backend下的所有记录2.新建 输入: arg = {'backend': 'www.oldboy.org','rec ...

  9. 【Alpha阶段】第五次scrum meeting

    一.会议照片 二.会议内容 姓名 学号 负责模块 昨日任务完成度 今日任务 杨爱清 099 界面设计和交互功能 完成 去酷狗选择合适的轻音乐 杨立鑫 100 数据库搭建和其他 完成 继续对数据库进行编 ...

  10. 团队作业4——第一次项目冲刺(Alpha版本)7th day

    一.Daily Scrum Meeting照片 二.燃尽图 三.项目进展 在计时模式下能够记录用户的用户名和成绩,没有弄登录功能, 将程序定义为单机的 未完成的卡片为登录功能和使用QQ登录. 四.困难 ...