Taking heavy computation off the Android UI Thread - java

My Android app employs a particularly big computation which keeps crashing the system because it is on the UI thread in the Activity.
I have little confidence in multithreading and so I want to get some tips on how to do it correctly.
This is what I have
class ActivtyName extends Activity{
boolean threadcomplete = false;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//stuff
Runnable newthread = new Runnable(){
#Override
public void run() {
doBigComputation();
threadcomplete=true;
}
};
newthread.run();
boolean b = true;
while(b){
if(threadcomplete){
b=false;
startTheApp();
}
}
}
}
Now, I am pretty sure what I have done is not "correct". (It seems to work though. The sistem doesn't crash). Basically, I'm not sure how the UI thread can be told that newthread has finished the computation without this boolean, threadcomplete. Is there a "correct" way to do this?

Just to expand a bit on Marek Sebera's comment, here's the framework on how you would accomplish that in an AsyncTask.
private class BigComputationTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
// Runs on UI thread
Log.d(TAG, "About to start...");
}
#Override
protected Void doInBackground(Void... params) {
// Runs on the background thread
doBigComputation();
}
#Override
protected void onPostExecute(Void res) {
// Runs on the UI thread
Log.d(TAG, "Big computation finished");
startTheApp();
}
}
And to call it:
BigComputationTask t = new BigComputationTask();
t.execute();

In addition to Marvin's answer, there's a good article on the Android developer site about precisely this.

That's not the correct way to start a thread. You need to do:
Runnable newthread = new Runnable(){
#Override
public void run() {
doBigComputation();
threadcomplete=true;
}
};
Thread t = new Thread(newthread);
newthread.start();
boolean b = true;
while(b){
if(threadcomplete){
b=false;
startTheApp();
}
}

When i want to use threads, I've almost a big while loop. I put a boolean condition who's always true, except when I want to stop it.
I use Thread (http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Thread.html) and I reimplement the stop method because it's deprecated. So in my stop method I can put a false value to the loop variable.
To terminate the thread, I use t.stop(). So you can di that in your activity.
If you want to know when a Thead stops, you can use t.isAlive(). If the thread t is started, isAlive will return true.

Related

How to kill a thread while finishing app from an AlertDialog in onBackPressed()?

I have an activity which runs a thread and also on that activity in the onBackPressed() void I have an AlertDialog which closes the activity.
The problem is that the thread is running after the activity was closed.
new Thread((new Runnable() {
#Override
public void run() {
//...
}
})).start();
#Override
public void onBackPressed(){
new AlertDialog.Builder(this)
.setTitle("Quit")
.setMessage("Are you sure you want to quit?")
.setNegativeButton("No", null)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent intent = new Intent(rest.this, MainActivity.class);
finish();
Thread.currentThread().interrupt();
}
}).create().show();
}
This is a trick , which i used also
Solution 1:-
private boolean isActivityRunning=false;
then in onCreate() set it true
isActivityRunning=true;
then in onStop()
isActivityRunning=false;
And in the thread method use it like
new Thread((new Runnable() {
#Override
public void run() {
if(isActivityRunning){
//do your stuff
}
//...
}
})).start();
Solution 2:- this is better solution according to the usage
Use Handler
private Handler mHandler;
private Runnable mRunnable;
in call this where u want to use
mHandler = new Handler();
mRunnable = new Runnable() {
#Override
public void run() {
// do your stuff here
}
};
Also Override
#Override
protected void onResume() {
super.onResume();
if (mHandler != null) {
if(mHandler!=null){
mHandler.postDelayed(mRunnable, 1000);//time in miliseconds
/
}
}
}
#Override
protected void onPause() {
super.onPause();
if (mHandler != null) {
if(mHandler!=null){
mHandler.removeCallbacks(mRunnable);//time in miliseconds
}
}
}
And you should go through this why we prefer worker thread instead of thread
Difference between UI Thread and Worker Thread in Android?
Make a Boolean flag and run the thread while (flag).
Thread {while (flag) { Your thread code goes here. } }
Change the flag to False in the OnClick function.
Having threads in activities is not a very good idea. The activity lifecycle does not mention that explicitly, but in my experience, the process might not finish and activity instance might not always be garbage collected once you call finish. Also, there are scenarios in which you might want to stop your background operation that are not related to you pressing "back".
Also note, that Thread.currentThread().interrupt() does not signal interrupt to the thread you started, but the to the current thread. Interrupt also does not guarantee that the thread function will finish. In order to interrupt() a thread, you need a reference to it.
To be clear: I advise against such approach. Use services for such processing or at AsyncTasks. Without knowing what you want to achieve, it is hard to say what is the best approach.
If you must stick with your thread-inside-activity idea, I suggest you signal to the thread function that it should exit. For thread signalling, you might want to check the following resources:
https://docs.oracle.com/javase/tutorial/essential/concurrency/
http://tutorials.jenkov.com/java-concurrency/thread-signaling.html
https://stackoverflow.com/a/289463/22099
https://stackoverflow.com/a/6859855/22099

