0

I have two simple activities MainActivity and ThreadActivity. I call ThreadActivity from MainActivity.
The code ofMainActivity:

public class MainActivity extends Activity {
    private Button btn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btn = (Button)findViewById(R.id.btn2);
        btn.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 Intent intent = new Intent(MainActivity.this, ThreadActivity.class); 
                 startActivity(intent);
             }
         });
    }
}

And the code of ThreadActivity:

public class ThreadActivity extends Activity{

    private Thread myThread=null;
    Button btn;
    int i = 0;

     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.custom);

         btn = (Button)findViewById(R.id.btn);
         btn.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                     runThread();
             }
         });
     }

     void  runThread(){
         myThread = new Thread() {
                public void run() {
                    while (i++ < 1000) {
                        try {
                            runOnUiThread(new Runnable() {
                                @Override 
                                public void run() {
                                    btn.setText("#" + i);
                                    Log.d("Thread", "I am running " + i);
                                }
                            });
                            Thread.sleep(300);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            return;
                        }
                    }
                }
            };
            myThread.start();
     }
}

When I start ThreadActivity I run a simple thread and change button text.

My Problem

  • When I loose focus from application, i.e when application becomes partially visible, and I come back I am redirected to ThreadActivity and the thread is still running.
  • When I leave application running and open a new application, and then come back, I am again redirected to ThreadActivity.

The problem is when I press back button, I am being redirected to first activity MainActivity. But instead when back button is being pressed I want my application to exit. In a few words MainActivity should not exist in the stack.

I tried setting android:noHistory="true" for MainActivity but I could not keep the behavior explained in bullet points working. I mean when I pause the application and restore it back, it redirected me to MainActivity instead of ThreadActivity.

user3764893
  • 697
  • 5
  • 16
  • 33
  • What is the purpose of your thread? You call `Thread.sleep();` on your main thread, which is not good. – Marius Jul 21 '14 at 11:59
  • Well, it's not like it's going to happen in any real-life situation. – Marius Jul 21 '14 at 12:01
  • You never accepted an answer to this question. If your issue was solved by one of them please accept the answer in question. This will indicate to future visitors which answer solved your problem and might help them find a solution to their problem more quickly. – Xaver Kapeller Jul 28 '14 at 11:40

3 Answers3

1

Just call finish() when starting the ThreadActivity:

btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(MainActivity.this, ThreadActivity.class);
        startActivity(intent);
        finish();
    }
});

BUT there is a problem with your app. Use a Timer to set the text of the Button! By using a Thread like you do you are creating a memory leak and that is very bad. Try this:

private int i = 0;
private Timer timer;
private final TimerTask timerTask = new TimerTask() {

    @Override
    public void run() {
        btn.post(new Runnable() {
            @Override
            public void run() {
                btn.setText("#" + i++);
            }
        });
    }
};


@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.custom);

    btn = (Button)findViewById(R.id.btn);
    btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            timer = new Timer();
            timer.schedule(timerTask, 300, 300);
        }
    });
}

