Android-LoaderManager异步加载数据库数据

LoaderManager异步加载数据库数据,是在(Activity/fragment/其他UI等) 加载大量的本地Database库表数据,由于数据大在加载过程中会导致UI线程阻塞,导致用户体验不好,Android为来解决这个问题,就设计了LoaderManager异步加载数据库数据

以前我在深圳做项目的时候,公司研发的APP是给中国联通人员在山上工作办事的,对这款APP要求离线数据,大量的离线数据(成百上千条)都是存储在本地Database表里面的,常常在查询本地Database数据的时候,导致UI线程阻塞,体验不好,哪个时候还不知道有LoaderManager异步加载数据库数据,如果早点知道就可以解决这个问题;


MySQLiteOpenHelper3 数据库帮助类 创建类表

package liudeli.datastorage.db;

import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class MySQLiteOpenHelper3 extends SQLiteOpenHelper {

    public static MySQLiteOpenHelper3 mySQLiteOpenHelper;

    /**
     * 由于表名每次使用很频繁,所有定义成常量
     */
    public static final String TABLE_NAME = "_student_table";

    private static final String DB_NAME = "student.db";
    private static final int VERSION = 1;

    public synchronized static MySQLiteOpenHelper3 getInstance(Context context) {
        if (null == mySQLiteOpenHelper) {
            mySQLiteOpenHelper = new MySQLiteOpenHelper3(context, DB_NAME, null, VERSION);
        }
        return mySQLiteOpenHelper;
    }

    /**
     * 当开发者调用 getReadableDatabase(); 或者 getWritableDatabase();
     * 就会通过此构造方法配置的信息 来创建 person_info.db 数据库
     * 此方法的另外作用是,如果存着数据库就打开数据库,不存着数据库就创建数据库
     * @param context 上下文
     * @param name    数据库名
     * @param factory 游标工厂
     * @param version 版本,最低为1
     */
    private MySQLiteOpenHelper3(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    /**
     * 此方法是何时调用? ,是需要开发者调用 getReadableDatabase(); 或者 getWritableDatabase();
     * 此方法的作用是,如果没有表就创建打开,如果有表就打开
     * @param db 可执行SQL语句
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table "+TABLE_NAME+"(_id integer primary key autoincrement, name text, age integer, my_assets text);");

        ContentValues values = new ContentValues();

        for (int i = 0; i < 6; i++) {
            values.clear();
            values.put("name", "张三" + i);
            values.put("age", 62 + i);
            values.put("my_assets", "1000000" + i);
            db.insert(TABLE_NAME, null, values);
        }
    }

    /**
     * 此方法用于数据库升级
     * @param db 可执行SQL语句
     * @param oldVersion 以前旧版本的版本号
     * @param newVersion 现在目前最新的版本号
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

ConnectMySQLiteOpenHelper3ContentProvider 内容提供者 暴露数据库里面的数据 进行查询 增加

package liudeli.datastorage;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;

import liudeli.datastorage.db.MySQLiteOpenHelper3;

public class ConnectMySQLiteOpenHelper3ContentProvider extends ContentProvider {

    private MySQLiteOpenHelper3 dbHelper;

    @Override
    public boolean onCreate() {
        Log.d("Provider", "ConnectMySQLiteOpenHelper3ContentProvider");
        dbHelper = MySQLiteOpenHelper3.getInstance(getContext()); // 必须这在里面,要是写在外面是无法获取上下文的
        return false;
    }

    @Override
    public Cursor query(Uri uri,  String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        SQLiteDatabase database = dbHelper.getReadableDatabase();
        Cursor cursor = database.query(MySQLiteOpenHelper3.TABLE_NAME, projection, selection, selectionArgs, sortOrder, null, "_id desc");
        return cursor;  // 内容提供者里面的 cursor / database 不能关闭
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase database = dbHelper.getWritableDatabase();
        long insertThisID = database.insert(MySQLiteOpenHelper3.TABLE_NAME, null, values);

        // insertThisID是 插入成功后,插入的这条数据ID
        Uri uriResult =  ContentUris.withAppendedId(uri, insertThisID);

        // 内容提供者里面的 cursor / database 不能关闭
        return uriResult;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(Uri uri,  ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }
}

在AndroidManifest.xml provider 对外提供可以访问的 Uir

      <!--  定义provider 内容提供者
              provider对外暴露
         -->
        <provider
            android:authorities="db.ConnectMySQLiteOpenHelper3ContentProvider"
            android:name=".ConnectMySQLiteOpenHelper3ContentProvider"
            android:exported="true"
            android:enabled="true" />

在LoaderActivity使用LoaderManager异步加载数据库数据

package liudeli.datastorage;

import android.app.Activity;
import android.app.LoaderManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class LoaderActivity extends Activity {

    private LoaderManager loaderManager;
    private ListView listView;

    // 访问内容提供者的Uir地址
    private Uri uri = Uri.parse("content://db.ConnectMySQLiteOpenHelper3ContentProvider");

    @Override
    protected void onCreate( Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_loader);

        loaderManager = getLoaderManager();

        /**
         * 参数一:ID
         * 参数二:参数
         * 参数三:LoaderCallbacks回调
         */
        loaderManager.initLoader(1, null, callbacks);

        listView = findViewById(R.id.list_view);
    }

    /**
     * 定义LoaderCallbacks回调
     */
    private LoaderManager.LoaderCallbacks<Cursor> callbacks = new LoaderManager.LoaderCallbacks<Cursor>() {
        /**
         * 此方法是 加载读取大量数据 异步执行
         * @param id
         * @param args
         * @return
         */
        @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
            CursorLoader cursorLoader = new CursorLoader(LoaderActivity.this);
            cursorLoader.setUri(uri);
            cursorLoader.setSortOrder(null);
            cursorLoader.setSelectionArgs(null);
            cursorLoader.setSelection(null);
            cursorLoader.setProjection(new String[]{"name", "age"});
            // ....

            /*
            第二种方式,都是可以的
            new CursorLoader(Context context, Uri uri, String[] projection, String selection,
                    String[] selectionArgs, String sortOrder);*/

            return cursorLoader;
        }

        /**
         * 此方法是 已经读取数据库数据完成?了 更新UI操作
         * @param loader
         * @param cursor
         */
        @Override
        public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
            List<Map<String, Object>> list = new ArrayList<>();

            while (cursor.moveToNext()) {
                Map<String, Object> mMap = new HashMap<>();
                mMap.put("name", cursor.getString(cursor.getColumnIndex("name")));
                mMap.put("age", cursor.getInt(cursor.getColumnIndex("age")));
                list.add(mMap);
            }

            ListAdapter listAdapter =
                    new SimpleAdapter(LoaderActivity.this,
                            list,
                            android.R.layout.simple_list_item_2,
                            new String[]{"name","age"}, // 从哪里来
                            new int[]{android.R.id.text1, android.R.id.text2}); // 到哪里去
            listView.setAdapter(listAdapter);
        }

        /**
         * Called when a previously created loader is being reset, and thus
         * making its data unavailable.  The application should at this point
         * remove any references it has to the Loader‘s data.
         *
         * @param loader The Loader that is being reset.
         */
        @Override
        public void onLoaderReset(Loader<Cursor> loader) {

        }
    };

    /**
     * 增加数据
     * @param view
     */
    public void insert(View view) {
        ContentResolver contentResolver = getContentResolver();
        ContentValues values = new ContentValues();
        values.clear();
        values.put("name", "大民");
        values.put("age", 99);
        values.put("my_assets", "9000000");
        contentResolver.insert(uri, values);

        /**
         * Loader restartLoader 会自动去读内容提供者里面的数据
         * 参数一:ID
         * 参数二:参数
         * 参数三:LoaderCallbacks回调
         */
        loaderManager.restartLoader(3333, null, callbacks);
    }
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="insert"
        android:text="增加"
        android:layout_centerInParent="true"
        />

