I need to retrieve some huge data from one database when an activity is started. To prevent a user with a frozen window, I decided to run a ProgressDialog while data is being processed.
From OnCreate I call my initDb Class:
new initDb().execute();
And then to do this I have one class inside my activity's class:
public class initDb extends AsyncTask<Void, Void, Void> {
ProgressDialog mDialog = new ProgressDialog(ClientsReg.this);
#Override
protected void onPreExecute() {
mDialog.setMessage("Please wait...");
mDialog.show();
}
#Override
protected Void doInBackground(Void... voids) {
opendb();
listCities();
return null;
}
#Override
protected void onPostExecute(Void unused) {
// Pass the result data back to the main activity
mDialog.dismiss();
}
}
The real problem happens while setting the adapter:
private void listCities() {
mRedrawHandler.sleep(100000);
c = db.executeSQL("SELECT * FROM RegDB WHERE Reg_Type = 1 AND cad_uzkow = 0 ORDER BY _id DESC");
//add some list items
ArrayList<String> finalList = new ArrayList<String>();
c.moveToFirst();
while (!c.isAfterLast()){
finalList.add(c.getString(0) + ")"+ c.getString(5));
c.moveToNext();
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
R.layout.row, R.id.itemShow, finalList);
sp.setAdapter(adapter);
}
It always happen to crash on sp.setAdapter(adapter);
Any ideas?
Thank you!
Try taking the finalList as an attribute of your initDb class this way you can populate it on the doInBackground method and then us it on the onPostExecute, like this:
public class initDb extends AsyncTask<Void, Void, Void> {
ProgressDialog mDialog = new ProgressDialog(ClientsReg.this);
ArrayList<String> finalList;
#Override
protected void onPreExecute() {
mDialog.setMessage("Please wait...");
mDialog.show();
}
#Override
protected Void doInBackground(Void... voids) {
opendb();
listCities();
return null;
}
#Override
protected void onPostExecute(Void unused) {
// Pass the result data back to the main activity
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
R.layout.row, R.id.itemShow, finalList);
sp.setAdapter(adapter);
mDialog.dismiss();
}
private void listCities() {
mRedrawHandler.sleep(100000);
c = db.executeSQL("SELECT * FROM RegDB WHERE Reg_Type = 1 AND cad_uzkow = 0 ORDER BY _id DESC");
//add some list items
finalList = new ArrayList<String>();
c.moveToFirst();
while (!c.isAfterLast()){
finalList.add(c.getString(0) + ")"+ c.getString(5));
c.moveToNext();
}
}
You should call:
sp.setAdapter(adapter);
in main UI thread. For example in onPostExecute() function. Always keep in mind that views (such as ListView) should only be accessed from main thread.
You can't access the UI from other thread than the thread that created the UI. So in AsyncTask, you can't use doInBackground() for this purpose.
Related
In my Android app, I have an activity which executes an AsyncTask<Void, Void, Void> named Scan using this code: new Scan().execute();.
In the onPreExecute() method, it starts a progress dialog, on the doInBackground(Void... voids) method it scans a table from DynamoDB, and on the onPostExecute(Void aVoid) method, it dismisses the progress dialog, and views the results of the DB scan in a ListView using a custom BaseAdapter class.
When I open the activity, everything runs great, but when I press the back button, and enter the activity again, then only the onPreExecute() and the onPostExecute(Void aVoid) methods are being executed, while doInBackground(Void... voids) isn't being executed, so it just shows and dismisses the progress dialog, and nothing else is being viewed on the screen.
How can I fix this?
Code:
MessagesListAdapter messages;
ListView messagesLv;
public static ArrayList<Message> arrayList;
public static ProgressDialog progressDialog;
public static DynamoDBScanExpression dbScanExpression;
public static List<Message> messageList;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messages);
new Scan().execute();
}
private class Scan extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new ProgressDialog(TestActivity.this);
progressDialog.setTitle(name);
progressDialog.setMessage("Searching for messages...");
progressDialog.setIndeterminate(false);
progressDialog.show();
}
#Override
protected Void doInBackground(Void... voids) {
dbScanExpression = new DynamoDBScanExpression();
Condition condition = new Condition()
.withComparisonOperator(ComparisonOperator.EQ)
.withAttributeValueList(new AttributeValue().withS(MainActivity.msgId));
dbScanExpression.addFilterCondition("msgId", condition);
messageList = MainActivity.mapper.scan(Message.class, dbScanExpression);
arrayList = new ArrayList<Message>();
for (Message msg : messageList) {
if (msg.getUserId() == null || msg.getUserId().equals(MainActivity.userId)) {
msg.setMsgId(msg.getMsgId());
msg.setDate(msg.getDate());
msg.setTime(msg.getTime());
msg.setMessage(msg.getMessage());
msg.setUserId(msg.getUserId());
arrayList.add(msg);
}
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (!messageList.isEmpty()) {
messagesLv = (ListView) findViewById(R.id.messagesListView);
messages = new MessagesListAdapter(MinaActivity.this, arrayList);
messagesLv.setAdapter(messages);
progressDialog.dismiss();
} else {
TextView tv = (TextView) findViewById(R.id.noMessages);
tv.setVisibility(View.VISIBLE);
progressDialog.dismiss();
}
}
}
The reason that it couldn't find any messages, was that I checked if the user ID of the message equals to the user ID of the registered user. The problem was that it was taken from MainActivity.java which got it from an Intent extra, therefore, when I have left the activity, the variable has been erased.
What I did is to refer to the user ID from the SharedPreferences and suddenly it worked.
try to add messages.notifyDataSetChanged(); in onPostExecute() method.
or use below code
MessagesListAdapter messages;
ListView messagesLv;
public static ArrayList<Message> arrayList;
public static ProgressDialog progressDialog;
public static DynamoDBScanExpression dbScanExpression;
public static List<Message> messageList;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messages);
messagesLv = (ListView) findViewById(R.id.messagesListView);
messages = new MessagesListAdapter(MinaActivity.this, arrayList);
messagesLv.setAdapter(messages);
new Scan().execute();
}
private class Scan extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new ProgressDialog(TestActivity.this);
progressDialog.setTitle(name);
progressDialog.setMessage("Searching for messages...");
progressDialog.setIndeterminate(false);
progressDialog.show();
}
#Override
protected Void doInBackground(Void... voids) {
dbScanExpression = new DynamoDBScanExpression();
Condition condition = new Condition()
.withComparisonOperator(ComparisonOperator.EQ)
.withAttributeValueList(new AttributeValue().withS(MainActivity.msgId));
dbScanExpression.addFilterCondition("msgId", condition);
messageList = MainActivity.mapper.scan(Message.class, dbScanExpression);
arrayList = new ArrayList<Message>();
for (Message msg : messageList) {
if (msg.getUserId() == null || msg.getUserId().equals(MainActivity.userId)) {
msg.setMsgId(msg.getMsgId());
msg.setDate(msg.getDate());
msg.setTime(msg.getTime());
msg.setMessage(msg.getMessage());
msg.setUserId(msg.getUserId());
arrayList.add(msg);
}
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (!messageList.isEmpty()) {
messages.notifyDataSetChanged();
progressDialog.dismiss();
} else {
TextView tv = (TextView) findViewById(R.id.noMessages);
tv.setVisibility(View.VISIBLE);
progressDialog.dismiss();
}
}
}
I want to declare arrayList into this line:
public class tlcity extends Activity {
//ArrayList<String> idArray = null;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
....
and into the other method,for example this method:
protected void onPostExecute(String result) {
//fill the arraylist
...
and into the other method for example this method read arraylist data:
public void readlist(){
//read the arraylist data and show
}
How can i do this?
You can declare ArrayList like this
ArrayList<String> list;
list = new ArrayList<String>();
You can add, remove items in ArrayList Like this
list.add("A");
list.remove(0);
ArrayList<String> abc=new ArrayList<String>();
You can initialize or create an instance of your array list like this
idArray = new ArrayList();
You can perform any operations to it using idArray object.
For example you can add items like this
idArray.add("item1");//In you case its a list of strings.
In the same way you would do that in another Java app / class:
public class tlcity extends Activity {
List<String> idArray;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
idArray = new ArrayList<String>();
}
protected void onPostExecute(String result) {
idArray.add("One");
idArray.add("Two");
idArray.add("Three");
...
}
public void readlist(){
for (final String element : idArray) {
// Use the nth string
}
}
I want to declare arrayList into this line:
ArrayList<String> myList;
and into the other method,for example this method:
myList = new ArrayList<String>;
and into the other method for example this method read arraylist data:
for(int i=0; i<myList.size(); i++)
System.out.println(myList.get(i).toString());
If you want to use ArrayList locally then declare it locally. if you want to use it in all methods then declare it globally in class.
public class tlcity extends Activity {
ArrayList<String> idArray = new ArrayList<>(); // to Use this arraylist globally.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayList<String> localaraaylist = new ArrayList<>(); //to use arraylist in only in oncreate method.
....
According to your post, telling how to declae ArrayList will not enough as you have some methods like onPreExecute() which is a method ofAsyncTask Interface.
Look at this,
public class MainActivity extends ActionBarActivity {
ArrayList<String> arrayList; // declaring ArrayList here
ArrayAdapter<String> adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
arrayList = new ArrayList<String>(); // Initializing arrayList
arrayList.add("initial text"); // adding a data to arrayList
ListView listView = (ListView)findViewById(R.id.listView);
adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,arrayList); // setting the arrayList in ArrayAdapter
listView.setAdapter(adapter);
new LongOperation().execute(); // async Task
}
private class LongOperation extends AsyncTask<Void, Void, Void> {
ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);
// progress dialog starts here
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog.setMessage("Loading...");
progressDialog.show();
}
#Override
protected Void doInBackground(Void... voids) {
// for understanding purpose, i made a thread to sleep for 5 sec and later it will add A,B & C to ArrayList.
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// adding few more items to arrayList
arrayList.add("A");
arrayList.add("B");
arrayList.add("C");
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
progressDialog.dismiss(); // dismissing the progress Dialog
adapter.notifyDataSetChanged(); // refreshing listview
readA(); // read the arrayList
}
}
public void readA()
{
for (int i = 0; i<arrayList.size(); i++)
{
Log.d("key",arrayList.get(i));
}
}
}
Output :
If you run the above code, Initially your list view will only contain only one item & after 5 sec loading it will add another 3 items. The below information will print in logcat that reads the ArrayList.
04-13 14:07:32.395 1123-1123/? D/key﹕ initial text
04-13 14:07:32.395 1123-1123/? D/key﹕ A
04-13 14:07:32.395 1123-1123/? D/key﹕ B
04-13 14:07:32.395 1123-1123/? D/key﹕ C
this is my code to load listview items
#SuppressLint("DefaultLocale")
public class SearchList extends Activity {
private ArrayList<String> founded = new ArrayList<String>();
private OrderAdapter m_adapter;
ListView lv;
/** Called when the activity is first created. */
#SuppressLint("DefaultLocale")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.searchlist);
lv = (ListView) findViewById(R.id.listView1);
new Load().execute();
m_adapter = new OrderAdapter(this, R.layout.itemview, founded);
lv.setAdapter(m_adapter);
lv.setTextFilterEnabled(true);
}
private class Load extends AsyncTask<Void, Void, Void> {
ProgressDialog progress;
#Override
protected void onPreExecute() {
progress = new ProgressDialog(SearchList.this);
progress.setMessage("loading....");
progress.show();
}
#Override
protected Void doInBackground(Void... params) {
try {
for (int i = 0; i <500000; i++) {
founded.add("String "+i);
}
} catch (Exception e) {
}
return null;
}
#Override
protected void onPostExecute(Void result) {
// write display tracks logic here
progress.dismiss(); // dismiss dialog
}
}
when i run the code the progress dialog already appear but after its dismiss i found that the list is empty no items added to it i do not know what is the problem and why the list is empty after the dialog loading .Pls need help
thanks in advance.
Set listadapter in onPostExecute Because you Are using AsyncTask to get adapter data and setting ListAdapter before Completing AsyncTask So Try to Set ListAdapter after Completing AsyncTask
So add these
m_adapter = new OrderAdapter(this, R.layout.itemview, founded);
lv.setAdapter(m_adapter);
lv.setTextFilterEnabled(true);
lines in onPostExecute method instead of onCreate Method
#Override
protected void onPostExecute(Void result) {
// write display tracks logic here
progress.dismiss(); // dismiss dialog
m_adapter = new OrderAdapter(YourActivity.this, R.layout.itemview, founded);
lv.setAdapter(m_adapter);
lv.setTextFilterEnabled(true);
}
I am getting from time to time testing my app error:
03-04 20:57:01.929: E/TestApp(13673): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
From questions like this: Whats this exception?, and my own experience (I got this same error from time to time as in mentioned question) I would like to ask you guys what I can do to get rid of it?
As far as I know, I can do some stuff on AsyncTask connected to View, so I don't know why I am getting this info.
This is my code:
private MyDBAdapter mySQLiteAdapter;
private ListView wordList;
private AsyncDBDownload asycn;
private ProgressDialog dbUpdate;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.smart_guide_ocr);
asycn = new AsyncDBDownload();
wordList = (ListView) findViewById(R.id.wordsList);
//...
}
#Override
protected void onResume() {
super.onResume();
asycn.execute(null);
}
private class AsyncDBDownload extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... params) {
try {
refreshList();//upload of contetn and set of adapter
} catch (Exception ex) {
Log.e(TAG, ex.toString());
}
return null;
}
#Override
protected void onPostExecute(String result) {
dbUpdate.dismiss();
}
#Override
protected void onPreExecute() {
dbUpdate = ProgressDialog.show(TestAppActivity.this, "Wait",
"DB download");
}
}
private void refreshList() {
mySQLiteAdapter = new MyDBAdapter(TestAppActivity.this);
mySQLiteAdapter.open();
String[] columns = { MyDBAdapter.KEY_TRANSLATED, MyDBAdapter.KEY_WORD, MyDBAdapter.KEY_LANG,
MyDBAdapter.KEY_ID };
Cursor contentRead = mySQLiteAdapter.getAllEntries(false, columns,
null, null, null, null, MyDBAdapter.KEY_ID, null);
startManagingCursor(contentRead);
Log.d(TAG, Integer.toString(contentRead.getCount()));
RowItem adapterCursor = new RowItem(this, R.layout.save_word_row,
contentRead, columns, new int[] { R.id.translatedWord, R.id.orgWord, R.id.langInfo }, 0);
wordList.setAdapter(adapterCursor);
mySQLiteAdapter.close();
}
You must not call wordList.setAdapter(adapterCursor); from within refresList method. That's a way of "changing a view from a non-UI thread".
So, instead, save the adapterCursor instance and use it from within the onPostExecute method.
You can not manipulate your Views within a background task. Do all the loading you need in your AsyncTask, pass it back into the activity in onPostExecute and set your adapter then. Doing any form of UI manipulation in a background task or service will throw this error.
Basically i have been try to do the damn this for over 40 hours - read all threads about it and still no result!!! So I can't update list adapter in list view while posting in onPostExecute adapter.notifyDataSetChanged();
ArrayAdapter<String> adapter;
private ProgressDialog dialog;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setListAdapter(adapter);
dialog = new ProgressDialog(
Table.this);
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setMessage("Загружаю. Подождите...");
AsyncTask<Void, Void, Void> loadingTask = new AsyncTask<Void, Void, Void>() {
#Override
protected void onPreExecute() {
dialog.show();
}
#Override
protected Void doInBackground(Void... params) {
........ adapter = new ArrayAdapter<String>(Table.this, android.R.layout.simple_list_item_1,CreateStringArray
.getString(myData, null, null, null, null));
return null;
}
#Override
protected void onPostExecute(Void result) {
adapter.notifyDataSetChanged();
Table.this.
dialog.dismiss();
}
};
loadingTask.execute();
getListView().setOnItemClickListener(this);
}
So it shows me the spinner and successfully load and deletes it. After a while of debugging i noticed that it successfully changes data in the adapter. Still it dent display it. Am working with listvew
By instantiating a new Adapter in doInBackGround you lose the reference to the adapter you set doing setListAdapter(adapter); Therefore, the adapter you notify in OnPostExecute is not the one in your ListView.