I try to send message from class that handles background process for my application communicates with server and prace responce than serialize the objects and send it to activity that visualise results. Activity has a public handler.
public final Handler _handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
String smsg = msg.getData().toString();
if (!smsg.contains("endResult")) {
Log.d("New 10 a","New 10");
deviceBasicList a = (deviceBasicList) msg.getData()
.getSerializable("devices");
List<deviceBasic> basicList = a.getList();
setDeviceList(basicList);
} else {
hideLoadingDialog();
}
super.handleMessage(msg);
}
};
In the background process class I have an instance of the activity
this.searchActivity = a;
After I parce the objects im sending message :
private void sendDeviceList() {
if (!this.deviceList.isEmpty()) {
deviceBasicList basicList = new deviceBasicList(this.deviceList);
Message msg = new Message();
msg.setTarget(this.searchActivity._handler);
Bundle bundle = new Bundle();
bundle.putSerializable("devices", basicList);
msg.setData(bundle);
this.searchActivity._handler.sendMessage(msg);
this.deviceList.clear();
}
}
Because of the large amounts of data Im sending information for objects 10 by 10.
The result is that In the background class I send exact amount of messages I should, but in the activity im reciveing about 5 times more that I have send.
Related
here is my hander:
public Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String mmsg = msg.getData().getByteArray("msg").toString();
Toast.makeText(clientActivity.this, mmsg,Toast.LENGTH_LONG).show();
}
};
I send data from separate thread:
public class client implements Runnable
{
private void showtoast(byte[] msgtoshow){
try {
Bundle mbundle = new Bundle();
Message greeting = new Message();
mbundle.putByteArray("msg", msgtoshow);
greeting.setData(mbundle);
handler.sendMessage(greeting);
}catch(Exception e){
Log.d("error",e.getMessage());
}
}
public void run()
{
msgstrng = "this is supposed to be some text";
showtoast(msgstrng.getBytes());
}
}
Instead of line that I send in msgstng I toast some [B#411dd11] which is always different. I guess it's timestamp or smth. how to get that String value msgstng?
Actually more important for me is to get bytes array, as I'll send bitmaps from socket to UI if i learn this issue
The problem seems to be the way you are getting the string in your Handler.
Try something like this:
String mmsg = new String(msg.getData().getByteArray("msg"));
I am trying to implement MUC with aSmack, but seem to be unable to update the UI with the ArrayAdapter correctly.
I have set up my listener in my ChatService.java like this:
connection.addPacketListener(new MessageListener(), new PacketFilter() {
public boolean accept(Packet packet) {
Message msg = (Message) packet;
return msg.getBody() != null;
}});
And my listener class looks like this:
private class MessageListener implements PacketListener {
/**
* Constructor.
*/
public MessageListener() {
}
public void processPacket(Packet packet) {
if(packet instanceof Message ) {
Message message = (Message) packet;
android.os.Message aosMessage = new android.os.Message();
aosMessage.what = message.getType().ordinal();
aosMessage.obj = message;
messageHandler.handleMessage(aosMessage);
}
}
}
Now the problem is as follows;
First, I tried updating the UI via the handler (the handler is defined in my ChatActivity.java):
private final Handler messageHandler = new Handler() {
private Message chatMessage;
#Override
public void handleMessage(final android.os.Message msg) {
chatMessage = (Message) msg.obj;
if(Message.Type.values()[msg.what].equals(Message.Type.groupchat)) {
conversationArrayAdapter.add("Received group message: " + chatMessage.getBody());
} else if (Message.Type.values()[msg.what].equals(Message.Type.chat)) {
conversationArrayAdapter.add("Received private message: " + chatMessage.getBody());
}
}
};
But this won't update the UI until an interaction is introduced from the user's end.
After a little web-digging, I realized I need to use the post (or runonUIthread) method to implement this, and so I tried:
private final Handler messageHandler = new Handler() {
private Message chatMessage;
#Override
public void handleMessage(final android.os.Message msg) {
chatMessage = (Message) msg.obj;
this.post(new Runnable() {
public void run() {
if(Message.Type.values()[msg.what].equals(Message.Type.groupchat)) {
conversationArrayAdapter.add("Received group message: " + chatMessage.getBody());
conversationArrayAdapter.notifyDataSetChanged();
} else if (Message.Type.values()[msg.what].equals(Message.Type.chat)) {
conversationArrayAdapter.add("Received private message: " + chatMessage.getBody());
conversationArrayAdapter.notifyDataSetChanged();
}
}
});
}
};
But now the run() method is called multiple times (confirmed on debug), and the UI is updated with a seemingly-random-amount of the same messages (different amount of repetitions for every message, but all the messages are shown).
This is my first android app (but I am a java developer by trade), and I am quite sure I am doing something wrong in regards to the architecture here. Any assistance (preferably a detailed one - with code samples and / or references to the correct areas of the documentation) would be greatly appreciated.
Thanks in advance!
Corrected working snippet after #Emil's answer
private class MessageListener implements PacketListener {
/**
* Constructor.
*/
public MessageListener() {
}
public void processPacket(Packet packet) {
if(packet instanceof Message ) {
Message message = (Message) packet;
android.os.Message aosMessage = new android.os.Message();
aosMessage.what = message.getType().ordinal();
aosMessage.obj = message;
aosMessage.setTarget(messageHandler);
aosMessage.sendToTarget();
}
}
}
Don't call handleMessage directly :
messageHandler.handleMessage(aosMessage);
You should obtain a message from a given Handler or setTarget(Handler handler) on a message and then call sendToTarget() on the message to get it to be sent and processed by the Handler. If you implement the Handler correctly you don't need the post for this.
I made a simple RPC mechanism apps for android and I faced a problem that I can not go back to the UI Thread from RPC class.
Basically I have 3 classes(ServerActivity,ServerView and ServiceImplementation), I created 3 classes because I use RPC and Protocol Buffer for drawing.
Server Activity :
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_sv = new ServerView(this);
setContentView(_sv);
rpcConnectionFactory = SocketRpcConnectionFactories.createServerRpcConnectionFactory(SERVER_PORT);
int nThreadPool = 1;
server = new RpcServer(rpcConnectionFactory, Executors.newFixedThreadPool(nThreadPool), true);
server.registerBlockingService(Service.newReflectiveBlockingService(new ServiceImpl(myServiceHandler)));
server.run();
}
Handler myServiceHandler = new Handler() {
public void handleMessage(Message msg) {
Log.i("Handler", "Handler IN");
_sv.set(msg.what); /*To communicate with the view*/
super.handleMessage(msg);
}
};
ServiceImplementation :
public CanvasServiceImpl(Handler mActivity) {
backToUIThread = mActivity;
}
public Response drawCircle(RpcController controller, Circle1 request)
throws ServiceException {
android.os.Message message = new android.os.Message();
message.what = 1;
ImplHandler.sendMessage(message);
Response response = Response.newBuilder().setResult("drawCircle Success").build();
return response;
}
I can not reach my UI Thread. Does anybody know why ?
Thanks,
Robert
Instead of
ImplHandler.sendMessage(message);
use
backToUIThread.sendMessage(message);
I'm implementing XMPP client for an Android application. For getting the chat messages that are sent to me, I'm using the PacketListener from Smack. With the XMPP part of the application, everything works fine. I can send and receive messages. But I'm having problems displaying the received messages.
For displaying messages, my application uses an ArrayAdapter that binds them to a ListView. The adapter itself works fine, since it displays the messages I send without any problems. But not so with the received messages. They are just displayed if some interaction with the UI happens. Apparently, this is a threading issue.
If I'm not mistaken by what the Javadoc and the Debugger tell me, the PacketListener.processPacket() method runs in an own thread, and the update of the ListView is only executed if the Handler has a next thing to do and therefore processes it. My question is now, how can I tell the Handler to process it immediately? How does the communication between this worker thread and the main thread work here? Since I didn't make a Runnable myself, I don't know how to handle this.
And here's the code:
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();
}
}
If you create a Handler within your UI thread, you can call post() on it with a Runnable argument that calls your displayMessage() method. Alternatively, you can call runOnUiThread(), which is part of the Activity class, again, passing a Runnable that calls displayMessage().
I've also noticed that you call sendPacket() from your onClick() handler. You should make sure that you don't block the UI thread. Maybe sendPacket() will actually spawn a new thread to do the actual send, but it's something that you should check.
I modified your code as follows.Hope it will work now.
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();
}
}
}
}
I seem to be having trouble with updating a TextView from a thread. I have a GameConnection class (which manages a socket connection) which I want to use across activities. It calls a local "onMessage", which then uses the target handler to call dispatch Message. The "Handler" in this case, is in my GameBrowser activity.
Here's code from the GameConnection class.
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(),true);
String message = "".intern();
// as a newline character is read, we interpret it as a message
while ((message = in.readLine()) != null && isConnected){
onMessage(message);
}
As said above, a local method "onMessage" method handles dispatching of the message.
private void onMessage(String message){
... // create message from String
handler.dispatchMessage( msg );
}
However, when I get the response in the GameBrowser class, I get a CalledFromWrongThreadException . Initially, I was using a callback method, which of course wasn't working. So, after some research, I've found that I have to use a Handler, but I can't seem to get it right.
public class GameBrowser extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(C.tag, "GameBrowser.onCreate addr:" + this);
handler = new Handler(new HandlerCallback());
connection.addMessageListener(handler);
connection.connect();
txtGameLabel = (TextView)findViewById( R.id.txtGamesLabel);
setContentView(R.layout.game_browser);
}
private class HandlerCallback implements Callback{
#Override
public boolean handleMessage(Message msg) {
if (txtGameLabel == null){
txtGameLabel = (TextView)findViewById( R.id.txtGamesLabel);
}
String message = msg.getData().getString("message");
Log.d(C.tag, "GameBrowser recieved message " + message);
txtGameLabel.setText("Data: " + message);
return true;
}
}
}
I figured out what I was doing wrong. Instead of calling the handler from the socket thread, I used a callback, then used Runnable to post to the handler in the GameConnection class. When onMessage executes "run", which executes "updateTextField", we're back in the main thread.
#Override
public void onMessage(final String message) {
handler.post(new Runnable(){
#Override
public void run() {
updateTextField(message);
}
});
}
private void updateTextField(String message){
if (txtGameLabel == null)
txtGameLabel = (TextView)findViewById( R.id.txtGamesLabel);
txtGameLabel.setText(message);
}