</RelativeLayout>

效果:

原文地址:https://www.cnblogs.com/android-deli/p/10176986.html

时间: 2024-11-08 03:02:45

Android-LoaderManager异步加载数据库数据的相关文章

Android之数据存储----使用LoaderManager异步加载数据库

一.各种概念: 1.Loaders: 适用于Android3.0以及更高的版本,它提供了一套在UI的主线程中异步加载数据的框架.使用Loaders可以非常简单的在Activity或者Fragment中异步加载数据,一般适用于大量的数据查询,或者需要经常修改并及时展示的数据显示到UI上,这样可以避免查询数据的时候,造成UI主线程的卡顿. 即使是查询SQLite数据库,用Loaders来操作会更加的简便. Loaders有以下特点: 可以适用于Activity和Fragment. 可以提供异步的方式

android listview 异步加载图片并防止错位

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题. 我简单分析一下: 当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView. 当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Ite

android listview 异步加载图片并防止错位+双缓存

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题. 我简单分析一下: 当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView. 当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Ite

Android AsyncTask异步加载WebAPI

之前做的程序一直存在很多问题,因为需要加载的Activity需要从网络加载数据.并没有完全正确的使用异步的方法去加载! 之前用的虽然是AsyncTask,但是在加载完成的时候还是并没有使用AsyncTask内置的方法去处理.而已傻傻的在OnCreate中处理.结果还是要等待AsyncTask加载完成后,等待OnCreate执行完成后,才load到Activity.这样子给人的第一感觉就是卡机了一样! 果断重新再写,这次的代码真是大换血.首先还是选择把带分页ListView进行封装了一下. 1 i

