onPause(), onStop(), onDestroy() do not stop a thread - android

When I run the app and press the back button neither onPause(), onStop() or onDestroy() stop the time_counter thread, which results in endless messages "elTime: " + elapsedTime.
public class GameBoard extends Activity {
DrawingView drawView;
Thread timer_thread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
setContentView(R.layout.activity_game_board);
DrawingView drawingView = (DrawingView)findViewById(R.id.drawing_view);
drawingView.setInitialParameters(size.x,size.y);
TextView time_counter_tv = (TextView)findViewById(R.id.time_counter);
runTimer(time_counter_tv);
}
private void runTimer(final TextView elapsedTimeTV) {
timer_thread = new Thread() {
public void run() {
final long startTime = SystemClock.elapsedRealtime();
while (!Thread.currentThread().isInterrupted()) {
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
long elapsedTime = SystemClock.elapsedRealtime() - startTime;
System.out.println("elTime: " + elapsedTime);
elapsedTimeTV.setText("Elapsed Time: " + elapsedTime/100 + "s");
}
});
timer_thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
timer_thread.start();
}
#Override
protected void onPause(){
super.onPause();
System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-----------------");
timer_thread.interrupt();
}
#Override
protected void onDestroy() {
super.onDestroy();
}
}
Also in DrawingView class, when trying to finish the activity the above methods do not stop the thread either.
public class DrawingView extends View {
public void doSth(){
if (time_has_come){
((Activity)getContext()).finish();
return;
}
}

Related

Infinite loop activity when using setRequestedOrientation() in API 19 (KitKat)

The 'setRequestedOrientation' method restarts the activity normally on any version of the android higher than Kitkat.
But in Kitkat, even using if, the activity continues to restart.
int orientation = getResources().getConfiguration().orientation;
// Doesn't work
if (orientation != ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
}
// Doesn't work
if (orientation != Configuration.ORIENTATION_LANDSCAPE) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
}
EDIT 1 - StackTrace:
https://gist.github.com/sshnakamoto/11ef6179a561054e54ec4d41a03238f0
Sorry, my log is too long to post here. I've created a gist. But essentially you will see a loop between onCreate() and onStart() methods.
EDIT 2 - ActivityCode:
public class TestActivity extends AppCompatActivity {
private static final String TAG = "TimerActivityCLONE";
private TextView textView;
private ConstraintLayout parentView;
private boolean isColorChanged;
private int textColor;
private int parentColor;
private Handler handler;
private Runnable runnable;
private Timer timer;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate: started ");
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate: super called ");
setContentView(R.layout.activity_timer);
Log.d(TAG, "onCreate: setContentView called ");
/* Find on layout*/
parentView = findViewById(R.id.parent);
textView = findViewById(R.id.textView);
textColor = Color.WHITE;
parentColor = Color.BLACK;
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
}
private void initTimer() {
handler = new Handler();
runnable = new Runnable() {
#Override
public void run() {
changeColors();
}
};
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
handler.post(runnable);
}
};
timer = new Timer();
timer.schedule(timerTask, 0, 1000);
}
private void changeColors() {
Log.d(TAG, "changeColors: size " + textView.getTextSize() / getResources().getDisplayMetrics().scaledDensity);
if (isColorChanged){
textView.setTextColor(parentColor);
parentView.setBackgroundColor(textColor);
isColorChanged = false;
} else {
textView.setTextColor(textColor);
parentView.setBackgroundColor(parentColor);
isColorChanged = true;
}
}
#Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart: ");
/* Start to show */
initTimer();
}
#Override
protected void onStop() {
super.onStop();
killTaskAndFinish();
}
#Override
public void onBackPressed() {
super.onBackPressed();
killTaskAndFinish();
}
private void killTaskAndFinish() {
/* Kill background Thread */
timer.cancel();
timer.purge();
handler.removeCallbacks(runnable);
/* Restore user screen orientation */
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
}
}
I've found the bug, it occurs when calling method killTaskAndFinish() inside onStop() due ResquestedOrientation() method restart activity.
But why does this loop only occur on Kitkat (emulator?)? Testing Lollipop it does not happen
I don't know why only occurs on KikKat, but I was able to fix removing handler use. Only TimerTask was need in my case.
This fixes that bug and prevent memory leaks.
#Override
protected void onStart() {
super.onStart();
timerTask = new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
changeColors();
}
});
}
};
timer = new Timer();
timer.schedule(timerTask, 0, speed);
}
#Override
protected void onStop() {
super.onStop();
timerTask.cancel();
timer.cancel();
timer.purge();
}

