I am doing lazy loading and almost done with it. But would like to implement a progress dialog with it because it takes about 10seconds between starting the activity and finishing displaying the contents. Once I click on a button to start, it stays at the current page(Main.java) for about 4 second before moving to the next page(Activity.java). Then it takes about 2-4 seconds to display contents.
Tried the examples available here and on the net but they aren't working well (able to display the dialog but unable to do a proper dismiss after content are all downloaded).
Question is, how can I implement a progress indicator immediately once the user clicks on the button?
Activity.java
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
list=(ListView)findViewById(R.id.list);
adapter=new LazyAdapter(this, mStrings, dStrings );
list.setAdapter(adapter);
}
private String[] mStrings = {};
private String[] dStrings = {};
public Activity()
{
String imageC = "";
String textC = "";
try {
// Get the URL from text box and make the URL object
URL url = new URL(targetURL);
// Make the connection
URLConnection conn = url.openConnection();
BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line = reader.readLine();
Pattern sChar = Pattern.compile("&.*?;");
Matcher msChar = sChar.matcher(line);
while (msChar.find()) line = msChar.replaceAll("");
while (line != null) {
if(line.contains("../../"))
{
int startIndex = line.indexOf("../../") + 6;
int endIndex = line.indexOf(">", startIndex + 1);
String abc = "http://www.petschannel.com/";
String imageSrc = line.substring(startIndex,endIndex);
//complete full url
String xyz = abc +imageSrc;
xyz = xyz.substring(0,xyz.indexOf('"'));
xyz = xyz +";";
imageC += xyz;
mStrings = imageC.split(";");
line = reader.readLine();
}
if(line.contains("../../") == false)
{
line = reader.readLine();
}
if (line.contains("Gnametag"))
{
int startIndex = line.indexOf("Gnametag") + 10;
int endIndex = line.indexOf("<", startIndex + 1);
String gname = line.substring(startIndex,endIndex);
textC += "Name: "+gname+ "\n";
}
if (line.contains("Last Update"))
{
reader.close();
}
}
// Close the reader
reader.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
Firstly, you are doing your network call on the main thread, this is a classic no-no for performance reasons, amongst others. Never do any operation that may be time consuming on the main (ui) thread.
I would suggest using AsyncTask, which ensures in this case, that your network call would be done in a worker thread, and the result posted back to the main thread.
AsyncTask has methods to manage progress bars, onProgressUpdate and publishProgress that will help you solve your stated problem. There are many good articles about this, here is one.
Something like this:
final ProgressDialog myDialog = ProgressDialog.show(this, "Title", "Message");
Thread thread = new Thread(new Runnable() {
public void run() {
/* Your code goes here */
mHandler.sendEmptyMessage(0);
}
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
myDialog.dismiss();
}
}
}
thread.start();
Related
I have a Activity that contains over 100 complex views (with images, text views etc). Showing up these views is to hard to do it without a Thread that loads the views asynchronously. So I tried to do it with an AsyncTask. I am not sure whether this is the correct way because the "hard staff" is something that HAS to be done in the UI Thread.
Now I've got the problem that the UI freezes though I used the onProgressUpdate for adding the views in the to parent view. I thought that this would result in single loading views that appear successive in the parent view. But this is not the case.
doInBackground fires all publishProgress calls and after that the main thread is blocked (activity frozen, loadbar does not rotate anymore). Is there a way to achieve what I wanted to have? I looked for solutions but alway ended up with ideas of using AsyncTask and no one had to do view-stuff as "hard staff". I am not using "get" in the AsyncTask what seems to be a problem with AsyncTask.
Here is my code for this. If you need any further information please tell me!
Is there any other way to solute this problem? Is my AsyncTask implementation not correct? I am looking for a way to load these complex views asyncronous to the parent view without blocking the main thread.
Thanks in advance!
public class LoadKraut extends AsyncTask<Integer,Kraut,Void> {
private Context context;
private LinearLayout parent;
private HashMap<String,HeadlineAlphabet> headlinesAlphabet = new HashMap<String, HeadlineAlphabet>();
private long time;
private Integer kategorie;
private char letter = 'X';
private int counter = 0;
private ProgressDialog dialog;
public LoadKraut(Context context) {
/**
* Kategorie:
* 1 - A-Z
* 2 - Notiz
* 3 - Favorit
* 4 - Giftig
*/
Log.i("Kraut", "Start thread" + (System.currentTimeMillis()-time) + "ms");
this.context = context;
this.dialog = new ProgressDialog(context);
this.time = System.currentTimeMillis();
}
#Override
protected void onPreExecute() {
dialog.setMessage("Lade Kräuter. Dieser Vorgang kann einen Moment dauern.");
dialog.show();
}
#Override
protected Void doInBackground(Integer... params) {
this.kategorie = params[0];
//Create overview
try {
DatabaseHelper databaseHelper = new DatabaseHelper(context);
Dao<Kraut,Integer> dao = databaseHelper.getKrautDAO();
parent = (LinearLayout) ((Activity) context).findViewById(R.id.ll_conainter_sv_uebersicht_kraeuter);
//setKraeuter(list, linearLayout, giftig)
long test = System.currentTimeMillis();
List<Kraut> list = new ArrayList<>();
switch (kategorie) {
case 1:
list = dao.queryForAll();
break;
case 2:
list = dao.queryBuilder().where().ne("notiz","").query();
break;
case 3:
list = dao.queryBuilder().where().eq("favorit",true).query();
break;
case 4:
list = dao.queryBuilder().where().eq("toedlichBunny",true).query();
break;
}
Log.i("Kraut","Fetching duration: " + String.valueOf(System.currentTimeMillis() - test));
Iterator<Kraut> iterator = list.iterator();
while(iterator.hasNext()) {
Kraut kraut = iterator.next();
Log.i("Kraut","called pp for" + kraut.getName());
publishProgress(kraut);
}
} catch (SQLException e) {
e.printStackTrace();
}
Log.i("Kraut", "End " + (System.currentTimeMillis()-time) + "ms");
return null;
}
#Override
protected void onProgressUpdate(Kraut... value) {
//Set all Krauts and headlines A-Z
long test = System.currentTimeMillis();
Kraut kraut = value[0];
Log.i("Kraut", String.valueOf(counter));
if((kategorie==1 || kategorie==4) && kraut.getName().charAt(0)!=letter) {
letter = kraut.getName().charAt(0);
HeadlineAlphabet letterHeadline = new HeadlineAlphabet(context);
letterHeadline.setText(String.valueOf(kraut.getName().charAt(0)));
headlinesAlphabet.put(String.valueOf(letterHeadline.getText()),letterHeadline);
parent.addView(letterHeadline);
}
KrautView krautView=null;
if(kategorie==1 || kategorie==3) {
krautView = new KrautUebersicht(context,kategorie);
} else if(kategorie==2) {
krautView = new KrautUebersichtNotiz(context);
}
if(krautView!=null) {
krautView.setKraut(kraut);
parent.addView((LinearLayout) krautView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
}
parent.getRootView().invalidate();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
counter++;
Log.i("Kraut","Kraut View creation duration: " + String.valueOf(System.currentTimeMillis() - test));
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if(kategorie==1) {
//Set Alphabet Column right side
ArrayList<String> anfangsbuchstaben = Kraut.getAnfangsbuchstaben(context);
// Do this with an xml !
for (int i = 1; i <= 26; i++) {
//Log.i("Kraut", String.valueOf(i));
String currentLetter = Helper.getCharForNumber(i);
int id = context.getResources().getIdentifier("tv_"+currentLetter.toLowerCase(),"id",context.getPackageName());
TextView textView = (TextView) ((Activity) context).findViewById(id);
//If no Kraut contains Letter
if (!anfangsbuchstaben.contains(currentLetter)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
textView.setTextColor(context.getResources().getColor(R.color.darkgrey, context.getTheme()));
} else {
textView.setTextColor(context.getResources().getColor(R.color.darkgrey));
}
//Make clickable to jump to A-Z Headlines
} else {
textView.setOnClickListener(new JumpToLetterOnClickListener(headlinesAlphabet));
}
}
}
parent.invalidate();
if(dialog.isShowing()) {
dialog.dismiss();
}
}
}
Note that onProgressView() is called repeatedly as your AsyncTask runs. Therefore, it should be kept as short as possible. This also means that your current code is creating lots of views and adding them to the UI. Instead, you should add the view just once and then update its data in onProgressView().
Also, as Mike M. states in the comments, you should not call Thread.sleep() in onProgressView() since it runs on the UI thread. This is most likely the main reason your app is freezing.
I have posted a question
Progress Dialog is not displaying while getting data from separate thread class
but I haven't got the appropriate answers. I have already used async task to display progress dialog but it is not displaying.
here is the sample code
public class JsonData extends AsyncTask<String, String, String> {
private ProgressDialog mProgressDialog;
Context context;
public JsonData(Context context)
{
this.context=context;
mProgressDialog = new ProgressDialog(context);
mProgressDialog.setMessage("Loading Please Wait.");
mProgressDialog.setIndeterminate(false);
mProgressDialog.setMax(100);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(true);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mProgressDialog.show();
}
#Override
protected String doInBackground(String... aurl) {
String results="";
try {
int k=0;
URL url1;
url1 = new URL(aurl[0]);
InputStream input=url1.openStream();
BufferedInputStream bis=new BufferedInputStream(input);
ByteArrayBuffer baf=new ByteArrayBuffer(1000);
while((k=bis.read())!=-1)
{
baf.append((byte)k);
}
results=new String(baf.toByteArray());
} catch (Exception e) {
e.printStackTrace();
}
return results;
}
#Override
protected void onPostExecute(String jsondata) {
mProgressDialog.dismiss();
}
}
Here is the method in which I have called the async task
private void getRecordsByCount(final String data) {
try {
int color=Color.BLACK;
tableLayoutGrid.removeAllViews();
final String[] details = data.split("_");
SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy");
String formattedDate = df.format(new Date());
String url = ipaddress + "/GrantLeavesList?Companyid=" + user_info.get("CompanyId") + "&divisionid=" + details[3] + "&userid=" + user_info.get("userid") + "&roleid="
+ user_info.get("RoleId") + "&Employeeid=" + user_info.get("EmployeeId") + "&leavetypeid=" + staticDetails.get(details[0]) + "&strStatus=" + staticDetails.get(details[1])
+ "&type=" + staticDetails.get(details[2]) + "&Date=" + formattedDate;
String url2=ipaddress + "/GrantLeavesChildList?Companyid=" + user_info.get("CompanyId") + "&divisionid=" + details[3] + "&userid=" + user_info.get("userid") + "&roleid="
+ user_info.get("RoleId") + "&Employeeid=" + user_info.get("EmployeeId") + "&leavetypeid=" + staticDetails.get(details[0]) + "&strStatus=" + staticDetails.get(details[1])
+ "&type=" + staticDetails.get(details[2]) + "&Date=" + formattedDate;
JsonData jdata=new JsonData(context);
jdata.execute(url,null,null);
String jsonString=jdata.get();
JSONObject obj=new JSONObject(jsonString);
JsonData jdataChild=new JsonData(context);
jdataChild.execute(url2,null,null);
String jsonChildString=jdataChild.get();
JSONObject objchild=new JSONObject(jsonChildString);
btnGrantSubmit.setVisibility(View.GONE);
if (obj != null) {
leaveforwardcounts = obj.getJSONArray("Table1");
leaveforwardchildcounts=objchild.getJSONArray("Table11");
ScrollView scrollGrid = new ScrollView(this);
TableRow datarow = new TableRow(this);
datarow.setWeightSum(100);
TableLayout table = new TableLayout(this);
for (int i = 0; i < leaveforwardcounts.length(); i++) {
btnGrantSubmit.setVisibility(View.VISIBLE);
JSONObject record = leaveforwardcounts.getJSONObject(i);
String applicantname = record.getString("Applicant");
String toDate = record.getString("ToDate");
String noofdays = record.getString("NumberOfDays");
String LOP = record.getString("LOP");
if(LOP!=null && LOP.trim().length()!=0)
{
color=Color.RED;
}
final int id = i;
final Button gridbutton = new Button(this);
gridbutton.setText(status);
gridbutton.setTextColor(Color.BLACK);
gridbutton.setBackgroundResource(R.drawable.grdbutton_30x30);
gridbutton.setGravity(Gravity.CENTER);
gridbutton.setPadding(2, 0, 2, 0);
gridbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
changeRadioButtonState(gridbutton, id, data);
}
});
gridbutton.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
setSelection(gridbutton);
return true;
}
});
TextView tvApplicantName = new TextView(this);
TextView tvToDate = new TextView(this);
TextView tvNoOfDays = new TextView(this);
TextView empty = new TextView(this);
TextView empty2 = new TextView(this);
if (applicantname.trim().length() >= 18) {
applicantname = applicantname.substring(0, 18);
}
tvApplicantName.setText(applicantname);
tvApplicantName.setTypeface(font2);
tvApplicantName.setWidth(70);
tvApplicantName.setTextColor(color);
tvApplicantName.setPadding(5, 0, 0, 0);
tvToDate.setText(toDate);
tvToDate.setTypeface(font2);
tvNoOfDays.setText(noofdays);
tvNoOfDays.setTypeface(font2);
tvNoOfDays.setGravity(Gravity.RIGHT);
Button ivDetails = new Button(this);
ivDetails.setText(" ");
ivDetails.setPadding(2, 0, 2, 0);
ivDetails.setBackgroundResource(R.drawable.detailsbutton_30x30);
ivDetails.setGravity(Gravity.CENTER);
ivDetails.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
leaveDetails = new PopupWindow(showLeaveDetails(id, leaveforwardcounts,data,leaveforwardchildcounts), (int) (width * 0.8), height / 2, true);
leaveDetails.showAtLocation(mainlayout, Gravity.CENTER, 0, 0);
}
});
TableRow row = new TableRow(this);
row.setPadding(0, 3, 0, 3);
row.setWeightSum(100);
row.addView(tvApplicantName, new TableRow.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 55));
row.addView(tvNoOfDays, new TableRow.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 5));
row.addView(empty2, new TableRow.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 20));
row.addView(ivDetails, new TableRow.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 5));
row.addView(empty, new TableRow.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 5));
row.addView(gridbutton, new TableRow.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 5));
table.addView(row);
}
scrollGrid.addView(table);
datarow.addView(scrollGrid, new TableRow.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 100));
tableLayoutGrid.addView(datarow);
}
} catch (Exception e) {
e.printStackTrace();
}
}
I need to build page based on the data from Service. in my app there are about 20-30 services.. if i use async task as inner class it works well and good ...but How can reuse my code...
you dont need to start a background method for postExecute. as #baske wrote, you have problem with .get() - that is blocking your thread even if your are using AsyncTask.
try someting related to the linked question, so only add your YourActivityClass as a param to the construdtor of JsonData
public JsonData(YourActivityClass activity)
{
this.activity=activity;
mProgressDialog = new ProgressDialog(activity);
}
protected void onPostExecute(String jsondata) {
if (mProgressDialog != null || mProgressDialog.isShowing()){
mProgressDialog.dismiss();
}
if(jsondata!=null) {
activity.yourcallback(jsondata)
}
}
And define the yourcallback() in YourActivityClass
private void yourcallback(String data) {
jsonRecordsData=data;
showRecordsFromJson();
}
user, I think you need to understand better what the reasons are for using an AsyncTask and what the uses are of the callback/hook methods it provides.
Starting with the reason: if you have a long-running task, you cannot run this on the main thread (also called UI thread) because your app will eventually show ANR errors. Now if your long-running task would not need to show output on the screen (when it is done, progress reports, etc) you can very well put it in a worker thread and let it run by itself (possibly even delegating it to a Service to guarantee run-to-completion, but that is another story). However, a lot of times this isn't the case and you want to update your UI based on the outcome/progress of your long-running task. To do this you would have to somehow branch off a thread and do the work there, but, since you can only manipulate the UI from the main thread, you would have to post back the result on the main thread when you are done.
This is where we move to the AsyncTask and its hook methods. An AsyncTask is actually just a Utility class that helps you do exactly what is explained above: put your work on a separate thread and get a callback on your main thread when it is done (and the result is available). Checking the documentation you will find:
onPreExecute(): guaranteed to run on the main thread. Allows you to do stuff (like show a progress dialog) BEFORE the work starts.
doInBackground(): guaranteed to run on a background thread. Do you long running stuff here.
onPostExecute(): guaranteed to run on the main thread AFTER your doInBackground() has finished. The result of your task is now available and you can do stuff with it (like put it on the screen).
Getting back to your suggestions about your .get() method having a problem: since you are calling .execute() on your AsyncTask and .get()-ing the result immediately thereafter, chances are that the background job has not yet finished. Instead you should be doing whatever you wanted to do, starting at the .get() in the AsyncTask's onPostExecute. So if your task downloads an image and you want to show a "downloading" message to the user while it is running, you should do the following:
//pseudo code
void exampleButtonClicked() {
new AsyncImageDownloader.execute();
}
class AsyncImageDownloader extends AsyncTask {
onPreExecute() {
show "downloading";
}
doInBackground() {
downloadImg();
}
onPostExecute() {
hide "downloading";
put downloaded img on ImageView;
}
}
//end of pseudo code
Hope this helps.. Not going to code out your answer, because then you would have learned nothing ;-)
Cheers!
You can do UI operations only from an UI thread. Try running it on an UI thread.
runOnUiThread(new Runnable() {
public void run() {
mProgressDialog.show();
}
});
You can do on override methods onPreExecute() and implement for this code,
#Override
protected void onPreExecute() {
super.onPreExecute();
ProgressDialog mProgressDialog = ProgressDialog.show(ActivityName.this, "Wait", "Loading....");
}
and onPostExecute() method implement and dismiss the dialog,
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (mProgressDialog != null || mProgressDialog.isShowing())
mProgressDialog.dismiss();
}
Try the following:
#Override
protected void onPreExecute()
{
mProgressDialog =ProgressDialog.show(GmailInbox.this, "", "Please Wait",true,false);
super.onPreExecute();
}
I am very new to android. I got two activities A, B . Activity A parse the data from the sever and iterate through the levels. and calls the activity B through intent. Activity B takes some time to display the data so I am trying to display the progress bar. Here is my code.
public class Display extends Activity {
ProgressDialog dialog;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.attributequestions);
new asynctask().execute();
}
class asynctask extends AsyncTask<Context,Void,Void>{
Survey[] surveyque=null;
// i hace created seperated class forsurvey that has info about data
String list[];
private ProgressDialog Dialog;
#Override
protected void onPreExecute()
{
Dialog=ProgressDialog.show(Display.this, "Parsing Data", "Please wait..........");
}
#Override
protected void onPostExecute(Void unused)
{
try
{
if(Dialog.isShowing())
{
Dialog.dismiss();
}
Intent intent=getIntent();
}
catch(Exception e)
{
Log.d("Onsitev4", "error");
}
}
#Override
protected Void doInBackground(Context... params) {
try {
LinearLayout layout1 = (LinearLayout) findViewById(R.id.linearLayout1);
//getting exception here. I dont understant why
// I have declared layout params and displaying activities in another class
ButtonView c = new ButtonView();
c.layout=layout1;
c.context =getBaseContext();
DbCoreSqlSurveys surveys=new DbCoreSqlSurveys(getBaseContext());
Document doc =surveys.getSurveySet();
surveyquestions= GetSurveyLevels(doc,c );
} catch (TransformerFactoryConfigurationError e) {
e.printStackTrace();
}
return null;
}
}
public SurveyObject[] GetSurveyLevels(Document doc, ButtonView c) {
NodeList nlQuestions = doc.getElementsByTagName("Survey");
SurveyObject[] allsurveys = new SurveyObject[nlQuestions.getLength()];
for (int i = 0; i < nlQuestions.getLength(); i++){
Node survey = nlQuestions.item(i);
String f =survey.getNodeName();
Log.d("OnsiteV4", "survey " + f);
NodeList surveyChildNodes = survey.getChildNodes();
SurveyObject s=new SurveyObject();
for (int j = 0; j < surveyChildNodes.getLength(); j++){
Node surveyChild = surveyChildNodes.item(j);
String h =surveyChild.getNodeName();
Log.d("OnsiteV4", "survey child node = " + h);
if (h !="#text"){
Surveys t = Surveys.valueOf(h);
switch(t){
case KeySurvey:
s.KeySurvey=surveyChild.getTextContent();
displaySurveyLink(s.SurveyDescription,"",c,0,s.SurveyDescription,"","","","");
break;
case SurveyDescription:
s.SurveyDescription=surveyChild.getTextContent();
displaySurveyLink(s.SurveyDescription,"",c,0,s.SurveyDescription,"","","","");
break;
case SurveyUserCode:
s.SurveyUserCode=surveyChild.getTextContent();
break;
case Level1:
if(surveyChild.hasChildNodes()){
s.Level1= processLevel1Nodes(surveyChild,c,s.SurveyDescription);
}
break;
default:
break;
}
}
allsurveys[i]=s;
}
}
return allsurveys;
}
// methods iterating through levels that is not showed
private void displaySurveyLink(final String description, String tag, ButtonView c, int indentation, final String surveyDescription, final String level1description, final String level2description, final String level3description, final String level4description)
{
if (description == null || tag == null){
return;
}
final TextView tv = c.addButton(description,tag,indentation);
tv.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
final Intent intent = new Intent();
intent.setClass(v.getContext(),ActivityB.class);
intent.putExtra("KeyLevel",tv.getTag().toString());
intent.putExtra("SurveyDescription",surveyDescription);
intent.putExtra("level1description",level1description);
intent.putExtra("level2description",level2description);
intent.putExtra("level3description",level3description);
intent.putExtra("level4description",level4description);
intent.putExtra("Description",description);
if (tv.getTag() != null){
if (tv.getTag().toString() != ""){
startActivity(intent);
}
}
}
});
}
}
I am getting exception in doinbackground. I am confused . please help me..
You are getting an exception because you are accessing UI elements on a non-UI thread. The main thread that the application creates is the UI thread, and that's where all of your visual elements are created and therefore the only thread in which you should access them.
To appropriately use AsyncTask, you run your long-running operations in doInBackground, and you use onPreExecute, onPostExecute and onProgressUpdated to work with the UI (show/hide progress dialogs, update views, etc). Whenever I use an AsyncTask and I want to show progress, I override onProgressUpdated giving it parameter type Integer and I call publishProgress from doInBackground. This would require a change of the base class signature from AsyncTask<Context,Void,Void> to AsyncTask<Context,Integer,Void>. You can use other object types for this as well...I just use Integer as an example if you want to show the percentage of the task that is complete, for example.
It's becoz your code should throwing exception as you are doing UI stuff in the doinbackgound of asyc task. Please remove all the UI related work from doingbackgound method.
So my code seems to run just fine until it hits this line
adapter.notifyDataSetChanged();
The error that pops up in the logcat is CalledFromWrongThreadException. The debug also shows the handler being run in the Background thread. How do I get the handler to bind to the main thread, and not the background one? I thought I just had to create the handler in the main thread, but I guess I am wrong, quite possible I am new to andriod. How do I fix this?
//Imports are included
public class DirectoryActivity extends ListActivity {
private ProgressDialog ProgressDialog = null;
private ArrayList<DirectoryListing> listing = null;
private DirectoryAdapter adapter;
private Runnable viewOrders;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.directory);
final Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (listing != null && listing.size() > 0) {
adapter.notifyDataSetChanged();
for (int i = 0; i < listing.size(); i++)
adapter.add(listing.get(i));
Log.e("log_tag", "\nStill running\n");
}
ProgressDialog.dismiss();
adapter.notifyDataSetChanged();
}
};
listing = new ArrayList<DirectoryListing>();
adapter = new DirectoryAdapter(this, R.layout.rows, listing);
setListAdapter(adapter);
ProgressDialog = ProgressDialog.show(DirectoryActivity.this, "Please wait...", "Retrieving data ...", true);
viewOrders = new Runnable() {
#Override
public void run() {
listing = PreparePage.getArrayList();
handler.handleMessage(null);
}
};
Thread thread = new Thread(null, viewOrders, "Background");
thread.start();
}
private static class PreparePage {
protected static ArrayList<DirectoryListing> getArrayList() {
ArrayList<DirectoryListing> listings = new ArrayList<DirectoryListing>();
JSONObject information = GetPageData.getJSONFromURL(url);
Iterator key = information.keys();
while (key.hasNext()) {
String id = (String) key.next();
JSONObject info = null;
try {
info = information.getJSONObject(id);
} catch (JSONException e) {
e.printStackTrace();
}
String name = "", title = "", photo = "";
try {
name = info.get("firstName") + " " + info.get("lastName");
title = info.getJSONObject("position").getString("name");
photo = info.optString("photoPath", "none");
} catch (JSONException e) {
e.printStackTrace();
}
listings.add(new DirectoryListing(name, title, photo));
}
return listings;
}
}
}
Try calling handler.sendEmptyMessage(0); instead of handler.handleMessage(null);
I don't know why this would cause the errors you are seeing, but this is how I have it set up when I use handler and thread instead of AsyncTask. And I have have never seen that error doing it this way.
#Nguyen is right though AsyncTask is the preferred way to handle these types of things now. And it actually makes it much easier to do.
AsyncTask docs
AsyncTask Example
In my experience, you should create your own class that extends AsyncTask class to do something at background. This is a simpler and more effectively than using thread + handler.
My android app connects to my website to retrieve and upload information so I use an AsyncTask thread.
In one instance, I need my thread to return a true or a false value to my main thread.
Is there a way to get this return value from an AsyncTask execute function?
When I do the following:
Toast.makeText(Locate.this, "Testing : "+locationUpdate.execute(location), Toast.LENGTH_LONG).show();
I just get alot of gibberish.
I think what I need is a means to pause the main thread until the second thread completes. The second thread calls a function in the main thread to set my return value.
So when the second thread completes, the main thread can unpause and access the return value as set by the second thread
If this logic is sound, please offer suggestions ... thanks!
You can use AsyncTask get() method for this. It waits if necessary for the computation to complete, and then retrieves its result:
Toast.makeText(Locate.this, "Testing : " + locationUpdate.execute(location).get(), Toast.LENGTH_LONG).show();
But be sure to not block the main thread for a long period of time, as this will lead to unresponsive UI and ANR.
UPDATE
I missed the point that question was about async web download/upload. Web/network operation should considered as a long one and thus the approach "pause UI thread and wait till download finishes" is always a wrong one. Use usual result publishing approach intstead (e.g.: AsyncTask.onPostExecute, Service + sendBroadcast, libraries like Volley, RoboSpice, DataDroid etc).
Handler is the best way to do this
in onPostExcecute() method simply do
#Override
protected void onPostExecute(Boolean bool) {
super.onPostExecute(bool);
Message msg=new Message();
msg.obj=bool;
mHandler.sendMessage(msg);
}
}
and your message handler will be
mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
bool i=(String)msg.obj;
}
};
public class RunWebScript {
String mString;
public RunWebScript(String url){
try {
URL updateURL = new URL(url);
URLConnection conn = updateURL.openConnection();
// now read the items returned...
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
ByteArrayBuffer baf = new ByteArrayBuffer(50);
int current = 0;
while((current = bis.read()) != -1){
baf.append((byte)current);
}
String s = new String(baf.toByteArray());
mString = s;
} catch (Exception e) {
Log.e("ANDRO_ASYNC", "exception in callWebPage",e);
mString = "error";
}
}
public String getVal(){
return mString;
}
}
this is executed as... (showing teh end of a method in teh calling class
asyncWebCall (url1,CONSTANT);
}
private void asyncWebCall(String url,int actionPostExecute){
new WebCall().execute(url,String.format("%d",actionPostExecute));
}
The Async part of the business is ... Note the case statement in onPostExecute this is the key to getting the returned value ito your program again. Note that the call new WebCall().execute(url,String.format("%d",actionPostExecute)); is the last thing done in a thread, no further statements can be executed, control returns through the onPostExecute.
class WebCall extends AsyncTask<String, String, String>{
int chooser = -1;
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(String... params) {
try {
chooser = Integer.parseInt(params[1]);
} catch(NumberFormatException nfe) {
Log.d("ANDRO_ASYNC",String.format("asyncReturn() mString numberformatexception = %s",params[1]));
chooser = 0;
}
return(new RunWebScript(params[0])).getVal();
}
protected void onProgressUpdate(String... progress) {
}
#Override
protected void onPostExecute(String gotFromDoInBkgnd) {
Log.d("ANDRO_ASYNC",String.format("chooser = %s",chooser));
switch (chooser){
case CONSTANT:
printStringx(gotFromDoInBkgnd);
asyncWebCall(url2,5);
break;
case 0:
Log.d("ANDRO_ASYNC",String.format("case 0 = %s",gotFromDoInBkgnd));
break;
case 5:
Log.d("ANDRO_ASYNC",String.format("case 5 = %s",gotFromDoInBkgnd));
asyncWebCall(url3,7);
break;
default:
Log.d("ANDRO_ASYNC",String.format("man we got problems = %s",gotFromDoInBkgnd));
break;
}
}
} // end of class
Here is a complete example of the issue of returning values from an async task. It may occur that there are many tasks to be done one after the other asynchronously.
Basics.
1. get a return value from a class.
public class Snippet {
int computVal;
public Snippet(){
computVal = 17*32;
}
public int getVal(){
return computVal;
}
}
this is called as...
int hooray = (new Snippet()).getVal();