• Android 之异步加载LoaderManager


    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里加载数据。

     1 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
     2             // TODO Auto-generated method stub
     3             /*CursorLoader loader=new CursorLoader(MainActivity.this);
     4             Uri uri = Uri
     5                     .parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
     6             loader.setUri(uri);*/
     7             Uri uri = Uri.parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
     8             CursorLoader loader=new CursorLoader(MainActivity.this, uri, null, null, null, null);
     9             return loader;
    10         }
    View Code

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

     1     public class myAdapter extends BaseAdapter{
     2 
     3         private Context context;
     4         private List<String> list;
     5         private myAdapter(){
     6             
     7         }
     8      private myAdapter(Context context){
     9             this.context=context;
    10         }
    11      
    12      private void setData(List<String> list){
    13          this.list=list;
    14      }
    15         @Override
    16         public int getCount() {
    17             // TODO Auto-generated method stub
    18             return list.size();
    19         }
    20 
    21         @Override
    22         public Object getItem(int position) {
    23             // TODO Auto-generated method stub
    24             return list.get(position);
    25         }
    26 
    27         @Override
    28         public long getItemId(int position) {
    29             // TODO Auto-generated method stub
    30             return position;
    31         }
    32 
    33         @Override
    34         public View getView(int position, View convertView, ViewGroup parent) {
    35             // TODO Auto-generated method stub
    36             TextView view=null;
    37             if(convertView==null){
    38                 view=new TextView(context);
    39             }
    40             else
    41                 view=(TextView)convertView;
    42             view.setText(list.get(position));
    43             return view;
    44         }
    45         
    46     }
    47     
    View Code
     1 @Override
     2         public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
     3             // TODO Auto-generated method stub
     4             List<String> list=new ArrayList<String>();
     5             while(cursor.moveToNext()){
     6                 String name=cursor.getString(cursor.getColumnIndex("name"));
     7                 list.add(name);
     8             }
     9             myAdapter adapter=new myAdapter(MainActivity.this);
    10             adapter.setData(list);
    11             listView.setAdapter(adapter);
    12             adapter.notifyDataSetChanged();
    13         }
    View Code

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

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

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

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

    最终源码:

    项目目录:

    MainActivity.java

      1 package com.example.android_07loader_manager;
      2 
      3 import java.util.ArrayList;
      4 import java.util.List;
      5 
      6 import android.app.Activity;
      7 import android.app.Dialog;
      8 import android.app.LoaderManager;
      9 import android.app.LoaderManager.LoaderCallbacks;
     10 import android.content.ContentResolver;
     11 import android.content.ContentValues;
     12 import android.content.Context;
     13 import android.content.CursorLoader;
     14 import android.content.Loader;
     15 import android.database.Cursor;
     16 import android.net.Uri;
     17 import android.os.Bundle;
     18 import android.view.ContextMenu;
     19 import android.view.ContextMenu.ContextMenuInfo;
     20 import android.view.Menu;
     21 import android.view.MenuItem;
     22 import android.view.View;
     23 import android.view.ViewGroup;
     24 import android.widget.BaseAdapter;
     25 import android.widget.Button;
     26 import android.widget.EditText;
     27 import android.widget.ListView;
     28 import android.widget.TextView;
     29 import android.widget.Toast;
     30 import android.widget.AdapterView.AdapterContextMenuInfo;
     31 
     32 public class MainActivity extends Activity {
     33 
     34     private LoaderManager manager;
     35     private ListView listView;
     36     myAdapter adapter=null;
     37     @Override
     38     protected void onCreate(Bundle savedInstanceState) {
     39         super.onCreate(savedInstanceState);
     40         setContentView(R.layout.activity_main);
     41         listView=(ListView)this.findViewById(R.id.listView1);
     42         adapter=new myAdapter(MainActivity.this);
     43         manager=getLoaderManager();//LoaderManager已经集成在activity中,用来完成异步加载
     44         manager.initLoader(1000, null, callback);//启动loader
     45         registerForContextMenu(listView);
     46     }
     47     
     48     private LoaderManager.LoaderCallbacks<Cursor> callback=new LoaderCallbacks<Cursor>() {
     49 
     50         @Override
     51         public Loader<Cursor> onCreateLoader(int id, Bundle args) {
     52             // TODO Auto-generated method stub
     53             /*CursorLoader loader=new CursorLoader(MainActivity.this);
     54             Uri uri = Uri
     55                     .parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
     56             loader.setUri(uri);*/
     57             Uri uri = Uri.parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
     58             CursorLoader loader=new CursorLoader(MainActivity.this, uri, null, null, null, null);
     59             return loader;
     60         }
     61 
     62         //完成提取 放到适配器中 更新UI
     63         @Override
     64         public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
     65             // TODO Auto-generated method stub
     66             List<String> list=new ArrayList<String>();
     67             while(cursor.moveToNext()){
     68                 String name=cursor.getString(cursor.getColumnIndex("name"));
     69                 list.add(name);
     70             }
     71             
     72             adapter.setData(list);
     73             listView.setAdapter(adapter);
     74             adapter.notifyDataSetChanged();
     75         }
     76 
     77         @Override
     78         public void onLoaderReset(Loader<Cursor> loader) {
     79             // TODO Auto-generated method stub
     80             
     81         }
     82     };
     83     public class myAdapter extends BaseAdapter{
     84 
     85         private Context context;
     86         private List<String> list;
     87         private myAdapter(){
     88             
     89         }
     90      private myAdapter(Context context){
     91             this.context=context;
     92         }
     93      
     94      private void setData(List<String> list){
     95          this.list=list;
     96      }
     97         @Override
     98         public int getCount() {
     99             // TODO Auto-generated method stub
    100             return list.size();
    101         }
    102 
    103         @Override
    104         public Object getItem(int position) {
    105             // TODO Auto-generated method stub
    106             return list.get(position);
    107         }
    108 
    109         @Override
    110         public long getItemId(int position) {
    111             // TODO Auto-generated method stub
    112             return position;
    113         }
    114 
    115         @Override
    116         public View getView(int position, View convertView, ViewGroup parent) {
    117             // TODO Auto-generated method stub
    118             TextView view=null;
    119             if(convertView==null){
    120                 view=new TextView(context);
    121             }
    122             else
    123                 view=(TextView)convertView;
    124             view.setText(list.get(position));
    125             return view;
    126         }
    127         
    128     }
    129     @Override    
    130     public boolean onCreateOptionsMenu(Menu menu) {
    131         // Inflate the menu; this adds items to the action bar if it is present.
    132         getMenuInflater().inflate(R.menu.main, menu);
    133         return true;
    134     }
    135 
    136     @Override
    137     public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {
    138         // TODO Auto-generated method stub
    139         super.onCreateContextMenu(menu, v, menuInfo);
    140         getMenuInflater().inflate(R.menu.main, menu);
    141     }
    142     @Override
    143     public boolean onMenuItemSelected(int featureId, MenuItem item) {
    144         // TODO Auto-generated method stub
    145         switch (item.getItemId()) {
    146         case R.id.add:
    147             //Toast.makeText(MainActivity.this, "add", 1).show();
    148             final Dialog dialog=createAddDialog(MainActivity.this);
    149             Button button1=(Button)dialog.findViewById(R.id.button1);
    150             button1.setOnClickListener(new View.OnClickListener() {
    151                 
    152                 @Override
    153                 public void onClick(View v) {
    154                     // TODO Auto-generated method stub
    155                     final EditText editText=(EditText)dialog.findViewById(R.id.editText1);
    156                     String name=editText.toString();
    157                     ContentResolver resolver=getContentResolver();
    158                     Uri uri = Uri.parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
    159                     ContentValues values = new ContentValues();
    160                     values.put("name", name);
    161                     Uri res_uri=resolver.insert(uri, values);
    162                     if(res_uri!=null)//添加成功的时候 刷新数据
    163                     {
    164                         manager.restartLoader(1000, null, callback);//重新启动查询器
    165                         dialog.dismiss();
    166                     }
    167                 }
    168             });
    169             dialog.show();
    170             break;
    171 
    172         case R.id.del:
    173             //AdapterContextMenuInfo对象包含了对话框选项的所有信息 AdapterContextMenuInfo into=item.getMenuInfo();
    174             AdapterContextMenuInfo info=(AdapterContextMenuInfo) item.getMenuInfo();
    175             ContentResolver resolver = getContentResolver();
    176             Uri uri = Uri.parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
    177             int position=info.position;
    178             String name=adapter.getItem(position).toString();
    179             int count = resolver.delete(uri, "name=?", new String[]{name});
    180             //Toast.makeText(MainActivity.this, "del", 1).show();
    181             if(count>0){
    182                 manager.restartLoader(1000, null, callback);//重新启动查询器
    183             }
    184                 
    185             break;
    186         }
    187         return super.onMenuItemSelected(featureId, item);
    188     }
    189 
    190     public Dialog createAddDialog(Context context){
    191         Dialog dialog=new Dialog(context);
    192         dialog.setContentView(R.layout.add);
    193          return dialog;
    194         
    195     }
    196 /*    @Override
    197     public boolean onOptionsItemSelected(MenuItem item) {
    198         
    199     int id = item.getItemId();
    200         if (id == R.id.action_settings) {
    201             return true;
    202         }
    203         
    204         
    205         return super.onOptionsItemSelected(item);
    206     }*/
    207 }
    View Code

    MyTest.java

     1 package com.example.android_07loader_manager;
     2 
     3 import android.content.ContentResolver;
     4 import android.content.ContentUris;
     5 import android.content.ContentValues;
     6 import android.database.Cursor;
     7 import android.net.Uri;
     8 import android.test.AndroidTestCase;
     9 
    10 public class MyTest extends AndroidTestCase {
    11 
    12     public MyTest() {
    13         // TODO Auto-generated constructor stub
    14     }
    15 
    16     public void add() {
    17         ContentResolver contentResolver = getContext().getContentResolver();
    18         ContentValues values = new ContentValues();
    19         values.put("name", "mlj");
    20         Uri url = Uri
    21                 .parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
    22         contentResolver.insert(url, values);
    23     }
    24 
    25     public void delete() {
    26         ContentResolver contentResolver = getContext().getContentResolver();
    27 
    28         Uri url = Uri
    29                 .parse("content://com.example.android_07loader_manager.StudentContentProvider/student/1");
    30         int count = contentResolver.delete(url, null, null);
    31         System.out.println("---count-->>" + count);
    32     }
    33 
    34     public void query() {
    35         ContentResolver contentResolver = getContext().getContentResolver();
    36         Uri url = Uri
    37                 .parse("content://com.example.android_07loader_manager.StudentContentProvider/student");
    38         Uri uri = ContentUris.withAppendedId(url, 2);
    39         
    40         Cursor cursor = contentResolver.query(uri, null, null, null, null);
    41         while (cursor.moveToNext()) {
    42             System.out.println("--->>"
    43                     + cursor.getString(cursor.getColumnIndex("name")));
    44         }
    45     }
    46 }
    View Code

    StudentContentProvider.java

      1 package com.example.android_07loader_manager;
      2 
      3 import com.example.android_07loader_manager.dbhelp.DBHelper;
      4 
      5 import android.R.integer;
      6 import android.content.ContentProvider;
      7 import android.content.ContentUris;
      8 import android.content.ContentValues;
      9 import android.content.UriMatcher;
     10 import android.database.Cursor;
     11 import android.database.sqlite.SQLiteDatabase;
     12 import android.net.Uri;
     13 
     14 public class StudentContentProvider extends ContentProvider {
     15 
     16     private final static UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
     17     private final static int STUDENT = 1;
     18     private final static int STUDENTS = 2;
     19     private DBHelper helper;
     20     static {
     21         URI_MATCHER.addURI(
     22                 "com.example.android_07loader_manager.StudentContentProvider","student", STUDENTS);
     23         URI_MATCHER.addURI(
     24                 "com.example.android_07loader_manager.StudentContentProvider","student/#", STUDENT);
     25     }
     26 
     27     public StudentContentProvider() {
     28         // TODO Auto-generated constructor stub
     29     }
     30 
     31     @Override
     32     public int delete(Uri uri, String selection, String[] selectionArgs) {
     33         // TODO Auto-generated method stub
     34         int count = 0;// 影响数据库的行数
     35         int flag = URI_MATCHER.match(uri);
     36         SQLiteDatabase database = helper.getWritableDatabase();
     37 
     38         switch (flag) {
     39         case STUDENT:
     40             long stuid = ContentUris.parseId(uri);
     41             String where_value = " stuid = " + stuid;
     42             if (selection != null && !selection.equals("")) {
     43                 where_value += selection;
     44             }
     45             count = database.delete("student", where_value, selectionArgs);
     46             break;
     47 
     48         case STUDENTS:
     49             count = database.delete("student", selection, selectionArgs);
     50             break;
     51         }
     52         return count;
     53     }
     54 
     55     @Override
     56     public String getType(Uri uri) {
     57         // TODO Auto-generated method stub
     58         int flag = URI_MATCHER.match(uri);
     59         switch (flag) {
     60         case STUDENT:
     61             return "vnd.android.cursor.item/student";
     62         case STUDENTS:
     63             return "vnd.android.cursor.dir/studens";
     64         }
     65         return null;
     66     }
     67 
     68     @Override
     69     public Uri insert(Uri uri, ContentValues contentValues) {
     70         // TODO Auto-generated method stub
     71         int flag = URI_MATCHER.match(uri);
     72         SQLiteDatabase database = helper.getWritableDatabase();
     73         Uri uri2 = null;
     74         switch (flag) {
     75         case STUDENTS:
     76             long id = database.insert("student", null, contentValues);
     77             uri2 = ContentUris.withAppendedId(uri, id);
     78             break;
     79         }
     80         System.out.println("-->>" + uri2.toString());
     81         return uri2;
     82     }
     83 
     84     @Override
     85     public boolean onCreate() {
     86         // TODO Auto-generated method stub
     87         helper = new DBHelper(getContext());
     88         return false;
     89     }
     90 
     91     @Override
     92     public Cursor query(Uri uri, String[] projection, String selection,
     93             String[] selectionArgs, String sortOrder) {
     94         // TODO Auto-generated method stub
     95         Cursor cursor = null;
     96         int flag = URI_MATCHER.match(uri);
     97         SQLiteDatabase database = helper.getReadableDatabase();
     98         switch (flag) {
     99         case STUDENT:
    100             long stuid = ContentUris.parseId(uri);
    101             String where_value = " stuid = " + stuid;
    102             if (selection != null && !"".equals(selection)) {
    103                 where_value += selection;
    104             }
    105             cursor = database.query("student", projection, where_value,
    106                     selectionArgs, null, null, null);
    107 
    108             break;
    109 
    110         case STUDENTS:
    111             cursor = database.query("student", projection, selection,
    112                     selectionArgs, null, null, null);
    113             break;
    114         }
    115         return cursor;
    116     }
    117 
    118     @Override
    119     public int update(Uri uri, ContentValues values, String selection,
    120             String[] selectionArgs) {
    121         // TODO Auto-generated method stub
    122         int count = 0;
    123         int flag = URI_MATCHER.match(uri);
    124         SQLiteDatabase database = helper.getWritableDatabase();
    125         switch (flag) {
    126         case STUDENT:
    127             long stuid = ContentUris.parseId(uri);
    128             String where_value = " stuid = " + stuid;
    129             if (selection != null && !selection.equals("")) {
    130                 where_value += selection;
    131             }
    132             count = database.update("student", values, where_value,
    133                     selectionArgs);
    134             break;
    135 
    136         case STUDENTS:
    137             count = database
    138                     .update("student", values, selection, selectionArgs);
    139             break;
    140         }
    141         return count;
    142     }
    143 
    144 }
    View Code

    DBHelper.java

     1 package com.example.android_07loader_manager.dbhelp;
     2 
     3 import android.content.Context;
     4 import android.database.DatabaseErrorHandler;
     5 import android.database.sqlite.SQLiteDatabase;
     6 import android.database.sqlite.SQLiteDatabase.CursorFactory;
     7 import android.database.sqlite.SQLiteOpenHelper;
     8 
     9 public class DBHelper extends SQLiteOpenHelper{
    10 
    11     private static String name = "mydb.db";
    12     private static int version = 1;
    13 
    14     public DBHelper(Context context) {
    15         super(context, name, null, version);
    16         // TODO Auto-generated constructor stub
    17     }
    18 
    19     public DBHelper(Context context, String name, CursorFactory factory,
    20             int version, DatabaseErrorHandler errorHandler) {
    21         super(context, name, factory, version, errorHandler);
    22         // TODO Auto-generated constructor stub
    23     }
    24 
    25 
    26     @Override
    27     public void onCreate(SQLiteDatabase db) {
    28         // TODO Auto-generated method stub
    29         String sql = "create table student (stuid integer primary key autoincrement,name varchar(64))";
    30         db.execSQL(sql);
    31     }
    32 
    33     @Override
    34     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    35         // TODO Auto-generated method stub
    36         
    37     }
    38 
    39 }
    View Code

    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;

    1 @Override
    2     public boolean onCreate() {
    3         // TODO Auto-generated method stub
    4         helper = new DBHelper(getContext());
    5         return false;
    6     }
    View Code

      匹配规则(静态代码块)

    1 static {
    2         URI_MATCHER.addURI(
    3                 "com.example.android_07loader_manager.StudentContentProvider","student", STUDENTS);
    4         URI_MATCHER.addURI(
    5                 "com.example.android_07loader_manager.StudentContentProvider","student/#", STUDENT);
    6     }
    View Code

       重写getType方法

     1 @Override
     2     public String getType(Uri uri) {
     3         // TODO Auto-generated method stub
     4         int flag = URI_MATCHER.match(uri);
     5         switch (flag) {
     6         case STUDENT:
     7             return "vnd.android.cursor.item/student";
     8         case STUDENTS:
     9             return "vnd.android.cursor.dir/studens";
    10         }
    11         return null;
    12     }
    View Code

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

    2.2 完成增删改查

    2.3建测试类 

    第三步:

  • 相关阅读:
    linux 中断映射
    undefined reference to `__stack_chk_guard'
    riscv_clocksource
    source insight
    Illegal instruction mret mret指令返回异常
    Linux内核内存分配函数之devm_kmalloc和devm_kzalloc
    设备树 compatible 属性
    miniriscvos 05Preemptive
    enter_supervisor_mode
    Store/AMO access fault
  • 原文地址:https://www.cnblogs.com/mlj5288/p/4964438.html
Copyright © 2020-2023  润新知