Android 版 Facebook 聊天应用程序中的 Smack 问题:Connection.getRoster().getEntries() 始终为空

发布于 2024-12-01 00:16:11 字数 2780 浏览 1 评论 0原文

我正在尝试在 Android 上实现一个简单的 Facebook 聊天应用程序。它允许用户登录,然后应用程序打开一个新的 ListActivity 枚举用户的在线联系人,单击某个项目后,将打开另一个 Activity 进行对话。

用户登录的第一个屏幕称为 MainActivity。它包含两个 EditText:一个用于 FB 用户名,另一个用于密码。还有两个按钮:清除,清空 EditText 和确定,单击后,通过 new LoginTask().execute() 执行以下 AsyncTask

class LoginTask extends AsyncTask<Void, Void, Void> {
   private ProgressDialog loading;

   @Override
   protected void onPreExecute() {
      loading = ProgressDialog.show(MainActivity.this, "", "Loading...");
   }

   @Override
   protected Void doInBackground(Void... params) {
      // setup XMPP connection
      ConnectionConfiguration config = new ConnectionConfiguration(
            "chat.facebook.com", 5222, "chat.facebook.com");
      config.setDebuggerEnabled(true);
      config.setSASLAuthenticationEnabled(true); // TRUE for fb
      conn = new XMPPConnection(config);

      try {
         // attempt login
         conn.connect();
         SASLAuthentication.supportSASLMechanism("PLAIN", 0);
         conn.login(login_field.getText().toString(),
               pwd_field.getText().toString(), "");

         Log.d("TRACE", "isAuthenticated? " + conn.isAuthenticated());

         // list online contacts
         Roster roster = conn.getRoster();
         Collection<RosterEntry> entries = roster.getEntries();
         Log.d("TRACE", "entries.size()=" + entries.size());
         for (RosterEntry e : entries) {
            Log.d("PRESENCE", e.getUser() + "=" + roster.getPresence(e.getUser()).isAvailable());
            if (roster.getPresence(e.getUser()).isAvailable()) {
               HashMap<String, String> contact = new HashMap<String, String>();
               contact.put(NAME_KEY, e.getName());
               contact.put(USERJID_KEY, e.getUser());
               Log.d("ADD", "NAME_KEY=" + e.getName() + " USERJID_KEY=" + e.getUser());
               contacts.add(contact);
            }
         }
         Collections.sort(contacts, new ContactComparator()); // sort alphabetically
         Log.d("TRACE", "MainActivity.contacts.size(): " + contacts.size());

         Intent in = new Intent(MainActivity.this, ContactList.class);
         startActivity(in);
      } catch (Exception e) {
         Log.d("EXCEPTION", e.toString());
      }

      return null;
   }

   @Override
   protected void onPostExecute(Void result) {
      loading.dismiss();
   }
}

:问题是,每当打开第二个屏幕时,ListView 通常不会显示任何内容,但有时会显示。我决定将日志消息添加到通过 conn.getRoster() 检索联系人的部分,我发现大多数时候都是对 conn.getRoster() 的调用.getEntries.size() 返回零,但当我实际通过浏览器登录 Facebook 时,我可以看到我确实有在线联系人。但我不明白的是,为什么它有时会返回正确的数字,但在我运行该应用程序的几乎每十亿次中只返回一次。

有人帮忙吗?这个应用程序将在四个小时内到期,但我不知道该怎么办了,因为我的代码似乎没有任何问题。

I'm trying to implement a simple Facebook chat application on Android. It lets the user login, after which the app opens a new ListActivity enumerating the user's online contacts, and upon clicking on an item, another Activity opens where their conversation will take place.

The first screen where the user logs in is called MainActivity. It contains two EditTexts: one for the FB username, and the other for password. There are also two buttons: Clear, which empties the EditTexts, and OK, which upon click, executes the following AsyncTask via new LoginTask().execute():

class LoginTask extends AsyncTask<Void, Void, Void> {
   private ProgressDialog loading;

   @Override
   protected void onPreExecute() {
      loading = ProgressDialog.show(MainActivity.this, "", "Loading...");
   }

   @Override
   protected Void doInBackground(Void... params) {
      // setup XMPP connection
      ConnectionConfiguration config = new ConnectionConfiguration(
            "chat.facebook.com", 5222, "chat.facebook.com");
      config.setDebuggerEnabled(true);
      config.setSASLAuthenticationEnabled(true); // TRUE for fb
      conn = new XMPPConnection(config);

      try {
         // attempt login
         conn.connect();
         SASLAuthentication.supportSASLMechanism("PLAIN", 0);
         conn.login(login_field.getText().toString(),
               pwd_field.getText().toString(), "");

         Log.d("TRACE", "isAuthenticated? " + conn.isAuthenticated());

         // list online contacts
         Roster roster = conn.getRoster();
         Collection<RosterEntry> entries = roster.getEntries();
         Log.d("TRACE", "entries.size()=" + entries.size());
         for (RosterEntry e : entries) {
            Log.d("PRESENCE", e.getUser() + "=" + roster.getPresence(e.getUser()).isAvailable());
            if (roster.getPresence(e.getUser()).isAvailable()) {
               HashMap<String, String> contact = new HashMap<String, String>();
               contact.put(NAME_KEY, e.getName());
               contact.put(USERJID_KEY, e.getUser());
               Log.d("ADD", "NAME_KEY=" + e.getName() + " USERJID_KEY=" + e.getUser());
               contacts.add(contact);
            }
         }
         Collections.sort(contacts, new ContactComparator()); // sort alphabetically
         Log.d("TRACE", "MainActivity.contacts.size(): " + contacts.size());

         Intent in = new Intent(MainActivity.this, ContactList.class);
         startActivity(in);
      } catch (Exception e) {
         Log.d("EXCEPTION", e.toString());
      }

      return null;
   }