bar.setProgress(0) does not work

The following code is from Beginning Android 3, Chapter 20. When the phone is rotated, a new activity will be created and onStart() will be called, and so bar.setProgress(0) is called. However, I don't see the bar's progress is back to the beginning. Why not?
public class HandlerDemo extends Activity {
ProgressBar bar;
Handler handler=new Handler() {
#Override
public void handleMessage(Message msg) {
bar.incrementProgressBy(5);
}
};
AtomicBoolean isRunning=new AtomicBoolean(false);
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
bar=(ProgressBar)findViewById(R.id.progress);
}
public void onStart() {
super.onStart();
bar.setProgress(0);
Thread background=new Thread(new Runnable() {
public void run() {
try {
for (int i=0;i<20 && isRunning.get();i++) {
Thread.sleep(1000);
handler.sendMessage(handler.obtainMessage());
}
} catch (Throwable t) {
// just end the background thread
}
}
});
isRunning.set(true);
background.start();
}
public void onStop() {
super.onStop();
isRunning.set(false);
}
}
Try using this code
#Override
protected void onPause() {
super.onPause();
isRunning.set(false);
bar.setProgress(0);
}

Android interrupting a Sleep Thread

I'd appreciate your help in interrupting an Android/Java sleep. What I have in my layout is a button, which if clicked, calls the Skip method and starts a new activity. FYI The same activity would be called anyway when the Sleep method terminates.
Here's my failing code:
public class Splash extends Activity {
private Thread timer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash_screen);
timer = new Thread() {
public void run() {
try {
sleep(5000);
} catch (Exception e) {
e.printStackTrace();
onPause();
return;
} finally {
onPause();
startActivity(new Intent("net.example.splashscreenexample.MainActivity"));
}
}
};
timer.start();
}
#Override
public void onPause() {
timer.interrupt();
super.onPause();
finish();
}
public void Skip() {
timer.interrupt();
startActivity(new Intent("net.example.splashscreenexample.MainActivity"));
}
Now Resolved!
I've now got it all working. In addition to #RocketSpock's suggestions there was also a stupid error in my code in that I'd failed to include the View view paramater into my Skip method call. So the fully working code now looks like this:
public class Splash extends Activity {
private Thread timer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash_screen);
timer = new Thread() {
public void run() {
synchronized (this) {
try {
sleep(5000);
} catch (Exception e) {
e.printStackTrace();
onPause();
return;
} finally {
onPause();
startActivity(new Intent(
"net.rogw.splashscreenexample.MainActivity"));
}
}
}
};
timer.start();
}
#Override
public void onPause() {
timer.interrupt();
super.onPause();
finish();
}
public void Skip(View view) {
synchronized (this) {
this.notify();
}
startActivity(new Intent("net.rogw.splashscreenexample.MainActivity"));
}
}
If you want to be able to interrupt it you should be using a wait.
public class Splash extends Activity {
private Thread timer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash_screen);
timer = new Thread() {
public void run() {
synchronized(this) {
try {
wait(5000);
} catch (Exception e) {
e.printStackTrace();
onPause();
return;
} finally {
onPause();
startActivity(new Intent("net.example.splashscreenexample.MainActivity"));
}
}
}
};
timer.start();
}
#Override
public void onPause() {
timer.interrupt();
super.onPause();
finish();
}
public void Skip() {
//You may need to replace this with the timer object
synchronized (this) {
//Informs the wait to interrupt.
this.notify();
}
startActivity(new Intent("net.example.splashscreenexample.MainActivity"));
}

Splash Screen not working with Thread

