There are an application that process many operations in an AsyncTask that has a runOnUIThread call.
The application has the code to show an indeterminate ProgressBar when the process it's doing in background, but the progress bar isn't shown always, in one cases yes, in other cases no.
With this model of (bad structure) code, how can I sure that the progress bar always is shown
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.TextView;
public class AsyncTestActivity extends ActionBarActivity {
private ProgressDialog pd = null;
private void loadingBar(String titulo){
pd = ProgressDialog.show(this, titulo, "Please wait ...", true, false);
}
private void closeLoading(){
pd.dismiss();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_test);
TextView textView = (TextView) findViewById(R.id.textView);
Task async = new Task(textView);
async.execute("");
}
private class Task extends AsyncTask<String, Void, String> {
private TextView tv;
public Task(TextView tv) {
this.tv = tv;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
loadingBar("Warning");//I want to show this always
}
#Override
protected String doInBackground(String... params) {
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
for(int i = 0; i <= 10; i++) {
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
return "End";
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
tv.setText(result);
closeLoading();
}
}
}
I, can't change the structure (AsyncTask/runOnUIThread)the code because is big and I have few time to do this. The goal is that the progress bar always is ahowed. I had been reading that this is a bad practice, but the code was made by another person.
Did you ever try to call bringToFront() inside your onPreExecute():
#Override
protected void onPreExecute() {
super.onPreExecute();
loadingBar.bringToFront();
loadingBar("Warning");//I want to show this always
}
(Untested code)
or if you want to make sure, you can include your progressBar as your Task parameter then directly execute bringToFront();
public Task(TextView tv, ProgressBar pB) {
this.tv = tv;
this.pb = pB.bringToFront();
}
hope it helps
Call loadingBar("Warning"); before async.execute("");.
If it's not showing, there is a problem with your ProgressDialog. Can you try edit loadingBar() method like this? :
ProgressDialog pd;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_test);
//init progress
pd = new ProgressDialog(this);
//
TextView textView = (TextView) findViewById(R.id.textView);
Task async = new Task(textView);
async.execute("");
}
private void loadingBar(String titulo){
pd.show();
pd.setTitle(titulo);
}
Related
I'm trying to get input stream from URL to be displayed on TextView when clicked on a certain button in my android app. I used someone else's code from internet tutorial step by step but it doesn't work for me. The TextView is always empty even though it should already show the text. I would deeply appreciate any help.
public class fetchData extends AsyncTask<Void, Void, Void> {
String data="";
#Override
protected Void doInBackground(Void... voids) {
try {
URL url = new URL("https://api.myjson.com/bins/j5f6b");
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = httpURLConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = "";
while(line != null){
line = bufferedReader.readLine();
data = data + line;
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
CurrencyConvert.text.setText(this.data);
}
}
public class CurrencyConvert extends AppCompatActivity {
Button click;
public static TextView text;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_currency_convert);
click = (Button) findViewById(R.id.button);
text = (TextView) findViewById(R.id.fetchText);
// data.setText("");
click.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
fetchData process = new fetchData();
process.execute();
}
});
}
}
You shouldn't declare TextView as static varible of Activity class. Instead pass activity instance to fetchData class constructor (btw. class names should start with uppercase)
public class FetchData extends AsyncTask<Void, Void, Void> {
CurrencyConvert activity;
public FetchData(CurrencyConvert activity) {
this.activity= activity;
}
#Override
protected Void doInBackground(Void... arg0) {
// do background stuff...
}
#Override
protected void onPostExecute(Void result) {
activity.setTextInTextView(this.data);
}
In Your Activity declare public method for setting text:
public class CurrencyConvert extends AppCompatActivity {
Button click;
TextView text;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_currency_convert);
click = (Button) findViewById(R.id.button);
text = (TextView) findViewById(R.id.fetchText);
data.setText("");
click.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
fetchData process = new fetchData(this);
process.execute();
}
});
}
void setTextInTextView(String data) {
text.setText(data);
}
}
I have a simple Android program that calculates how long it takes the phone to compute a certain mathematical problem. I want the mathematical problem to start when I hit the button, and while it is running I want a spinning progress bar to be displayed, and I want it to disappear after the math problem is done. This is the code I currently have:
public class main extends AppCompatActivity {
private TextView mScore;
private Button mRunButton;
private TextView mScoreText;
private ProgressBar mSpinner;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mScore = (TextView) findViewById(R.id.score);
mRunButton = (Button) findViewById(R.id.runbutton);
mScoreText = (TextView) findViewById(R.id.scoreText);
mSpinner = (ProgressBar) findViewById(R.id.progress);
mSpinner.setVisibility(View.GONE);
View.OnClickListener listener = new View.OnClickListener(){
#Override
public void onClick(View v) {
mSpinner.setVisibility(View.VISIBLE);
long startTime = System.nanoTime();
long start = System.currentTimeMillis();
long count = 0l;
for(long x=0;x<Integer.MAX_VALUE ;x++){
count+=1;
}
long end = System.currentTimeMillis();
long endTime = System.nanoTime();
long duration = ((endTime - startTime) / 1000000);
mScore.setText(duration + "");
mSpinner.setVisibility(View.GONE);
}
};
mRunButton.setOnClickListener(listener);
}
}
From what I can tell, nothing in the view of the app updates until after the phone is done with the entire onClick method, which is not what I want it to do. I want the progress bar to be displayed ONLY while the program is 'working'. How would I go about doing this?
Thank you
As Blackbelt and vilpe89 mentioned, you have to separate the threads. You can do this by having another class that extends ASyncTask which will handle the calculations. The problem with that is that the progress dialog needs to run on the UI thread. You can have an interface that changes the progress dialog in the main class.
Calculator class:
public final class Calculator extends AsyncTask<Void, Void, Void> {
Context context;
calcCallback mCallback;
public Calculator(Context c) {
this.context = c;
this.mCallback = (calcCallback) c;
}
//The main class needs to implement this interface
public interface calcCallback {
Void calcDone();
Void calcStarted();
//Other methods if necessary
}
#Override
protected Boolean doInBackground(Void... params) {
mCallback.calcStarted();
//Your calculations here
mCallback.calcDone();
return null;
}
}
MainActivity
public class MainActivity extends Activity implements Calculator.calcCallback, {
private TextView mScore;
private Button mRunButton;
private TextView mScoreText;
private ProgressBar mSpinner;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mScore = (TextView) findViewById(R.id.score);
mRunButton = (Button) findViewById(R.id.runbutton);
mScoreText = (TextView) findViewById(R.id.scoreText);
mSpinner = (ProgressBar) findViewById(R.id.progress);
mSpinner.setVisibility(View.GONE);
View.OnClickListener listener = new View.OnClickListener(){
#Override
public void onClick(View v) {
Calculator calculator = new Calculator(MainActivity.this);
calculator.execute();
}
};
mRunButton.setOnClickListener(listener);
}
#Override
public Void calcStarted() {
runOnUiThread(new Runnable() {
#Override
public void run() {
mSpinner.setVisibility(View.VISIBLE);
}
});
}
return null;
}
#Override
public Void calcDone() {
runOnUiThread(new Runnable() {
#Override
public void run() {
mSpinner.setVisibility(View.GONE);
}
});
}
return null;
}
}
You can also set up your calcDone() as calcDone(int duration) so that you can pass the calculated duration back to the main thread.
Is it okay to do this? Creating another MainAcitivity since my original MainAcitivity is tied on the v4.view.pager activity_main. Although, this code doesn't post errors, this class seems to not be reflecting on the output. I'm using a tab pager adapter. Do I need to refresh or something? (It doesn't do anything that even the clickAction on this class doesn't work.
public class MainActivity2 extends Activity {
String url = "http://blog.compassion.com/living-in-the-philippines-rural-life/";
ProgressDialog mProgressDialog;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home_fragment);
TextView titlebutton = (TextView) findViewById(R.id.TextView01);
titlebutton.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
// Execute Title AsyncTask
new Title().execute();
}
});
}
private class Title extends AsyncTask<Void, Void, Void> {
String body;
String title;
#Override
protected void onPreExecute() {
super.onPreExecute();
mProgressDialog = new ProgressDialog(MainActivity2.this);
mProgressDialog.setTitle("Android Basic JSoup Tutorial");
mProgressDialog.setMessage("Loading...");
mProgressDialog.setIndeterminate(false);
mProgressDialog.show();
}
#Override
protected Void doInBackground(Void... params) {
try {
Document document = Jsoup.connect(url).get();
Elements elements = document.select("p");
title = document.title();
for(Element elements123 : elements){
body+=elements123.text();
System.out.println(elements123.text());
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
TextView txttbody = (TextView) findViewById(R.id.textView2);
txttbody.setMovementMethod(new ScrollingMovementMethod());
txttbody.setText(title +"\n"+body);
mProgressDialog.dismiss();
}
}
}
I have the following code, in which I try to connect to google.com and parse the text on that site:
package com.example.parsetest;
import java.io.IOException;
import org.jsoup.*;
import org.jsoup.nodes.Document;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
Thread downloadThread = new Thread() {
public void run() {
Document doc;
try {
doc = Jsoup.connect("http://google.com/").get();
String title = doc.title();
TextView console = (TextView) findViewById(R.id.textview1);
console.setText(title);
} catch (IOException e) {
e.printStackTrace();
}
}
};
}
My issue is that I'm unsure as to whether I've created my new thread properly, and where I'm supposed to call downloadThread.start() from - am I supposed to create a new class? Or do I call it from my onCreate method?
Yes you need to call downloadThread.start(). You cannot update ui from a background thread. Use runOnUiThread
public class MainActivity extends Activity {
TextView console;
String title;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
console = (TextView) findViewById(R.id.textView1);
new Thread() {
public void run() {
Document doc;
try {
doc = Jsoup.connect("http://google.com/").get();
title = doc.title();
runOnUiThread( new Runnable()
{
public void run()
{
console.setText(title); // set text on the ui thread
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
As other have suggested using asynctask is easier.
http://developer.android.com/reference/android/os/AsyncTask.html
Using AsyncTask
public class MainActivity extends Activity {
TextView console;
String title;
ProgressDialog pd;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pd = new ProgressDialog(this);
pd.setMessage("Jsoup parsing...");
console = (TextView) findViewById(R.id.textView1);
new TheTask().execute();
}
class TheTask extends AsyncTask<Void,String,String>
{
#Override
protected String doInBackground(Void... arg0) {
// TODO Auto-generated method stub
Document doc;
try {
doc = Jsoup.connect("http://google.com/").get();
title = doc.title();
}
catch(Exception e)
{
e.printStackTrace();
}
return title;
}
#Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
pd.dismiss();
console.setText(title);
}
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
pd.show();
}
}
}
check out android AsyncTask usage example
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
the download should be taken place at doInBackground
I'm unsure as to whether I've created my new thread properly
Yes you have created Thread in right way but inside run method you are updating TextView text which is not valid because only main thread updated ui elements instead of any other thread. you should use runOnUiThread for updating TextView :
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
TextView console = (TextView) findViewById(R.id.textview1);
console.setText(title);
}
});
suggested way is use AsyncTask
where I'm supposed to call downloadThread.start() from
you should call downloadThread.start() in onCreate of Activity after setContentView
I have an AsyncTask which shows a ProgressDialog. The AsyncTask is started when the activity is started:
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_layout);
new MyTask().execute();
}
// ... other code
private class MyTask extends AsyncTask<Void, Void, Void>{
private ProgressDialog dialog = new ProgressDialog(MyActivity.this);
#Override
protected void onPreExecute() {
dialog.show();
}
#Override
protected Void doInBackground(Void... voids) {
// get data from a server
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
// call to a method in MyActivity which updates the UI.
if (dialog.isShowing()) {
dialog.dismiss();
}
}
}
}
This code works perfectly, untill I rotate my screen. Which makes sense, because the context that was used to create the dialog doesn't exist anymore (because the activity is re-created when rotating), and a window leak is caused.
The only solution I could think of isn't a really nice one: create a static instance of the task and dialog, and simply dismiss the dialog when the activity is destroyed, and recreate the dialog in the oncreate method if the task is still running.
So how would I solve something like this without losing functionality (so the dialog must always be shown when the task is running, and rotating the device should be allowed)?
As Raghunandan suggested in his comment, I looked into Fragments and solved my problem.
I created a Fragment which starts my AsyncTask, as explained in the blogpost that Raghunandan provided.
And to make sure that my Dialog didn't get leaked, I created a DialogFragment, as described here (Basic Dialog).
Here's my working code:
My Activity:
public class MyActivity extends Activity implements MyTaskFragment.TaskCallbacks {
private MyTaskFragment task;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_layout);
FragmentManager fm = getFragmentManager();
task = (MyTaskFragment) fm.findFragmentByTag("myTask");
if (task == null) {
task = new MyTaskFragment();
fm.beginTransaction().add(task, "myTask").commit();
}
}
#Override
public void onPreExecute() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
Fragment prev = getFragmentManager().findFragmentByTag("myDialog");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
StringProgressDialogFragment dialog = StringProgressDialogFragment.newInstance("My message");
dialog.show(ft, "myDialog");
}
#Override
public void onPostExecute() {
StringProgressDialogFragment dialog = (StringProgressDialogFragment) getFragmentManager().findFragmentByTag("myDialog");
if (dialog!=null) {
dialog.dismiss();
}
// update UI
}
// ... other code
}
My Task fragment:
public class MyTaskFragment extends Fragment {
private TaskCallbacks mCallbacks;
private Task mTask;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (TaskCallbacks) activity;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retain this fragment across configuration changes.
setRetainInstance(true);
// Create and execute the background task.
mTask = new Task();
mTask.execute();
}
#Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
private class Task extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
mCallbacks.onPreExecute();
}
#Override
protected Void doInBackground(Void... voids) {
// do stuff
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
mCallbacks.onPostExecute();
}
}
public static interface TaskCallbacks {
void onPreExecute();
void onPostExecute();
}
}
My Dialog fragment:
public class StringProgressDialogFragment extends DialogFragment {
private String message;
public static StringProgressDialogFragment newInstance(String message) {
StringProgressDialogFragment dialog = new StringProgressDialogFragment();
Bundle args = new Bundle();
args.putString("message", message);
dialog.setArguments(args);
return dialog;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
ProgressDialog dialog = new ProgressDialog(getActivity());
message = getArguments().getString("message");
dialog.setMessage(message);
return dialog;
}
}
New Loaders API can help you (available via support package) - man. They will solve problem with rotation, but not a mem. leak. To solve mem. leaks write your own "AsyncTask" (with a "clearContext" routine) and clear it's context in activity's onDestroy (or onPause, depends on your architecture). It may looks like a bicycle, but the task takes max 1 day, and you will have a full control on all the resources you background worker use.
By the way: consider using dialogs through fragments, because it solves dialog kill on screen rotation.
try with sample. it will work. basically just restrict the oncreate call by handling the config change. this solution may help you.
public class MainActivity extends Activity {
LoadProgrssdata task = new LoadProgrssdata();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(this, "oncreate called", Toast.LENGTH_SHORT).show();
task.execute();
}
public class LoadProgrssdata extends AsyncTask<Void, Void, Void> {
ProgressDialog progressDialog;
//declare other objects as per your need
#Override
protected void onPreExecute()
{
progressDialog= ProgressDialog.show(MainActivity.this, "Progress Dialog Title Text","Process Description Text", true);
//do initialization of required objects objects here
};
#Override
protected Void doInBackground(Void... params)
{
//do loading operation here
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result)
{
super.onPostExecute(result);
progressDialog.dismiss();
};
}
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Log.e("orientation ", "landscape");
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Log.e("orientation ", "portrait");
}
}
}
and in android manifest file:
<activity
android:name="com.example.MainActivity"
android:label="#string/app_name"
android:configChanges="orientation|keyboardHidden|screenSize" />
I managed to fix this problem by trying to catch any crash that, may occurs, in doInBackground.