之前写过一篇文章《Android学习小Demo(13)Android中关于ContentObserver的使用》,在里面利用ContentOberver去监测短信URI内容的变化。我们先来回顾一下,是如何利用ContentOberver来监测短信内容的变化的。
1)要自定义一个类,比如SmsContentObserver,继承ContentObserver,并且实现其onChange方法。
2)在onChange方法中去查询对应Uri,比如短信收件箱的内容,并将对应的记录利用Handler发送到主界面。
3)在主界面Activity中,要创建一个SmsContentObserver,并且将其注册到ContentReslover中去。
4)在主界面Activity的Handler中获得在SmsContentObserver中变化的消息,更新主界面。
总的来说,过程就大概是这样,大家有兴趣可以看一下这一篇文章。
而本文介绍的是另外一种方法,利用Loader来实现差不多的效果,既然能够说效果差不多,那么就说明了Loader有一个跟ContentObserver一样的特性,没错,它能够检测到对应内容的变化,其实原因就在于Loader内部已经帮我们实现了一个ContentObserver,所以不需要我们再自己去实现。
先简单说一下什么是Loader。
Loader是Android在3.0之后才引进的一个类,其主要目的在于让Android跟Data之间的交互变得更加简单和高效,概括起来,我觉得其功能有以下两点:
1)动态监测所处理对象状态的变化,大部分情况下是处理数据,但我觉得只是一方面。
2)当界面变化,需要被重新创建的时候,它们能够重新load到上一次的数据,而不需要再重新进行查询。
当然,它还是异步的,也就意味着不会阻塞到主界面的显示,不过这个功能很多其他的辅助类都有,也就不算啥特点了。
而正是到其第一点的特性,才让我们有机会可以不用那么麻烦去实现一个ContentObserver,而转而来利用Loader来实现相同的功能。
这一次我们做一个展示短信的Demo。当我们打开手机中的短信应用的时候,如果这个时候有新短信进来,我们会看到新短信马上就显示在界面上的,而我们这个Demo也正是如此效果,具体请看(截图大了点,莫怪)。
从上图中可以看到,当我们点击Send按钮的时候,短信发过去,ListView中马上就显示出来刚刚发送的短信。
那么应该怎么使用Loader呢,我们下面来看代码吧。
1)由于Loader是3.0之后才引进来的,所以在3.0之前,如果我们想要使用Loader的时候,主Activity必须要继承FragmentActivity,才能够拿到LoaderManager。
2)要实现LoaderManager的内部接口LoaderCallbacks<D>,这是一个泛型接口,其定义如下:
public interface LoaderCallbacks<D> { /** * Instantiate and return a new Loader for the given ID. * * @param id The ID whose loader is to be created. * @param args Any arguments supplied by the caller. * @return Return a new Loader instance that is ready to start loading. */ public Loader<D> onCreateLoader(int id, Bundle args); /** * ... * @param loader The Loader that has finished. * @param data The data generated by the Loader. */ public void onLoadFinished(Loader<D> loader, D data); /** * 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. */ public void onLoaderReset(Loader<D> loader); }
public class MainActivity extends FragmentActivity implements LoaderCallbacks<Cursor>{ ... private Uri uri = Uri.parse("content://sms/inbox"); ... @Override public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { String[] projection = new String[] {"_id","address","body","type"}; return new CursorLoader(this, uri, projection, null, null, "date desc"); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { mAdapter.swapCursor(cursor); } @Override public void onLoaderReset(Loader<Cursor> arg0) { // TODO Auto-generated method stub }3)要定义一个Uri,因为Loader它必须要从某个地方load数据,而这个Demo中,我们要获取的是收件箱的短信,所以在这里就是拿sms/inbox了。
4)在OnCreateLoader方法中,要创建一个CursorLoader。CursorLoader是AsyncTaskLoader的一个子类,所以它是一个异步的Loader,不会影响到主界面的展示。
5)在OnLoadFinished方法中,对Load完回来存放在cursor的数据进行处理。
上面的接口,只是实现的方式而已,而当调用下面这个方法的时候,Loader才开始真正地发挥作用。在onCreate方法中,
@Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lvListView = (ListView) findViewById(R.id.lvListView); mAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, null, new String[] {"address","body"}, new int[] {android.R.id.text1, android.R.id.text2}); lvListView.setAdapter(mAdapter); getSupportLoaderManager().initLoader(LOADER_ID, null, this); }
由于我们是用的support包,所以需要用getSupportLoaderManager类来调用initLoader方法,此方法有三个参数:
a)id,由于一个Activity或者Fragment只有一个LoaderManager,但是一个LoaderManager可以有多个Loader,用来处理不同的数据,所以id在这里能唯一地确定是哪个Loader。
b)bundle,这是传给LoaderManager的参数集合。
c)这是一个LoaderCallbacks的实现类,在这个Demo中,就是此Activity,所以就是this。
6)当调用getSupportLoaderManager().initLoader()方法的时候,Android首先会根据 id 去判断是否已经存在这样的一个loader了,如果存在的话,它就会直接使用已有的loader,而不会去创建一个新的,也就是说,它不会去调用接口方法中的onCreateLoader方法了。而如果不存在对应 id 的Loader,则会去调用onCreateLoader方法,并实例化一个新的Loader出来。
而当对应 id 的loader已经存在的时候,Android会直接load数据,而接口方法中的onLoadFinished也会在数据load完之后马上被调用,这样就会存在一种情况,如果在onLoadFinished方法中使用的变量是在onCreateLoader中才初始化的,那么这个变量根本都没被初始化,就被使用了,程序就会报错了,所以在实际开发中,要考虑到这样一种情况的存在,在 onLoadFinished方法中,要做好一些判断。
最后还有一个onLoaderReset方法,没有被用到,这个方法主要是在Loader不再被使用的时候,被关闭了等情况下,用来释放对Loader的使用的,比如在这个Demo中,如果loader不再用了,那么我们的Adpater就不应该再关联对应的cursor了,那么就可以在这里进行判断。
结束!源代码下载。