I am running a recursive handler which runs some code. I am posting the handler using a HandlerThread. I want to run the next recursive call only after the completion of the previous call.
Is it possible to do so? If not what are the alternatives.
HandlerThread ht = new HandlerThread();
ht.start();
Handler h = new Handler(ht.getLooper());
h.post(new Runnable() {
#override
public void run(){
//Some code
h.postDelay(this,1000);
}
});
Your code should work, but if you want a complete example how to run something recursively using HandlerThread, here it is:
public class Main2Activity extends AppCompatActivity {
private MyWorkerThread mWorkerThread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
mWorkerThread = new MyWorkerThread("myWorkerThread");
final Runnable task = new Runnable() {
#Override
public void run() {
Log.d("TAG", "Done.");
mWorkerThread.postTask(this);
}
};
mWorkerThread.start();
mWorkerThread.prepareHandler();
mWorkerThread.postTask(task);
}
#Override
protected void onDestroy() {
mWorkerThread.quit();
super.onDestroy();
}
}
class MyWorkerThread extends HandlerThread {
private Handler mWorkerHandler;
public MyWorkerThread(String name) {
super(name);
}
public void postTask(Runnable task){
mWorkerHandler.postDelayed(task, 1000); // set timeout which needed
}
public void prepareHandler(){
mWorkerHandler = new Handler(getLooper());
}
}
Don't forget to call handlerThread.quit() in onDestroy
Related
I can't find a none blocking way to invoke my callback method from the main thread as my second thread exits.
I tried to use the loading.isAlive() in a while loop but it freeze the screen.
public class MainActivity extends AppCompatActivity {
private ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.load_opening_screen);
Thread loading = new LoadingBarThread();
loading.start();
}
private class LoadingBarThread extends Thread {
#Override
public void run() {
for (int i=0;i<=100;i++) {
progressBar.setProgress(i);
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private void callback(){
// start a new activity for example
}
}
I would like to start a new activity as the ProgressBar reaches 100.
Use a Handler object and at the end of your run() function, post a Runnable to that Handler. Have the Runnable call the callback method. Handler will execute it on the thread its attached to (whatever thread it was created on).
So your new class looks like:
private class LoadingBarThread extends Thread {
Handler handler = new Handler();
#Override
public void run() {
for (int i=0;i<=100;i++) {
progressBar.setProgress(i);
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
handler.post(new Runnable() {
public void run() {
callback();
}
});
}
}
I am updating progress bar with the help of handler of MainActivity
but it gives the error :Can't create handler inside thread that has not called Looper.prepare()
Error
public class MainActivity extends AppCompatActivity {
ProgressBar progressBar;
Handler handler;
Thread thread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
thread = new Thread(new MyThread());
thread.start();
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
progressBar.setProgress(msg.arg1);
}
};
}
class MyThread implements Runnable {
#Override
public void run() {
Message message = Message.obtain();
for (int i = 0; i < 100; i++) {
message.arg1 = i;
handler.sendMessage(message);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
The error is because you are updating your UI from a different thread. So what you should be doing is like call the update method on main ui thread
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
runOnUiThread(new Runnable() {
#Override
public void run() {
// Do something on UiThread
// update progress
}
});
};
You should run progress bar only on Main Looper.
Add
handler = new Handler(context.getMainLooper()) {
.....
}
Should work.
I am implementing an inactivity timer based on this answer: https://stackoverflow.com/a/12481918/6298161
I need the timer to work across multiple activities, so as the comments suggest from the original post I have change the Handler and Runnable to be static.
How do I now redirect to a new activity in the runnable where I have put the comment? Any help is greatly appreciated
public class InactivityTimerActivity extends AppCompatActivity {
public static final long DISCONNECT_TIMEOUT = 300000; // 5 min = 5 * 60 * 1000 ms
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inactivity_timer);
}
private static Handler disconnectHandler = new Handler() {
public void handleMessage(Message msg) {
}
};
private static Runnable disconnectCallback = new Runnable() {
#Override
public void run() {
// I want to redirect here
}
};
public void resetDisconnectTimer() {
disconnectHandler.removeCallbacks(disconnectCallback);
disconnectHandler.postDelayed(disconnectCallback, DISCONNECT_TIMEOUT);
}
public void stopDisconnectTimer() {
disconnectHandler.removeCallbacks(disconnectCallback);
}
#Override
public void onUserInteraction() {
resetDisconnectTimer();
}
#Override
public void onResume() {
super.onResume();
resetDisconnectTimer();
}
#Override
public void onStop() {
super.onStop();
stopDisconnectTimer();
}
}
I think you shouldn't make it static. Just keep that protected, and then when start new activity, putting an integer variable inside the intent (the remaining time or the time that the timer has run). Then when on create new activity, you get that value out and set your timer base on that value.
I have tried to implement object in Android that would work in its own thread (I do not want to make handler public, I want to wrap sentMessage method with own public api). It has public methods to pass data to object. This object is associated with activity lifecycle (onResume, onPause). I would like to use looper and handler, not just pure Java thread with infinite loop. I want to start worker thread on resume and stop working on pause callback. Object has to wait for new message infinitely.
This is my code below:
public class ThreadingObject {
private MyThread thread;
public ThreadingObject() {}
public void onResume() {
thread = new MyThread();
thread.startWorking();
}
public void onPause() {
thread.stopWorking();
}
public void setMessage(Object object) {
Message msg = new Message();
msg.obj = object;
thread.handler.sendMessage(msg);
}
protected void work(Object object) {
//Do something with object in own thread
}
private class MyThread extends Thread {
public Handler handler;
#Override
public void run() {
Looper.prepare();
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
ThreadingObject.this.work((String[]) msg.obj);
}
};
Looper.loop();
}
public void startWorking() {
start();
}
public void stopWorking() {
handler.getLooper().quit();
}
}
}
Is it correct implementation? I receive warning: "sending message to a handler on a dead thread". Is there any issue that I do not see?
It is my implementation:
public class ThreadingObject {
private HandlerThread thread;
private Handler handler;
private Handler.Callback handlerCallback = new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
work(msg.obj);
return true;
}
};
public ThreadingObject() {}
public void onResume() {
thread = new HandlerThread("SurfaceView HandlerThread");
thread.start();
handler = new Handler(thread.getLooper(), handlerCallback);
}
public void onPause() {
if(thread != null) {
thread.quit();
thread = null;
}
handler = null;
}
public void setMessage(Object object) {
Message msg = new Message();
msg.obj = object;
handler.sendMessage(msg);
}
protected void work(Object obj) {
//Working
}
}
In that run() method, if you need to re run or use loop, you need to add it by your self, ex.
and your error, is happen because you try to call the thread that already finish.
boolean aLoopLoopLoop = true;
Handler handler;
// this handler, you no need to declare it repeatedly, just declare it in onCreate() is enough
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
ThreadingObject.this.work((String[]) msg.obj);
}
};
// -----
#Override
public void run() {
Looper.prepare();
final Looper looper = Looper.myLooper();
while(aLoopLoopLoop) {
// write your code here
Looper.loop();
}
// after loop exit, quit loop
handler.postDelayed(new Runnable() {
#Override
public void run() {
looper.quit();
}
}, 3000);
}
// -----
public void stopWorking() {
aLoopLoopLoop = false;
}
I want to make a callback function in the ShipInfoManager to inform the MainActivity to do some action.
If I put onEvent() into Runnable, it runs.
However If I put it like this, it shows an error.
Is there any way to fire the callback after loading data?
Or, is there any way to do the callback like Android's API's LocatioManger's requestLocationUpdates, giving a callback when the data/variables is changed?
Thank you for any replies!
MyCallback Interface:
interface MyCallback {
void callbackCall();
}
ShipInfoManager class:
public class ShipInfoManager {
Context mContext;
public ShipInfoManager(Context _mContext) {
this.mContext = _mContext;
reloadData();
startTimer();
}
MyCallback callback;
void onEvent() {
callback.callbackCall();
}
private void startTimer() {
/* RUN EVERY MIN */
final Handler handler = new Handler();
Timer timer = new Timer();
TimerTask doAsynchronousTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
#SuppressWarnings("unchecked")
public void run() {
try {
reloadData();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
};
// TEMP SUSPEND FROM PREVENTING RELOAD //
timer.schedule(doAsynchronousTask, 0, 5000);
}
/* JSON handling and extraction */
private void reloadData() {
//Do sth to reload the data
//After reload, I want to fire the callback
onEvent();
}
}
It looks like you haven't any listeners to your callback and you're not checking for this.
You should replace your ShipInfoManager with this:
public class ShipInfoManager {
public interface MyCallback {
void callbackCall();
}
public void setCustomEventListener(MyCallback eventListener) {
callback = eventListener;
}
Context mContext;
public ShipInfoManager(Context _mContext) {
this.mContext = _mContext;
reloadData();
startTimer();
}
MyCallback callback;
void onEvent() {
// Check if we have listeners
if (callback != null)
callback.callbackCall();
}
private void startTimer() {
/* RUN EVERY MIN */
final Handler handler = new Handler();
Timer timer = new Timer();
TimerTask doAsynchronousTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
#SuppressWarnings("unchecked")
public void run() {
try {
reloadData();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
};
// TEMP SUSPEND FROM PREVENTING RELOAD //
timer.schedule(doAsynchronousTask, 0, 5000);
}
/* JSON handling and extraction */
private void reloadData() {
//Do sth to reload the data
//After reload, I want to fire the callback
onEvent();
}
}
Inside your Activity or Fragment you should have something like:
public class MainActivity extends ActionBarActivity implements ShipInfoManager.MyCallback {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ShipInfoManager s = new ShipInfoManager(this);
s.setCustomEventListener(this);
}
#Override
public void callbackCall() {
}
}
I changed my MainActivity like this. It works now.
Thank you for your suggestion and reply!!!!!
public class MainActivity extends ActionBarActivity {
ShipInfoManager mShipInfo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mShipInfo = new ShipInfoManager(this);
Log.i("Show Ship List Size", String.valueOf(mShipInfo.get_shipsList().size()));
Log.i("Show Ship - 6", String.valueOf(mShipInfo.getShip(6).getShip_name()));
mShipInfo.callback = new ShipInfoManager.MyCallback() {
#Override
public void callbackCall() {
Log.i("Call Back", "it is called");
}
};
}