I'm using the AsyncTask to open a URL, access the server, fetch the content and display them in a list view in the main activity. The content extracted consists of a title of the newspaper and a URL to the website, which will be displayed on a WebView in a second activity, if a "read" button is clicked. I coded out the program straight away and it works, but when I looked back at it, I found something that seems unreasonable, so mainly I want to make clear how the code works. Here is the code for the main activity:
package com.example.newsapp;
public class MainActivity extends Activity {
static final private String LOG_TAG = "main";
private ArrayList<Content> aList;
private class Content{
Content() {};
public String title;
public String url;
}
private class MyAdapter extends ArrayAdapter<Content>{
int resource;
public MyAdapter(Context _context, int _resource, List<Content> titles) {
super(_context, _resource, titles);
resource = _resource;
// this.context = _context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout newView;
final Content content = getItem(position);
// Inflate a new view if necessary.
if (convertView == null) {
newView = new LinearLayout(getContext());
String inflater = Context.LAYOUT_INFLATER_SERVICE;
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(inflater);
vi.inflate(resource, newView, true);
} else {
newView = (LinearLayout) convertView;
}
// Fills in the view.
TextView tv = (TextView) newView.findViewById(R.id.listText);
ImageButton b = (ImageButton) newView.findViewById(R.id.listButton);
b.setBackgroundResource(0);
tv.setText(content.title);
Typeface type = Typeface.createFromAsset(getAssets(),"LiberationSerif-BoldItalic.ttf");
tv.setTypeface(type);
// Sets a listener for the button, and a tag for the button as well.
b.setTag(Integer.toString(position));
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Reacts to a button press.
Intent intent = new Intent(MainActivity.this, WebPage.class);
Bundle bundle = new Bundle();
bundle.putString("URL", content.url);
intent.putExtras(bundle);
startActivity(intent);
}
});
return newView;
}
}
class MyAsyncTask extends AsyncTask<String, String, String> {
private ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);
InputStream inputStream = null;
String result = "";
Content content;
protected void onPreExecute() {
super.onPreExecute();
progressDialog.setMessage("Downloading the news...");
progressDialog.show();
progressDialog.setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface arg0) {
MyAsyncTask.this.cancel(true);
}
});
}
@Override
protected String doInBackground(String... params) {
String url_select = params[0];
ArrayList<NameValuePair> param = new ArrayList<NameValuePair>();
try {
// Set up HTTP post
// HttpClient is more then less deprecated. Need to change to URLConnection
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url_select);
httpPost.setEntity(new UrlEncodedFormEntity(param));
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
// Read content & Log
inputStream = httpEntity.getContent();
} catch (UnsupportedEncodingException e1) {
Log.e("UnsupportedEncodingException", e1.toString());
e1.printStackTrace();
} catch (ClientProtocolException e2) {
Log.e("ClientProtocolException", e2.toString());
e2.printStackTrace();
} catch (IllegalStateException e3) {
Log.e("IllegalStateException", e3.toString());
e3.printStackTrace();
} catch (IOException e4) {
Log.e("IOException", e4.toString());
e4.printStackTrace();
}
// Convert response to string using String Builder
try {
BufferedReader bReader = new BufferedReader(new InputStreamReader(inputStream, "iso-8859-1"), 8);
StringBuilder sBuilder = new StringBuilder();
String line = null;
while ((line = bReader.readLine()) != null) {
sBuilder.append(line + "\n");
}
inputStream.close();
result = sBuilder.toString();
} catch (Exception e) {
Log.e("StringBuilding & BufferedReader", "Error converting result " + e.toString());
}
return result;
} // protected Void doInBackground(String... params)
protected void onPostExecute(String result) {
//parse JSON data
try {
super.onPostExecute(result);
Log.i(LOG_TAG, result);
JSONObject object = new JSONObject(result);
JSONArray jArray = object.getJSONArray("sites");
for(int i=0; i < jArray.length(); i++) {
JSONObject jObject = jArray.getJSONObject(i);
content = new Content();
if (jObject.has("title") && jObject.has("url")){
content.title = jObject.getString("title");
content.url = jObject.getString("url");
aList.add(content);
aa.notifyDataSetChanged();
}
} // End Loop
progressDialog.dismiss();
} catch (JSONException e) {
// progressDialog.dismiss();
Log.e("JSONException", "Error: " + e.toString());
}
} // protected void onPostExecute(String result)
}
private MyAdapter aa;
private MyAsyncTask loadTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadTask = new MyAsyncTask();
loadTask.execute("http://luca-ucsc.appspot.com/jsonnews/default/news_sources.json");
aList = new ArrayList<Content>();
aa = new MyAdapter(this, R.layout.list_element, aList);
ListView myListView = (ListView) findViewById(R.id.listView1);
myListView.setAdapter(aa);
aa.notifyDataSetChanged();
}
public void refresh(View v){
if (loadTask.getStatus() == AsyncTask.Status.FINISHED){
aList.clear();
aa.notifyDataSetChanged();
new MyAsyncTask().execute("http://luca-ucsc.appspot.com/jsonnews/default/news_sources.json");
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
So you can see that only after loadTask.execute()
in onCreate()
, do I create the object for alist and aa, but I'm already using them in onPostExecute()
in the AsyncTaks class, so I'm not very clear what happens here, because onPostExecute()
and the UI are on the same thread, so the code in onPostExecute()
should be executed first.
I thought I should put
aList = new ArrayList<Content>();
aa = new MyAdapter(this, R.layout.list_element, aList);
into onPostExecute()
, which is more logical to me, but the app crashes this way. Also I think deleting aa.notifyDataSetChanged();
in onPostExecute()
shouldn't be a problem because it's also in the onCreate()
method, but this actually causes the list view to be blank, without any content. Actually, putting any of the codes after loadTask.execute()
into the if block of the onPostExecute()
method causes some problem, or crashes the app. That would be great if somebody can give some insight or hint. Thanks for reading.