android listview 异步加载图片并防止错位 解决办法

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题. 我简单分析一下: 当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView. 当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Ite

ext combobox动态加载数据库数据

前台: var provinceStore = new Ext.data.Store({ proxy: new Ext.data.HttpProxy({ url: basePath + "/stationManage/station_getProvinceJSON.action" }), reader: new Ext.data.JsonReader( { root: "" }, ["PROVINCEID", "PROVINCENAME

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

将近一个月没有更新博客了,由于这段时间以来准备毕业论文等各种事务缠身,一直没有时间和精力沉下来继续学习和整理一些东西.最近刚刚恢复到正轨,正好这两天看了下Android上关于图片异步加载的开源项目,就顺便整理记录下来,作为这一个多月来博客的重新开火做饭吧.从今天起我会陆续恢复博客的更新,也希望大家继续支持. 今天要介绍的是Github上一个使用非常广泛的图片异步加载库Android-Universal-Image-Loader,该项目的功能十分强大,可以说是我见过的目前功能最全.性能最优的图片异

Android图片异步加载之Android-Universal-Image-Loader类库的使用

Android开发中我们会经常遇到图片过多或操作不当造成Out of Memory异常,有时虽然是解决了这个问题但却会影响程序的运行效率,例如:当用户在快速滑动滚动条的过程中,我们程序在仍在艰难的加载服务器端的图片,这样给用户造成了极不好的体验.其实网络上关于图片的异步加载和缓存的讲解很多,但是其实,写一个这方面的程序还是比较麻烦的,要考虑多线程,缓存,内存溢出等很多方面,针对这一广大开发者都会遇到的问题,一些牛人们已经帮我们解决了这一问题,今天我为大家介绍一款很流行的开源类库,可以很很好的解决

ajax验证表单元素规范正确与否 ajax展示加载数据库数据 ajax三级联动

一.ajax验证表单元素规范正确与否 以用ajax来验证用户名是否被占用为例 1创建表单元素<input type="text" id="t"> 2在js中用keyup事件来进行操作 3创建ajax格式和内容:格式: $.ajax({ url:"哪一个服务端处理器", data:{"自己起名",所需要传给处理器的数据}, type:"post", dataType:"json"