Android - pause and continue thread

In my Activity I create a Thread for multiply objects and "save" them to a hashmap. Is it possible to pause just a single thread of the hasmap and continue it?
public void onCreate(Bundle savedInstanceState) {
Thread oThread = new Thread(new Runnable() {
public void run() {
while (true) {
// doing some work
}
}
}
oThread.start();
aThreads.put(name, oThread);
}
public void anyMethod(){
// here I want to start and continue a thread
aThreads.get(name).stop();
aThreads.get(name).resume();
}
But .stop() and .resume() aren't working :/ any suggestions?
Use a boolean variable to control the thread exection.
ie.
while(!isThreadRunning){
...your statements
}
Once you are moving out of the screen in OnPause method make the variable false/true based on your usage to stop the thread execution.And you have to take care of the persisting hashmap.

Android - Returning ArrayList to activity from background thread

I have an activity named BuildingActivity that extends ListActivity.
In the onCreate() method, i'm running 7 database queries in a background thread. In that background thread, I am building an ArrayList<String> object from the data returned from the queries.
Now, I want to return that ArrayList<String> object back to my BuildingActivity thread.
Here's part of the code that Im working on:
public class BuildingActivity extends ListActivity {
private ProgressBar mProgressBar;
public ArrayList<String> buildings;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_building);
new Thread(new Runnable() {
#Override
public void run() {
ArrayList<String> list;
DataSource dbSource = new DataSource(getApplicationContext());
dbSource.open();
list = dbSource.getBuildingsList();
dbSource.close();
//*** NOW HOW DO I PASS list BACK TO onCreate()?
//*** I WANT TO MAKE buildings = list.
}
}).start();
if(!buildings.isEmpty()) {
// do Something.
// If i do buildings = list in the background thread,
// This will always be executed because the background thread can take
// some time to return the data. How do i make sure this part of
// code is executed only after the data has been returned?
}
}
}
My objective after this is to create a list from this returned list of buildings. And on clicking a building, another activity shall open. How do I get about this problem?
Thanks!
How do i make sure this part of code is executed only after the data
has been returned?
Instead of using Thread for doing task in background use AsyncTask which provide doInBackground for performing operation in background and onPostExecute methods run on UI Thread after completing background task
here use this
public void myMethod(){
Thread background = new Thread(new Runnable(){
#Override
public void run(){
Looper.prepare();
//Do your data rerieval work here
Runnable r=new Runnable() {
#Override
public void run() {
//return your list from here
}
};
handler.post(r);
Looper.loop();
}
});
background.start();
}

Temporary threads leaking things they reference (target java.lang.thread)

