Changing the UI from a singleton handler class - android

I have the below singleton handler class
public class MyHandler
{
private static Handler handler;
private static boolean isRunning;
public static Handler getHandler(Runnable myRunnable)
{
if (handler == null)
{
initHandler(myRunnable);
}
return handler;
}
private static void initHandler(Runnable myRunnable)
{
handler = new Handler();
isRunning = true;
handler.postDelayed(myRunnable, 5000);
}
public static void reRunHandler(Runnable myRunnable)
{
isRunning = true;
handler.postDelayed(myRunnable, 45000);
}
public static void stopMyHandler()
{
isRunning = false;
handler.removeCallbacksAndMessages(null);
}
}
However, how can I update my UI from here ? As the runnables are inside my activity. Apparently I cannot use getHandleMessage to communicate with it.
If you need more code, how am I using this, I can share.

It's very simple:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
//do whatever you want on the UI thread
}
});

Handle has functions for this purposes:
private final Handler handler = new Handler() {
public void handleMessage(Message msg) {
// here you can get data from Message and update your UI. runs in UI thread
}
};
If you will send message with data to your Handler use next code:
Message m = new Message();
Bundle b = new Bundle();
b.putInt("myNumber", 5); // for example
m.setData(b);
myHandler.sendMessage(m);

Related

Can my code be suspected of memory leak in android?

I use code like below for periodic execution
but i am suspecting a memory leak
Maybe my code is wrong?
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
public void run() {
if(isRunning)
{
...code
}
handler.postDelayed(this, 5000);
}
};
handler.postDelayed(runnable, 5000);
This is the code that uses another handler
private Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
mHandler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(#NonNull Message msg) {
return true;
}
});
}

What is the solution of "This Handler Class Should Be Static Or Leaks Might Occur"?

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

Can't create handler inside thread that has not called Looper.prepare() inside Handler()

I am calling a Handler class from a background thread. In the Handler class, I am trying to display a toast. Theoratically it should work flawlessly because Handler is the Queue that forwards the UI tasks to the main UI thread. However, in my case the I am getting exception.
private void firstTimeLogin() {
final LoginUiThreadHandler loginHandler = new LoginUiThreadHandler();
new Thread(new Runnable() {
#Override
public void run() {
Message m = loginHandler.obtainMessage();
Bundle bund = new Bundle();
bund.putInt("loginResult", 1);
m.setData(bund);
loginHandler.handleMessage(m);
}
}).start();
}
private class LoginUiThreadHandler extends Handler {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int loginResult = msg.getData().getInt("loginResult");
if(loginResult == 0)
Toast.makeText(getActivity().getApplicationContext(), "Login success", Toast.LENGTH_SHORT).show();
}
}
What am I doing wrong?
Replace with -
LoginUiThreadHandler loginHandler = new LoginUiThreadHandler(Looper.getMainLooper());
instead of-
LoginUiThreadHandler loginHandler = new LoginUiThreadHandler();

How update the UI from another thread in Android?

I searched for a way to update UI from another thread, and found that the available approach is to use Handler.post(Runnable) as shown in the code snippet below:
public class MyClass extends Activity {
private final Handler myHandler = new Handler();
final Runnable updateRunnable = new Runnable() {
public void run() {
// Update UI
}
};
private OnClickListener buttonListener = new OnClickListener() {
public void onClick(View v) {
new Thread(new Runnable() {
myHandler.post(updateRunnable);
}).start();
}
};
}
Instead can't we use Handler.sendMessage and do the UI updates from main UI thread in handleMessage():
public class MyClass extends Activity {
private final Handler myHandler = new Handler();
private Handler myHandler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch(msg.what) {
// Do logic here
}
}
};
private OnClickListener buttonListener = new OnClickListener() {
public void onClick(View v) {
new Thread(new Runnable() {
myHandler.sendEmptyMessage(0);
}).start();
}
};
}
I'm sorry if this is a very basic question, however I'm quite confused with the above two approaches.
You need to use runOnUiThread. You can post a runnable which does the UI operation to main thread as follows,
public class Utils {
public static void runOnUiThread(Runnable runnable){
final Handler UIHandler = new Handler(Looper.getMainLooper());
UIHandler .post(runnable);
}
}
Utils.runOnUiThread(new Runnable() {
#Override
public void run() {
// UI updation related code.
}
});
Read more at:
android: update UI from another thread in another class
Updating UI / runOnUiThread / final variables: How to write lean code that does UI updating when called from another Thread
https://developer.android.com/training/multiple-threads/communicate-ui.html

Using HandlerThread and Handler on a separate thread freezes UI thread

In my project, I want to implement a timer in another thread that counts down the time spent executing a certain method in my application, and do something if it is taking too much time. When executing the MyManager.startCommand() method, the application is freezing and I don't know why since I think I am not doing anything on the UI thread. Is there anything wrong with my code?
I had originally asked this question and was not experiencing an app freeze, so I don't think it's because I'm sleeping the thread: Runnable posted in another runnable not executed.
public class MyManager{
private MyManager sInstance;
private HandlerThread mHandlerThread;
private Handler mHandler;
private Runnable mTimeoutTimer;
public static MyManager getInstance(){
if(sInstance == null){
sInstance = new MyManager();
}
return sInstance;
}
private MyManager(){
mHandlerThread = new HandlerThread("mHandlerThread");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mTimeoutTimer = new Runnable() {
#Override
public void run() {
Log.e(“RUNNABLE RUNNING!”);
}
};
public class MyCommand {
private Runnable myRun;
public MyCommand(){
myRun = new Runnable() {
#Override
public void run() {
MyManager.getInstance().startTimeoutTimer();
MyCommand.this.run();
}
};
}
public void execute() {
myRun.run();
}
abstract public void run();
}
private void startTimeoutTimer(){
mHandler.post();
}
public void startCommand(){
new MyCommand().execute();
}
}
And how I'm using a MyCommand object:
MyCommand command =
new MyCommand() {
#Override
public void run() {
//make api call that happens in another thread
}
};
So I am basically trying to create the timeout timer for that API call.
Try this:
Just wrap your first runnable in a Thread.
public class MyManager{
private MyManager sInstance;
private HandlerThread mHandlerThread;
private Handler mHandler;
private Runnable mTimeoutTimer;
public static MyManager getInstance(){
if(sInstance == null){
sInstance = new MyManager();
}
return sInstance;
}
private MyManager(){
mHandlerThread = new HandlerThread();
mHandler = new Handler(mHandlerThread.getLooper());
mTimeoutTimer = new Runnable() {
#Override
public void run() {
Log.e(“RUNNABLE RUNNING!”);
}
};
public class MyCommand {
private Thread th;
private Runnable myRun;
public MyCommand(){
myRun = new Runnable() {
#Override
public void run() {
MyManager.getInstance().startTimeoutTimer();
try {
Thread.sleep(COMMAND_TIMEOUT_MILLIS * 3);
} catch (InterruptedException e) {}
MyCommand.this.execute();
}
};
th = new Thread(myRun);
}
public void execute() {
th.start();
mHandler.postDelayed(mTimeoutTimer);
}
}
private void startTimeoutTimer(){
mHandlerThread.start();
mHandler.postDelayed(mTimeoutTimer);
}
public void startCommand(){
new MyCommand().execute();
}
}
Also you forgot to start the mHandlerThread
private void startTimeoutTimer(){
mHandlerThread.start();
mHandler.postDelayed(mTimeoutTimer);
}

Categories

Resources