为啥 ContentObserver 会被多次调用?
Posted
技术标签:
【中文标题】为啥 ContentObserver 会被多次调用?【英文标题】:Why is the ContentObserver called multiple times?为什么 ContentObserver 会被多次调用? 【发布时间】:2011-11-21 12:43:37 【问题描述】:我有以下ContentObserver
实现接收和编写短信,但它被多次调用。
代码:
public class SMSObserverActivity extends Activity
protected MyContentObserver observer = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String url = "content://mms-sms/";
Uri uri = Uri.parse(url);
observer = new MyContentObserver(new Handler());
getContentResolver().registerContentObserver(uri, true, observer);
@Override
protected void onDestroy()
super.onDestroy();
getContentResolver().unregisterContentObserver(observer);
class MyContentObserver extends ContentObserver
ContentValues values = new ContentValues();
Handler handler;
public MyContentObserver(Handler handler)
super(handler);
this.handler = handler;
@Override
public boolean deliverSelfNotifications()
return false;
@Override
public void onChange(boolean arg0)
super.onChange(arg0);
Log.v("SMS", "Notification on SMS observer");
values.put("status", 5);
Message msg = new Message();
msg.obj = "xxxxxxxxxx";
int threadId = 0;
handler.sendMessage(msg);
Uri uriSMSURI = Uri.parse("content://sms/");
Cursor cur =
getContentResolver().query(uriSMSURI, null, null, null,
null);
cur.moveToNext();
Log.e("sms", cur.getString(4)+" "+cur.getString(11));
清单:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="8" />
<uses-permission android:name="android.permission.READ_SMS"></uses-permission>
<uses-permission android:name="android.permission.WRITE_SMS"></uses-permission>
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".SMSObserverActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
为什么会被多次调用?
编辑:
有人认为问题是由于缺少unregisterContentObserver
引起的,但没有任何区别。
【问题讨论】:
你有想过这个吗? 还没有,我还没有时间,但是请阅读答案上的 cmets 并尝试使用服务。也许你会得到它的工作。 ;) 如果在服务中运行,恐怕问题是一样的 是的,同样的问题发生了,不过我已经找到了原因。见答案。 【参考方案1】:这是因为您正在为整个 SMS 数据库注册内容观察器。因此,每次更新数据库中的表条目时,您的内容观察者都会收到通知。
在这种情况下,例如发送消息时,大约 7 个表条目会更新,因此您的内容观察者会收到 7 次通知。
由于我只对是否发送消息感兴趣,因此我已更改为仅观察排队的消息,这意味着我的观察者总是会收到准确的 3 次通知,因此我已经实现了代码来防止这种情况发生。
可能还有其他一些问题,例如多收件人或多部分消息,但到目前为止基本工作正常。
【讨论】:
好吧,我以为每个对象的所有相关更新只调用一次观察者。我认为有一个唯一的 ID 可以查看,哪些通知属于一个更新,不是吗?【参考方案2】:为了避免内容观察者发送多条短信,试试这个
public class SmsObserver extends ContentObserver
SharedPreferences trackMeData;
private Context context;
private static int initialPos;
private static final String TAG = "SMSContentObserver";
private static final Uri uriSMS = Uri.parse("content://sms/sent");
public SmsObserver(Handler handler, Context ctx)
super(handler);
context = ctx;
trackMeData = context.getSharedPreferences("LockedSIM", 0);
initialPos = getLastMsgId();
@Override
public void onChange(boolean selfChange)
super.onChange(selfChange);
queryLastSentSMS();
public int getLastMsgId()
Cursor cur = context.getContentResolver().query(uriSMS, null, null, null, null);
cur.moveToFirst();
int lastMsgId = cur.getInt(cur.getColumnIndex("_id"));
Log.i(TAG, "Last sent message id: " + String.valueOf(lastMsgId));
return lastMsgId;
protected void queryLastSentSMS()
new Thread(new Runnable()
@Override
public void run()
Cursor cur =
context.getContentResolver().query(uriSMS, null, null, null, null);
if (cur.moveToNext())
try
String body = cur.getString(cur.getColumnIndex("body"));
if (initialPos != getLastMsgId())
String receiver = cur.getString(cur.getColumnIndex("address"));
Log.i("account", myDeviceId);
Log.i("date", day + "-" + month + "-" + year + " "
+ hour + ":" + minute + ":" + seconde);
Log.i("sender", myTelephoneNumber);
Log.i("receiver", receiver );
// Then, set initialPos to the current position.
initialPos = getLastMsgId();
sendsmstoph(receiver, body);
catch (Exception e)
// Treat exception here
cur.close();
).start();
【讨论】:
调用 initialPos = getLastMsgId(); from 构造函数抛出空指针异常 我注意到这很好,但并不总是万无一失的。所以我认为使用 SharedPreferences 可以解决这个问题。这是我的做法: if (!sharedPreferences.getString("lastMessageTime", "0") .equals(String.valueOf(timeInMillis)) || !sharedPreferences.getString("lastMessageNumber", "0") .equals(数字))【参考方案3】:如果您希望仅在活动处于活动状态时启用观察者,我建议您将registerContentObserver()
和unregisterContentObserver()
分别移动到方法onResume()
和onPause()
。如果您的应用程序退出,onDestroy()
可能不会被调用,但onPause()
一定会被调用。
【讨论】:
当 Activity 运行时,观察者应始终处于活动状态。这只是一个观察内容测试。 Activity 在onResume()
和onPause()
之间运行,其他时间它要么在后台要么不可见。它甚至可能在没有调用onDestroy()
的情况下被杀死,当您在onCreate()
中重新创建应用程序时,您将第二次注册观察者。
为什么要注册两次?我有一个注销声明。
你在onDestroy()
方法中有,不保证会被调用。当您的活动停止运行(离开屏幕)时调用的方法是onPause()
。来自有关onDestroy()
的文档:“在某些情况下,系统会简单地终止活动的托管进程而不调用此方法(或任何其他方法)”。
好的,我现在已经尝试使用包含观察者的静态类成员。这没什么区别。使用onResume()
和onPause()
不适合我,因为我想启动Activity
,然后我会向我发送短信。以上是关于为啥 ContentObserver 会被多次调用?的主要内容,如果未能解决你的问题,请参考以下文章
短信 ContentObserver onChange() 触发多次
为啥在某些网站上多次调用 QWebView.loadFinished,例如YouTube?
Android:内容观察者的“onChange()”方法被多次调用
当且仅当 ContactsContract.Contacts.CONTENT_URI 更改时,ContentObserver 应该调用