I have an android app with a non-UI thread that needs to be responsive, but which needs to occasionally perform a long running task. To do this, I make it spawn a thread to run the task. This thread takes a reference to an object. This object appears to leak. I run out of memory amazingly fast.
I anticipate lots of people telling me to use AsyncTask. I find AsyncTask irritating and harder to use than a plain ol' thread, and I am not in the UI thread here. I will listen to advice, but I'd appreciate it if the advice explained why!
I have written a very simple program to demonstrate the problem, which runs a thread that, every second, updates a textview with the value of an integer. Having done so, it spawns a thread which increments the integer by one. The thread also, for no good reason, takes a reference to an object called "BigFatBlob". It leaks like a sieve. I don't understand why. After the program code I have attached a screen shot of part of the MAT analysis of the heap showing the leak and the incoming references.
The function to look at below is "increment()". If anyone can tell me why my program is leaking I'd be very grateful - I'm a bit lost here.
public class MainActivity extends Activity {
TextView text_;
int value_;
boolean ready_;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text_ = (TextView)findViewById(R.id.textView);
value_ = 0;
ready_ = false;
watcher();
increment(new BigFatBlob());
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
/** Updates the text view object to show the current value */
public void updateText() {
Runnable r = new Runnable() {
#Override
public void run() {
text_.setText("" + value_);
}
};
runOnUiThread(r);
}
/** runs forever, checking for new values every second */
private void watcher() {
Runnable r = new Runnable() {
#Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
if (pollReady()) {
updateText();
increment(new BigFatBlob());
}
}
}
};
Thread t = new Thread(r);
t.start();
}
/** runs once - increments the value by 1 and flags "ready" */
public void increment(final BigFatBlob junk) {
Runnable r = new Runnable() {
private BigFatBlob j;
#Override
public void run() {
setJ(junk);
++value_;
setReady(true);
}
public BigFatBlob getJ() {
return j;
}
public void setJ(BigFatBlob j) {
this.j = j;
}
};
Thread t = new Thread(r);
t.start();
}
/** ready is synchronized */
private synchronized void setReady(boolean state) {
ready_ = state;
}
/** get the ready value and, if it was true, make it false again */
private synchronized boolean pollReady() {
if (ready_) {
ready_ = false;
return true;
}
return false;
}
}
public class BigFatBlob {
byte[] blob_;
public BigFatBlob() {
blob_ = new byte[1000];
}
}
I'm sorry I asked. If I can answer it myself in less than 24 hours from asking I wasn't trying hard enough :(
While I am debugging my test app (in Eclipse) it leaks and leaks and leaks and eventually crashes.
If I disconnect the debugger before the crash all the leaked memory suddenly becomes available again and it stops leaking.
I am not 100% sure that a memory analyzer that works over a connection that itself causes my data to leak all over my heap is all that useful.
In fact, it may be the opposite of useful. If this question saves someone else a couple of hours of banging their head on the same artificial wall maybe it was worth asking anyway? Anyone want to shed any more light on this before I close it?

Java Timer equivalent in Android

I recently began working with Java and am exploring Android development. I was trying to port over one of the Java programs I made, but I am having some difficulty with getting the java Timer to function the same way in Android. I read through a number of posts and they, for the most part, indicated that it would be better to use the Handler class in android as opposed to Timer.
This was my timer in Java:
playTimer = new Timer(1000/model.getFPS(), new ActionListener() {
public void actionPerformed(ActionEvent evt) {
// do something
...
if( finished everything ) playTimer.stop();
}
});
And once a certain button was clicked, I would simply run "playTimer.start()" to start it.
As you can see, I had it set up so that the user could set the FPS they wanted (by simply setting the first parameter of the Timer class to 1000/model.getFPS()).
Now I've tried to do something similar in Android using handlers, but I am having some difficulty. It appears that the Handler ticks are not firing at the proper intervals. It seems that they are quite slow compared to what I need it to be. This is what I did in android so far:
public void startTimer() {
playHandler = new Handler();
startTime = System.currentTimeMillis();
playHandler.removeCallbacks(updateTimeTask);
playHandler.postDelayed(updateTimeTask, 0);
}
private Runnable updateTimeTask = new Runnable() {
public void run() {
// do something
...
if( finished everything ) playHander.cancel();
else {
playHandler.postDelayed(updateTimeTask, 1000/model.getFPS());
}
}
};
Excuse the semi-pseudocode. Can anyone shed any light? Thanks guys.
You can use a timer as below. The timer runs every second incrementing the counter. Displs the counter value in textview.
Timer runs on a different thread. SO you should set the text on the UI Thread.
The counter runs from 0 to 99. After 99 the timer is cancelled. Also cancel the timer when not required like in onPause().
public class MainActivity extends Activity {
TextView _tv,tv2;
Timer _t;
int _count=0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
_tv = (TextView) findViewById( R.id.textView1 );
_t = new Timer();
_tv.setText(R.string.app_name);
_t.scheduleAtFixedRate( new TimerTask() {
#Override
public void run() {
_count++;
runOnUiThread(new Runnable() //run on ui thread
{
public void run()
{
_tv.setText(""+_count);
if(_count==99)
{
_t.cancel();
}
}
});
}
}, 1000, 1000 ); //change this value of 1000 to whatever you need.
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
_t.cancel();
}
}
If you decide not to use Timer (for whatever reason) you can just write a separate Thread that sleeps for x milliseconds and then wakes up and calls whatever Runnable you want it to call. That's going to be pretty precise. I have it working at the 10 millisecond level and it works quite nicely.
Just remember that it HAS to call a Runnable because a separate Thread can't have direct effect on anything on the main display thread.
public boolean keepPlayingAnimation = true
Handler h = new Handler()
Runnable updateDisplay = new Runnable(){
public void run(){
//do something in my display;
}
}
new Thread(){
public void run(){
while(keepPlayingAnimation){
try{
sleep(10);
}catch(Exception e){
}
h.post(updateDisplay);
}
}
}.start();
Just don't forget to set keepPlayingAnimation to false when you're done with this cause otherwise it will sit there running in the background for ever (or just about).
Take a look at Android Timer
It already has everything you need i guess. From ticking every 1 second to finish handly and so on.
Here is an example how to setup an TimerTask: setup
Not sure if you need such but i just remembered that i made this.

Categories

Resources