I have one class in which a listener is implemented and depending on the methods call in listener, I am to update the data in corresponding activities. For this, i am interested in using the different-different handlers for each activity.
Since, my handler class in each activity is static and hence, handler object is non-static in activity.
So, problem is how to use those activity's handlers to send Message to them.? Help?
This is one of my class in which I am to update the data from another class: How to do?
public class HandlerActivity extends Activity {
TextView tv;
int counter;
Handler handler = new HandlerExtension(this);
//handler static class
private static class HandlerExtension extends Handler {
private final WeakReference<HandlerActivity> targetActivity;
//constructor
HandlerExtension(HandlerActivity activity){
this.targetActivity = new WeakReference<HandlerActivity>(activity);
}
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
HandlerActivity activity = targetActivity.get();
if(activity != null){
final String data = msg.getData().getString("counter");//gettting msg data
activity.tv.setText(data);
}
}
}//HandlerExtension
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.handler_activity);
Button startB = (Button) findViewById(R.id.button1);
tv = (TextView) findViewById(R.id.textView1);
final Runnable runnable = new Runnable() {//object to be executed in the thread
#Override
public void run() {
counter+=10;
String result = String.valueOf(counter);
Message msg = new Message();
Bundle bundle = new Bundle();
bundle.putString("counter", result);
msg.setData(bundle);
handler.sendMessage(msg);//Now, updating the view from background thread via handler
SystemClock.sleep(1000);
}
};
//starting the background thread on button click
startB.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new Thread(runnable).start();
}
});
}//onCreate
}//HandlerActivity
Related
I am working on a Bluetooth communication project where I need to transfer data among devices.Upon receiving an InputStream I pass the data to the UI thread from the worker thread using the following code:-
// Read from the InputStream.
numBytes = mmInStream.read(mmBuffer);
// Send the obtained bytes to the UI activity.
Message readMsg = handler.obtainMessage(MessageConstants.MESSAGE_READ,numBytes, -1,mmBuffer);
readMsg.sendToTarget();
Below is my handler class:-
public Handler mHandler = new Handler() {
public synchronized void handleMessage(Message msg) {
byte[] readBuf=(byte[])msg.obj;
String readMsg=new String(readBuf,0,msg.arg1);
TextView textView=findViewById(R.id.textview);
textView.setText(readMsg);
}
}
But This shows the following warning:
This Handler class should be static or leaks might occur(anonymous android.os.Handler).
I tried making the class static but then it gives the following error:-
Non-static method findViewById(int) can't be referenced from a static context.
What should I do to resolve this?
public MyHandler mHandler;
public static class MyHandler extends Handler {
WeakReference<TextView> mTextViewReference;
public MyHandler(TextView textView) {
mTextViewReference = new WeakReference<TextView>(textView);
}
public synchronized void handleMessage(Message msg) {
byte[] readBuf=(byte[])msg.obj;
String readMsg = new String(readBuf,0,msg.arg1);
TextView textView = mTextViewReference.get();
if(textView != null) {
textView.setText(readMsg);
};
}
public void clear() {
mTextViewReference.clear();
mTextViewReference = null;
}
}
protected void onCreate(final Bundle savedInstanceState) {
....
mHandler = new MyHandler(findViewById(R.id.textView));
....
}
#Override
public void onDestroy() {
if(mHandler != null) {
mHandler.clear();
mHandler = null;
}
super.onDestroy();
}
EDIT
Fix above works fine if you just want to update one single TextView. However, very often, you need to take more actions and update more stuff (not only a single TextView). So, I think you can create a Interface that is invoked every time a message is received. Something like:
public class MyActivity extends Activity {
public MyHandler mHandler;
protected final void onCreate(final Bundle savedInstanceState) {
//....
mHandler = new MyHandler(new MyHandler.OnMessageReceivedListener() {
#Override
public void handleMessage(final String message) {
// Update the views as you with
}
});
//....
}
#Override
public void onDestroy() {
super.onDestroy();
mHandler.clear();
}
public static class MyHandler extends Handler {
WeakReference<OnMessageReceivedListener> mListenerReference;
public MyHandler(OnMessageReceivedListener listener) {
mListenerReference = new WeakReference<>(listener);
}
public synchronized void handleMessage(Message msg) {
byte[] readBuf=(byte[])msg.obj;
String readMsg = new String(readBuf,0,msg.arg1);
OnMessageReceivedListener listener = mListenerReference.get();
if(listener != null) {
listener.handleMessage(readMsg);
};
}
public void clear() {
mListenerReference.clear();
}
public interface OnMessageReceivedListener {
void handleMessage(String message);
}
}
}
You're not doing very heavy staff in your handleMessage part, so no need to extend Handler keep it simple and ligthweight; just add a callback instead. Create a callback in your Activity/Fragment:
private class MessageCallback implements Handler.Callback {
#Override
public boolean handleMessage(#NonNull Message message) {
// Here you can call any UI component you want
TextView textView=findViewById(R.id.textview);
textView.setText(readMsg);
return true;
}
}
Then call it as:
Handler handler = new Handler(getMainLooper(), new MessageCallback());
Message readMsg = handler.obtainMessage(what, arg1, arg2, object);
readMsg.sendToTarget();
I am trying the following scenario
1)send a message from UI thread , to worker using HandlerThread
2)Read it using handlemessage
3)send it back to a text field in UI
I am using the below code , The issue now is that , the message in the handlemessage is coming as null
public class MainActivity extends AppCompatActivity {
private TextView serverStatus;
private TextView clientStatus;
private Handler mUiHandler = new Handler();
private BluetoothServerSocket serverSocket;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button readButton = (Button) findViewById(R.id.button2);
readButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//TODO Code to manage read data from client here !!
/* mWorkerThread2 = new MyWorkerThread("myWorkerThread2");
mWorkerThread2.start();
*/
}});
final Button submitButton = (Button) findViewById(R.id.button);
serverStatus=(TextView) findViewById(R.id.editText);
submitButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
HandlerThread myThread = new HandlerThread("Worker Thread");
myThread.start();
Looper mLooper = myThread.getLooper();
MyHandler mHandler = new MyHandler(mLooper);
/* Bundle data = new Bundle();
data.put
msg.setData(data);*/
Message msg = mHandler.obtainMessage();
msg.obj = serverStatus.getText().toString();// Some Arbitrary object
/* Toast.makeText(getApplicationContext(), msg.obj.toString(), Toast.LENGTH_SHORT).show();*/
mHandler.sendMessage(msg);
}});
}
class MyHandler extends Handler {
public MyHandler(Looper myLooper) {
super(myLooper);
}
public void handleMessage(final Message msg) {
//final String text=msg.getData().getString("");
mUiHandler.post(new Runnable() {
#Override
public void run() {
String ms=String.valueOf(msg.obj);
serverStatus.setText("from server !!! "+ms );
}
});
}
}
}
As per comments from #pskink , I have used CallBack . Below code works fine
public class MainActivity extends AppCompatActivity {
private TextView serverStatus;
private TextView clientStatus;
private Handler mUiHandler = new Handler();
Handler mHtHandler;
// Handler mUiHandler;
private BluetoothServerSocket serverSocket;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button readButton = (Button) findViewById(R.id.button2);
readButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//TODO Code to manage read data from client here !!
}});
final Button submitButton = (Button) findViewById(R.id.button);
serverStatus=(TextView) findViewById(R.id.editText);
submitButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
HandlerThread ht = new HandlerThread("MySuperAwsomeHandlerThread");
ht.start();
Handler.Callback callback = new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
if (msg.what == 0) {
final Message msg1=mUiHandler.obtainMessage(0);
msg1.obj=msg.obj;
mUiHandler.post(new Runnable() {
#Override
public void run() {
final String ms=(String)msg1.obj;
serverStatus.setText("from server !!! "+ms );
}
});
}
return false;
}
};
mHtHandler = new Handler(ht.getLooper(), callback);
Message msg=mHtHandler.obtainMessage(0);
msg.obj = serverStatus.getText().toString();// Some Arbitrary object
mHtHandler.sendMessage(msg);
}});
}
}
I am updating the text user types in a textview to database every 5 seconds.
The handler collects data from textview and save it to database.
But the update code runs even after I press back button and return to MainActivity.
How can I stop running the code after exiting from activity.
public class CreateBoxActivity extends AppCompatActivity {
Long current_BoxID = null, parent_BoxID = null;
Handler h=new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_box);
h.post(new Runnable(){ //Run this every five seconds to update the data
#Override
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String editeddate = sdf.format(Calendar.getInstance().getTime());
String titleText = ((EditText)findViewById(R.id.new_box_title)).getText().toString();
String descriText = ((EditText)findViewById(R.id.new_box_descri)).getText().toString();
Boxes nbx = new Boxes(1, null , titleText, descriText, editeddate, editeddate);
BoxesController.UpdateBox(nbx);
h.postDelayed(this,5000);
}
});
}
You need to release the handler resource when the activity is destroyed.
#override
protected void onDestroy() {
super.onDestroy();
h.removeCallbacksAndMessages(null);
h = null;
}
Create runnable outside of the method like :
Runnable runnable =new Runnable() {
#Override public void run() {
}
};
On your activity's onPause call:
h.removeCallback(runnable);
You can remove the handler in onPause
Runnable runnable =new Runnable() {
#Override public void run() {
}
};
new Handler().removeCallbacks(runnable);
I don't understand how an handler can be used to update a TextView during the flow of a program. I'm testing it on a simple code like this
public class MainActivity extends AppCompatActivity {
TextView text;
boolean isReady; //boolean to check a new Message
String update; //String to send to TextView
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.textView);
Handler handler = new MyHandler(); //defined down
MyHandlerThread thr = new MyHandlerThread(handler); //defined in other .class file
thr.start();
for(int i=0;i<100;i++){ //a simple for
if(i%2==0)
thr.setMessage(i + ": Divisible for 2");
else
thr.setMessage(i+": Not Divisible for 2");
}
}
private class MyHandler extends Handler { //inner class
#Override
public void handleMessage(Message msg) {
Bundle bundle = msg.getData();
if(bundle.containsKey("refresh")) {
String value = bundle.getString("refresh");
text.setText(value);
}
}
}}
and this is the thread code
public class MyHandlerThread extends Thread {
private Handler handler;
private boolean isSent;
String text;
public MyHandlerThread(Handler handler) {
this.handler = handler;
isSent=false;
text="";
}
public void run() {
try {
while(true) {
if(isSent){
notifyMessage(text);
Thread.sleep(1000);
isSent=false;
}
}
}catch(InterruptedException ex) {}
}
public void setMessage(String str){
text=str;
isSent=true;
}
private void notifyMessage(String str) {
Message msg = handler.obtainMessage();
Bundle b = new Bundle();
b.putString("refresh", ""+str);
msg.setData(b);
handler.sendMessage(msg);
}}
this code just print the last expected string "99: not divible for 2"
You are just calling 100 times your thread setMessage, so texts will overwrite each other before your thread loop has any chance to print them.
You should implement a queue in your setMessage, then the thread loop should pop the next element from the queue, print it (sending a message via the Handler) then sleep. If no more elements are in the queue, just loop until one becomes available.
getting CalledFromWrongThreadException, Only the original thread that created a view hierarchy can touch its views.
on the line, handler2.sendMessage(msg); which is for sending a short text message from the UI thread to MulitThreaderSendback class below.
calling the sendMessage(msg) works perfectly when calling it from inside of the onCreate method, however when i put it inside of the onClick listener it is causing this exception crash.
how can I avoid this crash and be able to call it from an onclick listener so I can use a button press to send the message?
FROM LOGCAT:
E/AndroidRuntime(14366): FATAL EXCEPTION: Thread-1433
E/AndroidRuntime(14366): android.view.ViewRootImpl$CalledFromWrongThreadException:
Only the original thread that created a view hierarchy can touch its views.
E/AndroidRuntime(14366): android.view.ViewRootImpl.checkThread(ViewRootImpl.java:5432)
E/AndroidRuntime(14366): android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:970)
Code is:
public class MainActivity extends Activity implements Runnable {
TextView textViewOne;
TextView textViewTwo;
TextView textViewThree;
TextView textViewFour;
TextView textViewFive;
RelativeLayout relativeLayoutOne;
Handler handler;
Handler handler2;
Message msg;
Context context;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
relativeLayoutOne = (RelativeLayout) findViewById(R.id.relativeLayout1);
textViewOne = (TextView) findViewById(R.id.textView1);
textViewTwo = (TextView) findViewById(R.id.textView2);
textViewThree = (TextView) findViewById(R.id.textView3);
textViewFour = (TextView) findViewById(R.id.textView4);
textViewFour = (TextView) findViewById(R.id.textView5);
handler = new Handler();
// starting this class that implements Runnable
this.run();
// starting class that extends Thread
MultiThreader multiThreader = new MultiThreader(handler);
multiThreader.start();
// starting class that implements Runnable
MultiThreader2 multiThreader2 = new MultiThreader2(handler);
Thread thread = new Thread(multiThreader2);
thread.start();
MulitThreaderSendback multiSendBack = new MulitThreaderSendback();
Thread thread2 = new Thread(multiSendBack);
thread2.start();
synchronized (multiSendBack) {
while (handler2 == null) {
try {
multiSendBack.wait();
} catch (InterruptedException e) {
//Ignore and try again.
}
}
}
Bundle bundle = new Bundle();
String messageToSend = "message from UI class";
bundle.putString("message", messageToSend);
Random random = new Random();
int messageText = random.nextInt(30);
String putIt = String.valueOf(messageText);
bundle.putString("message", putIt);
msg = Message.obtain();
msg.setData(bundle);
// handler2.sendMessage(msg); // <-- WORKS OK HERE, NO CRASH
relativeLayoutOne.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
handler2.sendMessage(msg); // <-- FATAL ERROR: CalledFromWrongThreadException
}
});
} // end oncreate
public void setTextViewThree(String textToSet) {
textViewFour.setText(textToSet);
}
public void setTextViewFour(String textToSet) {
textViewFive.setText(textToSet);
}
// local class runnable
#Override
public void run() {
textViewOne.setText("message from local class runnable");
}
// multiThreader inner class
class MultiThreader extends Thread {
Handler handler;
public MultiThreader(Handler handler) {
this.handler = handler;
}
#Override
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
textViewTwo.setText("message from multithreader class");
} // end inner run
});
} // end outer run
} // end MultiThreader inner class
// external multiThreader2 inner class
class MultiThreader2 implements Runnable {
Handler handler;
public MultiThreader2(Handler handler) {
this.handler = handler;
}
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
textViewThree.setText("message from MultiThreader2 class");
} // end inner run
});
} // end outer run
} // end MultiThreader2 inner class
class MulitThreaderSendback implements Runnable {
int x = 0;
#Override
public void run() {
// TODO Auto-generated method stub
Looper.prepare();
synchronized(this) {
handler2 = new Handler() {
public void handleMessage(Message msg) {
Bundle bundle = msg.getData();
String receivedString = bundle.getString("message");
setTextViewThree("string received from UI thread: " + receivedString);
}
};
notifyAll();
}
Looper.loop();
} // end run method
} // end MultiThreaderSendback inner class
} // end outer class mainactivity
You can embed the line handler2.sendMessage(msg); in this method
runOnUiThread(new Runnable() {
public void run() {
}
});
Try this