The use of Android Loader and the acquisition of mobile phone address book

Today is the last day of 2017, and everyone should get ready for the new year. Hope your life will be better in 2018.


A long-time operation is performed in the main thread of Android, which causes the interface to become unresponsive and causes ANR. If you need to perform a long-term operation, it will generally be processed in another thread, and then the data will be transferred to the main thread for display. Android itself provides us with some mechanisms to deal with this situation. Let's take a look at Loader today. Loader is mainly used to load data asynchronously in Activity and Fragment, and it is also very simple to use.


The initialization of Loader is very simple. Activity provides an interface to obtain LoaderManager, and then calls LoaderManager's initLoader. initLoader accepts three parameters:

  1. The unique identifier ID of the Loader, which is used to distinguish multiple Loaders;

  2. The parameter passed to Loader, optional;

  3. Loader's callback.


To destroy the Loader, you only need to use the destroyLoader of the LoaderManager. The parameter only passes the ID of the Loader.


Loader's callback interface LoaderCallbacks has three methods:

public interface LoaderCallbacks<D> {
	Loader<D> onCreateLoader(int var1, Bundle var2);

	void onLoadFinished(Loader<D> var1, D var2);

	void onLoaderReset(Loader<D> var1);
}

  1. onCreateLoader is called when Loader is created;

  2. onLoadFinished is called when Loader finishes loading data;

  3. onLoaderReset is called when the Loader is reset.


That's all for the Loader interface. Let's see how to use the Loader through an actual application case. Here, we choose to obtain the phone's address book. To obtain the phone's address book information, we need to use the Phone's ContentProvider, which is detailed in the following example.


Since you want to access the address book, you need to add permissions to the manifest file:

<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>

activity_main.xml is very simple, including a ListView to display the list of contacts.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.yjp.contractgetter.MainActivity">

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

</FrameLayout>

The code of the MainActivity class is relatively long. Let's break it down. MainActivity needs to implement the LoaderCallbacks interface.

class MainActivity : AppCompatActivity(), LoaderManager.LoaderCallbacks<Cursor> {

Using Kotlin's partner object, static member variables are defined, LOADER_ID is the ID of the Loader, and PHONE_PROJECTION is the set of columns that you want to find when querying the address book

companion object {
	private val LOADER_ID = 0
	private val PHONE_PROJECTION = arrayOf(Phone._ID, Phone.DISPLAY_NAME, Phone.NUMBER)
}

onCreate uses SimpleCursorAdapter as the Adapter of ListView, and then calls initLoader to initialize the Loader

private var mAdapter:SimpleCursorAdapter? = null

override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate (savedInstanceState)
	setContentView(R.layout.activity_main)

	mAdapter = SimpleCursorAdapter(this,
			android.R.layout.simple_list_item_2,
			null,
			arrayOf(Phone.DISPLAY_NAME, Phone.NUMBER),
			intArrayOf(android.R.id.text1, android.R.id.text2),
			0)
	listView.adapter = mAdapter
	listView.onItemClickListener = AdapterView.OnItemClickListener {
		_, _, position, _ ->
		val cursor = listView.getItemAtPosition(position) as Cursor
		val displayNameIndex = cursor.getColumnIndex(Phone.DISPLAY_NAME)
		Toast.makeText(this, cursor.getString(displayNameIndex), Toast.LENGTH_SHORT).show()
	}

	loaderManager.initLoader(LOADER_ID, null, this)
}

onDestory destroys Loader

override fun onDestroy() {
	super.onDestroy ()
	loaderManager.destroyLoader(LOADER_ID)
}

Three callback methods:

override fun onCreateLoader(id: Int, bundle: Bundle?): Loader<Cursor> {
	return CursorLoader(this,
			Phone.CONTENT_URI,
			PHONE_PROJECTION,
			null,
			null,
			Phone.DISPLAY_NAME)
}

override fun onLoaderReset(cursor: Loader<Cursor>?) {
	mAdapter?.swapCursor(null)
}

override fun onLoadFinished(loader: Loader<Cursor>?, cursor: Cursor?) {
	mAdapter?.swapCursor(cursor)
}

  1. onCreateLoader creates a CursorLoader, the Loader executes the operation of ContentProvider, and then returns a cursor;

  2. When onLoaderReset, set the cursor in the Adapter to null;

  3. When onLoadFinished, since the CursorLoader finishes querying the data, it will return a new cursor. We use the new Cursor to replace the cursor in the previous Adapter.


In this way, we can automatically load data asynchronously when the Activity starts. Try it on the mobile phone, and you can find that the loading is very smooth. github has uploaded the code .


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325581203&siteId=291194637