Updating main thread tablelayout from async thread - android

I have a problem with updating my table-layout from my async class.
MY async class is fetching XML data so I don't block the main thread. My logs show the XML data is coming through but I don't know how to update my view with the data.
So I am attempting to put the data in tablerows and add the rows to the TableLayout but the logs show errors suggesting the Async class is not allowed to update my TableLayout view.
My code is as follows:
public class RemotePrimary extends Activity {
private static String SERVER_PATH = "http://test2.icerge.com/";
private static String XML_FILE1 = "samplexml";
//private static String SERVER_PATH = "http://tqs.mamlambo.com/";
//private static String XML_FILE1 = "scores.jsp";
private String[] data = new String[10];
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TableLayout datatable = (TableLayout)findViewById(R.id.TableLayout_data);
Downloader downloader = new Downloader();
downloader.execute(SERVER_PATH + XML_FILE1, datatable);
}
private class Downloader extends AsyncTask<Object, String, Boolean>{
TableLayout table;
#Override
protected Boolean doInBackground(Object... params) {
// TODO Auto-generated method stub
String path = (String)params[0];
table = (TableLayout)params[1];
XmlPullParser xmldata = null;
try {
URL serverPath = new URL(path);
xmldata = XmlPullParserFactory.newInstance().newPullParser();
xmldata.setInput(serverPath.openStream(), null);
addRecord(xmldata, table);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
#Override
protected void onProgressUpdate(String... values) {
// TODO Auto-generated method stub
//super.onProgressUpdate(values);
}
private boolean addRecord(XmlPullParser data, TableLayout table){
int eventType = -1;
boolean bFoundScores = false;
//find some records from xml
while(eventType != XmlResourceParser.END_DOCUMENT){
if( eventType == XmlResourceParser.START_TAG ){
//get the name of the tag(eg scores or score)
String strName = data.getName();
if( strName.equals("node") ){
bFoundScores = true;
String scoreValue = data.getAttributeValue(null, "Title");
String scoreRank = data.getAttributeValue(null, "Type");
String scoreUserName = data.getAttributeValue(null, "Nid");
Log.e("ADDING: ", scoreValue);
//Log.e("RETRIEVED", "collected : "+scoreValue+", "+scoreRank+", "+scoreUserName);
//publishProgress(scoreValue, scoreRank, scoreUserName);
TableRow newRow = new TableRow(RemotePrimary.this);
TextView rowText = new TextView(RemotePrimary.this);
rowText.setText(scoreValue);
newRow.addView(rowText);
table.addView(newRow);
}
}
try {
eventType = data.next();
} catch (XmlPullParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return true;
}
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
}
#Override
protected void onPostExecute(Boolean result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
}
}//close Downloader class
}//close RemotePrimary class
It's a bit much I know but I'll appreciate any help.
Thanks a great deal :-)

You can only make changes from the UI on the UI thread. The AsyncTask gives you an easy place to do this via onPostExecute. As it says in the docs, onPostExecute is always performed on the UI thread.
In doInBackground, do all of the hard work of building up the structured data that wish to display. Return that data so that it will be passed into onPostExecute, then in there add the appropriate table rows.

Related

android - changing Activity UI from application class

I extended the Application class in order to create singleton-like object in android.
in this object I have all the HTTP work with my server, and all the other activities can access it and call methods to GET, POST etc.
Code:
public class HttpManagerInstance extends Application {
private HttpClient httpClient;
private HttpGet get;
#Override
public void onCreate() {
httpClient = new DefaultHttpClient();
get = new HttpGet("http://10.100.102.9:8000/users/");
super.onCreate();
}
public Void getUsers() throws Exception {
new executeRequest().execute(get);
return null;
}
private class executeRequest extends AsyncTask<HttpRequest, Void, Integer> {
#Override
protected Integer doInBackground(HttpRequest... params) {
// TODO Auto-generated method stub
HttpRequest request = params[0];
HttpResponse response;
String result="";
try {
response = httpClient.execute((HttpUriRequest) request);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return responseCode;
}
#Override
protected void onPostExecute(Integer result) {
// TODO Auto-generated method stub
switch (result) {
case HttpStatus.SC_OK:
// request was fine
// Here I want to updated the GUI of the activity that called this method.
break;
}
}
}
}
This is how I call the method from the Activity:
HttpManagerInstance sampleApp = (HttpManagerInstance)getApplicationContext();
sampleApp.getUsers();
Again - I want to access the UI of the Activity that called the method to put an REQUEST ACCEPTED message.
Maybe pass a context? any ideas?
I'd create a listener:
public class HttpManagerInstance extends Application {
private HttpClient httpClient;
private HttpGet get;
public interface ResponseListener{
public void onSuccess(Object data);
}
#Override
public void onCreate() {
httpClient = new DefaultHttpClient();
get = new HttpGet("http://10.100.102.9:8000/users/");
super.onCreate();
}
public Void getUsers(ResponseListener listener) throws Exception {
new executeRequest(listener).execute(get);
return null;
}
private class executeRequest extends AsyncTask<HttpRequest, Void, Integer> {
private ResponseListener mListener;
public executeRequest(ResponseListener listener){
this.mListener = listener;
}
#Override
protected Integer doInBackground(HttpRequest... params) {
// TODO Auto-generated method stub
HttpRequest request = params[0];
HttpResponse response;
String result="";
try {
response = httpClient.execute((HttpUriRequest) request);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return responseCode;
}
#Override
protected void onPostExecute(Integer result) {
// TODO Auto-generated method stub
switch (result) {
case HttpStatus.SC_OK:
// request was fine
// Here I want to updated the GUI of the activity that called this method.
if(this.mListener != null) mListener.onSuccess(whatEverDataYouWant);
break;
}
}
}
}
Then, in your activity:
HttpManagerInstance sampleApp = (HttpManagerInstance)getApplicationContext();
sampleApp.getUsers(new ResponseListener(){
public void onSuccess(Object data){
//update your ui!
}
});
The short answer is you can't directly reference to the UI from another activity. My advice would be for you to set up a callback on your Application class and call in on executeRequest#onPostExecute then implement that callback on your Activity and update your UI from there.
If you need help to implement the callback check this question
If you need to show message is good option the Dialog Class or the Toast Class, you can see more info are here:
Dialogs: http://developer.android.com/guide/topics/ui/dialogs.html
Toasts: http://developer.android.com/guide/topics/ui/notifiers/toasts.html
But if you want to access or modify a control in your actual activity, then use Runnable class, and context.runOnUiThread() method if you work inside AsyncTask. The real problem is that you can't change UI in a AsyncTask using declaration of the controls. You need to throw a Runnable process to communicate with activity!!. For example:
context.runOnUiThread(new Runnable() {
public void run() {
//Declaration of variables
TextView MyTextView = (TextView) context.findViewById(R.id.txtvMyControl);
MyTextView.setText("My title");
}
}
If I can helps you say me, good luck!

Android program stops at doInBackground and doesn't come to onPostExecute

My program crashs after doInBackground and doesn't come to onPostExecute.
My activity code's related parts are like this:
public static class News {
private String title;
private String content;
private Bitmap image;
public News(String nTitle, String nContent, Bitmap nImage){
title = nTitle;
content = nContent;
image = nImage;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news);
final AsyncTask task = new DatabaseConnection(this, Method.GET_ALL_NEWS).execute();
try {
task.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public final void fillListView(List<News> news){
recentNews = news;
if(recentNews != null && !recentNews.isEmpty()){
((ListView)findViewById(R.id.lvNews)).setOnItemClickListener(this);
final int size = recentNews.size();
final String newsTitles[] = new String[size];
for(int i=0; i<size; ++i)
newsTitles[i] = recentNews.get(i).title;
((ListView)findViewById(R.id.lvNews)).setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, newsTitles));
}
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final News selectedNews = recentNews.get(position);
startActivity(new Intent(this, ANewsActivity.class)
.putExtra("title", selectedNews.title)
.putExtra("content", selectedNews.content)
.putExtra("image", BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher)));
}
My AsyncTask code's related parts are like this:
public DatabaseConnection(Context nContext, Method nMethod){
method = nMethod;
context = nContext;
}
#Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(context);
progressDialog.setMessage(context.getString(R.string.database_connection_wait_message));
progressDialog.setTitle(R.string.database_connection_wait_title);
progressDialog.show();
}
#SuppressWarnings("incomplete-switch")
#Override
protected Void doInBackground(String... params) {
if(method != Method.NONE){
open();
try{
switch(method){
case GET_ALL_NEWS:
final ResultSet rs = conn.createStatement().executeQuery("select baslik, metin, resim from haberler");
news = new ArrayList<News>();
while(rs.next())
news.add(new News(rs.getString(1), rs.getString(2), BitmapFactory.decodeStream(rs.getBlob(3).getBinaryStream())));
break;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close();
}
}
return null;
}
#SuppressWarnings("incomplete-switch")
#Override
protected void onPostExecute(Void temp) {
if (progressDialog.isShowing()){
progressDialog.dismiss();
switch(method){
case GET_ALL_NEWS:
((NewsActivity)context).fillListView(news);
break;
}
method = Method.NONE;
}
}
I want UI thread waits until database operations finishes.
By the way there is no initialization problem at variables etc and database returns proper infos and my "news" variable is filled normally.
By the way again I realized it is WORKING on PHONE, STUCKS on EMULATOR interestingly (if I remove wait() method and its try-catch block on main thread code).
It's difficult to say what is crashing without the logcat output, but it would most likely be the main thread of the app because of the .wait() method you are calling in onCreate(). Your onCreate() cannot wait - it must initialize and exit, otherwise you are blocking the main thread of your app and defeating the purpose of the AsyncTask.

AsyncTask getting called everytime

I have a navigation drawer containing 2 items. Now in my first item click, I load data using asynctask and the loaded data is populated in a listview in the corresponding fragment. Now when I switch to 2nd item, again I load data using AsyncTask for the 2nd fragment and show it in in listview.
Now the problem starts. When I go back to the 1st fragment, my
asyncTask is called again and the data is again fetched from the
server, I want to prevent this and load my data directly if it has
been already loaded once.
Please suggest
P.S - Please ask for the code if anyone needs it.
USERPAYFRAGMENT
public class UserPay extends Fragment {
ProgressDialog prg;
Properties prop;
private PrefSingleton mMyPreferences;
private JSONParser jsonParser = new JSONParser();
ArrayList<HashMap<String, String>> RequestList;
HashMap<String, String> map;
UserAdapter req_adp;
ListView req;
private boolean flag;
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
Toast.makeText(getActivity(), "ATTACHED", 1000).show();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
Toast.makeText(getActivity(), "CREATE", 1000).show();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.user_pay, container, false);
initViews(rootView);
Toast.makeText(getActivity(), "ONCREATEVIEW", 1000).show();
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Toast.makeText(getActivity(), "ONACTIVITYCREATED", 1000).show();
mMyPreferences = PrefSingleton.getInstance();
mMyPreferences.Initialize(getActivity());
RequestList = new ArrayList<HashMap<String, String>>();
Resources resources = this.getResources();
AssetManager assetManager = resources.getAssets();
try {
InputStream inputStream = assetManager.open("jsonURL.properties");
prop = new Properties();
prop.load(inputStream);
} catch (IOException e) {
System.err.println("Failed to open jsonURL property file");
e.printStackTrace();
}
req_adp = new UserAdapter(getActivity(), RequestList);
req.setAdapter(req_adp);
if (!flag) {
new GetRequests().execute();
} else {
}
}
#Override
public void onStart() {
// TODO Auto-generated method stub
super.onStart();
Toast.makeText(getActivity(), "ONSTART", 1000).show();
}
#Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
Toast.makeText(getActivity(), "ONRESUME", 1000).show();
}
private void initViews(View v) {
req = (ListView) v.findViewById(R.id.req_list);
}
private class GetRequests extends AsyncTask<Void, Void, Integer> {
#Override
protected void onPreExecute() {
super.onPreExecute();
prg = new ProgressDialog(getActivity());
prg.setIndeterminate(true);
prg.setMessage("Fetching Pending Requests...");
prg.setCanceledOnTouchOutside(false);
prg.show();
}
#Override
protected Integer doInBackground(Void... params) {
List<NameValuePair> params1 = new ArrayList<NameValuePair>();
params1.add(new BasicNameValuePair("userID", mMyPreferences
.getPreference("LoginId")));
String error_code = null;
Log.e("URL ", "is" + prop.getProperty("GET_REQUESTS_URL"));
try {
// getting JSON string from URL
JSONObject json = jsonParser.makeHttpRequest(
Appconstant.GET_REQUESTS_URL, "POST", params1);
// Check your log cat for JSON response
Log.d("Inbox JSON: ", json.toString());
JSONObject jsonObj = json.getJSONObject("data");
error_code = jsonObj.getString("Error_Code");
RequestList.clear();
if ("1".equals(error_code)) {
JSONArray jArray = jsonObj.getJSONArray("result");
for (int i = 0; i < jArray.length(); i++) {
map = new HashMap<String, String>();
JSONObject jsonObj1 = jArray.getJSONObject(i);
String FBankId = jsonObj1
.getString("payment_from_bank_id");
String DestBankId = jsonObj1
.getString("payment_to_bank_id");
String FBank = jsonObj1.getString("fBank");
String TBank = jsonObj1.getString("tBank");
String reason = jsonObj1.getString("payment_reason");
String amt = jsonObj1.getString("amount");
String p_type = jsonObj1.getString("payment_type");
String status = jsonObj1.getString("status");
String r_date = jsonObj1
.getString("request_created_date");
map.put("FBankId", FBankId);
map.put("TBankId", DestBankId);
map.put("SourceBank", FBank);
map.put("DestBank", TBank);
map.put("ReqDate", r_date);
map.put("PayReason", reason);
map.put("Amt", amt);
map.put("PayType", p_type);
map.put("Status", status);
if (status.equals("pending")) {
if (p_type.equals("cheque")
|| p_type.equals("Net Banking")) {
RequestList.add(map);
}
}
}
}
} catch (JSONException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return Integer.parseInt(error_code);
}
#Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
if (prg.isShowing()) {
prg.cancel();
}
if (result == 2) {
Toast.makeText(getActivity(),
"No User Request Details Available.Please Try Again",
Toast.LENGTH_SHORT).show();
}
req_adp.notifyDataSetChanged();
flag = true;
}
}
#Override
public void onPause() {
// TODO Auto-generated method stub
super.onPause();
Toast.makeText(getActivity(), "ONPAUSE",1000).show();
}
#Override
public void onStop() {
// TODO Auto-generated method stub
super.onStop();
Toast.makeText(getActivity(), "ONSTOP", 1000).show();
}
#Override
public void onDestroyView() {
// TODO Auto-generated method stub
super.onDestroyView();
Toast.makeText(getActivity(), "ONDESTROYVIEW", 1000).show();
}
#Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Toast.makeText(getActivity(), "ONDESTROY", 1000).show();
}
#Override
public void onDetach() {
// TODO Auto-generated method stub
super.onDetach();
Toast.makeText(getActivity(), "ONDETACH", 1000).show();
}
}
There are 2 ways to solve
1 - store the data locally and make use stored data based on appropriate condition checks
2 - If your app is based on this 2 fragments, just create the instance of these fragments and store in in member variable of parent activity. do not give chance to recreate again and again