@Override
public void onPause() {
    super.onPause();

    if(timer != null) {
        timer.cancel();
    }
}
Xaver Kapeller
  • 49,491
  • 11
  • 98
  • 86
  • I called `finish()`, exited the app, restored it back, and it again redirected me to `MainActivity` – user3764893 Jul 21 '14 at 11:57
  • That can't be. If you `finish()` the `Activity` it is gone, you must be doing something else wrong. – Xaver Kapeller Jul 21 '14 at 11:58
  • Also please look at the other part of my answer to correct a critical flaw in your app! – Xaver Kapeller Jul 21 '14 at 11:59
  • What do you exactly mean by exiting and restoring the app? – Xaver Kapeller Jul 21 '14 at 12:01
  • All my code is there. As I said I first called `ThreadActivity` started the thread, pressed back button, the app exited. And I restored it back it brought me to `MainActivity` instead of `ThreadActivity` – user3764893 Jul 21 '14 at 12:02
  • What I am asking is do you mean really exiting the app or just putting it in the background. And I can only say it again: Don't create a `Thread` like that, especially not from an `Activity`. This creates a memory leak. Use a `Timer` like in my answer. – Xaver Kapeller Jul 21 '14 at 12:04
  • When you press the BACK key while in `ThreadActivity`, `ThreadActivity` is finished. It doesn't exist any more. This is why, when you return to the app, it starts the app again from the beginning (by launching `MainActivity`). – David Wasser Jul 21 '14 at 12:05
  • I mean putting it in a background. – user3764893 Jul 21 '14 at 12:05
  • @DavidWasser exactly. I would like to redirect me back to where I left in `ThreadActivity` – user3764893 Jul 21 '14 at 12:07
  • Yeah well that is not possible. If you really `finish()` the `MainActivity` it can't just come back. Maybe you have an error in your manifest? – Xaver Kapeller Jul 21 '14 at 12:07
  • I don't think so. I have just activities definitions there – user3764893 Jul 21 '14 at 12:08
  • Oh now I understand, of course, @DavidWasser is right. That's why I asked if you really put it in the background. if you press back the `Activity` is `finished()` and your app starts over again. – Xaver Kapeller Jul 21 '14 at 12:08
  • Yes there is, just explain to me what exactly you want to do and I can tell you the best way to implement it. – Xaver Kapeller Jul 21 '14 at 12:11
  • As FunkTheMonk said, I want the `ThreadActivity` to always be resumed instead of the `MainActivity`. And while being in `ThreadActivity`, when the back button is pressed my application should be put in background, not being returned to `MainActivity` – user3764893 Jul 21 '14 at 12:16
  • Yes but why? What do you want to achieve with that? And from what you are explaining it sounds more like `ThreadActivity` should be the main `Activity`. – Xaver Kapeller Jul 21 '14 at 12:20
  • Well I have a simple app, in the first activity it has a `sign in` procedure and after the user is signed in it will be redirected to second activity. When the user presses back button, I do not want him to see `sign in` screen again. – user3764893 Jul 21 '14 at 12:22
  • As I suspected, you got the order of your `Activities` wrong. The `Activity` with the login only has to be shown once, so why would you make that one the launcher `Activity`? Do it the other way around, check in the `ThreadActivity` if you are logged in and if not, open the `Activity` with the login. – Xaver Kapeller Jul 21 '14 at 12:25
  • Yeah that might one solution, it will lead me to do lots work and changes in my codes. I gave the above example for that reason. I mean the idea is almost the same. I hoped it could have been just solved without major changes. Using above example is it impossible to achieve what I meant? – user3764893 Jul 21 '14 at 12:35
  • Is it a major change? Just cut & paste the intent filter from one `Activity` to the other in the manifest and start the login `Activity` if necessary. Those changes are just a few lines of code. – Xaver Kapeller Jul 21 '14 at 12:37
  • Thanks @XaverKapeller for your time and suggestions. I solved it and will post the solution right away. – user3764893 Jul 21 '14 at 12:55
  • Was the solutions something other than I suggested? Don't post duplicate answers! – Xaver Kapeller Jul 21 '14 at 12:56
  • I did not need to add `finish()`. But still thanks for the suggestions – user3764893 Jul 21 '14 at 13:01
  • Of course you don't need that if you change the order of the `Activity`. I ask you: was that really the core of your problem? – Xaver Kapeller Jul 21 '14 at 13:02
  • What do you mean? If you mean for not changing the order of activities, yeah it was a part of my problems and I feel relieved now. – user3764893 Jul 21 '14 at 13:07
  • Oh I forgot to ask. Why does the thread creates a memory leak? And would it be possible to modify it for not creating a memory leak? – user3764893 Jul 21 '14 at 13:54
  • Because the `Thread` is holding a reference to the `Activity` preventing it from being garbage collected. Even well after the `Activity` is `finished()` or in the background it can never be destroyed or removed from memory until the `Thread` shuts down or is killed. – Xaver Kapeller Jul 21 '14 at 14:02
  • what if I declared the thread as a private static inner class? – user3764893 Jul 21 '14 at 14:08
  • Yes you got it, if you declare it static you solve the problem, **however** as I explained to you numerous times. You are not supposed to use a `Thread` to update the text of a `Button` all 300 milliseconds. In almost all programming languages there is one common rule: **NEVER** use `Thread.sleep()` or some equivalent of `Thread.sleep()`. It's bad, it's ugly, and there are solutions which are a million times better. Look at my answer, I demonstrated how to do the exact same thing much simpler and better with a `Timer`. – Xaver Kapeller Jul 21 '14 at 14:10
  • ok I got it :). One last question. Looking at your code, what if I had another button trying to change the `timer schedule` time. I mean it would be possible to change `timer schedule` time before it gets started. But once the `timer schedule` time is started is it possible to reschedule its time? – user3764893 Jul 21 '14 at 14:21
  • There might be an easier way, but you can always just call `cancel()` on the `Timer` and reschedule the `TimerTask`. – Xaver Kapeller Jul 21 '14 at 14:25
1

From your comments to other peoples' answers, it seems like you want the ThreadActivity to always be resumed instead of the MainActivity when your thread is running.

  1. Do the thread in a Service - the service will mean your application's VM is likely to be kept alive longer. An app with no foreground activities can be killed off quite quickly (even if it has background threads running).
  2. You need to persist that the thread is running, and the progress (if, in the real code that is applicable). Currently you could persist the value of i in your while loop.
  3. Your application's default launcher activity (MainActivity) will launch when you click on it from your launcher. Check if the persisted value has been set, and act as though the user started the ThreadActivity in onCreate, if you finish() in onCreate, the user won't see any UI from the MainActivity
  4. Depending on what you're actually trying to do, you might be able to resume the thread depending on the progress persisted - in this example, you could start from the persisted value of i (instead of 0).
FunkTheMonk
  • 10,908
  • 1
  • 31
  • 37
0

What I had to is set android:noHistory="true" for MainActivity and in the ThreadActivity I had to add the solution mention by @NeTeInStEiN in this quesiotn

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            moveTaskToBack(true);
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
Community
  • 1
  • 1
user3764893
  • 697
  • 5
  • 16
  • 33