Android: обновление ArrayAdapter / ListView из рабочего потока PacketListener
Я реализую XMPP-клиент для приложения Android. Для получения сообщений чата, которые мне отправляют, я использую PacketListener от Smack. С XMPP частью приложения все работает нормально. Я могу отправлять и получать сообщения. Но у меня проблемы с отображением полученных сообщений.
Для отображения сообщений мое приложение использует ArrayAdapter, который связывает их с ListView. Сам адаптер работает нормально, так как он отображает сообщения, которые я отправляю без проблем. Но не так с полученными сообщениями. Они просто отображаются, если происходит какое-либо взаимодействие с пользовательским интерфейсом. Видимо, это проблема с многопоточностью.
Если я не ошибаюсь из-за того, что Javadoc и отладчик говорят мне, метод PacketListener.processPacket() запускается в собственном потоке, и обновление ListView выполняется только в том случае, если у обработчика есть следующая вещь, которую нужно сделать, и, следовательно, обрабатывает Это. Мой вопрос сейчас, как я могу сказать обработчику немедленно обработать его? Как здесь работает связь между этим рабочим потоком и основным потоком? Поскольку я не делал Runnable сам, я не знаю, как с этим справиться.
И вот код:
public class Chat extends Activity {
private ArrayList<String> mMessages;
private ArrayAdapter<String> mAdapter;
private ListView mMessageListView;
private EditText mInput;
private String mRecipient;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chat);
Bundle extras = getIntent().getExtras();
mRecipient = extras.getString("jabberid");
mMessages = new ArrayList<String>();
mMessageListView = (ListView) findViewById(R.id.chatMessageList);
mInput = (EditText) findViewById(R.id.chatInput);
mAdapter = new ArrayAdapter<String>(this, R.layout.channelentry, mMessages);
mAdapter.notifyDataSetChanged();
mMessageListView.setAdapter(mAdapter);
// Getting messages
PacketFilter packetFilter = new MessageTypeFilter(Message.Type.chat);
// XMPPConnection already connected and authenticated
XmppManager.connection.addPacketListener(new PacketListener() {
// Here is where it doesn't display the received message
@Override
public void processPacket(Packet packet) {
Message message = (Message) packet;
displayMessage(message);
}
}, packetFilter);
// Sending messages
Button send = (Button) findViewById(R.id.chatSend);
send.setOnClickListener(new View.OnClickListener() {
// Here everything works just fine
@Override
public void onClick(View v) {
Message message = new Message(mRecipient, Message.Type.chat);
message.setBody(mInput.getText().toString());
XmppManager.connection.sendPacket(message);
displayMessage(message);
}
});
}
private void displayMessage(Message message) {
String sender = message.getFrom();
String chat = sender + " > " + message.getBody();
mAdapter.add(chat);
mAdapter.notifyDataSetChanged();
}
}
2 ответа
Если вы создаете Handler
в вашем потоке пользовательского интерфейса вы можете позвонить post()
на это с Runnable
аргумент, который вызывает ваш displayMessage()
метод. Кроме того, вы можете позвонить runOnUiThread()
, которая является частью Activity
класс, опять же, передавая Runnable
что вызывает displayMessage()
,
Я также заметил, что вы звоните sendPacket()
от твоего onClick()
обработчик. Вы должны убедиться, что вы не блокируете поток пользовательского интерфейса. Может быть sendPacket()
фактически создаст новый поток для фактической отправки, но это то, что вы должны проверить.
Я изменил ваш код следующим образом. Надеюсь, он будет работать сейчас.
public class Chat extends Activity {
private ArrayList<String> mMessages;
private ArrayAdapter<String> mAdapter;
private ListView mMessageListView;
private EditText mInput;
private String mRecipient;
String chat;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chat);
Bundle extras = getIntent().getExtras();
mRecipient = extras.getString("jabberid");
mMessages = new ArrayList<String>();
mMessageListView = (ListView) findViewById(R.id.chatMessageList);
mInput = (EditText) findViewById(R.id.chatInput);
mAdapter = new ArrayAdapter<String>(this, R.layout.channelentry, mMessages);
mAdapter.notifyDataSetChanged();
mMessageListView.setAdapter(mAdapter);
// Getting messages
PacketFilter packetFilter = new MessageTypeFilter(Message.Type.chat);
// XMPPConnection already connected and authenticated
XmppManager.connection.addPacketListener(new PacketListener() {
// Here is where it doesn't display the received message
@Override
public void processPacket(Packet packet) {
Message message = (Message) packet;
//displayMessage(message);
String sender = message.getFrom();
chat = sender + " > " + message.getBody();
Message msg = handler.obtainMessage();
msg.arg1 = 1;
handler.sendMessage(msg);
}
}, packetFilter);
// Sending messages
Button send = (Button) findViewById(R.id.chatSend);
send.setOnClickListener(new View.OnClickListener() {
// Here everything works just fine
@Override
public void onClick(View v) {
Message message = new Message(mRecipient, Message.Type.chat);
message.setBody(mInput.getText().toString());
XmppManager.connection.sendPacket(message);
displayMessage(message);
}
});
}
private void displayMessage(Message message) {
String sender = message.getFrom();
String chat = sender + " > " + message.getBody();
mAdapter.add(chat);
mAdapter.notifyDataSetChanged();
}
private final Handler handler = new Handler() {
public void handleMessage(Message msg) {
if(msg.arg1 == 1){
mAdapter.add(chat);
mAdapter.notifyDataSetChanged();
}
}
}
}