问题
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
.
回答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;
}
});
回答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();
}
回答3:
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.
来源:https://stackoverflow.com/questions/29703226/how-to-delay-spawned-thread-when-a-runnable-run-on-ui-thread-isnt-an-option