I'm stuck in some problems with handlers. In particular, I need to stop and resume a callback either when I respectively turn my device lock and unlock, or when OnPause() and OnResume() are called. I put handler.Post(action) and handler.RemoveCallbacks(action) in OnPause() and in OnResume() but they don't work properly. Actually, when I exit and enter again in my application, It seems that handler.Post(action) is called twice because the update of TextView becomes faster than usual. Besides, the same thing happens when I lock or unlock the device. I dont' know how can I solve this issue.
This is my code:
public class MainActivity : Activity
{
int count = 1;
TextView text;
Handler handler;
myrunnable runnable;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
text = FindViewById<TextView>(Resource.Id.textView1);
handler = new Handler();
runnable = new myrunnable(text, handler);
}
protected override void OnResume()
{
handler.Post(runnable.Run);
base.OnResume();
}
protected override void OnPause()
{
handler.RemoveCallbacks(runnable.Run);
base.OnPause();
}
}
public class myrunnable : Java.Lang.Object, IRunnable
{
int i;
TextView text;
Handler handler;
public myrunnable() { }
public myrunnable(TextView text, Handler handler)
{
this.handler = handler;
this.text = text;
i = 0;
}
public IntPtr Handle
{
get
{
return (IntPtr) 0;
}
}
public void Dispose()
{
}
public void Run()
{
i++;
text.Text = i.ToString();
if (i < 100)
handler.PostDelayed(Run, 1000);
}
}
Thank You in advance.
Im having the same problem. The however, the problem is not the text view it's that onResume is called when the application starts and also when the suspended activity is resumed. Id recommend setting a boolean like canRun that if set false the handler will not post. And if it's true the handler can post and cancel as it pleases. I hope this helps and that im not way off.
Related
I want to update my online status in firebase periodically but when is in foreground but when it's gone in background then i have to set the status offline.
So Please help me how to manage.
Here is the code through which i update it on firebase
private void fireStoreUpdate() {
PreferenceManager preferenceManager = new PreferenceManager(getApplicationContext());
String chefId = preferenceManager.getChefId();
String restuarantId = preferenceManager.getMerchantId();
Restaurant restaurant = new Restaurant("online", String.valueOf(System.currentTimeMillis()), chefId, restuarantId);
// Firestore
FirebaseFirestore.getInstance().collection("restaurants").document("Restaurant ID : " + restuarantId).set(restaurant);
}
It's updating but how do I make it so it repeats it every 5 seconds?
You can use a Handler and execute your function every "x" time. When the lifecycle is onPause(), you just stop this handler and when the app is back to foreground in the onResume() execute the handler again.
I am gonna show you the approach with a simple Activity
MainActivity:
public class MainActivity extends AppCompatActivity {
private final long EVERY_FIVE_SECOND = 5000;
private Handler handler;
private Runnable runnable;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Executing the handler
executeHandler();
}
private void executeHandler(){
//If the handler and runnable are null we create it the first time.
if(handler == null && runnable == null){
handler = new Handler();
runnable = new Runnable() {
#Override
public void run() {
//Updating firebase store
fireStoreUpdate();
//And we execute it again
handler.postDelayed(this, EVERY_FIVE_SECOND);
}
};
}
//If the handler and runnable are not null, we execute it again when the app is resumed.
else{
handler.postDelayed(runnable, EVERY_FIVE_SECOND);
}
}
#Override
protected void onResume() {
super.onResume();
//execute the handler again.
executeHandler();
}
#Override
protected void onPause() {
super.onPause();
//we remove the callback
handler.removeCallbacks(runnable);
//and we set the status to offline.
updateStatusToOffline();
}
}
I hope this help you.
I'm writing an app that will display the current download speed which will be updated every second. My Runnable class is able to update the UI with the value, but when I try to place it inside a loop so that it will continuously run and update the UI TextView every second, the app now hangs.
This is my MainActivity.java:
public class MainActivity extends Activity implements SpeedMeter.TaskRunnableSpeedMeterMethods{
private Thread mSpeedMeterThread;
private Handler mHandler;
private TextView downloadSpeedOutput;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
downloadSpeedOutput = (TextView) findViewById(R.id.speed);
mHandler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message inputMessage) {
SpeedMeter speedMeter = (SpeedMeter) inputMessage.obj;
downloadSpeedOutput.setText(Long.toString(speedMeter.getmDownloadSpeedKB()));
}
};
SpeedMeter speedMeter = new SpeedMeter(this);
speedMeter.run();
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(this, MainActivity.class);
}
#Override
public void setSpeedMeterThread(Thread currentThread) {
mSpeedMeterThread = currentThread;
}
#Override
public void setInternetSpeed(SpeedMeter speedMeter) {
Message completeMessage = mHandler.obtainMessage(1, speedMeter);
completeMessage.sendToTarget();
}
}
And here's the other SpeedMeter.java:
public class SpeedMeter implements Runnable {
final TaskRunnableSpeedMeterMethods mMainActivity;
private long mDownloadSpeedKB;
public SpeedMeter(TaskRunnableSpeedMeterMethods mainActivity) {
mMainActivity = mainActivity;
}
#Override
public void run() {
mMainActivity.setSpeedMeterThread(Thread.currentThread());
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
// while(true) {
long rxBytesPrevious = TrafficStats.getTotalRxBytes();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long rxBytesCurrent = TrafficStats.getTotalRxBytes();
long downloadSpeed = rxBytesCurrent - rxBytesPrevious;
setmDownloadSpeedKB(downloadSpeed/1000);
mMainActivity.setInternetSpeed(this);
// }
}
public long getmDownloadSpeedKB() {
return mDownloadSpeedKB;
}
public void setmDownloadSpeedKB(long mDownloadSpeedKB) {
this.mDownloadSpeedKB = mDownloadSpeedKB;
}
interface TaskRunnableSpeedMeterMethods {
void setSpeedMeterThread(Thread currentThread);
void setInternetSpeed(SpeedMeter speedMeter);
}
}
Any help will be appreciated!
You didnt start your runnable as a new thread instead you called the run function like a normal function (so u do the while loop on ur UI thread which blocks it)
replace
speedMeter.run();<br />
SpeedMeter speedMeter = new SpeedMeter(this);
with
new Thread(new SpeedMeter(this)).start();
see https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html for more infos on how to start a Runnable :)
The ideal way to do this would be to create an AsyncTask that would post a message to your UI thread, after it complete the task in the doInBackground() call.
Standards
also the interface structure you are following does not make sense and does not follow good standards. Usually an interface is used as a callback, which is basically what you are doing. But the standard is to say onSomethingChangedA() or onSomethingChangedB() from OnSomethingChangedListener interface.
I think your loop is always true so app hangs its better to create a boolean and use while(mboolean) and put this in your loop
if(something){
mboolean=false;
}
you can also use CountDownTimer.
for example:
new CountDownTimer(miliseconds,1000)
//if you have download speed and download size you can find miliseconds
#Override
public void onTick(long l) {
//something you want to do every seconds
}
#Override
public void onFinish() {
//something you want to do on finish
}
EDIT: I've found that what I'm describing below only occurs on my emulated device (Nexus 5, target api 19, 4.4.2 with Intel Atom (x86) cpu), but NOT on my physical device (HTC One)....
EDIT2: Edit1 was due to an IllegalStateException that I didnt catch. Added some code to check if the thread was already running before trying to start it. This combined with the accepted answer resolved my issue.
I have implemented an activty that starts a new thread in the activity's onCreate method, like this:
...
private boolean running;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
running = true;
new Thread(null, work, "myThread").start();
}
Runnable work = new Runnable() {
#Override
public void run() {
while (running) {
//Doing work
}
}
};
I'm "pausing" my thread with my activity's onPause method, like this:
#Override
protected void onPause() {
running = false;
super.onPause();
}
So I thought that resuming it would be just as easy...ยจ
#Override
protected void onResume(){
running = true;
super.onResume();
}
but my thread isn't resuming. Any ideas why? Thankful for any help.
Marcus
All of the answers i think have some issues about your running variable because you can not write and read a variable from two different Threads without synchronized block so i post my own answer:
package com.example.threadandtoast;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity {
public class MonitorObject{
public boolean running = true;
public String message = "";
public boolean mustBePost = true;
}
Thread t;
int threadNameCounter = 0; // i use this variable to make sure that old thread is deleted
// when i pause, you can see it and track it in DDMS
Runnable work = new Runnable() {
boolean myRunning;
#Override
public void run() {
synchronized(mSync) {
myRunning = mSync.running;
}
while (myRunning) {
runOnUiThread(new Runnable() { // in order to update the UI (create Toast)
#Override // we must switch to main thread
public void run() {
// i want to read the message so i must use synchronized block
synchronized(mSync) {
// i use this variable to post a message just for one time because i am in an infinite loop
// if i do not set a limit on the toast i create it infinite times
if(mSync.mustBePost){
Toast.makeText(MainActivity.this, mSync.message, Toast.LENGTH_SHORT).show();
// the message post so i must set it to false
mSync.mustBePost = false;
// if i am going to pause set mSync.running to false so at the end of infinite loop
//of thread he reads it and leaves the loop
if(mSync.message.equals("Main Activity is going to pause")){
mSync.running=false;
}
}
}
}
});
synchronized(mSync) {
myRunning = mSync.running;
}
}
}
};
final MonitorObject mSync = new MonitorObject();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onPause() {
super.onPause();
synchronized(mSync) {
// mSync.running = false; you can not set it here because
// it is possible for the thread to read it and exit the loop before he posts your message
mSync.mustBePost=true;
mSync.message = "Main Activity is going to pause";
}
}
#Override
protected void onResume(){
super.onResume();
threadNameCounter++;
synchronized(mSync) {
mSync.running = true;
mSync.mustBePost=true;
mSync.message = "Main Activity is going to resume";
}
t = new Thread(work,"My Name is " + String.valueOf(threadNameCounter));
t.start();
}
}
Or you can use this code:
public class MainActivity extends ActionBarActivity {
Thread t;
int threadNameCounter = 0; // i use this variable to make sure that old thread is deleted
// when i pause, you can see it in DDMS
String message = "";
boolean isPost = false;
Runnable work = new Runnable() {
#Override
public void run() {
while (true) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if(!isPost){
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
isPost = true;
if( message.equals("Main Activity is going to pause")){
t.interrupt();
}
}
}
});
if(Thread.currentThread().isInterrupted()){
break;
}
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onPause() {
super.onPause();
message = "Main Activity is going to pause";
isPost = false;
}
#Override
protected void onResume(){
super.onResume();
message = "Main Activity is going to resume";
isPost = false;
threadNameCounter++;
t = new Thread(work,"My Name is " + String.valueOf(threadNameCounter));
t.start();
}
}
you can also use semaphore or wait-notify approach.
i put public String message = ""; and public boolean mustBePost = true; in to mSync object but it is
not necessary because only main thread have an access to them.
if you have any problem please ask.
The statement running = false; will stop execution of the Thread, instead of pausing it. Use two variables: One for stopping current Thread, and another for pausing and resuming the Thread, as follow:
boolean isThreadPause=false;
Runnable work = new Runnable() {
#Override
public void run() {
while (running) {
if (!isThreadPause) {
// Doing work
}
}
}
};
In the onPause event of the Activity, set isThreadPause to true, and in the onResume event, set isThreadPause to false.
This is because your Runnable object stops when the while loop stops. You could try this:
Runnable work = new Runnable() {
#Override
public void run() {
while () {
if(running){
//Doing work
}
}
}
};
Activity.onPause() and onStop() are called in (at least) two situations:
The another Activity was launched on top of the current one.
The app was minimized.
Is there an easy way to tell the difference?
You could do it this way. Make all of your activities extend from a base activity. The base activity needs to keep a visibility counter that is incremented/decremented during onResume/onPause:
public abstract class MyBaseActivity extends ActionBarActivity {
private static int visibility = 0;
private Handler handler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new Handler();
}
#Override
protected void onResume() {
super.onResume();
visibility++;
handler.removeCallBacks(pauseAppRunnable);
}
#Override
protected void onPause() {
super.onPause();
visibility--;
handler.removeCallBacks(pauseAppRunnable);
// give a short delay here to account for the overhead of starting
// a new activity. Might have to tune this a bit (not tested).
handler.postDelayed(pauseAppRunnable, 100L);
}
#Override
protected void onDestroy() {
super.onDestroy();
// uncomment this if you want the app to NOT respond to invisibility
// if the user backed out of all open activities.
//handler.removeCallBacks(pauseAppRunnable);
}
private Runnable pauseAppRunnable = new Runnable() {
#Override
public void run() {
if (visibility == 0) {
// do something about it
}
}
};
}
I have a problem with pageviewer. I want to my page scroll every two seconds. I try something like this:
handler.postDelayed(new Runnable() {
public void run() {
viewPager.setCurrentItem(viewPager.getCurrentItem()+1, true);
}
},2000);
But it works only when I start activity. If i put this code to public void onPageSelected(int page) it works but I want to do when I click and manually scroll page I want to stop this handler but it doeasn't work. How I can do that?
You should define your Runnable and Handler like this:
private boolean pagerMoved = false;
private static final long ANIM_VIEWPAGER_DELAY = 2000;
private Handler h = new Handler();
private Runnable animateViewPager = new Runnable() {
public void run() {
if (!pagerMoved) {
viewPager.setCurrentItem(viewPager.getCurrentItem()+1, true);
h.postDelayed(animationFrame, ANIM_VIEWPAGER_DELAY);
}
}
};
Make sure that you setup and tear down in your onPause & onResume methods
#Override
public void onPause() {
super.onPause();
if (h != null) {
h.removeCallbacks(animateViewPager);
}
}
#Override
public void onResume() {
super.onResume();
h.postDelayed(animateViewPager, ANIM_VIEWPAGER_DELAY);
}
Finally, you'll need to listen for a touch event on your viewpager so that you can set pagerMoved to true (which will then stop further automatic page transitions).