I want to know whether a practice I am using is correct or not? Please have a look!
I am using an AsyncTask to call a Handler to animate (increment gradually over time) the progress in a ProgressBar.
I could only use Handler to animate it but I needed to animate multiple ProgressBars using the same Handler. I did not find it possible to pass a paramerer "ProgressBar" to a Handler, nor did declaring a global variable (maybe array?) looked clean.
So I pass the ProgressBar into an AsyncTask as a parameter, and then within the AsyncTask, I have defined the Handler which manipulates the passed ProgressBar using the method of AsyncTask called publishProgress(int). So Each call to AsyncTask is effectively equal to serving the Handler a unique passed ProgressBar.
I am including a stripped down code which shows the structure:
public class animateProgress extends AsyncTask<ArrayList<ArrayList>, Integer, Boolean> {
ProgressBar pb;
int incrementProgress;
#Override
protected Boolean doInBackground(ArrayList<ArrayList>... arrayLists) {
ArrayList<ArrayList> gotArrayList = arrayLists[0]; //get passed arraylist
ArrayList<ProgressBar> alP = gotArrayList.get(1);
pb = alP.get(0); // REFERENCE TO PROGRESSBAR IS ACQUIRED HERE.
scoreCounter.postDelayed(scoreCounterUpdate, 0);
return null;
}
private Handler scoreCounter = new Handler();
private Runnable scoreCounterUpdate = new Runnable() {
public void run() {
if (MY_LOGIC_THAT_PROGRESS_SHOULD_INCREMENT) {
publishProgress(incrementProgress);
}
if (MY_LOGIC_THAT_WE_SHOULD_EXIT) {
scoreCounter.removeCallbacks(scoreCounterUpdate);
} else {
scoreCounter.postDelayed(this, 50);
}
}
};
#Override
protected void onProgressUpdate(Integer... values) {
pb.incrementProgressBy(values[0]);
super.onProgressUpdate(values);
}
}
The code works perfectly but my concern is that I think I should either not use AsyncTask or not use Handler.
Also, I think that doInBackground() should theoretically finish as soon as I call scoreCounter.postDelayed(), and so should the whole AsyncTask. But in reality, the publishProgress() called by handler many seconds later still works. This is confusing.
Related
I am sorry for my bad english skills. I'm new to programming/stackoverflow and try to create a little android quiz app. This app has to connect to a php server and login/getquestion...
The simplest example is the login. The user has to type in his data and then i have to connect.
To provide that the Gui doesnt freeze i have to use asynchronous tasks.
Here the activity's code:
public void login(final String username, final String password) {
final Activity a = this;
FutureTask t = new FutureTask(new Callable() {
public Object call() {
Connection.GetInstance(a).login(username,password);
afterLoginTry(username,password);
return null;
}
});
t.run();
}
This calls a method in another class, which calls another FutureTask which calls an AsyncTask. At the end there is always an public synchronized method such as afterlogintry(). This works but it's a bit slow and i think dirty code.
My main problem is that i don't know how to give results back through different layers of classes and especially to the activity without using hotfixes all the time.
Is there any good explanation or tutorial, which describes how to design such a construct?
Thx for help
The way you can pass AsyncTask results back to other classes, is by declaring callbacks for the task, that will then report the result to a listener. Here is how it works.
First, you must declare an interface in your AsyncTask which contains a method that will send out the result of the task. So in my example task below, my result is a String. The String gets passed to onPostExecute() when the task finishes its work. I then call my callback method on a registered listener, and pass that return value on to whoever is listening for it. You register a listener by passing in an instance of your callbacks from whichever class is creating the task.
public class MyTask extends AsyncTask<String, Void, String> {
MyTaskCallback listener;
public MyTask(MyTaskCallback listener) {
this.listener = listener;
}
protected String doInBackground(String... params) {
String input = params[0];
//do work
input += "did some work on this String";
return input;
}
//When the thread finishes its work, this gets
//called on the main UI thread
protected void onPostExecute(String result) {
listener.onResultReceived(result);
}
public interface MyTaskCallback {
void onResultReceived(String result);
}
}
So next we need to register a listener for these callbacks, so when the result comes in from the task, it will get reported directly to our class. So let's say we have a simple Activity. The way we register the callbacks is to use the implements keyword on our class declaration, and then to actually implement the callback method in the class itself. We then create our task, and we pass in this which is our Activity that implements the callbacks. A simple example Activity that does this looks like this:
public class TaskActivity extends AppCompatActivity implements MyTask.MyTaskCallback {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
//we pass in "this" because our Activity itself
//implements the callbacks below.
MyTask myTask = new MyTask(this);
myTask.execute();
}
//Here we implement our callback method, so the task
//can send its results straight through here
public void onResultReceived(String theResult) {
Log.d("TASK RESULT", "Here is our result String: "+theResult);
}
}
Now, our task has our Activity connected to it, through the callbacks we passed into it. So now when our task gets a result, we can send it directly to our listener, which is our Activity, and the result will come right through to our implemented onResultReceived method.
Callbacks are a great way to pass information around between classes while also keeping everything very separated. Hope this helps!
So as the title probably suggests - I've done a lot of research on the topic, but I am still confused and unable of achieving what I want.
In very simplified scenario, I have a LoginActivity in which is method boolean validateUserInput(String mail, String password) and I want to do the check input in the separate thread. I suppose I will extend it in the future to do the log-in itself as well (http request). Naturally I would like to get boolean value if the operation was successful or not - and in the process of operation I want to show progressbar dialog.
Make a thread, run the code, return its result, show the progress bar in a meantime, piece of cake right?
Should I use asynctask or runnable? How do I do this so I do not block the UI thread?
This is code I tried to use in LoginActivity:
new Thread(new Runnable() {
#Override
public void run() {
mUserInputValidated = validateUserInput(inputEmail.getText().toString(), inputPassword.getText().toString());
}
}).start();
if(mUserInputValidated)
{
attemptUserLogin(inputEmail.getText().toString(), inputPassword.getText().toString());
}
I also tried asynctask approach, but ended up with various errors since I started progress dialog in onPreExecute() and ended it in onPostExecute(), using reference like LoginActivity.this where was the problem with memory leak which I was also unable to fix?
I assume this is pretty usual scenarios, since almost every app use it, so - what are common approaches? How do I fix my code?
You have to use asynctask this will take the work off from main-thread and place it on background thread once the work is done
This is a sample that shows how to do it
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.interrupted();
}
}
return "Executed";
}
#Override
protected void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed"); // txt.setText(result);
// might want to change "executed" for the returned string passed
// into onPostExecute() but that is upto you
}
#Override
protected void onPreExecute() {}
#Override
protected void onProgressUpdate(Void... values) {}
}
Reference
I have a bunch of Buttons (custom, with a few extra methods to apply other backgrounds, and revert to original), which can be assigned a background. But since I don't know how large these backgrounds will or can be, I decided to set the background in a separate Thread. First, I had this code:
public void updateButton(final Button b, final String d, final String s) {
b.nullify(); //Recycles previous background, else OOM error
b.setText(s);
if (d != null) {
new Thread() {
#Override
public void run() {
b.setBackgroundFromBase64(d);
}
}.run();
}
else b.setBackgroundToDefault(); //Sets standard android background
}
but I soon found out that this wasn't the way to go. Slowly, the memory got flooded when I called this method a few thousand times. When I removed the new Thread() part, the memory wasn't flooded, so the Thread was the cause of this (Memory Analyzer Tool told me the same).
Then I tried the AsyncTask:
private class UpdateButtonTask extends AsyncTask<Object, Void, Void> {
#Override
protected Void doInBackground(Object... objs) {
String s = (String)objs[0];
Button b = (Button)objs[1];
String d = (String) objs[2];
b.nullify(); //Recycles previous background, else OOM error
b.setText(s);
if (d != null) b.setBackgroundFromBase64(d);
else b.setBackgroundToDefault();
return null;
}
#Override
protected void onProgressUpdate(Void v) {
//As far as I know, I don't need this method
}
#Override
protected void onPostExecute(Void v) {
//Neither this one
}
}
and call
new UpdateButtonTask().execute("Button", myButton, base64String);
in the button update method. But that didn't work either (the button wasn't updated at all. No text, no custom background). I read some articles and questions about Threading (this, this, and this amongst others), but I can't seem to get it working. For now, no Thread and hoping the phones are fast enough to not cause trouble on the UI thread seems to be the best option, but I'd rather have something more reliable, like a Thread.
What am I doing wrong? or maybe just the full question How can I update a Button background on a background Thread (so the UI doesn't stop working when applying larger backgrounds)?
I'm guessing that the problem is that you're trying to update the UI on a non-UI thread. This throws an exception and kills your AsyncTask.
You should separate the processing-intensive tasks to the AsyncTask and then apply UI changes on the main thread (using runOnUiThread(Runnable)).
Since you're using a custom button, I don't really know what you're not allowed to do on a non-UI thread. I'm guessing the setText function is causing problems, if you haven't overridden it.
You should start by wrapping the whole body of your doInBackground method in a try/catch block and logging the exception to isolate the problem.
May be this code will help you
new Thread(new Runnable() {
public void run() {
context.runOnUiThread(new Runnable() {
public void run() {
b.setBackgroundFromBase64(d);
}
});
}
}).start();
I'm developing an android application, and I have a button which starts/pauses certain simulation process. While this process is running, I need to output some data from it real-time. But when I create a new thread for the simulation, I can't access views (let it be TextView) from this thread, because they only can be accessed from the thread where they were created. On the other hand, new thread is necessary because otherwise user wouldn't be able to do anything while simulation is running (for example, press some other buttons). Creating a new service also requires creating a new thread in this case. How should I solve this problem?
You can handle it in many ways,
Try to use AsyncTask in this, your background work done in doInBackGround() method, and your UI will not block and you can also access the views of Activity from where you call AsyncTask by its context via publishProgress() and onProgressUpdate() .
If you are using a simple Thread then using Handler or message or runOnUiThread you can update the view of main thread.
but, in your way I think AsyncTask is best for you.
You can solve the problem with android.os.Handler. Handler instance is bound to the thread that creates it, you can post Runnables to it and it will execute them in the thread it is bound to. For example:
import android.os.Handler;
import android.widget.TextView;
public class MyActivity extends Activity {
private Handler uiHandler;
private TextView simulationStatus;
#Override
public void onCreate(Bundle savedInstanceState) {
...
uiHandler = new Handler();
...
simulationStatus = (TextView) findViewById(R.id.simulationStatus);
...
}
// This method is to be executed on the simulation thread.
public void simulate() {
...
final String simulationStatusText = ...;
...
uiHandler.post(new Runnable() {
#Override
public void run() {
// This is run on the UI thread.
simulationStatus.setText(simulationStatusText);
...
}
});
}
...
}
You can also use AsyncTask. See the link for example.
They can be accessed but only read-only. What you mean is you want to trigger changes from a thread on the views. From the worker thread (non-UI) these changes cannot be triggered, you need to use .runOnUiThread() or use Handlers. Use these at the point where you want to show something, e.g update the textview in .runOnUiThread(),
Example:
myThread = new Thread()
{
#Override
public void run() {
//yourOperation
MyActivity.this.runOnUiThread(new Runnable(){
#Override
public void run() {
if(e!=null)
{
myTextView.setText("something");
}
}});
super.run();
}
};
myThread.start();
Pass the activity to your thread, and use it from within to access your views.
// from your activity:
myExternalThread = new MyCustomCode(this); // <-- activity passed
// your "simulation process" constructor:
public MyCustomCode(Activity parent) {
this.parent = parent;
}
// and when you want to access views:
parent.runOnUiThread(new Runnable() {
#Override
public void run() {
parent.findViewById(R.id.text_view);
... do something to your UI ...
}
});
My preferred way of creating these external tasks is to use AsyncTask (then you don't have to worry about runOnUiThread, among other benefits). I don't know if it applies to your case, but you should check it out.
you have to store the data which you are wanting do show into a public var and let your Thread call a Runnable via Handler.post(runnable)
The code in the run() method of the Runnable is able to access the Textview
I am having a problem with modifying EditText in another function started by the thread:
Thread thRead = new Thread( new Runnable(){
public void run(){
EditText _txtArea = (EditText) findViewById(R.id.txtArea);
startReading(_txtArea);
}
});
my function is as follows:
public void startReading(EditText _txtArea){
_txtArea.setText("Changed");
}
It always force closes while trying to modify the edittext. Does someone know why?
UI views should not be modified from non-UI thread. The only thread that can touch UI views is the "main" or "UI" thread, the one that calls onCreate(), onStop() and other similar component lifecycle function.
So, whenever your application tries to modify UI Views from non-UI thread, Android throws an early exception to warn you that this is not allowed. That's because UI is not thread-safe, and such an early warning is actually a great feature.
UPDATE:
You can use Activity.runOnUiThread() to update UI. Or use AsyncTask. But since in your case you need to continuously read data from Bluetooth, AsyncTask should not be used.
Here is an example for runOnUiThread():
runOnUiThread(new Runnable() {
#Override
public void run() {
//this will run on UI thread, so its safe to modify UI views.
_txtArea.setText("Changed");
}
});
First of all take a look at your log, it usually contains a stack trace when an app shuts down.
You shouldn't run the thread like you normally do, instead use runOnUiThread:
Runnable thRead = new Runnable(){
public void run() {
EditText _txtArea = (EditText) findViewById(R.id.txtArea);
startReading(_txtArea);
}
};
runOnUiThread(thRead);
The explaination: Only the UI thread is allowed to change the state of UI components.
This article may help you.
http://android-developers.blogspot.com/2009/05/painless-threading.html
There are few options:
1. run it on UI thread Activity.runOnUiThread(Runnable)
2. use AsyncTask
Except runOnUiThread() (which works), there is also another way, which I know of:
Define a handler in your UI (Activity) class:
public class MainActivity extends Activity {
.....
Handler uiThreadHandler = new Handler() {
public void handleMessage(Message msg) {
Object o = msg.obj;
if (o==null) o = "";
TextView textIn = (TextView)findViewById(R.id.textin);
textIn.setText(o.toString());
}
};
}
and from inside some thread you can call it:
Message msg = uiThreadHandler.obtainMessage();
msg.obj = "Text for EditView";
uiThreadHandler.sendMessage(msg);
By default, the main thread is the UI thread. All code that modifies the appearance of the application needs to be run in this thread. If you want to have multiple threads in your application that can modify the UI I would suggest using the AsyncTask class.
public someMethod(){
new ChangeTextTask().execute();
}
private class ChangeTextTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
startReading(_txtArea);
return null;
}
}
However, you need to take steps to prevent multiple threads from accessing the EditText object at once. Otherwise you'll wind up getting a CurrentModificationException error.
http://developer.android.com/reference/android/os/AsyncTask.html