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();
Related
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
after some time of programming I decided to download the Android SDK and see if I should start focusing on mobile development to start making money.
At the moment I am just trying to get used to how the android works and up until now I have a basic understanding of the events and the way the xml files interact with the .java files.
To learn I'm just trying to do tests of basic stuff and now I have something like this :
TextView text = (TextView)findViewById(R.id.lol);
number mnumber = new number();
mnumber.change_in_number();
text.setText(mnumber.get_in_number() + "");
Let me explain;
number is a class I make which has an integer varibale inside, a function to get the value (get_in_number) and a function to change the integer variable to a random value (change_in_number).
All of this functions work es intended for they are very simple but when I run the code this only happens once (as expected).
My question now is...
Exactly how would I make this code repeat itself every X amount of seconds?
You know, to have the value changed multiple times without the need of any event while the application is running.
I know this question is really simple and probably also easy to answer but right now I really need help with getting started.
Thanks in advance for your help.
This might be a lot over your head but you need to create a separate thread with a while loop to periodically update your TextView. I didn't compile and run this but it should be pretty close to exactly what you want:
public class YourActivity extends Activity
{
private UpdaterThread updaterThread;
private Runnable changeNumberRunnable;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
changeNumberRunnable=new Runnable()
{
#Override
public void run()
{
YourActivity.this.updateNumber();
}
};
}
#Override
protected void onResume()
{
super.onResume();
updaterThread=new UpdaterThread();
updaterThread.start();
}
#Override
protected void onPause()
{
super.onPause();
updaterThread.kill();
}
private void updateNumber()
{
TextView text = (TextView)findViewById(R.id.lol);
number mnumber = new number();
mnumber.change_in_number();
text.setText(mnumber.get_in_number() + "");
}
private class UpdaterThread extends Thread
{
private boolean running;
public void kill()
{
running=false;
}
#Override
public void run()
{
running=true;
while(running)
{
//you can't change a view from a separate thread, so call the update on the main UI thread
YourActivity.this.runOnUiThread(changeNumberRunnable);
//sleep for 5 seconds, if we're interrupted then exit the loop
try { Thread.sleep(5000); }
catch(InterruptedException e) { running=false; }
}
}
}
}
I'm working with a fairly common situation right now - download some data over the web, then update a view to display it. Clearly, I want to do the web download in the background, and then update the view on the main UI thread. Now looking at my code, I'm a little worried about my Activity and its UI elements being killed off before I update them. Here's the essence of what I have in mind:
Thread update = new Thread() {
public void run() {
final Data newData = requestData();
if (newData != null) {
post(new Runnable() {
public void run() {
Toast.makeText(MyClass.this, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show();
}
});
}
}
};
update.start();
It seems possible that while I'm downloading data, the activity may be destroyed. What happens then? Will my thread continue to execute? Will I end up trying to access dead objects?
Usually I do this by AsycTask, but the work seemed simple enough this time to just inline the threads-launching-threads stuff. Will I make things any better by using an AsyncTask instead?
If your Context is an Activity, you can check if it is finishing or has finished with the isFinishing() method:
if ( context instanceof Activity ) {
Activity activity = (Activity)context;
if ( activity.isFinishing() ) {
return;
}
}
Toast.makeText(context, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show();
What you really want to use is an AsyncTaskLoader. These are my new favorite classes in the Android API. I use them all the time and they were made to solve problems just like this. You won't have to worry about when to stop your download or anything like that. All the threading logic is taken care of for you, including telling the thread to stop if the activity has been closed. Just say what it is you want to do in the loadInBackground() method. Note that if you are developing for an API lower than 3.0, you can still access all the loaders via the Android Support Package.
If you use anonymous classes, they will have an internal reference to the outer class, so it's not like it becomes inaccessible all of a sudden because other references have been cleared. AsyncTask actually doesn't change anything, it uses similar mechanics for notifying about results.
You can use loaders, they are designed to be in sync with the activity lifecycle. They are available only since Android 3.0, but you can use support package to work with them on any device with 1.6 or later.
There is even a simpler solution, you can just use a boolean field which indicates whether activity has gone away. You should set this field in onPause() (or whenever you think you won't need the notifications anymore) and check for it when you show toast. You won't even have to use synchronization, since this field is confined to the main thread, so it's absolutely safe. By the way, if you change this field somewhere else than in onDestroy(), don't forget to add a statement which resets your field back in the counterpart method.
public class MyActivity extends Activity {
private boolean activityDestroyed = false;
#Override
protected void onDestroy() {
activityDestroyed = true;
}
private void updateData() {
new Thread() {
#Override
public void run() {
final Data newData = requestData();
if (newData == null) return;
runOnUiThread(new Runnable() {
public void run() {
if (activityDestroyed) return;
Toast.makeText(MyActivity.this, "Blah",
Toast.LENGTH_SHORT).show();
}
});
}
}.start();
}
}
I usually use Weak Reference to avoid leaking context in views
Weak Reference for Context
private var mContext: WeakReference<Context?>? = null
Assign Context
mContext = WeakReference(appContext)
Get Context
mContext .get()
Validate Context
if (mContext?.get() is Activity &&
(mContext?.get() as Activity).isFinishing){
return
}
Kurtis is right. However, if you REALLY want to keep it simple, you can try this:
class MyActivity extends Activity {
static MyActivity context;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
MyActivity.context = this;
}
#Override
public void onDestroy() {
super.onDestroy();
MyActivity.context = null;
}
}
And then you just use MyActivity.context in your class (and check for null there). If you want the toast to not even show up when your app is in the background, use onPause/onResume instead.
Again, this is the quick and lazy approach. AsyncTask or AsyncTaskLoader is how you should be doing things.
I'm trying to update the tab being displayed, however it seems to wait until the end of the method and then update. Is there a way to make the tab being displayed update immediately?
Here is an example of the code where I'm having this issue:
private static void someButtonMethod()
{
Button = new JButton("My Button");
Button(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
tabs.setSelectedIndex(1);
// Do some other things (In my case run a program that takes several seconds to run).
runProgram();
}
});
}
The reason for this is that the method is being executed in the Event Dispatch thread, and any repaint operations will also occur in this thread. One "solution" is to update the tab index and then schedule the remaining work to be invoked later on the EDT; this should cause the tab state to be updated immediately; e.g.
public void actionPerformed(ActionEvent evt) {
tab.setSelectedIndex(1);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Do remaining work.
}
});
}
EDIT
Per your comment below an example of how to invoke a SwingWorker in order to call your runProgram method would look something like this:
// Typed using Void because runProgram() has no return value.
new SwingWorker<Void, Void>() {
protectedVoid doInBackground() {
runProgram();
return null; // runProgram() doesn't return anything so return null.
}
protected void done() {
// Called on the EDT when the background computation has completed.
// Could insert code to update UI here.
}
}.execute()
However, I sense a bigger problem here: The fact that you are seeing a significant delay in updating the tab makes me think you are performing long running calculations on the EDT. If this is the case you should consider performing this work on a background thread. Take a look at the SwingWorker class.
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