Hi I'm having problem refreshing my listview after Async operation.
I have a simplecursoradapter, and custon listview and a button. Initially when application starts, it sets the listview from the data read from database. Then when user clicks a button, it starts a async code to download some data which gets inserted into a database. When async task start, I'm displaying a progressdialog, which I dismiss in postexecute(). Data is getting downloaded fine, but now how do I requery the cursor and update listview on the main thread after background job is done?
A Method "refreshRemoteData" gets called via a menu button.
This is how my AsyncTask looks like.
public class MyActivity extends ListActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void onStart() {
myDBAdapter = new DBAdapter(this);
myDBAdapter.open();
populateMyList();
}
private void populateMyList() {
myCursor = myDBAdapter.fetchAllItems();
startManagingCursor(myCursor);
getListView().setAdapter(myDBAdapter);
}
private void refreshRemoteData() {
mPleaseWaitDialog = ProgressDialog.show(ExpirationDateTrackingActivity.this,
"Data", "Downloading data", true, true);
download_task = new InfoDownloaderTask();
download_task.execute();
}
private class InfoDownloaderTask extends AsyncTask<Object, String, Boolean> {
private static final String DEBUG_TAG = "InfoDownloaderTask";
protected DBAdapter mylocalDBAdapter=null;
#Override
protected void onPreExecute() {
Log.e(DEBUG_TAG, "onPreExecute: ");
mylocalDBAdapter = new DBAdapter(this);
mylocalDBAdapter.open();
}
#Override
protected void onPostExecute(Boolean result) {
Log.i(DEBUG_TAG, "onPostExecute: " );
mPleaseWaitDialog.dismiss();
mlocalDBAdapter.close();
}
#Override
protected Boolean doInBackground(Object... arg0) {
Log.v(DEBUG_TAG, "doInBackground");
///...
//Update the database
mylocalDBAdapter.insertData(....);
return true;
}
} //AsyncTask
}
I don't see my listview getting updated with new list data right after async operation is complete. But If I invoke another ativity and comeback to the listview then I see all new items (list update).
What am I missing?
You're inserting data through mylocalDBAdapter, but you aren't telling myDBAdapter about it. Try calling myDBAdapter.notifyDataSetChanged(); at the end of onPostExecute().
Related
I am pretty much new in Java (& android), hence I can't solve it with my limited knowledge despite searching extensively.
In the code below, I want to get a JASONArray from a weblink through AsyncTask. After that I want to populate my database from the array data.
To do that I created the RemoteConnectivity class where I can populate an ArrayList importdata with all the data from JASONArray. But the problem is, I cannot access my database class mylibmandbhandler from inside RemoteConnectivity class (I guess because that is not extended to Activity). And in ImportExport class, if I write the code to insert into database just after calling RemoteConnectivity().execute() [as in code below], it starts inserting before execution ends (very obvious because it is property of AsyncTask).
Now, can anybody please guide me through this? Or any link please to understand the whole process (I am lost in at least 50 pages I read about it :( ).
P.S. mylibmandbhandler is a Class I created (not activity) in my package src folder for my database operations.
public class ImportExport extends Activity {
public void webimport(View v){
new RemoteConnectivity().execute(); // START OF ASYNCTASK
//INSERT importdata INTO DATABASE AFTER EXECUTE
mylibmandbhandler db = new mylibmandbhandler(this);
for (String[] s : importdata){
db.addRecord(new mylibman(s));
}
}
final ArrayList<String[]> importdata = new ArrayList<String[]>();
private class RemoteConnectivity extends AsyncTask<Void, Void, Void>
{
#Override
protected Void doInBackground(Void... arg0) {
..................
..................
importdata.add(dataline); // POPULATE ARRAYLIST IMPORTDATA
return null;
}
#Override
protected void onPostExecute(Void result1)
{
// WHAT TO WRITE HERE
}
}
}
You should insert the data in the onPostExecute as follows:
public class ImportExport extends Activity {
public void webimport(View v){
new RemoteConnectivity().execute(); // START OF ASYNCTASK
}
final ArrayList<String[]> importdata = new ArrayList<String[]>();
private class RemoteConnectivity extends AsyncTask<Void, Void, Void>
{
#Override
protected Void doInBackground(Void... arg0) {
..................
..................
importdata.add(dataline); // POPULATE ARRAYLIST IMPORTDATA
return null;
}
#Override
protected void onPostExecute(Void result1)
{
//INSERT importdata INTO DATABASE AFTER EXECUTE FINISH (POST EXECUTE)
mylibmandbhandler db = new mylibmandbhandler(ImportExport.this);
for (String[] s : importdata){
db.addRecord(new mylibman(s));
}
}
}
}
The reason is because, onPostExecute is the method (callback) which is executed after the data has been done downloading in the doInBackground method.
Alternatively: You can also put the insert statements inside the doInBackground if you want the inserting to be done by background thread. This may be helpful, if inserting into the database is time consuming. In such a case the UI thread won't block. '
public class ImportExport extends Activity {
public void webimport(View v){
new RemoteConnectivity().execute(); // START OF ASYNCTASK
}
final ArrayList<String[]> importdata = new ArrayList<String[]>();
private class RemoteConnectivity extends AsyncTask<Void, Void, Void>
{
#Override
protected Void doInBackground(Void... arg0) {
..................
..................
importdata.add(dataline); // POPULATE ARRAYLIST IMPORTDATA
//INSERT importdata INTO DATABASE. NOW DONE IN THE BACKGROUND THREAD (Alternate method)
mylibmandbhandler db = new mylibmandbhandler(ImportExport.this);
for (String[] s : importdata){
db.addRecord(new mylibman(s));
}
return null;
}
#Override
protected void onPostExecute(Void result1)
{
}
}
}
Update: The important this to highlight in my answer is that I used new mylibmandbhandler(ImportExport.this); instead of new mylibmandbhandler(this); as this refers to AsyncTask which is not what the constructor supports.
You need to use method onPostExecute to insert data to datatable. Like this:
private class RemoteConnectivity extends AsyncTask<Void, Void, Void>
{
#Override
protected Void doInBackground(Void... arg0) {
..................
..................
importdata.add(dataline); // POPULATE ARRAYLIST IMPORTDATA
return null;
}
#Override
protected void onPostExecute(Void result1)
{
//INSERT importdata INTO DATABASE AFTER EXECUTE FINISH (POST EXECUTE)
mylibmandbhandler db = new mylibmandbhandler(ImportExport.this);
for (String[] s : importdata){
db.addRecord(new mylibman(s));
}
}
}
I seem to be going round in circles.
I have some code that even on a Galaxy S3 takes a few seconds to run. Drags data from database.
I want to add a progress bar popup (spinning circle) around this to give the user that the app is doing something.
I have tried Asyntasks elsewhere in app and work fine but for this type the main UI is not waiting for the Asyntask to finish before moving on and so the new activity that is called does not have all the data it needs and crashes.
Is AsyncTask the best way round this or is there an easier way to Puase the main Activity, show a progress bar and then move on once the long deley has been completed.
Thanks for time
UPDATE
public class UpdateDetailsAsyncTask extends AsyncTask<Void, Void, Boolean> {
private Context context;
private TaskCallback callback;
private ArrayList<Object> object;
private ProgressDialog progress;
public UpdateDetailsAsyncTask (
Context pContext,
ArrayList<Object> pObject,
TaskCallback pCallback) {
context = pContext;
callback = pCallback;
object = pObject;
}
#Override
protected void onPreExecute() {
Log.i("AsyncTask", "onPreExecuted");
progress = new ProgressDialog(context);
progress.setMessage(context.getResources().getString(R.string.loading));
progress.show();
}
#Override
protected Boolean doInBackground(Void... params) {
Log.i("Archery", "AsyncTask Excuted");
Log.i("Archery Scorepad", "Building Continue Round Details");
// Save Data to Database
return true;
}
protected void onPostExecute(Boolean result) {
Log.i("AsyncTask", "onPostExuted");
progress.dismiss();
callback.startNewActivity();
}
}
Task is called from main Activity
new UpdateDetailsAsyncTask(this, ArrayListOfObjects, this).exute();
UPDATE 2
..
UPDATE 3
The Code that does some work calls an a method within a Util Class which in calls a database class. I have log messages showing for all the rows of data I am saving to the database. It starts correctly and runs through it but the onPostExecute() appears to be called before the database method has completed.
Is my issue that I have nested classes within the activity and the task appears to have completed when the class below it has not?
Thanks
You must change to the next activity in onPostExecute from Asyntask
Yes!
Here is a simple code of AsuncTask
private class LoadImageAction extends AsyncTask<String, String, String>{
private Course course;
private ProgressBar pb;
public LoadImageAction(Course course, ProgressBar pb){
this.course = course;
this.pb = pb;
}
#Override
protected void onPreExecute(){
}
#Override
protected String doInBackground(String... arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
protected void onProgressUpdate(String... string){
}
#Override
protected void onPostExecute(String result){
}
}
You can run the action by
new LoadImageAction().execute();
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.
my issue is the same as this
Instance variable of Activity not being set in onPostExecute of AsyncTask or how to return data from AsyncTask to main UI thread
but i want to send the data back to the same calling activity. Doesnt startActivity for intents always restart the activity
On option is to use listeners, where you create an interface that your activity implents, something like:
public interface AsyncListener {
public void doStuff( MyObject obj );
}
That way, if you're subclassing AsyncTask, it is easy to add this listener, then in onPostExecute(), you could do something like:
protected void onPostExecute( MyObject obj ) {
asyncListener.doStuff(obj);
}
This depends on your class structure, but if your AsyncTask is a class within your Activity then you can reference methods of that activity.
What you would do is in your onPostExecute method call a function of your Activity that passes some data that was retrieved in the AsyncTask to the activity where you can then use it..
The code would look like this
class YourActivity extends Activity {
private static final int DIALOG_LOADING = 1;
public void onCreate(Bundle savedState) {
setContentView(R.layout.yourlayout);
showDialog(DIALOG_LOADING);
new LongRunningTask1().execute(1,2,3);
}
protected Dialog onCreateDialog(int dialogId) {
switch(dialogId) {
case DIALOG_LOADING:
ProgressDialog pDialog = new ProgressDialog(this);
pDialog.setTitle("Loading Data");
pDialog.setMessage("Loading Data, please wait...");
return pDialog;
default:
return super.onCreateDialog(dialogId);
}
}
private void onBackgroundTaskDataObtained(List<String> results) {
dismissDialog(DIALOG_LOADING);
//do stuff with the results here..
}
private class LongRunningTask extends AsyncTask<Long, Integer, List<String>> {
#Override
protected void onPreExecute() {
//do pre execute stuff
}
#Override
protected List<String> doInBackground(Long... params) {
List<String> myData = new ArrayList<String>();
for (int i = 0; i < params.length; i++) {
try {
Thread.sleep(params[i] * 1000);
myData.add("Some Data" + i);
} catch(InterruptedException ex) {
}
}
return myData;
}
#Override
protected void onPostExecute(List<String> result) {
YourActivity.this.onBackgroundTaskDataObtained(result);
}
}
}
So the typical flow is like this, set the view of the current page, and then show a progress dialog. Right after that start the async task (or whenever, it doesn't matter really).
After your async task is complete, call a function of the activity and pass it the data.
Don't use shared data within the async task or you risk issues with threading.. Instead once you are done with it pass it to the activity. If you want to update the view progressively while doing work you can use on onProgressUpdate
I have a Tabs Activity that contains Fragments for tabs. One of the tabs displays a list of data from a SQLite databse table (or a different Fragment if the table is empty). When initially creating the tabs, the Tabs Activity checks to see if the table contains any data. If it does, then it initialises the list Fragment but doesn't set up the CursorAdapter. The CursorAdapter is initialised by an AsyncTask which syncs the SQLite database with a central database, and then creates the Cursors and CursorAdapters. The list Fragment displays a ProgressDialog while waiting for the AsyncTask to create the CursorAdapter. When the CurserAdapter is initialised, the ProgressDialog is dismissed but the ListView remains on its 'empty list' view, despite calls to notifyDataSetChanged(). If I switch tabs and come back, the ListView displays the data correctly. How can I make the ListView update once the CursorAdapter has been initialised?
Relevant bits of code:
Tabs:
private static ImageCursorAdapter friendCursorAdapter = null;
public static ImageCursorAdapter getFriendsListAdapter() {
return friendCursorAdapter;
}
public static void setFriendsListAdapter(ImageCursorAdapter adapter) {
friendCursorAdapter = adapter;
friendCursorAdapter.notifyDataSetChanged();
}
SyncTask:
protected Void doInBackground(Void... params) {
sql = "SELECT COUNT(*) FROM " + WhereWolfOpenHelper.FRIEND_TABLE_NAME;
statement = db.compileStatement(sql);
count = statement.simpleQueryForLong();
if(count>0) {
friendCursor = db.query(WhereWolfOpenHelper.FRIEND_TABLE_NAME, null, null, null, null, null, WhereWolfOpenHelper.FRIEND_FIRST_NAME_COLUMN+", "+WhereWolfOpenHelper.FRIEND_LAST_NAME_COLUMN);
}
statement.close();
}
#Override
protected void onPostExecute(Void param) {
if(friendCursor!=null) {
ImageCursorAdapter adapter = new ImageCursorAdapter(WhereWolfActivity.this, friendCursor, 0, ImageCursorAdapter.FRIENDS);
Tabs.setFriendsListAdapter(adapter);
}
}
FriendsList:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(Tabs.getFriendsListAdapter());
}
#Override
public void onStart() {
super.onStart();
if(Tabs.getFriendsListAdapter()==null) {
final ProgressDialog dialog = ProgressDialog.show(getActivity(), getString(R.string.loadingtitle), getString(R.string.loading), true, false);
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
while(Tabs.getFriendsListAdapter()==null) {}
dialog.dismiss();
Tabs.getFriendsListAdapter().notifyDataSetChanged();
}
});
thread.start();
}
}
You are creating a new adapter, but I don't see any code to set the adapter to the list. Since the list knows nothing about your brand new adapter, calling notifyDataSetChanged() doesn't help. BTW, if you are already using fragments, you might consider using a Loader.