最近做项目时,用户频繁切换账号需更新数据,一开始用的是广播,广播会有延迟后来老大指点用了ContentObserver(观察者模式)。
下面是写了个小Demo
MainActivity:
package com.example.contentobserverdemo;
import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
private UserContentObserver observer;
private Uri uri;
private TextView text;
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
observer = new UserContentObserver(this, handler);
uri = Provider.CONTENT_URI_CHANEGE;
// 注册监听器
getContentResolver().registerContentObserver(uri, false, observer);
text = (TextView) findViewById(R.id.text);
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
getContentResolver().notifyChange(uri, null);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (0 == msg.what) {
text.setText((String) msg.obj);
}
};
};
// 不用时解除广播
protected void onDestroy() {
super.onDestroy();
};
}
UserContentObserver:
package com.example.contentobserverdemo;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
public class UserContentObserver extends ContentObserver {
private Context context;
private Handler handler;
public UserContentObserver(Context context, Handler handler) {
super(handler);
this.context = context;
this.handler = handler;
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
handler.obtainMessage(0, "测试一下ContentObserver").sendToTarget();
}
}
provider:
public class Provider {
private static final String Authority = "com.example.contentobserverdemo";
public static final Uri CONTENT_URI_CHANEGE = Uri.parse("content://" + Authority
+ "/datachange");
}
总结: 使用ContentObserver的情况主要有一下两者情况:
1、需要频繁检测的数据库或者某个数据是否发生改变,如果使用线程去操作,很不经济而且很耗时 ;
2、在用户不知晓的情况下对数据库做一些事件,比如:悄悄发送信息、拒绝接受短信黑名单等;
在这两种情形下,使用ContentObserver无疑是最好的利刃了。
///////////////////////////////////////////////////////////////////////////////////////
// 重要补充 ---- > ContentObserver监听原理说明
///////////////////////////////////////////////////////////////////////////////////////
为什么数据改变后会回调至ContentObserver ? 为什么我们自定义的ContentProvider数据源发生改变
后,却没有监听到任何反应 ? 这与系统的回调系统逻辑有关。
每个ContentProvider数据源发生改变后,如果想通知其监听对象, 例如ContentObserver时,必须在其对应方法 update /
insert / delete时,显示的调用this.getContentReslover().notifychange(uri , null)方法,回调监听处理逻辑。否则,我们
的ContentObserver是不会监听到数据发生改变的。 具体原理,大家可以参考老罗的这篇文章:
《Android应用程序组件Content Provider的共享数据更新通知机制分析》
PS:老罗关于ContentProvider的原理分析还是很给力的,大家可以认真看看老罗这个系列,照着源代码,仔细分析下。
额外补充,最后强烈建议构造ContentObserver对象时,传递主线程所在的Handler,如下:
- airplaneCO = new AirplaneContentObserver(this, mHandler); // mHandler为UI线程的Handler对象
否则,在更新UI时,可能会报异常(非UI线程更新UI时,即SecurityException)。系统级的进程,会导致
重启手机。或者在onChange()方法中,使用Handler类相关方法,回调到UI线程更新UI视图。