在开发Android的过程中,我们经常会用到SQLite进行资料的存储,本文给出了一种自定义Loader的方式,直接访问SQLite的API进行交互。
Today we’ll discuss about custom loaders in Android. Loaders have been introduced from Android 3.0 but we can also use loaders in prior versions of Android starting from Android 1.6 with the help of Android compatibility library.
In this tutorial we won’t discuss the basics of loader which is vastly available in the Internet. A very good tutorial on loaders in Android can be found here . Here we’ll discuss about how to create a custom loader in Android which can read data from SQLite Database and return the read data as a collection of POJO.
Android provides a ‘CursorLoader’ class which can read data from content providers but to read data directly from the SQLite database we need our own custom loader. A very nice loader library is written for this which reads data from cursor and can be found here and I’ve taken the idea of the custom loader from these mentioned links. But our custom loader is capable to read data from the SQLite database without using any content provider and returns the data as collection of objects.
Create a new Android project and remember to use the library ‘android-support-v4.jar’ and implement the custom loader as follows :
First we’ll create a generic data source class file ‘DataSource.java’ with common methods to perform CRUD operations on data. The code is as follows :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package com.example.customloaderexample.db; import java.util.List; import android.content.Context; import android.database.sqlite.SQLiteDatabase; public abstract class DataSource { protected SQLiteDatabase mDatabase; public DataSource(SQLiteDatabase database) { mDatabase = database; } public abstract boolean insert(T entity); public abstract boolean delete(T entity); public abstract boolean update(T entity); public abstract List read(); public abstract List read(String selection, String[] selectionArgs, String groupBy, String having, String orderBy); } |
package com.example.customloaderexample.db; import java.util.List; import android.content.Context; import android.database.sqlite.SQLiteDatabase; public abstract class DataSource { protected SQLiteDatabase mDatabase; public DataSource(SQLiteDatabase database) { mDatabase = database; } public abstract boolean insert(T entity); public abstract boolean delete(T entity); public abstract boolean update(T entity); public abstract List read(); public abstract List read(String selection, String[] selectionArgs, String groupBy, String having, String orderBy); }
Next we’ll create our model class file ‘Test.java’ with the following code. This class represents the data that we want to store and retrieve from the database.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package com.example.customloaderexample.model; public class Test { private int id; private String name; public Test() {} public Test(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return name; } } |
package com.example.customloaderexample.model; public class Test { private int id; private String name; public Test() {} public Test(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return name; } }
Next we’ll create a subclass of ‘TestDataSource.java’ to specifically perform CRUD operations on ‘Test’ objects.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | package com.example.customloaderexample.db; import java.util.ArrayList; import java.util.List; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import com.example.customloaderexample.model.Test; public class TestDataSource extends DataSource { public static final String TABLE_NAME = "test"; public static final String COLUMN_ID = "_id"; public static final String COLUMN_NAME = "name"; // Database creation sql statement public static final String CREATE_COMMAND = "create table " + TABLE_NAME + "(" + COLUMN_ID + " integer primary key autoincrement, " + COLUMN_NAME + " text not null);"; public TestDataSource(SQLiteDatabase database) { super(database); // TODO Auto-generated constructor stub } @Override public boolean insert(Test entity) { if (entity == null) { return false; } long result = mDatabase.insert(TABLE_NAME, null, generateContentValuesFromObject(entity)); return result != -1; } @Override public boolean delete(Test entity) { if (entity == null) { return false; } int result = mDatabase.delete(TABLE_NAME, COLUMN_ID + " = " + entity.getId(), null); return result != 0; } @Override public boolean update(Test entity) { if (entity == null) { return false; } int result = mDatabase.update(TABLE_NAME, generateContentValuesFromObject(entity), COLUMN_ID + " = " + entity.getId(), null); return result != 0; } @Override public List read() { Cursor cursor = mDatabase.query(TABLE_NAME, getAllColumns(), null, null, null, null, null); List tests = new ArrayList(); if (cursor != null && cursor.moveToFirst()) { while (!cursor.isAfterLast()) { tests.add(generateObjectFromCursor(cursor)); cursor.moveToNext(); } cursor.close(); } return tests; } @Override public List read(String selection, String[] selectionArgs, String groupBy, String having, String orderBy) { Cursor cursor = mDatabase.query(TABLE_NAME, getAllColumns(), selection, selectionArgs, groupBy, having, orderBy); List tests = new ArrayList(); if (cursor != null && cursor.moveToFirst()) { while (!cursor.isAfterLast()) { tests.add(generateObjectFromCursor(cursor)); cursor.moveToNext(); } cursor.close(); } return tests; } public String[] getAllColumns() { return new String[] { COLUMN_ID, COLUMN_NAME }; } public Test generateObjectFromCursor(Cursor cursor) { if (cursor == null) { return null; } Test test = new Test(); test.setId(cursor.getInt(cursor.getColumnIndex(COLUMN_ID))); test.setName(cursor.getString(cursor.getColumnIndex(COLUMN_NAME))); return test; } public ContentValues generateContentValuesFromObject(Test entity) { if (entity == null) { return null; } ContentValues values = new ContentValues(); values.put(COLUMN_NAME, entity.getName()); return values; } } |
package com.example.customloaderexample.db; import java.util.ArrayList; import java.util.List; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import com.example.customloaderexample.model.Test; public class TestDataSource extends DataSource { public static final String TABLE_NAME = "test"; public static final String COLUMN_ID = "_id"; public static final String COLUMN_NAME = "name"; // Database creation sql statement public static final String CREATE_COMMAND = "create table " + TABLE_NAME + "(" + COLUMN_ID + " integer primary key autoincrement, " + COLUMN_NAME + " text not null);"; public TestDataSource(SQLiteDatabase database) { super(database); // TODO Auto-generated constructor stub } @Override public boolean insert(Test entity) { if (entity == null) { return false; } long result = mDatabase.insert(TABLE_NAME, null, generateContentValuesFromObject(entity)); return result != -1; } @Override public boolean delete(Test entity) { if (entity == null) { return false; } int result = mDatabase.delete(TABLE_NAME, COLUMN_ID + " = " + entity.getId(), null); return result != 0; } @Override public boolean update(Test entity) { if (entity == null) { return false; } int result = mDatabase.update(TABLE_NAME, generateContentValuesFromObject(entity), COLUMN_ID + " = " + entity.getId(), null); return result != 0; } @Override public List read() { Cursor cursor = mDatabase.query(TABLE_NAME, getAllColumns(), null, null, null, null, null); List tests = new ArrayList(); if (cursor != null && cursor.moveToFirst()) { while (!cursor.isAfterLast()) { tests.add(generateObjectFromCursor(cursor)); cursor.moveToNext(); } cursor.close(); } return tests; } @Override public List read(String selection, String[] selectionArgs, String groupBy, String having, String orderBy) { Cursor cursor = mDatabase.query(TABLE_NAME, getAllColumns(), selection, selectionArgs, groupBy, having, orderBy); List tests = new ArrayList(); if (cursor != null && cursor.moveToFirst()) { while (!cursor.isAfterLast()) { tests.add(generateObjectFromCursor(cursor)); cursor.moveToNext(); } cursor.close(); } return tests; } public String[] getAllColumns() { return new String[] { COLUMN_ID, COLUMN_NAME }; } public Test generateObjectFromCursor(Cursor cursor) { if (cursor == null) { return null; } Test test = new Test(); test.setId(cursor.getInt(cursor.getColumnIndex(COLUMN_ID))); test.setName(cursor.getString(cursor.getColumnIndex(COLUMN_NAME))); return test; } public ContentValues generateContentValuesFromObject(Test entity) { if (entity == null) { return null; } ContentValues values = new ContentValues(); values.put(COLUMN_NAME, entity.getName()); return values; } }
Now we’ll create our SQLite database open helper ‘DbHelper.java’. This class extends the SQLiteOpenHelper which helps us to create the initial database and tables.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package com.example.customloaderexample.db; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class DbHelper extends SQLiteOpenHelper { private static final String DATABASE_NAME = "test.db"; private static final int DATABASE_VERSION = 1; public DbHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase database) { database.execSQL(TestDataSource.CREATE_COMMAND); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + TestDataSource.TABLE_NAME); onCreate(db); } } |
package com.example.customloaderexample.db; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class DbHelper extends SQLiteOpenHelper { private static final String DATABASE_NAME = "test.db"; private static final int DATABASE_VERSION = 1; public DbHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase database) { database.execSQL(TestDataSource.CREATE_COMMAND); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + TestDataSource.TABLE_NAME); onCreate(db); } }
Next we’ll create a generic AsyncTask subclass ‘ContentChangingTask.java’ to perform content changing in background. This class is the base class for changing contents. We’ll create subclasses of this base class to perform CRUD operation on data in background.
1 2 3 4 5 6 7 8 9 10 11 12 13 | package com.example.customloaderexample.loader; import android.os.AsyncTask; import android.support.v4.content.Loader; public abstract class ContentChangingTask extends AsyncTask { private Loader loader=null; ContentChangingTask(Loader loader) { this.loader=loader; } @Override protected void onPostExecute(T3 param) { loader.onContentChanged(); } } |
package com.example.customloaderexample.loader; import android.os.AsyncTask; import android.support.v4.content.Loader; public abstract class ContentChangingTask extends AsyncTask { private Loader loader=null; ContentChangingTask(Loader loader) { this.loader=loader; } @Override protected void onPostExecute(T3 param) { loader.onContentChanged(); } }
Now we’ll create a generic base class for our custom loader ‘AbstractDataLoader.java’. This class is the base class of our custom loader.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | package com.example.customloaderexample.loader; import java.util.List; import android.content.Context; import android.support.v4.content.AsyncTaskLoader; public abstract class AbstractDataLoader> extends AsyncTaskLoader { protected E mLastDataList = null; protected abstract E buildList(); public AbstractDataLoader(Context context) { super(context); } /** * Runs on a worker thread, loading in our data. Delegates the real work to * concrete subclass' buildCursor() method. */ @Override public E loadInBackground() { return buildList(); } /** * Runs on the UI thread, routing the results from the background thread to * whatever is using the dataList. */ @Override public void deliverResult(E dataList) { if (isReset()) { // An async query came in while the loader is stopped emptyDataList(dataList); return; } E oldDataList = mLastDataList; mLastDataList = dataList; if (isStarted()) { super.deliverResult(dataList); } if (oldDataList != null && oldDataList != dataList && oldDataList.size() > 0) { emptyDataList(oldDataList); } } /** * Starts an asynchronous load of the list data. When the result is ready * the callbacks will be called on the UI thread. If a previous load has * been completed and is still valid the result may be passed to the * callbacks immediately. * * Must be called from the UI thread. */ @Override protected void onStartLoading() { if (mLastDataList != null) { deliverResult(mLastDataList); } if (takeContentChanged() || mLastDataList == null || mLastDataList.size() == 0) { forceLoad(); } } /** * Must be called from the UI thread, triggered by a call to stopLoading(). */ @Override protected void onStopLoading() { // Attempt to cancel the current load task if possible. cancelLoad(); } /** * Must be called from the UI thread, triggered by a call to cancel(). Here, * we make sure our Cursor is closed, if it still exists and is not already * closed. */ @Override public void onCanceled(E dataList) { if (dataList != null && dataList.size() > 0) { emptyDataList(dataList); } } /** * Must be called from the UI thread, triggered by a call to reset(). Here, * we make sure our Cursor is closed, if it still exists and is not already * closed. */ @Override protected void onReset() { super.onReset(); // Ensure the loader is stopped onStopLoading(); if (mLastDataList != null && mLastDataList.size() > 0) { emptyDataList(mLastDataList); } mLastDataList = null; } protected void emptyDataList(E dataList) { if (dataList != null && dataList.size() > 0) { for (int i = 0; i < dataList.size(); i++) { dataList.remove(i); } } } } |
package com.example.customloaderexample.loader; import java.util.List; import android.content.Context; import android.support.v4.content.AsyncTaskLoader; public abstract class AbstractDataLoader> extends AsyncTaskLoader { protected E mLastDataList = null; protected abstract E buildList(); public AbstractDataLoader(Context context) { super(context); } /** * Runs on a worker thread, loading in our data. Delegates the real work to * concrete subclass' buildCursor() method. */ @Override public E loadInBackground() { return buildList(); } /** * Runs on the UI thread, routing the results from the background thread to * whatever is using the dataList. */ @Override public void deliverResult(E dataList) { if (isReset()) { // An async query came in while the loader is stopped emptyDataList(dataList); return; } E oldDataList = mLastDataList; mLastDataList = dataList; if (isStarted()) { super.deliverResult(dataList); } if (oldDataList != null && oldDataList != dataList && oldDataList.size() > 0) { emptyDataList(oldDataList); } } /** * Starts an asynchronous load of the list data. When the result is ready * the callbacks will be called on the UI thread. If a previous load has * been completed and is still valid the result may be passed to the * callbacks immediately. * * Must be called from the UI thread. */ @Override protected void onStartLoading() { if (mLastDataList != null) { deliverResult(mLastDataList); } if (takeContentChanged() || mLastDataList == null || mLastDataList.size() == 0) { forceLoad(); } } /** * Must be called from the UI thread, triggered by a call to stopLoading(). */ @Override protected void onStopLoading() { // Attempt to cancel the current load task if possible. cancelLoad(); } /** * Must be called from the UI thread, triggered by a call to cancel(). Here, * we make sure our Cursor is closed, if it still exists and is not already * closed. */ @Override public void onCanceled(E dataList) { if (dataList != null && dataList.size() > 0) { emptyDataList(dataList); } } /** * Must be called from the UI thread, triggered by a call to reset(). Here, * we make sure our Cursor is closed, if it still exists and is not already * closed. */ @Override protected void onReset() { super.onReset(); // Ensure the loader is stopped onStopLoading(); if (mLastDataList != null && mLastDataList.size() > 0) { emptyDataList(mLastDataList); } mLastDataList = null; } protected void emptyDataList(E dataList) { if (dataList != null && dataList.size() > 0) { for (int i = 0; i < dataList.size(); i++) { dataList.remove(i); } } } }
Now we’ll create the specific loader ‘SQLiteTestDataLoader.java’ for ‘Test’ object which is a subclass of the previous generic loader. This class is the custom loader for ‘Test’ objects. We’ll instantiate this class as our loader.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | package com.example.customloaderexample.loader; import java.util.List; import android.content.Context; import com.example.customloaderexample.db.DataSource; import com.example.customloaderexample.model.Test; public class SQLiteTestDataLoader extends AbstractDataLoader { private DataSource mDataSource; private String mSelection; private String[] mSelectionArgs; private String mGroupBy; private String mHaving; private String mOrderBy; public SQLiteTestDataLoader(Context context, DataSource dataSource, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) { super(context); mDataSource = dataSource; mSelection = selection; mSelectionArgs = selectionArgs; mGroupBy = groupBy; mHaving = having; mOrderBy = orderBy; } @Override protected List buildList() { List testList = mDataSource.read(mSelection, mSelectionArgs, mGroupBy, mHaving, mOrderBy); return testList; } public void insert(Test entity) { new InsertTask(this).execute(entity); } public void update(Test entity) { new UpdateTask(this).execute(entity); } public void delete(Test entity) { new DeleteTask(this).execute(entity); } private class InsertTask extends ContentChangingTask { InsertTask(SQLiteTestDataLoader loader) { super(loader); } @Override protected Void doInBackground(Test... params) { mDataSource.insert(params[0]); return (null); } } private class UpdateTask extends ContentChangingTask { UpdateTask(SQLiteTestDataLoader loader) { super(loader); } @Override protected Void doInBackground(Test... params) { mDataSource.update(params[0]); return (null); } } private class DeleteTask extends ContentChangingTask { DeleteTask(SQLiteTestDataLoader loader) { super(loader); } @Override protected Void doInBackground(Test... params) { mDataSource.delete(params[0]); return (null); } } } |
package com.example.customloaderexample.loader; import java.util.List; import android.content.Context; import com.example.customloaderexample.db.DataSource; import com.example.customloaderexample.model.Test; public class SQLiteTestDataLoader extends AbstractDataLoader { private DataSource mDataSource; private String mSelection; private String[] mSelectionArgs; private String mGroupBy; private String mHaving; private String mOrderBy; public SQLiteTestDataLoader(Context context, DataSource dataSource, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) { super(context); mDataSource = dataSource; mSelection = selection; mSelectionArgs = selectionArgs; mGroupBy = groupBy; mHaving = having; mOrderBy = orderBy; } @Override protected List buildList() { List testList = mDataSource.read(mSelection, mSelectionArgs, mGroupBy, mHaving, mOrderBy); return testList; } public void insert(Test entity) { new InsertTask(this).execute(entity); } public void update(Test entity) { new UpdateTask(this).execute(entity); } public void delete(Test entity) { new DeleteTask(this).execute(entity); } private class InsertTask extends ContentChangingTask { InsertTask(SQLiteTestDataLoader loader) { super(loader); } @Override protected Void doInBackground(Test... params) { mDataSource.insert(params[0]); return (null); } } private class UpdateTask extends ContentChangingTask { UpdateTask(SQLiteTestDataLoader loader) { super(loader); } @Override protected Void doInBackground(Test... params) { mDataSource.update(params[0]); return (null); } } private class DeleteTask extends ContentChangingTask { DeleteTask(SQLiteTestDataLoader loader) { super(loader); } @Override protected Void doInBackground(Test... params) { mDataSource.delete(params[0]); return (null); } } }
Next we’ll create the initial view xml ‘activity_main.xml’ for our launcher activity into the res/layout folder.
1 2 | <FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"android:layout_height="match_parent"> <fragmentclass="com.example.customloaderexample.CustomLoaderExampleListFragment" android:id="@+id/titles" android:layout_width="match_parent"android:layout_height="match_parent"/> |
<FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"android:layout_height="match_parent"> <fragmentclass="com.example.customloaderexample.CustomLoaderExampleListFragment" android:id="@+id/titles" android:layout_width="match_parent"android:layout_height="match_parent"/>
Now we’ll create the launcher activity to launch our project. This is a FragmentActivity which holds our ListFragment and performs initial record insertion into database.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package com.example.customloaderexample; import java.util.List; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import com.example.customloaderexample.db.DbHelper; import com.example.customloaderexample.db.TestDataSource; import com.example.customloaderexample.model.Test; public class MainFragmentActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DbHelper helper = new DbHelper(this); SQLiteDatabase database = helper.getWritableDatabase(); TestDataSource dataSource = new TestDataSource(database); List list = dataSource.read(); if(list == null || list.size() == 0) { dataSource.insert(new Test("Samik")); dataSource.insert(new Test("Piyas")); dataSource.insert(new Test("Sujal")); } helper.close(); database.close(); } } |
package com.example.customloaderexample; import java.util.List; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import com.example.customloaderexample.db.DbHelper; import com.example.customloaderexample.db.TestDataSource; import com.example.customloaderexample.model.Test; public class MainFragmentActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DbHelper helper = new DbHelper(this); SQLiteDatabase database = helper.getWritableDatabase(); TestDataSource dataSource = new TestDataSource(database); List list = dataSource.read(); if(list == null || list.size() == 0) { dataSource.insert(new Test("Samik")); dataSource.insert(new Test("Piyas")); dataSource.insert(new Test("Sujal")); } helper.close(); database.close(); } }
Don’t forget to maintain the entry of the launcher activity into the ‘AndroidManifest.xml’ along with proper intent filter (to make it the launcher activity).
Now we’ll create the ListFragment to retrieve the list of Test objects from the database and show using our custom loader.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | package com.example.customloaderexample; import java.util.List; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.ArrayAdapter; import com.example.customloaderexample.db.DbHelper; import com.example.customloaderexample.db.TestDataSource; import com.example.customloaderexample.loader.SQLiteTestDataLoader; import com.example.customloaderexample.model.Test; public class CustomLoaderExampleListFragment extends ListFragment implements LoaderManager.LoaderCallbacks { private ArrayAdapter mAdapter; // The Loader's id (this id is specific to the ListFragment's LoaderManager) private static final int LOADER_ID = 1; private static final boolean DEBUG = true; private static final String TAG = "CustomLoaderExampleListFragment"; private SQLiteDatabase mDatabase; private TestDataSource mDataSource; private DbHelper mDbHelper; @Override public void onActivityCreated(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onActivityCreated(savedInstanceState); //setHasOptionsMenu(true); mDbHelper = new DbHelper(getActivity()); mDatabase = mDbHelper.getWritableDatabase(); mDataSource = new TestDataSource(mDatabase); mAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1); setEmptyText("No data, please add from menu."); setListAdapter(mAdapter); setListShown(false); if (DEBUG) { Log.i(TAG, "+++ Calling initLoader()! +++"); if (getLoaderManager().getLoader(LOADER_ID) == null) { Log.i(TAG, "+++ Initializing the new Loader... +++"); } else { Log.i(TAG, "+++ Reconnecting with existing Loader (id '1')... +++"); } } // Initialize a Loader with id '1'. If the Loader with this id already // exists, then the LoaderManager will reuse the existing Loader. getLoaderManager().initLoader(LOADER_ID, null, this); } @Override public Loader onCreateLoader(int id, Bundle args) { SQLiteTestDataLoader loader = new SQLiteTestDataLoader(getActivity(), mDataSource, null, null, null, null, null); return loader; } @Override public void onLoadFinished(Loader loader, List data) { if (DEBUG) { Log.i(TAG, "+++ onLoadFinished() called! +++"); } mAdapter.clear(); for(Test test : data) { mAdapter.add(test); } if (isResumed()) { setListShown(true); } else { setListShownNoAnimation(true); } } @Override public void onLoaderReset(Loader arg0) { mAdapter.clear(); } @Override public void onDestroy() { super.onDestroy(); mDbHelper.close(); mDatabase.close(); mDataSource = null; mDbHelper = null; mDatabase = null; } } |
package com.example.customloaderexample; import java.util.List; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.ArrayAdapter; import com.example.customloaderexample.db.DbHelper; import com.example.customloaderexample.db.TestDataSource; import com.example.customloaderexample.loader.SQLiteTestDataLoader; import com.example.customloaderexample.model.Test; public class CustomLoaderExampleListFragment extends ListFragment implements LoaderManager.LoaderCallbacks { private ArrayAdapter mAdapter; // The Loader's id (this id is specific to the ListFragment's LoaderManager) private static final int LOADER_ID = 1; private static final boolean DEBUG = true; private static final String TAG = "CustomLoaderExampleListFragment"; private SQLiteDatabase mDatabase; private TestDataSource mDataSource; private DbHelper mDbHelper; @Override public void onActivityCreated(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onActivityCreated(savedInstanceState); //setHasOptionsMenu(true); mDbHelper = new DbHelper(getActivity()); mDatabase = mDbHelper.getWritableDatabase(); mDataSource = new TestDataSource(mDatabase); mAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1); setEmptyText("No data, please add from menu."); setListAdapter(mAdapter); setListShown(false); if (DEBUG) { Log.i(TAG, "+++ Calling initLoader()! +++"); if (getLoaderManager().getLoader(LOADER_ID) == null) { Log.i(TAG, "+++ Initializing the new Loader... +++"); } else { Log.i(TAG, "+++ Reconnecting with existing Loader (id '1')... +++"); } } // Initialize a Loader with id '1'. If the Loader with this id already // exists, then the LoaderManager will reuse the existing Loader. getLoaderManager().initLoader(LOADER_ID, null, this); } @Override public Loader onCreateLoader(int id, Bundle args) { SQLiteTestDataLoader loader = new SQLiteTestDataLoader(getActivity(), mDataSource, null, null, null, null, null); return loader; } @Override public void onLoadFinished(Loader loader, List data) { if (DEBUG) { Log.i(TAG, "+++ onLoadFinished() called! +++"); } mAdapter.clear(); for(Test test : data) { mAdapter.add(test); } if (isResumed()) { setListShown(true); } else { setListShownNoAnimation(true); } } @Override public void onLoaderReset(Loader arg0) { mAdapter.clear(); } @Override public void onDestroy() { super.onDestroy(); mDbHelper.close(); mDatabase.close(); mDataSource = null; mDbHelper = null; mDatabase = null; } }
Now run the project as Android application and you’ll get the following screen :
Acknowledgement :
http://www.vogella.com/articles/AndroidSQLite/article.html
https://github.com/commonsguy/cwac-loaderex
http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html
文章节选:http://www.phloxblog.in/android-custom-loader-load-data-sqlite-database-android-version-1-6/