   @Override
   protected void onPostExecute(Void result) {
      loading.dismiss();
   }
}

My problem is that whenever the second screen is opened, the ListView often doesn't show anythying, BUT IT SOMETIMES DOES. I decided to add log messages to the part where I'm retrieving the contacts via conn.getRoster() and I found out that most of the time, a call to conn.getRoster().getEntries.size() returns a zero, but when I actually login to Facebook via browser, I can see that I do have online contacts. What I don't understand, though, is why it sometimes returns the correct number, but only once in almost every billion times I run the application.

Somebody help, please? This app's due in four hours and I don't know what to do anymore since there seems to be nothing wrong with my code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

允世 2024-12-08 00:16:11

我在这里发布这个问题的第二天就找到了答案。显然,当调用 conn.login() 时,用户将通过身份验证,Facebook 服务器开始发送名册项目。大多数时候第二个屏幕上什么也没有出现的原因是,即使在 Facebook 服务器发送包含名册项目的数据包之前,代码就到达了对 startActivity() 的调用。但有时,服务器的速度足以发送数据包,但 startActivity() 被调用,并且添加到 ArrayList 会被中断,从而导致不完整的 ListView< /code> 您的在线联系人。我采取的解决方法是在 conn.login() 之后调用 Thread.sleep() 并创建 5 到 10 秒的延迟,以便服务器有足够的时间发送所有数据包。

这种方法只工作了一段时间,但当我还需要检索联系人的照片(使用 VCard)时,即使 30 秒的延迟也不够。您应该找到一种方法来确定服务器何时完成发送数据包和 VCard,并仅在收到信号时才继续添加到 ArrayList。

不幸的是,查看 Smack 文档,似乎没有办法做到这一点。但那是因为我的目标总体来说是错误的。我想做的是:

  1. 登录 Facebook。
  2. 获取所有在线联系人。
  3. 将它们放入 ArrayList 中。
  4. 打开ListActivity
  5. ListActivity中显示ArrayList内容。

何时应该这样做:

  1. 登录 Facebook。
  2. 打开ListActivity
  3. 获取您的所有联系人。
  4. 根据您的联系人的状态动态更新 ListView(尚未完成这部分)。

简而言之,Smack API 被设计为处理实时事件,并且因为它是一个即时消息传递库,所以我认为它不应该被设计成其他方式。我在第一个示例中尝试做的事情是相当静态的。

I found the answer to this question the day after I posted it here. Apparently, when conn.login() is called, the user becomes authenticated and the Facebook server starts sending the roster items. The reason why nothing appears in the second screen most of the time is that the code reaches the call to startActivity() EVEN BEFORE the Facebook server is done sending the packets containing the roster items. Sometimes, though, the server is fast enough to send the packets but startActivity() is called and adding to the ArrayList becomes interrupted, resulting to an incomplete ListView of your online contacts. A workaround I did was to call Thread.sleep() after conn.login() and create a 5- to 10-second delay so that the server would have enough time to send all the packets.

That worked only for a while but when I has the added task of retrieving even the photos of my contacts (using VCard), even a 30-second delay wasn't enough. You should find a way then to determine when the server is done sending packets and the VCards and proceed with adding to the ArrayList only when you get the signal.

Unfortunately, looking at the Smack documentation, there doesn't seem to be a way to do that. But that's because my goal was, overall, wrong. What I was trying to do is this:

  1. Log in to Facebook.
  2. Get all online contacts.
  3. Put them in an ArrayList.
  4. Open a ListActivity.
  5. Display the ArrayList contents in the ListActivity.

When it should be doing this:

  1. Log in to Facebook.
  2. Open a ListActivity.
  3. Get ALL your contacts.
  4. Dynamically update the ListView depending on your contacts' presence (still haven't done this part).

In short, the Smack API has been designed to handle real-time events, and because it's an instant messaging library, I don't think it should have been designed otherwise. What I was trying to do in the first example was rather static.

轻拂→两袖风尘 2024-12-08 00:16:11

也许您还考虑 roster.reloadAndAwait()

public void getRoaster(final Callback<Collection<RosterEntry>> callback) {
    final Roster roster = Roster.getInstanceFor(connection);
    if (!roster.isLoaded())
    try {
        roster.reloadAndWait();
    } catch (SmackException.NotLoggedInException | SmackException.NotConnectedException | InterruptedException e) {
        e.printStackTrace();
    }

    Collection<RosterEntry> entries = roster.getEntries();
    for (RosterEntry entry : entries) {
        android.util.Log.d(AppConstant.PUBLIC_TAG, entry.getName());
    }
}

Maybe you also consifer roster.reloadAndAwait()

public void getRoaster(final Callback<Collection<RosterEntry>> callback) {
    final Roster roster = Roster.getInstanceFor(connection);
    if (!roster.isLoaded())
    try {
        roster.reloadAndWait();
    } catch (SmackException.NotLoggedInException | SmackException.NotConnectedException | InterruptedException e) {
        e.printStackTrace();
    }

    Collection<RosterEntry> entries = roster.getEntries();
    for (RosterEntry entry : entries) {
        android.util.Log.d(AppConstant.PUBLIC_TAG, entry.getName());
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文