【问题标题】:Content Observer onChange method called twice after 1 change in cursorContent Observer onChange 方法在光标 1 次更改后调用两次
【发布时间】:2012-04-16 12:23:26
【问题描述】:

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

我在“ContactsContract.Contacts.CONTENT_URI”上设置了一个 ContentObserver,该服务在手机启动时启动。

我有几个问题,前两个是偶然的,第三个是我主要关心的问题。

1:一旦我设置了一个在我的光标上注册 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

【问题讨论】:

    标签: java android android-contentprovider


    【解决方案1】:

    我做了一个类似的实现,并在日历的事件URI 上设置了一个ContentObserver。我面临着所有的问题,你现在面临着。所以你对我的解决方案/建议的问题就在这里......

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

    不,它不会通过 URI 观察您的内容。但有解决办法。您可以创建一个始终运行的服务。由于某些内存问题或其他意外解除,该服务将在被解除后立即重新创建。除非明确解除,否则它将始终继续运行。要在您的服务类中创建此类服务覆盖 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 指向的数据发生了一些变化。但我认为你处理这种情况的效率很低,因为你正在编译所有联系人的列表并将它们发送回服务器。
    我建议您在本地存储中维护一个成功发送到服务器的联系人列表。下次当您在contentObserveronChange() 方法中接到电话时,您会从content_URI 获取所有contacts,将它们与先前存储的列表进行比较并仅将更新的联系人发送到服务器。

    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,10000 毫秒秒是否在所有情况下都有效,您有没有收到用户对此的任何反馈,谢谢?
    • 我还没有收到用户对此的任何回复。我不会说它的最佳实施方式,但它现在工作良好。它是一种我们必须实施的补丁。
    • 我更喜欢currentTime = System.CurrentTimeMillis() if (nextCallTime < treshold) nextCallTime = currentTime + treshold 但其他方面的解决方案也不错。
    • 另外,如果阈值为 10 秒,我们会错过那些在 10 秒内发生的“真实”变化。就像用户在 10 秒内更改 2 个联系人,那么只有第一个会被同步,因为第二个会被同步忽略。在这种情况下该怎么办?在我的研究中,虚假的第二次/第三次呼叫可能会在 3.4 秒后持续..
    • 当您下次接到电话时,您将不得不遍历内容提供商提供的所有数据并将其与您的数据库进行匹配。这是我能看到的唯一方法,以确保没有任何东西被忽略。
    【解决方案2】:

    考虑如何注册观察者。 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();
        }
    }
    

    【讨论】:

      【解决方案3】:

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

      能否关闭同步,看看它是否仍会被调用两次?

      【讨论】:

      • 啊,这是个好电话。我已经根据上面提供的建议解决了这个问题,但你的回复很有道理。
      • 完全有道理,没试过但有道理..如果是这样的话怎么办?我仍然认为我们需要使用一些阈值场景..
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          猜你喜欢
          • 2015-05-23
          • 1970-01-01
          • 2021-04-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-06-05
          • 1970-01-01
          相关资源
          最近更新 更多