i am calling this function from the menu and calls the upload(item) function to pass the index of the selected priority.
public void showPriorityDialog()
{
final CharSequence[] priority = {"1 Hour", "12 Hours", "24 Hours", "Cancel"};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Select Priority");
builder.setItems(priority, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
if(item != 3)
upload(item);
}
});
AlertDialog alert = builder.create();
alert.show();
}
however, whenever i call the upload function, the thread doesn't run in background, and the OS thinks that the app is not responding due to executing timeout.
public void upload(int priority)
{
final int _priority = priority;
uploadThread = new Thread()
{
#Override
public void run()
{
try
{
super.run();
mHandler.post(new Runnable() {
#Override
public void run() {
try
{
//ftp commands...
}
catch (Exception e)
{
Toast.makeText(getApplicationContext(), e.toString() , Toast.LENGTH_SHORT).show();
}
}
});
}
catch (Exception e)
{
Toast.makeText(getApplicationContext(), e.toString() , Toast.LENGTH_SHORT).show();
}
}
};
uploadThread.start();
}
am i doing something wrong? TIA
When you do mHandler.post(), your entire Runnable executes on UI thread and your background thread just exits. To fix, do FTP before posting to handler. Then do mHandler.post() to have Toast appear. Note that you catch also need to display Toast via post.
Related
I'm having some problems running a thread in my android application, It should show a dialog asking the user something and if the user clicks yes, a loading dialog should appear while it's doing something in the background, I created a thread but when I click the yes button, the UI still locks up until the process is done.
Code:
Dialog:
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setMessage("LOGO.bin Was Not Found, Would You Like To Extract It?")
.setTitle("LOGO Not Found!");
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
getAndExtract();
}
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
finish();
System.exit(0);
}
});
AlertDialog dialog = builder.create();
dialog.show();
getAndExtract:
public void getAndExtract()
{
new Thread(new Runnable() {
#Override
public void run() {
try {
showLoad("Grabbing Logo...");
getLogo();
Thread.sleep(2000);
progressDialog.cancel();
showLoad("Extracting Images...");
extractImages();
Thread.sleep(2000);
progressDialog.cancel();
}catch (InterruptedException iE)
{
iE.printStackTrace();
}
}
}).run();
}
showLoad:
progressDialog.setMessage(msg);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.show();
basics of extractImages:
Command cmd = new Command(0, "LogoInjector -i " + getFilesDir() + "/LOGO.bin -d -g " + getFilesDir() + "/");
RootTools.getShell(true).add(cmd);
basics of getLogo:
Command cmd = new Command(0, "dd if=/dev/block/mmcblk0p" + partitionIndex + " of=" + getFilesDir() + "/LOGO.bin");
RootTools.getShell(true).add(cmd);
I also tried putting showLoad in runOnUiThread but there was no change... if I remove progressDialog.cancel(); it does show the loading dialog but after the extract is already complete. I press Yes and it just hangs until getLogo() and extractImages() both completed
Can anyone help me find out why this isn't working?
Thanks!
Try using AsyncTask:
final AsyncTask<Void,Void,Void> asyncTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
// do whatever you need to do in background
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute( aVoid);
// do after finished
}
};
asyncTask.execute();
Hope that helps =]
i am trying to display a Toast on the screen and when Toast fades off then move to the next question. I have tried with Thread but cannot seem to manage.
My code:
next.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (getUserSelection()){
position = position + 3;
if (position < questionsArray.size()) {
curName = questionsArray.get(position).getName();
curArray = questionsArray.get(position).getAnswers();
curIscorrect = questionsArray.get(position).getIscorrect();
setupQuestionView(curName, curArray, curIscorrect);
} else {
StringGenerator.showToast(QuestionsActivity.this, "Your score : " + score + "/" + (questionsArray.size() / 3));
}
}else {
StringGenerator.showToast(QuestionsActivity.this, getString(R.string.noanswerselected));
}
}
});
and the getUserSelectionMethod:
private boolean getUserSelection() {
correct = (RadioButton)findViewById(group.getCheckedRadioButtonId());
if (correct == null){
return false;
}else {
correctAnswerText = correct.getText().toString();
if (map.get(correctAnswerText).equals(Constants.CORRECTANSWER)) {
score++;
setCorrectMessage();
return true;
} else {
setWrongMessage();
return true;
}
}
}
private void setCorrectMessage() {
correctToast = new Toast(QuestionsActivity.this);
correctToastView = getLayoutInflater().inflate(R.layout.correct, (ViewGroup) findViewById(R.id.correctRootLayout));
correctText = (TextView)correctToastView.findViewById(R.id.correctTextView);
correctText.setText(getString(R.string.correctAnswer));
correctToast.setDuration(Toast.LENGTH_SHORT);
correctToast.setView(correctToastView);
correctToast.show();
correctThread = new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
correctToast.cancel();
}
});
correctThread.start();
}
private void setWrongMessage() {
wrongToast = new Toast(QuestionsActivity.this);
wrongToastView = getLayoutInflater().inflate(R.layout.wrong, (ViewGroup) findViewById(R.id.wrongRootLayout));
wrongText = (TextView)wrongToastView.findViewById(R.id.wrongTextView);
wrongText.setText(getString(R.string.wrongAnswer));
wrongToast.setDuration(Toast.LENGTH_SHORT);
wrongToast.setView(wrongToastView);
wrongToast.show();
wrongThread = new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
wrongToast.cancel();
}
});
wrongThread.start();
}
Any suggestion on how to do this?
You can determine the toast visibility:
toast.getView().getWindowToken()
If the result is null, than your toast isn't visible anymore, and than you can run any code you want.
as stated in this answer you can start a thread that waits the duration of the Toast:
Thread thread = new Thread(){
#Override
public void run() {
try {
Thread.sleep(3500); // 3.5seconds!
// Do the stuff you want to be done after the Toast disappeared
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Toast.LENGTH_SHORT and Toast.LENGTH_LONG are only flags so you have to either hard code the duration or keep them in a constant. The durations are 3.5s (long) and 2s (short).
If you want to manipulate some of your views, you cannot do this in another thread than the "main" UI thread. So you have to implement a kind of callback/polling mechanism to get notified when the SleepThread has finished.
Check this answer to read about a couple of ways to do this. Probably the easiest of them to understand and implement is this:
After you started your Thread you can check if it is still alive and running by calling thread.isAlive(). In this way you can do a while loop that runs while the thread is running:
// start your thread
while(thread.isAlive()){}
// continue the work. The other thread has finished.
Please note that this is NOT the most elegant way to do this! Check the other possibilities in the answer I've mentioned above for more elegant solutions (especially the last one with the listeners is very interesting and worth reading!)
That's because the Thread class is purely executed in the background and you need to manipulate the view in the Main thread. To solve your issue just replace the Thread with AsynTask.
AsyncTask<Void,Void,Void> a = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
correctToast.cancel();
}
};
a.execute();
If you look at my code you can see my onPostExecute, this method is called in the Main Thread.
My Error was because i was trying to acess UI Elements through another Thread so modifying the code like this:
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(500);
QuestionsActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
moveToNextQuestion();
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
did the trick. I hope my answer helps someone!!!
I have several buttons that can be clicked on a fragment. After clicking each button I show a Toast message that is exactly the same for each button. Thus if you press 5 different buttons one after another you will layer up 5 toast messages which will end up showing the same message for a long time. What I want to do is show a Toast if there is no Toast currently running.
The method that I use to show the Toast message
public void showToastFromBackground(final String message) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show();
}
});
}
When a button is pressed I simply call showToastFromBackground("Text to show");.
What I actually want is something like
public void showToastFromBackground(final String message) {
if(toastIsNotAlreadyRunning)
{
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show();
}
});
}
}
Use:
toast.getView().isShown();
Or:
if (toast == null || toast.getView().getWindowVisibility() != View.VISIBLE) {
// Show a new toast...
}
EDIT:
Toast lastToast = null; // Class member variable
public void showToastFromBackground(final String message) {
if(isToastNotRunning()) {
runOnUiThread(new Runnable() {
#Override
public void run() {
lastToast = Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG);
lastToast.show();
}
});
}
}
boolean isToastNotRunning() {
return (lastToast == null || lastToast.getView().getWindowVisibility() != View.VISIBLE);
}
Try isShown(). It returns a fatal error if no toast is shown. So you can use try and catch the error.
//"Toast toast" is declared in the class
public void showAToast (String st){
try{
toast.getView().isShown(); // true if visible
toast.setText(st);
} catch (Exception e) { // invisible if exception
toast = Toast.makeText(theContext, st, toastDuration);
}
toast.show(); //finally display it
}
From here.
This does not wait if there is toast already, then show. But it does change active toast's text and show new one immediately without toasts overlapping each other.
For some reason my call to AsyncTask.cancel only works once, i.e. for the first instance of the task, and never again. The first task cancels beautifully and hits the onCancelled method. All the others seem to ignore the cancel() call and end up in onPostExecute.
The task is executed from a service:
public class ZitFtpService extends Service implements ZitFtpServiceInterface
{
//Blah blah
public void connect(String server, int port)
{
if(!isConnecting){
isConnecting = true;
ConnectTask task = new ConnectTask();
task.execute(server, String.valueOf(port));
}
}
//Blah blah blah
As you can see it is a new instance every time. I can't see why the first one would behave differently from the subsequent ones. The AsyncTask is a private inner class:
private class ConnectTask extends AsyncTask<String, String, Boolean> {
#Override
protected Boolean doInBackground(String... params) {
boolean result = false;
try {
publishProgress(
"start", "Connecting to "+ params[0] + ":" + params[1]);
Log.v("ZIT", params[0] + " " + params[1] + " " + params.length);
conn.connect(params[0], Integer.valueOf(params[1]), 1000);
result = true;
} catch (NumberFormatException e) {
Log.e("ZIT", e.getMessage());
} catch (IOException e) {
failMessage = e.getMessage();
e.printStackTrace();
}
return Boolean.valueOf(result);
}
private void cancelConnect() {
try {
conn.disconnect();
} catch (IOException e) {
e.printStackTrace();
} finally {
conn = new ZMobileFTPImpl();
}
if(!(dialog==null)) {
dialog.dismiss();
}
}
#Override
protected void onCancelled() {
Log.v("ZIT", "I was cancelled.");
isConnecting = false;
}
#Override
protected void onProgressUpdate(String... values) {
if(dialog == null) {
dialog = new ProgressDialog(progressActivity);
dialog.setCancelable(true);
dialog.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
ConnectTask.this.cancel(true);
cancelConnect();
dialog.dismiss();
}
});
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
}
dialog.setMessage(values[1]);
dialog.setCancelable(true);
dialog.show();
}
#Override
protected void onPostExecute(Boolean result) {
dialog.dismiss();
if(!result) {
AlertDialog.Builder builder =
new AlertDialog.Builder(progressActivity);
builder.setMessage(failMessage).setTitle("Error");
failMessage = "";
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
AlertDialog failDialog = builder.create();
failDialog.show();
}
isConnecting = false;
}
}
From Doc's
There are a few threading rules that must be followed for this class to work properly:
The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN.
The task instance must be created on the UI thread.
execute(Params...) must be invoked on the UI thread.
Do not call onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) manually.
The task can be executed only once (an exception will be thrown if a second execution is attempted.)
So, you can call an AsyncTask multiple times by creating new instance every time like
new ConnectTask().execute(params);
This is intentional as you can only execute an AsyncTask instance once, you can run task.execute multiple times though...
Anyhow, I believe you forgot to add the super.onCancelled in following override:
#Override
public void onCancelled() {
//...
super.onCancelled();
}
Try if that helped, and otherwise you should share the error or log so we can troubleshoot that :)
Let me sum up the situation for you:
I have a button (btnChooseEp), and when you press it an AlertDialog appears.
When something is picked in the AlertDialog, three if statements must be evaluated.
While they are being evaluated, a ProgressDialog appears. It indicates that the app is "busy".
After the evaluation of these statements, the ProgressDialog must disappear.
My problem is described beneath the code.
The entire code block is shown here:
ProgressDialog getTracklistProgressDialog = null;
...
Button btnChooseEp = (Button)findViewById(R.id.btnChooseEp);
btnChooseEp.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
final AlertDialog.Builder builder = new AlertDialog.Builder(GA.this);
builder.setTitle(getText(R.string.chooseEpisode));
builder.setItems(episodes, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, final int pos)
{
getTracklistProgressDialog = ProgressDialog.show(GA.this, "Please wait...",
"Retrieving tracklist...", true);
new Thread()
{
public void run()
{
try
{
String str1, epURL;
if(pos < 9)
{
str1 = getResources().getString(R.string.epNo1);
epURL = String.format(str1, pos+1);
setTracklist(epURL, tracklist);
}
else if(pos < 100)
{
str1 = getResources().getString(R.string.epNo2);
epURL = String.format(str1, pos+1);
setTracklist(epURL, tracklist);
}
else if(pos >= 100)
{
str1 = getResources().getString(R.string.epNo3);
epURL = String.format(str1, pos+1);
setTracklist(epURL, tracklist);
}
}
catch(Exception e)
{}
// Remove progress dialog
getTracklistProgressDialog.dismiss();
}
}.start();
}
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
});
Not sure if needed, but here is the code for the function setTracklist:
public void setTracklist(String string, TextView tv)
{
try
{
tv.setText(getStringFromUrl(string));
}
catch(Exception e)
{
e.printStackTrace();
}
}
And the code for the function getStringFromUrl can be seen here: http://pastebin.com/xYt3FwaS
Now, the problem:
Back when I didn't implement the ProgressDialog thing (which I have from here, btw: http://www.anddev.org/tinytut_-_displaying_a_simple_progressdialog-t250.html), it worked fine - the setTracklist function retrieves a string from a text file on the internet, and sets the text to a TextView. Now, when I have added the ProgressDialog code, and put the original code into the try statement, only a very little part of the text file is added to the TextView - approximately 22-24 characters, not more. The "busy" ProgressDialog shows up just fine. It worked perfectly before; it was more than capable of loading more than 1300 characters into the TextView.
I don't know if it has anything to do with the thread - I have Googled a lot and found no answer.
So, how do I get it to load in all data instead of just a small part?
(By the way, I would love to be able to set the line "setTracklist(epURL, tracklist);" beneath all of the if statements, but then it says it can't resolve "epURL". Seems stupid to write the same line 3 times!)
Updated 25/1 with current code:
final Handler uiHandler = new Handler();
final Button btnChooseEp = (Button)findViewById(R.id.btnChooseEp);
btnChooseEp.setEnabled(false);
btnChooseEp.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
builder.setTitle(getText(R.string.chooseEpisode));
builder.setItems(episodes2, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, final int pos)
{
replay.setVisibility(View.VISIBLE);
replayWeb.setVisibility(View.VISIBLE);
getTracklistProgressDialog = ProgressDialog.show(GA.this, "Please wait...",
"Retrieving tracklist...", true);
new Thread()
{
public void run()
{
try
{
String str1, epURL;
if(pos < 9)
{
str1 = getResources().getString(R.string.gaEpNo1);
epURL = String.format(str1, pos+1);
setTracklist2(epURL, tracklist);
}
else if(pos < 100)
{
str1 = getResources().getString(R.string.gaEpNo2);
epURL = String.format(str1, pos+1);
setTracklist2(epURL, tracklist);
}
else if(pos >= 100)
{
str1 = getResources().getString(R.string.gaEpNo3);
epURL = String.format(str1, pos+1);
setTracklist2(epURL, tracklist);
}
}
catch(Exception e)
{}
// Remove progress dialog
uiHandler.post(new Runnable()
{
public void run()
{
getTracklistProgressDialog.dismiss();
}
}
);
}
}.start();
}
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
});
public void setTracklist2(final String string, final TextView tv)
{
try
{
uiHandler.post(
new Runnable()
{
public void run()
{
try
{
tv.setText(getStringFromUrl(string));
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
}
});
}
catch(Exception e)
{
e.printStackTrace();
}
}
Notes: "replay" and "replayWeb" are just two TextView's. "btnChooseEp" is enabled when another button is pressed.
My guess is that you are getting bizarre behavior because you are invoking a ui method on a non-ui thread.
getTracklistProgressDialog.dismiss();
must be executed on a ui thread. My guess is that it is crashing and your thread is crashing then leaving some of your resources in a bad state. This would explain why you get a varying amount of characters.
I would try creating a final Handler in your onCreate method which would get bound to the uiThread. In that thread, you can then call
uiHandler.post(
new Runnable() {
public void run(){
getTracklistPRogressDialog.dismiss();
}
}
);
This is quick, so it may not be syntactically correct, check your ide.
This is the best i can get from what you've posted. If you post more of the code I can try to run it to give you more help.
Update:
I think I found your problem:
The idea of having another thread is to do the long running work there, but what we have right now actually does the long running work on the ui thread, the opposite of our goal. What we need to do is move the call to getStringFromUrl(url) from the setTracklist call up into the thread. I would rewrite setTracklist as follows:
public void setTracklist(String tracklistContent, TextView tv)
{
try
{
runOnUiThread(
new Runnable() {
public void run() {
tv.setText(tracklistContent);
}
});
}
catch(Exception e)
{
e.printStackTrace();
}
}
Then in your inner onClick method, do this:
public void onClick(DialogInterface dialog, final int pos)
{
getTracklistProgressDialog = ProgressDialog.show(GA.this, "Please wait...",
"Retrieving tracklist...", true);
new Thread()
{
public void run()
{
try
{
String str1, epURL;
if(pos < 9)
{
str1 = getResources().getString(R.string.epNo1);
epURL = String.format(str1, pos+1);
String tlContent = getStringFromUrl(epUrl);
setTracklist(epURL, tracklist);
}
else if(pos < 100)
{
str1 = getResources().getString(R.string.epNo2);
epURL = String.format(str1, pos+1);
String tlContent = getStringFromUrl(epUrl);
setTracklist(epURL, tracklist);
}
else if(pos >= 100)
{
str1 = getResources().getString(R.string.epNo3);
epURL = String.format(str1, pos+1);
String tlContent = getStringFromUrl(epUrl);
setTracklist(epURL, tracklist);
}
}
catch(Exception e)
{}
// Remove progress dialog
getTracklistProgressDialog.dismiss();
}
}.start();
}
I'm so, we make the call to the web service/url before we regain ui thread execution. The long running internet retrieval runs on the bg thread and then the ui update happens on the ui thread. Think this should help.