LoaderManager管理Loader实现异步动态加载数据,并使用RecyclerView 显示

时间:2022-08-27 15:54:30

关键知识点
1 RecyclerView的显示
2 Loader异步加载数据

一、Loader异步加载数据

1.Loader特性:

  1. 对于每个Activity或者Fragment都可用
  2. 提供异步加载数据
  3. 监视数据资源,当内容改变时重新更新
  4. 当配置改变时,自动重新连接最新的cursor,故不需要重新查询数据

2.Loader相关类接口

  1. LoaderManager
    对于每个activity或者fragment只存在一个与之相关的LoaderManager对象,该LoaderManager对象可以存在多个可供管理loader对象。
  2. LoaderManager.LoaderCallbacks
    LoaderManager.LoaderCallbacks是个回掉接口,用于客户端与LoaderManager的交互,loader对象就是在其接口的onCreateLoader()方法中得到,在使用时需要覆盖其方法。
  3. CursorLoader
    CursorLoader是AsyncTaskLoader的子类,通过它可以查询ContentResolver并返回一个Cursor对象,并使用该cursor对象在后台线程执行查询操作,以不至于会阻塞主线程,从一个内容提供者去异步加载数据是CursorLoader对象最大用处。

3.简单使用Loaders

(1).得到LoaderManager对象

//得到LoaderManager对象 
LoaderManager manager
= content.getLoaderManager();

(2).初始化loader

在activity的onCreate()方法区或者在fragment的onActivityCreated()方法区中,需要初始化一个Loader对象(可能已经存在或者新创建),

getLoaderManager().initLoader(0, null, this);调用initLoader()方法是确保loader对象已经初始化且可用,然而存在下面2种情况

1).ID存在

如果指定ID的loader已经存在,将重新使用最新的loader对象

2).ID不存在

如果指定的ID不存在,通过initLoader()方法,将会触发LoaderManager.LoaderCallbacks的onCreateLoader()方法并返回一个新的loader
虽然通过initLoader()可以得到loader对象,但是我们不需要捕获该对象,但LoaderManager对象可以自动管理loader生命周期,因此不需要直接与loader对象直接交互

(3).实现LoaderCallbacks

以典型的CursorLoader为例,app允许数据在onStart() 和 onStop()函数中传递,一旦的当用户重新进入app,不必等待数据重新加载,LoaderManager.LoaderCallbacks包含下面3个重要函数

1).onCreateLoader()

Instantiate and return a new Loader for the given ID.
当loadermanager调用initLoader()时, 首先检查指定的id是否存在,如果不存在才会触发该方法,通过该方法才能创建一个loader。返回创建的CursorLoader对象,其中可以在创建对象时,指定查询的条件,并携带一个Cursor对象。
CursorLoader接收 uri projection selection selectionArgs sortOrder 等参数信息
// 返回一个new CursorLoader对象 
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri uri = Uri.parse("content://com.baidu.provider/music");
return new CursorLoader(MainActivity.this, uri, null, null, null,
null);
}

2).onLoadFinished()

Called when a previously created loader has finished its load.
可以完成对Ui控件的更新,比如更新一个RecyclerView列表。一旦应用不在使用,将自动释放loader的数据,不需要使用close();

// 完成对UI主界面的更新 
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if(cursor == null){
Toast.makeText(MainActivity.this, "失败", 1).show();
return;
}
//ui主界面更新相关操作
}

3).onLoaderReset()

Called when a previously created loader is being reset, thus making its data unavailable.

(4).重启loader

通过指定相同的loader ID,使用loadermanager的restartLoader()方法,消除就数据,加载新数据,达到动态更新的目的!可参考,RecyclerView数据动态更新。

// 当检测到数据已经放手改变时,重启指定ID的loader  
if(u != null){
MainActivity.this.getLoaderManager().restartLoader(0, null, myLoader);
Toast.makeText(MainActivity.this, "插入成功", 1).show();
}else{
Toast.makeText(MainActivity.this, "插入失败", 1).show();
}

4.loader向下兼容

(1)当前Activity继承FragmentActivity

(2).得到Loadermanager对象

public void load(View view) {  
LoaderManager manager = this.getSupportLoaderManager();//注意,不同
manager.initLoader(0, null, myLoader);
}

(3).其余操作均不变,只不过导包时需要导入Android.support.v4下的包

5.应用

(1).需求

一个RecyclerView和一个按钮,点击按钮后使用内容提供者,往数据库中添加数据,同时达到RecyclerView动态更新的目的,但是RecyclerView回到第一个item,因为在cursor在restart的后,RecyclerView又重新设置了adapter,RecyclerView初始化后设置适配器可以避免该问题,可参考RecyclerView数据动态更新一文。

(2).代码实现


public class LoadManagerActivity2 extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>{

RecyclerView recyclerView;
List<String> list;
MyAdapter2 myAdapter;
private static final int LOADER_ID = 1;
// The callbacks through which we will interact with the LoaderManager.
private LoaderManager.LoaderCallbacks mCallbacks;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_load_manager);
recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
}
});

recyclerView.setItemAnimator(new DefaultItemAnimator());

//
mCallbacks = this;
LoaderManager lm = getSupportLoaderManager();
lm.initLoader(LOADER_ID, null, mCallbacks);

}

private static final String TAG = "LoadManagerActivity2";
private void initRecylerView(List<String> list){
myAdapter = new MyAdapter2(LoadManagerActivity2.this,list);
recyclerView.setAdapter(myAdapter);
myAdapter.notifyDataSetChanged();
}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Log.i(TAG, "onCreateLoader: --------");
return new CursorLoader(LoadManagerActivity2.this,
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,
ContactsContract.CommonDataKinds.Phone.SORT_KEY_PRIMARY);
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Log.i(TAG, "onLoadFinished: ------");
if(data == null){
Toast.makeText(LoadManagerActivity2.this, "失败", Toast.LENGTH_LONG).show();
return;
}
if (data != null) {
list = new ArrayList<>();
while (data.moveToNext()) {
// 获取查询结果中电话号码列中数据。
String phoneNumber = data.getString(data
.getColumnIndex(ContactsContract
.CommonDataKinds.Phone.NUMBER));
String name = data.getString(data.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
list.add(name+"\n"+phoneNumber);
}
}

// 2.2.2新建一个适配器对象
initRecylerView(list);

}

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
getMenuInflater().inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case R.id.id_action_add:
myAdapter.addData(1);
break;
case R.id.id_action_delete:
myAdapter.removeData(1);
break;
}
return true;
}




@Override
public void onLoaderReset(Loader loader) {
Log.i(TAG, "onLoaderReset: -----------");

}


class MyAdapter2 extends RecyclerView.Adapter<MyAdapter2.MyViewHolder> {

Context context;
List<String> list;

MyAdapter2(Context context,List<String> list){
this.context = context;
this.list = list;
}

@Override
public MyAdapter2.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(LoadManagerActivity2.this).inflate(R.layout.loader_item, parent, false);
return new MyAdapter2.MyViewHolder(view);
}

@Override
public int getItemCount() {
return list.size();
}

@Override
public void onBindViewHolder(MyAdapter2.MyViewHolder holder, int position) {
holder.tv.setText(list.get(position));

}

public void addData(int position) {
list.add(position, "Insert One");
notifyItemInserted(position);
}

public void removeData(int position) {
list.remove(position);
notifyItemRemoved(position);
}


class MyViewHolder extends RecyclerView.ViewHolder {

TextView tv;

public MyViewHolder(View view) {
super(view);
tv = (TextView) view.findViewById(R.id.load_tv);
}
}
}