I write a Splash Screeen to run at the boot time of application
public class SplashScreen extends Activity {
ImageView imgView;
int[] imgID = new int[]{R.drawable.frame0, R.drawable.frame1, R.drawable.frame2, R.drawable.frame3,
R.drawable.frame4, R.drawable.frame5, R.drawable.frame6};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
imgView = (ImageView) findViewById(R.id.imgSplash);
new Thread(new WelcomeScreen()).start();
}
private class WelcomeScreen implements Runnable {
#Override
public void run() {
try {
for (int i = 0; i < imgID.length; i++)
{
imgView.setImageResource(imgID[i]);
sleep(500);
}
} catch (InterruptedException e) {
}finally {
Intent intent = new Intent(SplashScreen.this,LoginActivity.class);
startActivity(intent);
finish();
}
}
}
}
It getting error "Sorry the application has stopped unexpectedly" . I don't know why . Somebody can help me ????
you can not set the resource for yuor ImageView inside a thread different from the UI Thread.
you can use runOnUiThread. It takes as paramter a runnable, and post it in the UI Thread queue. There, the UI thead takes it and update your ImageView. All in all your runnable will become:
private class WelcomeScreen implements Runnable {
#Override
public void run() {
try {
for (int i = 0; i < imgID.length; i++)
{
final int resuorceId = imgID[i];
runOnUiThread(new Runnable() {
#Override
public void run() {
imgView.setImageResource(resuorceId);
}
});
sleep(500);
}
} catch (InterruptedException e) {
}finally {
Intent intent = new Intent(SplashScreen.this,LoginActivity.class);
startActivity(intent);
finish();
}
}
You can not access your views from Thread.
You will need to put your code imgView.setImageResource(imgID[i]); in runOnUiThread
use like:
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
imgView.setImageResource(imgID[i]);
}
});
Thanks
You can not change something in UI from non-UI thread so replace this you code:
imgView.setImageResource(imgID[i]);
to:
runOnUiThread(new Runnable() {
#Override
public void run() {
imgView.setImageResource(imgID[i]);
}
});
//try code this way...
public class SplashScreen extends Activity {
private Intent launchIntent;
private Thread splashThread; //used for perform splash screen operation
private int splashTime = 10000, sleepTime = 50; //used for threading operation
private boolean active = true; //used for touch event
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splashscreen); //Set splashscreen.xml here
try {
splashThread = new Thread() { // Creating Thread for splash the screen
#Override
public void run() { // run method implemented to perform threading operation
try {
int waitTime = 0; //counter for threading
do {
sleep(sleepTime); //delay for specific time
if (active)
waitTime += 100;
//write your image code here that display your no. of images
} while (active && (waitTime < splashTime)); //Check touch condition and counter
} catch (Exception e) {
// to handle runtime error of run method
Validation.displayToastMessage(SplashScreen.this, e.toString()); //Call static method of class ToastMessage
}
finish(); //finish current activity
startJustCoupleActivityScreen(); //Call below defined function
}
};
splashThread.start(); //start thread here
} catch (Exception e) {
message("SplashScreen : "+ e.toString()); //Call static method of class ToastMessage
}
}
public void startJustCoupleActivityScreen() {
launchIntent=new Intent(SplashScreen.this,JustCoupleActivity.class); //call Next Screen
startActivity(launchIntent); //start new activity
}
#Override
public boolean onTouchEvent(MotionEvent event) { //onTouch Event
//on touch it immediate skip splash screen
if(event.getAction()==MotionEvent.ACTION_DOWN) active=false; //Check Touch happened or not
return true;
}
public void message(String msg)
{
Validation.displayToastMessage(SplashScreen.this, msg); //display Error Message
}
}

Handler call to notifyDataSetChanged() not executing

