4

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

调用用户登录的第一个屏幕MainActivity。它包含两个EditTexts:一个用于 FB 用户名,另一个用于密码。还有两个按钮:清除,清空EditTexts 和确定,单击后通过 执行以下 AsyncTask 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();
   }
}

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

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

4

2 回答 2

6

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

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

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

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

什么时候应该这样做:

  1. 登录 Facebook。
  2. 打开一个ListActivity.
  3. 获取您的所有联系人。
  4. 根据您的联系人的存在动态更新ListView(仍然没有完成这部分)。

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

于 2011-09-06T06:04:55.747 回答
0

也许你也同意 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());
    }
}
于 2015-10-25T05:31:06.570 回答