内容观察者onChange方法在光标1更改后调用两次

时间:2012-04-16 12:23:27

标签: java android android-contentprovider

我有一个应用程序,我希望将android联系人列表中的详细信息发送到远程服务器,以便用户可以在线查看他的联系人。 为此,我想通知远程服务器手机上对联系人列表所做的任何更改。

我已经在启动手机时启动的服务中的'ContactsContract.Contacts.CONTENT_URI'上设置了一个ContentObserver。

我有一些静止,前两个是偶然的,第三个是我的主要关注点。

1:一旦我设置了一个在我的Cursor上注册ContentObserver的服务,该观察者是否只存在于服务中?我的意思是,如果服务被杀死,contentObserver会继续观察吗?

2:我怀疑答案是否定的,但无论如何我都会问。无论如何知道哪个联系人正在更新触发我的contentObserver的onchange方法?目前我必须编制手机上所有联系人的列表并将其发送到我的远程服务器,只需发送正在更新的联系人的详细信息就会轻松得多。

3:这是我的主要问题,当我对联系人列表进行更改时,onChange方法会连续两次触发。 1次更改,2次通话。无论如何管理这个?

    public class ContactService extends Service {

    JSONArray contactList;

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onCreate() {
        Log.i("C2DM","content observers initialised");
        super.onCreate();



        //Call Log Content Provider observer

        MyContentContactsObserver contactsObserver = new MyContentContactsObserver();
        ContactService.this.getContentResolver().registerContentObserver (ContactsContract.Contacts.CONTENT_URI, true, contactsObserver);   

    }

    private class MyContentContactsObserver extends ContentObserver {

        public MyContentContactsObserver() {
            super(null);
        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);    
            Log.i("LOG","detected change in contacts: "+selfChange);
        }
    }
}

在我的logCat中输入2个快速行:

 detected change in contacts: false 
 detected change in contacts: false

5 个答案:

答案 0 :(得分:30)

我做了类似的实施,并在日历活动ContentObserver上设置了URI。我遇到了你现在面临的所有问题。所以我的解决方案/建议的问题就在这里......

  

1)一旦我设置了一个注册ContentObserver的服务   我的光标,       那个观察者只存在于服务中吗?我的意思是,如果服务被杀,       contentObserver会继续观察吗?

不,它不会通过URI观察您的内容。但是有解决方案。您可以创建一个始终运行的服务。由于某些内存问题或其他意外解雇,该服务将在解除后立即再次创建。除非明确驳回,否则它将一直继续运行。要在Service类中创建此类服务覆盖OnStartCommand(Intent intent, int flags, final int startId)并返回START_STICKY。看看下面的代码。

public int onStartCommand(Intent intent, int flags, final int startId) {

    String authenticationKey = ((MainApplication) getApplicationContext()).getDbConnector().getUserAuthDefault() ;
    // if user is not registered yet, stop this service, it may be called from boot reciever.
    if(authenticationKey == null){
        stopSelf();
    }

    // restart, if we get killed.
    return START_STICKY;
}  

  

2:我怀疑答案是否定的,但无论如何我都会问。无论如何还有   知道哪个       正在更新的联系人是否触发了我的contentObserver的onchange方法?       目前我必须编制手机上所有联系人的列表并发送给他们       我的远程服务器,只是发送正在更新的联系人的详细信息会更容易。

你再次猜对了。:)答案是否 无法确切知道哪个联系人已更新。 ContentObserver只提供一个事件,让您知道对uri指向的数据进行了一些更改。但是我认为你正在低效地处理这种情况,因为你正在编译所有联系人的列表并将它们发送回服务器。
我建议你在本地存储中维护一个成功发送到服务器的联系人列表。下次当您使用onChange() contentObserver方法接听来自contacts的所有content_URI时,请将其与之前存储的列表进行比较,并仅将更新的联系人发送到服务器。

  

3:当我对联系人列表进行更改时,这是我的主要问题   onChange方法       正在连续两次被解雇。 1次更改,2次通话。无论如何管理       此?

是的,这种情况发生了,但我猜你的印象是错误的。在我的情况下,它曾经随机射击两次或三次甚至更多次。所以我设置了threshold_time间隔。如果再次触发ContentObserver的时间间隔,那么我就不会处理它。我做了这样的事情..

long lastTimeofCall = 0L;
long lastTimeofUpdate = 0L;
long threshold_time = 10000;

    /* (non-Javadoc)
     * @see android.database.ContentObserver#onChange(boolean)
     */
    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);

        Log.d("content service", "on change called");

        lastTimeofCall = System.currentTimeMillis();

        if(lastTimeofCall - lastTimeofUpdate > threshold_time){

         //write your code to find updated contacts here

          lastTimeofUpdate = System.currentTimeMillis();
        }

    }  

希望这些建议/解决方案有所帮助。

答案 1 :(得分:12)

考虑如何注册观察者。 registerContentObserver的第二个参数是boolean notifyForDescendents,您已将其设置为true。因此,当ContactsContract.Contacts.CONTENT_URI或其任何descandants uris被更改时,您将收到通知。可能是某些与给定id联系的操作会触发特定联系人uri和全局联系人uri的通知 - 第二个参数为true,您的观察者会观察到这两者。

另一件事是您可以注册特定联系人ID uri的内容观察者 - ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId)。然而,这与您的问题无关,但为每个联系人注册单独的观察者看起来相当丑陋;)

另一个建议:也许保持观察者作为最终字段不在onCreate中重新创建它(但只在那里注册)。

public class ContactService extends Service {

    private final ContentObserver contactsObserver = new ContentObserver(null) {

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);    
            Log.i("LOG","detected change in contacts: "+selfChange);

            //your code here
        }
    };

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onCreate() {

        super.onCreate();

        getContentResolver().registerContentObserver(
                ContactsContract.Contacts.CONTENT_URI, true, contactsObserver);   
    }

    @Override
    public void onDestroy() {

        getContentResolver().unregisterContentObserver(contactsObserver);

        super.onDestroy();
    }
}

答案 2 :(得分:5)

来到你的nr。 3:两次给观察者的电话之间有多长时间?我认为你的观察者因为联系人同步而被调用两次,这也会更新数据库。 IE浏览器。第一次是您的实际更新,第二次是同步完成并在同一联系行中注册其同步值。

你能否关闭同步并查看它是否仍然被调用两次?

答案 3 :(得分:0)

您可能有两个或更多个ContactService实例,所有这些实例都注册了相同的ContactsContract.Contacts.CONTENT_URI。

答案 4 :(得分:-1)

在我自己的代码中面对同样的问题,发现我两次调用registerContentObserver()。虽然它是完全相同的URI并且在同一个类中,但我的onChange(boolean selfChange)方法被调用了两次。