Passing data from bg thread to UI thread using Handler? - android

In Handler, we can pass some data from a background thread to the UI thread like this:
private void someBackgroundThreadOperation() {
final String data = "hello";
handler.post(new Runnable() {
public void run() {
Log.d(TAG, "Message from bg thread: " + data);
}
}
}
If we use the above, we cannot then use Handler.removeCallbacks(Runnable r), because we won't have references to any of the anonymous runnables we created above.
We could create a single Runnable instance, and post that to the handler, but it won't allow us to pass any data through:
private void someBackgroundThreadOperation() {
String data = "hello";
handler.post(mRunnable); // can't pass 'data' through.
}
private Runnable mRunnable = new Runnable() {
public void run() {
Log.d(TAG, "What data?");
}
}
We can however use the Handler.removeCallbacks(mRunnable) method then, since we have a reference to it.
I know I can setup some synchronization myself to get the second method working, but I'm wondering if Handler offers any utility for passing data through to a single referenced Runnable, almost like in the example above?
The closest I can think of from browsing the docs is to use something like:
private void someUiThreadSetup() {
mHandler = new Handler(new Callback() {
#Override
public boolean handleMessage(Message msg) {
String data = (String)msg.obj;
return false;
}
});
}
private void someBackgroundThreadOperation() {
String data = "please work";
Message msg = mHandler.obtain(0, data);
mHandler.sendMessage(msg);
}
private void cleanup() {
mHandler.removeMessages(0);
}
Is this the proper way of doing it?
Thanks

IMHO, the pattern you seek is packaged as the AsyncTask class.

Related

Converting Java Thread processing messages to queue using Kotlin Coroutines

My Android app has a dedicated thread to make network api calls using retrofit.The thread makes a retrofit api call depending on the type of the message received. I would like to migrate this code to use Kotlin co-routines but I not sure how to implement message queues without using a Thread. Any help will be greatly appreciated.
Pseudo java code:
public class MyThread extends Thread {
private final Messenger mPushMessenger;
private Handler mHandler;
private Object mThreadLock;
private IMessage mMessage;
private MyThread(IMessage message) {
super(message.getThreadLink().name());
mMessage = message;
start();
mPushMessenger = new Messenger(getHandler());
mThreadLock = new Object();
}
public static MyThread getInstance(IMessage ISenThread)
{
return new MyThread(ISenThread);
}
public void NotifyThreadLock() {
synchronized (mThreadLock)
{
try {
mThreadLock.notify();
}
catch(Exception e)
{
}
}
}
private Handler getHandler() {
synchronized (this) {
while (mHandler == null) {
try {
wait();
} catch (InterruptedException ignored) {
}
}
}
return mHandler;
}
public void run() {
Looper.prepare();
synchronized (this) {
mHandler = new MyThread().MyHandler();
notifyAll();
}
Looper.loop();
}
public void pushMessage(Object message, int what) {
try {
android.os.Message msg = android.os.Message.obtain(null, what, message);
mPushMessenger.send(msg);
} catch (Exception ignored) {
}
}
public void EmptyQueue() {
}
/*
public void pushMessage(String message) {
pushMessage(message, SenFCMLink.SenEyeStatusSender.STATUS_GENERAL_MESSAGE);
}
*/
private class MyHandler extends Handler {
public void handleMessage(Message msg) {
switch(msg.Type)
{
case A:
// call retrofit API Method A
case B:
// call retrofit API Method B
case C:
// call retrofit API method C.
}
}
}
}
Retrofit methods can be declared for either synchronous or asynchronous execution
call.execute();
is a synchronous network call ,0
Synchronous requests are declared by defining a return type.Synchronous methods are executed on the main thread. That means the UI blocks during request execution and no interaction is possible for this period. Using the .execute() method on a call object will perform the synchronous request. The deserialized response body is available via the .body() method on the response object.
call.enqueue()
is a asynchronous network call, Asynchronous requests don’t have a return type. Instead, the defined method requires a typed callback as last method parameter.Using asynchronous requests forces you to implement a Callback with its two callback methods: success and failure. When calling the asynchronous getTasks() method from a service class, you have to implement a new Callback and define what should be done once the request finishes.
like below:
call.enqueue(new Callback<Object>() {
#Override
public void onResponse(Call<Object> call, Response<Object> response) {
response = response.body();
}
#Override
public void onFailure(Call<Object> call, Throwable t) {
}

webmethod not call on firebase notification onMessageReceived method

public void onMessageReceived(RemoteMessage remoteMessage) {
try {
String type=remoteMessage.getData().get("type");
if(type.equals("1001")) {
CommonClass common = new CommonClass(getApplication());
CommonClass.MyTaskSendLog.execute(getApplicationContext(), DeviceDetails,lines);
}
} catch (Exception ex) {
}
}
this code gives error:
Method execute must be called from the main thread, currently inferred
thread is worker
I suppose your method is in a service.
To access UI thread (main thread) in a service you have to create an handler and call method inside as this:
if(type.equals("1001")) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
CommonClass common = new CommonClass(getApplication());
CommonClass.MyTaskSendLog.execute(getApplicationContext(), DeviceDetails,lines);
}
});
}
You could create Handler in onCreate of service.

I cant receive text sent from thread to UI

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"));

Android runnable on thread called multiple times

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.

Trying to update a TextField through a Handler, and getting CalledFromWrongThreadException

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);
}

Categories

Resources