Progress Dialog does not display due to use of thread.get() method

I want to display a Progress Dialog while I have two threads running one after the other, but my data structure that I use gets populated via the threads, becomes null. Thus I used thread.get() method to wait for the thread to be finished....not sure how I can get around this here is an example of one of my Async Threads:
private void performDetailSearch(String reference) {
String addplus = searchterm.replace(" ", "+");
RestClientDS restpSd = new RestClientDS();
String url = PLACES_DETAILS_URL +"reference="+ reference + "&sensor=false&key=" + API_KEY;
Log.d("url",url);
String[] URL = {url};
restpSd.execute(URL);
try {
restpSd.get();
}
catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
catch (ExecutionException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
Use AsyncTask instead of Thread and call another task after one gets completed.
AsyncTask can be called this way new FetchData().execute();
private class FetchData extends AsyncTask<String, Void, Boolean> {
private ProgressDialog dialog = new ProgressDialog(HomeActivity.this);
/** progress dialog to show user that the backup is processing. */
/** application context. */
protected void onPreExecute() {
this.dialog.setMessage(getResources().getString(
R.string.Loading_String));
this.dialog.show();
}
protected Boolean doInBackground(final String... args) {
try {
//do your background work
return true;
} catch (Exception e) {
Log.e("tag", "error", e);
return false;
}
}
#Override
protected void onPostExecute(final Boolean success) {
if (dialog.isShowing()) {
dialog.dismiss();
}
if (success) {
//call the another asynctask from here.
// new FetchData2().execute();
}
}
}

Listadapter and database

I have a zip file which I have to extract information from which I can take specific information . The process of taking out the information takes about .7 of a second estimated. What I did was add in an Asynchronous class inside my ListAdapter (To make multiple threads so it can also load other similar threads) and now in my Asynchronous class it makes multiple threads which causes the database add information to its pre existing information.
Now my question is "How would I make an asynchronous threading on a listadapter without causing duplicates on the database?"
Here is the code:
Map<TextView, String> authorViews=Collections.synchronizedMap(new WeakHashMap<TextView, String>());
Map<TextView, String> dateViews=Collections.synchronizedMap(new WeakHashMap<TextView, String>());
private class PresentInformation extends AsyncTask<Context, Void, Void>{
private TextView Tauthor;
private TextView Tlabel;
String position;
String date = null;
String author = null;
public PresentInformation(TextView author, TextView label, String Position) {
// TODO Auto-generated constructor stub
this.Tauthor = author;
this.Tlabel = label;
this.position = Position;
}
#Override
protected Void doInBackground(Context... params) {
// TODO Auto-generated method stub
Boolean addToDB;
if(author_exist(Tauthor)){
author = getAuthorFName(position);
addToDB = false;
}else{
//Declare an action to test if author does exist
authorViews.put(Tauthor, position);
author = getAuthor(position);
addToDB = true;
}
dateViews.put(Tlabel, position);
if(date_exist(Tlabel)){
date = db.getDate(position);
addToDB = false;
}else{
dateViews.put(Tlabel, position);
date = date/time();
addToDB = true;
}
if(addToDB){//Adds to database if they don't exist
db.addDatabase(new Database(position, author, date));
}
return null;
}
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
if(author == null){
author = "Author not found!";
}
if(date == null){
date = "Date not found!";
}
Tlabel.setText(date);
Tlabel.setText(author);
}
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
Tauthor.setText("Loading author please wait...");
Tlabel.setText("Loading date please wait...");
}
public Boolean author_exist(TextView tv){
String temp = authorViews.get(tv);
if(temp ==null)
return true;
return false;
}
public Boolean date_exist(TextView tv){
String temp = dateViews.get(tv);
if(temp ==null)
return true;
return false;
}
}
public class IconicAdapter extends ArrayAdapter<String>{
IconicAdapter() {
super(main.this, R.layout.bookselection_row, R.id.Book_Title, bookLocation);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View row = null;
if(row == null)
row = super.getView(position, convertView, parent);
icon=(ImageView)row.findViewById(R.id.icon);
author = (TextView)row.findViewById(R.id.book_Author);
date_label = (TextView)row.findViewById(R.id.label);
String path = bookLocation.get(position);
// Collections(path, position, author, date_label);
new PresentInformation(author, date_label, path).execute(main.this);
try{
Log.i("BookString input", bookLocation.get(position));
loadImage.Uploader(bookLocation.get(position), icon);
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return row;
}
}
Below is an example of an AsyncTask I'm using in the app I'm currently developing. I hope it helps get you on the right track.
private class prepCombat extends AsyncTask<String, Void, String>{
#Override
protected String doInBackground(String... params) {
playerBattlePrep();
findNPC();
return null;}
#Override
protected void onPostExecute(String result) {
new layoutCombat().execute();
}
}
Then when I want to call it...
#Override
protected void onResume() {
new pullCombatActions().execute();
new prepCombat().execute();
super.onResume();}
To insure it doesn't keep adding the same data some If() statements should work. Another option would be to include the data already in your database. If this is data that should always be there, having it already there when the program first runs could save you some trouble.
I see your put statements but I'm not seeing where you tell it which row to place it.
public void updateEntry(int rowId, String str, String cha, String wis, String dex, String name, int damId, int HPId, int npcId, int attId, int dodgeId, int dreadId, int critId) throws SQLException {
ContentValues cvUpdate = new ContentValues();
cvUpdate.put("Str", str);
cvUpdate.put("Cha", cha);
cvUpdate.put("Wis", wis);
cvUpdate.put("Dex", dex);
cvUpdate.put("Name", name);
cvUpdate.put("StatDam", damId);
cvUpdate.put("StatHP", HPId);
cvUpdate.put("HP", HPId);
cvUpdate.put("StatNpc", npcId);
cvUpdate.put("StatAtt", attId);
cvUpdate.put("StatDodge", dodgeId);
cvUpdate.put("StatDread", dreadId);
cvUpdate.put("StatCrit", critId);
cvUpdate.put("Rank", "0");
cvUpdate.put("Lvl", "1");
...
ContentValues csUpdate = new ContentValues();
csUpdate.put("PlayerHp", HPId);
csUpdate.put("CombatFlag", "0");
dbhelper.myDataBase.update(dbhelper.SAVE_TABLE, cvUpdate, "_id" + "=" + rowId, null);
dbhelper.myDataBase.update(dbhelper.COMBATSAVE_TABLE, csUpdate, "_id" + "=" + rowId, null);
}
Would be a method to setup what and where I put the data in my database. Then I can call the method by
updateEntry(slot, String.valueOf(strNumber), String.valueOf(chaNumber), String.valueOf(wisNumber), String.valueOf(dexNumber), nameNumber, damId, HPId, npcId, attId, dodgeId, dreadId, critId);
Whenever you want to preform this save you would call the above code. Likely in a doInBackground()

Categories

Resources