1

I had issues with a previous question where I was unsure whether my code had a memory leak. A few answers were concerned with it being run on the UI thread and so blocking. It is true, it runs on the UI thread and doesn't spawn a new one..

So to solve it I use Thread instead of Handler to spawn a new thread outside of UI. The problem now is that I can't manage to delay it like I did with the one ran in the UI thread.

This is my previous question where my original UI thread code is: Is this Runnable safe from memory leak? and the following is the updated code which spawns a new thread:

package com.example.helloworld;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.TextView;
import android.util.Log;
import java.lang.ref.WeakReference;

public class HelloWorldActivity extends Activity
{

    private static TextView txtview;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        txtview = (TextView) findViewById(R.id.mainview);

        Thread t = new Thread(new WeakRunnable(txtview));
        t.start();
    }

    private static final class WeakRunnable implements Runnable {
        private final WeakReference<TextView> mtextview;

        protected WeakRunnable(TextView textview){
            mtextview = new WeakReference<TextView>(textview);
        }

            @Override
            public void run() {
                TextView textview = mtextview.get();
                if (textview != null) {

                    try {
                        Thread.sleep(1500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    int test = 5*5;
                    txtview.setText("Hola Mundo"+test);
                }
                Log.d("com.example.helloworld", "" + Thread.currentThread().getName()); // Outputs "Thread-<num>" if not running on UI thread
            }
    }           

}

It just sets the view text and appends the result of 5*5.

As soon as I start the app it quits itself and I don't get why. Something tells me I'm delaying it the wrong way or using runOnUiThread wrong. Even changing txtview.setText("Hola Mundo"+test); to runOnUiThread( txtview.setText("Hola Mundo"+test) ); doesn't compile giving error: 'void' type not allowed here.

In a nutshell: Computation (5*5 in this case) should be done on a separate thread to avoid blocking the main (UI) thread, and the text should be set on the UI taking the computated item from the separate thread. A simple example of your own would be fine too.

UPDATE I have posted an answer to my own question implementing AsyncTask.

Community
  • 1
  • 1
Alper Turan
  • 1,220
  • 11
  • 24

3 Answers3

2

Use postDelayed method found in the Handler class and Views

change,

    Thread t = new Thread(new WeakRunnable(txtview));
    t.start();

to :

    txtview.postDelayed(new WeakRunnable(txtview), 1500);

Then remove the following sleep clause in your runnable as it is no longer needed

                try {
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
petey
  • 16,914
  • 6
  • 65
  • 97
  • This will use the UI thread and block until it's done, it's not using the spawned thread to do the `5*5` computation. – Alper Turan Apr 17 '15 at 15:38
  • 1
    You will need to change the text on the UI thread, cant be done on a background thread. See my edit where I also say to remove the thread sleep. This should now work the way you need it to. – petey Apr 17 '15 at 15:44
  • That's what I did. The problem is that the computation is ran on the UI thread, which if more complex than `5*5` will most likely hang the UI. That's why I needed to do computation on another thread, while doing the view text set on the UI thread (obviously). I just checked with `Thread.currentThread().getName()` to be sure. – Alper Turan Apr 17 '15 at 22:33
  • 1
    I see. In this case, you should use an [AsyncTask : http://stackoverflow.com/a/9671602/794088](http://stackoverflow.com/a/9671602/794088). – petey Apr 17 '15 at 23:38
1

You can use condition until it is met instead of using thread.sleep();

 Condition.wait(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                return textview != null;
            }
        });
Joseph118
  • 505
  • 6
  • 21
1

As @Axxiss said, this case is better suited to AsyncTask. I updated my code to use AsyncTask with a WeakReference to avoid memory leaks:

package com.example.helloworld;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.AsyncTask;
import android.widget.TextView;
import android.util.Log;
import java.lang.ref.WeakReference;

public class HelloWorldActivity extends Activity
{

    private static TextView txtview;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        txtview = (TextView) findViewById(R.id.mainview);

        SimpleTask objSimpleTask=new SimpleTask();
        objSimpleTask.execute();
    }

    private static class SimpleTask extends AsyncTask<Void,Void,String> {
        private WeakReference<TextView> mtextview = new WeakReference<TextView>(txtview);

        @Override
        protected String doInBackground(Void... params) {
            Log.d("com.example.helloworld", "" + Thread.currentThread().getName());
            try{
                Thread.sleep(1500);
            } catch(InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
            return "Hola Mundo";
        }
        @Override
        protected void onPostExecute(String result) {
            TextView mtxtview = mtextview.get();
            mtxtview.setText(result);
        }


    }           

}

doInBackground executes the code in a separate thread, while you can later catch the result with onPostExecute and execute it in the main (UI) thread without any frills.

Alper Turan
  • 1,220
  • 11
  • 24
  • `AsyncTask` is massive overkill for something so simple. Maybe if your code were doing something more than multiplying 5*5... – Kevin Krumwiede Apr 17 '15 at 22:27
  • @KevinKrumwiede Well let's keep this as a reference for those situations then. This is just a test prototype that I'll adapt to a resource intensive computation. I simplify my code when posting on StackOverflow. – Alper Turan Apr 17 '15 at 22:29
  • 2
    Fair enough. As a rule of thumb, use `Handler#postDelayed(...)` for anything logically constrained by the lifecycle of an activity that can run on the UI thread, `AsyncTask` for anything constrained by the lifecycle of the activity that cannot run on the UI thread, and `Service` for anything not constrained by the lifecycle of a single activity. – Kevin Krumwiede Apr 17 '15 at 22:38
  • @KevinKrumwiede Thank you. This is valuable info that I'll use when in doubt. – Alper Turan Apr 17 '15 at 22:42