I have an Handler registered in an Activity. handleMessage() calls notifyDataSetChanged on an Adapter. Things work while the Activity has initial focus. However, when I navigate out of the Activity and back in, notifyDataSetChanged() does not work.
FileAdapter is an ArrayAdapter. MergeAdapter is a custom class by CommonsWare. _mergeAdapter contains _fileAdapter.
Activity code:
public void setUpDownloadHandler() {
// Define the Handler that receives messages from the thread and update the progress
_downloadHandler = new Handler() {
public void handleMessage(Message message) {
super.handleMessage(message);
String fileId = (String) message.obj;
int progress = message.arg1;
FileInfo tempFile = null;
for (FileInfo file: _files) {
if (file.getFileId().equals(fileId)) {
file.setDownloadProgress(progress);
tempFile = file;
}
}
if (tempFile != null) {
_files.remove(tempFile);
_files.add(tempFile);
}
_fileAdapter.notifyDataSetChanged();
_mergeAdapter.notifyDataSetChanged();
}
};
}
Passing the handler:
RunnableTask task = new DownloadFileRunnableImpl(application, the_workspace_url, the_file_info, the_workspace_info.getTitle(), the_internal_storage_directory,
_downloadHandler);
Background thread code:
if(temp > previous) {
Message message = new Message();
message.arg1 = _currentProgress.intValue();
message.obj = _fileId;
_progressHandler.sendMessage(message);
previous = temp;
}
The other piece of information is that I'm passing the handler through a Binder and then into the runnable. I do this to run the background thread in a Service. I don't think this is the problem.
EDIT:
It seems like the handler is not associated with the activity the second time it is navigated to (perhaps because onCreate creates a new handler). Is there a way to re-associate or retain the old handler?
Update
The activity is being destroyed when it loses focus to another activity.
I would try putting a log message in your activity's onDestroy method to see if it is getting destroyed, when you navigate away from your activity. So your task may have the handler from the old activity.
Here is my answer, I relied heavily on http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentRetainInstance.html
Really just took their code and changed it so that I have to remake the fragment everytime I want to start the thread (work) again. And it communicates with the Activity through a handler.
public class Main extends Activity implements WorkProgressListener {
private static final String TAG = "tag";
private Handler handler;
private Button startWorkBtn;
private ProgressDialog progressDialog;
private boolean onSaveInstanceFlag = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG,"Main onCreate " + Utils.getThreadId());
setContentView(R.layout.main);
handler = new ProgressHandler();
startWorkBtn = (Button)this.findViewById(R.id.start_work_btn);
startWorkBtn.setEnabled(false);
startWorkBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick (View v) {
Log.i("tag","Main: startWorkBtn onClick ");
startWorkBtn.setEnabled(false);
FragmentManager fm = getFragmentManager();
Fragment workF = (Fragment)fm.findFragmentByTag("work");
if (null == workF) {
workF = new WorkFragment();
Log.i(TAG,"Main new WorkF" + Utils.getThreadId());
startProgressDialog(true);
startWorkBtn.setEnabled(false);
fm.beginTransaction().add(workF, "work").commit();
Log.i(TAG,"Main add(workF) " + Utils.getThreadId());
}
else {
// should never be able to get here.
}
}
});
FragmentManager fm = getFragmentManager();
Fragment loadingFragment = fm.findFragmentByTag("work");
Log.i(TAG,"Main findFragment " + Utils.getThreadId());
if (null == loadingFragment) {
this.startWorkBtn.setEnabled(true);
}
else {
// could also decide to show progress dialog based on savedInstanceState
this.startProgressDialog(true);
}
} // end onCreate
#Override
public void onRestart() {
Log.i(TAG,"Main onRestart " + Utils.getThreadId() );
super.onRestart();
this.onSaveInstanceFlag = false;
}
#Override
public void onResume () {
Log.i(TAG,"Main onResume " + Utils.getThreadId());
super.onResume();
this.onSaveInstanceFlag = false;
}
#Override
public void onSaveInstanceState (Bundle savedInstanceState) {
Log.i(TAG,"Main onSaveInstanceState "+ Utils.getThreadId());
this.onSaveInstanceFlag = true;
super.onSaveInstanceState(savedInstanceState);
if (null != this.progressDialog) {
savedInstanceState.putBoolean("progressDialog", true);
}
else {
savedInstanceState.putBoolean("progressDialog", false);
}
}
#Override
public void onStop () {
Log.i(TAG,"Main onStop " + Utils.getThreadId());
super.onStop();
}
#Override
public void onDestroy () {
Log.i(TAG,"Main onDestroy " + Utils.getThreadId());
super.onDestroy();
this.closeProgressDialog();
this.handler.removeCallbacksAndMessages(null);
}
public class ProgressHandler extends Handler {
#Override
public void handleMessage (Message msg) {
Log.i(TAG,"Main ProgressDialogHandler handleMessage");
Bundle b = msg.getData();
boolean isDone = b.getBoolean("isDone");
String tag = b.getString("tag");
if (isDone && !onSaveInstanceFlag) {
FragmentManager fm = getFragmentManager();
Fragment loader = (Fragment)fm.findFragmentByTag(tag);
fm.beginTransaction().remove(loader).commit();
closeProgressDialog();
Main.this.startWorkBtn.setEnabled(true);
}
}
}
#Override
public void sendProgress(String tag, int progress, int max) {
if ( progress == max) {
Log.i(TAG,"Main sendProgress " + Utils.getThreadId());
Message message = handler.obtainMessage();
Bundle b = new Bundle();
b.putBoolean("isDone", true);
b.putString("tag",tag);
message.setData(b);
this.handler.sendMessage(message);
}
}
private void startProgressDialog(boolean show) {
this.progressDialog = new ProgressDialog(this);
this.progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
this.progressDialog.setMessage("loading");
this.progressDialog.setCancelable(false);
this.progressDialog.show();
}
private void closeProgressDialog() {
if (null != this.progressDialog) {
progressDialog.cancel();
this.progressDialog = null;
}
}
} // end Main
public class WorkFragment extends Fragment {
private static final String TAG = "tag";
private boolean mReady = false;
private boolean mQuiting = false;
private boolean done = false;
public WorkFragment () {}
final Thread mThread = new Thread() {
#Override
public void run () {
synchronized(this) {
while (!mReady) {
Log.i(TAG,"WorkF notReady"+ Utils.getThreadId());
if (mQuiting) {
return;
}
try {
wait();
} catch (InterruptedException e) {
}
}
} // end synchronized
Log.i(TAG,"WorkF starting work "+ Utils.getThreadId());
try {
Log.i(TAG,"WorkF about to sleep"+ Utils.getThreadId());
Thread.currentThread().sleep(10000l);
Log.i(TAG,"WorkF almost finished "+ Utils.getThreadId());
done = true;
} catch (InterruptedException e1) {
e1.printStackTrace();
}
synchronized(this) {
while (!mReady) {
Log.i(TAG,"Activity notReady"+ Utils.getThreadId());
if (mQuiting) {
return;
}
try {
wait();
} catch (InterruptedException e) {
}
}
((WorkProgressListener)getActivity()).sendProgress(WorkFragment.this.getTag(), 100, 100);
} // end synchronized 2
}
};
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.i(TAG,"WorkF, onAttach: "+ Utils.getThreadId());
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG,"WorkF, onCreate: "+ Utils.getThreadId());
setRetainInstance(true);
mThread.start();
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.i(TAG,"WorkF, onActivityCreated: "+ Utils.getThreadId());
if (done) {
((WorkProgressListener)getActivity()).sendProgress(WorkFragment.this.getTag(), 100, 100);
}
synchronized (mThread) {
mReady = true;
mThread.notify();
}
}
#Override
public void onStart()
{
super.onStart();
Log.i(TAG,"WorkF, onStart: "+ Utils.getThreadId() );
}
#Override
public void onDestroy() {
synchronized (mThread) {
mReady = false;
mQuiting = true;
mThread.notify();
}
super.onDestroy();
}
#Override
public void onDetach() {
synchronized (mThread) {
mReady = false;
mThread.notify();
}
super.onDetach();
}
public void restart() {
synchronized (mThread) {
mThread.notify();
}
}
}// end WorkFragment
public interface WorkProgressListener {
public void sendProgress (String tag, int progress, int max);
}

Categories

Resources