I would like to update spinner object after I get a google calendar name in a different thread. When I execute this, it crashes. I am not sure if I need to make it work with a different approach or if there is something wrong with it.
private void updateGoogleCalendar() {
try {
Thread.sleep(4000);
List<String> list = new ArrayList<String>();
list.add("Sample Calendar");
updatedCalendarNames = list.toArray(new String[0]);
progressBar.dismiss();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void initializeWidgets() {
final Spinner spinner = (Spinner) layout.findViewById(R.id.googleCalendarSelection);
final Button refreshCalendarBtn = (Button) layout.findViewById(R.id.refreshCalendarBtn);
refreshCalendarBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
progressBar = ProgressDialog.show(getContext(), "", "Loading...");
new Thread(new Runnable() {
#Override
public void run() {
updateGoogleCalendar();
final ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(getContext(), android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
while (updatedCalendarNames == null) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (String calendarName : updatedCalendarNames) {
CharSequence charSequence = calendarName + "";
adapter.add(charSequence);
}
}
}).start();
}
});
}
you need to add your ui update code into event thread only, and to notify UI/Event Thread you need to implement Handler or AsyncTask, for example you can update by handler as follows:
public void initializeWidgets() {
final Spinner spinner = (Spinner) layout.findViewById(R.id.googleCalendarSelection);
final Button refreshCalendarBtn = (Button) layout.findViewById(R.id.refreshCalendarBtn);
refreshCalendarBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
progressBar = ProgressDialog.show(getContext(), "", "Loading...");
new Thread(new Runnable() {
#Override
public void run() {
updateGoogleCalendar();
final ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(getContext(), android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
while (updatedCalendarNames == null) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (String calendarName : updatedCalendarNames) {
Message msg=handler.obtainMessage();
msg.obj = calendarName + "";
handler.sendMessage(msg);
}
}
}).start();
}
});
}
Handler handler=new Handler()
{
public void handleMessage(Message msg)
{
String str=(String)msg.obj;
adapter.add(charSequence);
}
};
You don't say where it crashes or how, but I imagine it might be due to you trying to update the UI from a non-UI thread. Take a look at the AsyncTask (see here) for information.
Related
Im trying to understand how threading works in Android.
I've created this AsyncTask class, but I still get this warning in my console:
Skipped 295 frames! The application may be doing too much work on its main thread.
LoadAnswersTask class
public class LoadAnswersTask extends AsyncTask<String, Void, ArrayList<MessageItemModel>> {
public interface LoadAnswersEventHandler {
void onLoadFinished(ArrayList<MessageItemModel> answers);
}
protected LoadAnswersEventHandler event;
public LoadAnswersTask(LoadAnswersEventHandler event) {
this.event = event;
}
#Override
protected ArrayList<MessageItemModel> doInBackground(String... params) {
try {
QuestionModel q = QuestionModel.getById(Integer.parseInt(params[0]));
ArrayList<MessageItemModel> items = new ArrayList<>();
for (AnswerModel answer : q.getAnswers()) {
MessageItemModel messageItem = new MessageItemModel();
messageItem.message = answer.getComment();
messageItem.id = answer.getId();
messageItem.parentId = answer.getParentId();
messageItem.gender = answer.getGender();
messageItem.name = answer.getName();
messageItem.reply = (answer.getParentId() > 0);
messageItem.email = answer.getEmail();
messageItem.answer = true;
items.add(messageItem);
}
return items;
} catch (Exception e) {
Log.d(getClass().getName(), "Failed to load question", e);
}
return null;
}
#Override
protected void onPostExecute(ArrayList<MessageItemModel> messageItemModels) {
this.event.onLoadFinished(messageItemModels);
}
}
I also tried this approach, which seems to work - well sort of as I have my items in a Fragment inside a viewpager - and it sometimes didn't load the answers, im suspecting it's because of the WeakReference combined with the viewpager causing event.get() to be null, but i'm really not sure...
private static class LoadAnswersHandler extends Handler {
private WeakReference<LoadAnswersEventHandler> event;
public LoadAnswersHandler(LoadAnswersEventHandler event) {
this.event = new WeakReference<>(event);
}
#Override
public void handleMessage(Message msg) {
if(event.get() != null) {
event.get().onLoadFinished((ArrayList<MessageItemModel>) msg.obj);
}
}
}
private LoadAnswersHandler loadAnswersHandler;
// ...
protected void loadAnswers(final LoadAnswersEventHandler event) {
loadAnswersHandler = new LoadAnswersHandler(event);
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
QuestionModel q = QuestionModel.getById(question.getId());
ArrayList<MessageItemModel> items = new ArrayList<>();
for (AnswerModel answer : q.getAnswers()) {
MessageItemModel messageItem = new MessageItemModel();
messageItem.message = answer.getComment();
messageItem.id = answer.getId();
messageItem.parentId = answer.getParentId();
messageItem.gender = answer.getGender();
messageItem.name = answer.getName();
messageItem.reply = (answer.getParentId() > 0);
messageItem.email = answer.getEmail();
messageItem.answer = true;
items.add(messageItem);
}
loadAnswersHandler.sendMessage(Message.obtain(loadAnswersHandler, UPDATE_UI, items));
} catch (Exception e) {
Log.d(getClass().getName(), "Failed to load question", e);
}
}
});
thread.start();
}
Thanks!
- Simon
I have created an adapter and i used Google Places API to show address. I call notifyDataSetChanged(); for update list. When i typed in TextView the list is not change.
Activity
public class SelectAddressActivity extends Activity implements SelectAddressAdapter.OnClickInAdapter {
ArrayList<AddressList> addressList;
SelectAddressAdapter adapter;
EditText addresseEditText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_select_address);
final ListView addressListView = (ListView) findViewById(R.id.addressListView);
addresseEditText = (EditText) findViewById(R.id.addressEditText);
//Adapter
adapter = new SelectAddressAdapter(this, android.R.layout.simple_list_item_1, addressList);
addressListView.setAdapter(adapter);
addresseEditText.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(final Editable input) {
scheduler= Executors.newScheduledThreadPool(1);
scheduler.schedule(new Runnable() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
GoogleAPIRequestHandler handler = new GoogleAPIRequestHandler();
String date= handler.execute(input).get();
addressList = new ArrayList<AddressList>();
JSONObject jsonObj = new JSONObject(date);
JSONArray predsJsonArray = jsonObj.getJSONArray("predictions");
for (int i = 0; i < predsJsonArray.length(); i++) {
AddressList a = new AddressList();
String addressJson = predsJsonArray.getJSONObject(i).toString();
a.Deserialize(addressJson);
addressList.add(a);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
adapter.notifyDataSetChanged();;
}
});
}
}, 300, TimeUnit.MILLISECONDS);
}
});
}
You are not setting the updated address list in the adapter. it still has old data so when you call notifyDataSetChanged() method it does not have any effect.
I am new to JSON concept
in below code the the values of "AgentId" and "Type" are changing based on agents but i am
taking constantly like below
jsonReq.put("agentsId", 7); and
jsonReq.put("types", 0);
so i have to change them time to time. How to get it please help me
public class Busi_perfo_infor_Activity extends BaseActivity
{
private ProgressDialog dialog;
private TableLayout tableLayout;
private Handler handler = new Handler();
private Spinner Month_Spinner;
private List<Indi_Busi_Agent_perfo> agentsList = new ArrayList<Indi_Busi_Agent_perfo>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.busi_perfo_info);
Month_Spinner = (Spinner) findViewById(R.id.Current_prvious_month_spinner);
setupHeader();
tableLayout = (TableLayout) findViewById(R.id.Table_busi_perfo);
dialog = new ProgressDialog(this);
dialog.setMessage("Loading...");
dialog.setIndeterminate(true);
dialog.setCancelable(true);
setupBranchSpinner();
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, new String[] { "Previous Month",
"Current Month" }) {
};
dataAdapter
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Month_Spinner.setAdapter(dataAdapter);
Month_Spinner
.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int pos, long arg3) {
updateCollectons();
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
});
}
private void setupBranchSpinner() {
dialog.show();
new Thread() {
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
dialog.dismiss();
}
});
}
}.start();
}
protected void updateCollectons() {
// TODO Auto-generated method stub
dialog.show();
tableLayout.setVisibility(View.GONE);
final JSONObject json = new JSONObject();
try {
JSONObject jsonReq = new JSONObject(Utilities.loadPref(
getApplicationContext(), "Login", ""));
jsonReq.put("agentsId", 7);
// jsonReq.put("Area", 0);
// jsonReq.put("Mode", 2);
SimpleDateFormat fmtOut = new SimpleDateFormat("dd MMM yyyy");
// dd MMM yyyy
String dateFormat = fmtOut.format(new Date());
Calendar cal = Calendar.getInstance();
cal.set(Calendar.DATE, 1);
if (Month_Spinner.getSelectedItemPosition() == 0) {
cal.add(Calendar.MONTH, -1);
}
String fromdateFormat = fmtOut.format(cal.getTime());
//
jsonReq.put("FromDate", fromdateFormat);
jsonReq.put("ToDate", dateFormat);
jsonReq.put("types", 0);
json.put("AgntPerformanceRequest", jsonReq);
new Thread() {
private Indi_Busi_Agent_perfo info;
public void run() {
String response = WebServices.postRequest(
WebServices.INDIVI_BUSI_PERFO_URL, json,
Busi_perfo_infor_Activity.this);
if (response != null) {
try {
JSONObject jsonResp = new JSONObject(response);
if (jsonResp
.has("AgntPerformanceResult")) {
info = new Indi_Busi_Agent_perfo(
jsonResp.getJSONObject(
"AgntPerformanceResult"));
} else {
Toast.makeText(getApplicationContext(),
"their are no subscribers",
Toast.LENGTH_LONG).show();
}
}
catch (JSONException e) {
e.printStackTrace();
}
}
handler.post(new Runnable() {
#Override
public void run() {
dialog.dismiss();
if (info != null) {
updateCollectionTableView(info);
}
}
});
}
}.start();
} catch (JSONException e) {
e.printStackTrace();
}
}
private void updateCollectionTableView(Indi_Busi_Agent_perfo info) {
tableLayout.setVisibility(View.VISIBLE);
((TextView) findViewById(R.id.bch_name)).setText(Utilities
.getString(info.getBranchName()));
((TextView) findViewById(R.id.busi_Amt)).setText(Utilities
.getDecimalFormat(info.getBusinessAmt()));
}
}
any help will be a suggestion to me
Thank you.
Could you help me to change piece of code to show progresDialog or some information during database creation. I have tried to do this with Thread with no success. When I put most of code to thread I get error, when only db.createDataBase() "text" is set before database is created.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myCalendar = new MyCalendar();
menu = (TextView) findViewById(R.id.poleMenu);
text = (TextView) findViewById(R.id.glownePoleTekstowe);
menu.setText(kalendarz.setCurrentDateOnView());
value = menu.getText().toString();
db = new DatabaseHandler(this);
try {
// separate class to load database
// MOST IMPORTANT
db.createDataBase();
} catch (IOException ioe) {
throw new Error("nie można utworzyć bazy danych");
}
dane = db.getDataFromDate(value);
db.close();
try {
log = dane.getTekst();
}catch(Exception e) {System.out.println(e);}
text.setText(log);
}
Update, with thread and handler:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
kalendarz = new Kalendarz();
menu = (TextView) findViewById(R.id.poleMenu);
text = (TextView) findViewById(R.id.glownePoleTekstowe);
menu.setText(kalendarz.setCurrentDateOnView());
value = menu.getText().toString();
db = new DatabaseHandler(this);
// thread
ladujDane();
dane = db.getDaneFromDate(value);
db.close();
try {
log = dane.getTekst();
}catch(Exception e) {System.out.println(e);}
text.setText(log);
}
//------------------------------------------
public void ladujDane() {
mLoadDBHandler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
progressDialog.dismiss();
}
};
progressDialog = ProgressDialog.show(
this,
"Ładowanie danych", "Proszę chwilkę poczekać",
true,
false);
Thread t = new Thread(new LoadDBThread());
t.start();
}
//----------------------------------------
private class LoadDBThread implements Runnable
{
public void run()
{
try {
db.createDataBase();
} catch (IOException ioe) {
throw new Error("nie można utworzyć bazy danych");
}
mLoadDBHandler.sendEmptyMessage(0);
}
}
Your problem is that you are trying to do UI operations on another thread than the main thread.
You could use a handler for that. First declare a handler:
Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
// do UI stuff
}
};
Then from your thread that creates DB you call: handler.sendEmptyMessage(0);
Just use AsyncTask you can safetly mage progress bar from it's onProgress.
You can't change UI elements from thread other than UI thread, on the other hand you can't do long lasting operations in UI thread. AsyncTask is your answer.
I never got this working in a straightforward manner. Sorry if I'm being a little vague. I'll try to elaborate on what I'm trying to do. I am trying to build a listview that grabs its data from a webservice. Once I initialize a listview, I want to keep polling the webserver periodically and update the contents of the listview. For this I am doing something like this:
public class SampleAutoUpdateList extends Activity {
//Autoupdate handler
private Handler handler = new Handler();
private Runnable updater = new Runnable() {
public void run() {
/*
* Update the list
*/
try {
Log.i("UPDATE", "Handler called");
searchAdapter = getFeed(URL);
searchAdapter.notifyDataSetChanged();
handler.postDelayed(this, Configuration.REFRESH_INTERVAL);
} catch(Exception e) {
Log.e("UPDATE ERROR", e.getMessage());
}
}
};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.linearmode);
this.context = this;
searchAdapter = getFeed(URL);
LinearLayout l2 = (LinearLayout) findViewById(R.id.secondaryLayout);
ListView list = new ListView(context);
l2.addView(list);
// display UI
UpdateDisplay(list);
updater.run();
}
private SearchAdapter getFeed(String URL) {
try
{
SearchHandler handler = new SearchHandler();
URL url = new URL(URL);
String data = convertStreamToString(url.openStream());
data = data.substring(data.indexOf('['), data.length()-1);
handler.parseJSON(data);
return handler.getFeed();
}
catch (Exception ee)
{
// if we have a problem, simply return null
Log.e("getFeed", ee.getMessage());
return null;
}
}
private void UpdateDisplay(View searchView) {
// TODO Auto-generated method stub
// TODO Auto-generated method stub
searchList = (ListView) searchView;
myProgressDialog = ProgressDialog.show(this,
"Please wait...", "Loading search....", true);
new Thread() {
public void run() {
try{
Thread.sleep(2000);
} catch (Exception e) { }
runOnUiThread(new Runnable() {
#Override
public void run() {
if (searchAdapter == null)
{
Log.e("ERROR", "No Feed Available");
return;
}
searchAdapter.setContext(context);
searchList.setAdapter(searchAdapter);
searchList.setSelection(0);
}
});
// Dismiss the Dialog
myProgressDialog.dismiss();
}
}.start();
}
}
And the SearchHandler class is simple:
public class SearchHandler extends DefaultHandler {
SearchAdapter _adapter;
SearchItem _item;
public SearchHandler()
{
}
public SearchAdapter getFeed()
{
return _adapter;
}
public void parseJSON(String data) {
// TODO Auto-generated method stub
_adapter = new SearchAdapter();
JSONArray parseArray;
try {
parseArray = new JSONArray(data);
for (int i=0; i < parseArray.length(); i++) {
SearchItem item = new SearchItem();
JSONObject jsonUser = parseArray.getJSONObject(i);
item.set_from(jsonUser.getString ("from"));
item.set_msg(jsonUser.getString("msg"));
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
No matter what I do, the handler gets called and the new items are fetched, but the list is never refreshed... Any ideas on what could be going wrong?
Well, it is a little bit difficult to follow your code, since you only have a fragment of it, and few of the really relevant bits. For example, based on your available code, your list should be forever empty, since you never associate the searchAdapter with a ListView...at least in the code you have shown.
That being said, the following lines seem particularly odd:
searchAdapter = getFeed(URL);
searchAdapter.notifyDataSetChanged();
I am going to assume that getFeed() (not shown) creates a new ListAdapter of some sort. If getFeed() is creating a new ListAdapter, there is no need to call notifyDataSetChanged() on it, as its data set hasn't changed -- it's brand new. Moreover, unless you are associating this new ListAdapter to your ListView, the new ListAdapter will have no effect.
If I'm barking up the wrong tree, consider adding lines to your sample showing the implementation of getFeed() and where you are using searchAdapter.