Error in AsyncTask update of view - android

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.

Related

Android AsyncTask, Pass view from UI Activity

I have a web browser application.
A AutoCompleteTextView act as a Url textbox, and being fetch a list (Cursor Type) when application start.
Below code is work well, but i don't know it correct to use AsyncTask or not.
So, did the AutoCompleteTextView will freeze will application launch?
And i monitor the thread via Eclipse thread monitor, the AsyncTask#1 thread keep in wait status when done.So how can i close the thread???
Code:
public class BrowserActivity extends StandOutWindow {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Build the layout
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.activity_browser, frame, true);
AutoCompleteTextView txtUrl = (AutoCompleteTextView) v.findViewById(R.id.txtUrl);
//Run the async task
BrowserDataTask bdTask = new BrowserDataTask();
bdTask.execute(txtUrl);
}
//A method belong to BrowserActivity class and reusable.
public Cursor getBrowserData() {
String[] projection = new String[] { "_id", Browser.BookmarkColumns.TITLE,
Browser.BookmarkColumns.URL };
Cursor mCur = getContentResolver().query(android.provider.Browser.BOOKMARKS_URI,
projection, null, null, null);
return mCur;
}
//Sub Class of BrowserActivity
private class BrowserDataTask extends AsyncTask<AutoCompleteTextView, Integer, Cursor>
{
private AutoCompleteTextView m_acText;
#Override
protected Cursor doInBackground(AutoCompleteTextView...params) {
m_acText = params[0];
return getBrowserData();
}
#Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
}
#Override
protected void onPostExecute(Cursor result) {
//UrlAdapter, custome Cursor Adapter from other class.
UrlAdapter adapter = new UrlAdapter(BrowserActivity.this, result);
m_acText.setAdapter(adapter);
}
}
}
Why is it waiting?
AyncTask uses ThreadPoolExecutor and hence you they might not get destroyed but rather kept, because destroying and reinitializing them would be a definite waste. In case you really want to close it, call cancel() on it, that might help.
Will it freeze the app?
No, it won't.
Also, you are never setting your progress!

ArrayList not populating in AsyncTask

In my AsyncTask, I use Jsoup to pull all of the p tags from a web page, and I add them to an ArrayList that should then be used by an ArrayAdapter to fill the screen with the posts, but for some reason, the ArrayList is empty when I go to check it after the methods.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
newsItems = new ArrayList<String>();
fillNewsItems();
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
newsItems));
Log.d("news", Integer.toString(newsItems.size()));
}
private class GetNewsItemsTask extends AsyncTask<String, Void, String> {
protected String doInBackground(String... urls) {
try {
Document doc = Jsoup.connect(URL).get();
for (Element e : doc.getElementsByTag("p")) {
newsItems.add(e.text());
}
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Couldn't fetch articles, try again later.",
Toast.LENGTH_SHORT).show();
}
return null;
}
}
private void fillNewsItems() {
GetNewsItemsTask getNews = new GetNewsItemsTask();
getNews.execute(URL);
}
}
Does anyone know why the log statement in onCreate returns 0, and my list is empty?
AsyncTask has more possibilities than you are using right now. Basically the AsyncTask is a thread (which cannot change UI elements by default) but it provides a special feature: it synchronizes to the UI thread in the method onPostExecute().
So you can use this to set the ArrayAdapter inside the AsyncTask. Feel free to make use of onPreExecute() to show an information dialog.
This code should to the trick:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fillNewsItems();
Log.d("news", Integer.toString(newsItems.size()));
}
private class GetNewsItemsTask extends AsyncTask<Void, Void, ArrayList<String>> {
protected ArrayList<String> doInBackground(Void... urls) {
try {
ArrayList<String> items = new ArrayList<String>();
Document doc = Jsoup.connect(URL).get();
for (Element e : doc.getElementsByTag("p")) {
items.add(e.text());
}
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Couldn't fetch articles, try again later.",
Toast.LENGTH_SHORT).show();
}
return items;
}
#Override
protected void onPostExecute(ArrayList<String> items) {
newsItems = items; // I assume that newsItems is used elsewhere.
// If that's not the case -> remove it
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
items));
}
}
private void fillNewsItems() {
GetNewsItemsTask getNews = new GetNewsItemsTask();
getNews.execute();
}
This a nice tutorial about asynchronous programming in Android: Android Threads, Handlers and AsyncTask
Most likely because the AsyncTask hasn't finished executing yet.
An AsyncTask is just that, async. It runs in the background at the same time.
It looks like you are expecting your code to block on fillNewsItems() until the AsyncTask
has finished, when in reality it returns almost immediately, right after starting the AsyncTask. So when you are trying to get the size of the list it still is zero, the AsyncTask hasn't finished yet.

AsyncTask as Inner class and static field issue

I have a method searchPlace() that updates a static Arrays of custom Place Object in a class A (FindItOnMap) with a google map, and a method updateMap() that updates the various geopoints .
I invoke these methods Button.onClick and all works properly.
Since these methods use internet data this operation could take a while, I have been looking for the implementation of an inner class B(YourCustomAsyncTask) inside the class A that extends AsyncTask to show a waiting dialog during the processing of these two methods
An user suggested a solution in this form (that apparently seems valid):
public class FindItOnMap extends MapActivity {
static Place[] foundResults;
private ProgressDialog dialog;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ricerca_condominio);
mapView = (MapView)findViewById(R.id.mapView);
...........
((ImageButton) findViewById(R.id.btSearch)).setOnClickListener(mSearchListenerListener);
}
OnClickListener mSearchListener = new OnClickListener() {
public void onClick(View v) {
String location=editorLocation.getText().toString();
String name=editorName.getText().toString();
//Call the AsyncTask here
new YourCustomAsyncTask().execute(new String[] {name, location});
}
};
private class YourCustomAsyncTask extends AsyncTask <String, Void, Void> {
#Override
protected void onPreExecute() {
dialog = new ProgressDialog(Main.this);
dialog.setMessage("Loading....");
dialog.setIndeterminate(true);
dialog.setCancelable(true);
dialog.show(); //Maybe you should call it in ruinOnUIThread in doInBackGround as suggested from a previous answer
}
#Override
protected Void doInBackground(String... strings) {
try {
search(strings[0], string[1]);
return null;
} catch(Exception e) {
}
}
#Override
protected void onPostExecute(Void params) {
updateMapWithResult();
dialog.dismiss();
//result
}
.....
}
The waiting dialog is showed and the methods are invoked in background,
However for some strange reason the static list foundResults results filled with various null items...
How is this possible?
If I invoke the method search(location, name) outside the inner class all works properly and updateMapWithResult(); updates all geopoint, so these two methods are ok. Only if I try to invoke this in the inner class the json calls seem to be working but the static variable foundResults is filled with null elements and the program doesn't work properly.
Any suggestion?
I have understand where is the problem.
You have to run the search method on the UI thread.
So change this code block:
#Override
protected Void doInBackground(String... strings) {
try {
search(strings[0], string[1]);
return null;
} catch(Exception e) {
}
}
with this
#Override
protected Void doInBackground(final String... strings) {
try {
runOnUiThread(new Runnable() {
public void run() {
search(strings[0], string[1]);
return null;
}
});
} catch(Exception e) {
e.printStackTrace();
}
}
And all should works correctly.
Here is one problem:
OnClickListener mSearchListener = new OnClickListener() {
public void onClick(View v) {
String Location=editorLocation.getText().toString();
String name=editorName.getText().toString();
//Call the AsyncTask here
new YourCustomAsyncTask().execute(new String[] {name, location});
}
Your Location should be location.
Also here:
#Override
protected Void doInBackground(String... strings) {
try {
search(strings[0], string[1]);
} catch(Exception e) {
}
}
#Override
protected void onPostExecute(Void params) {
updateMapWithResult();
dialog.dismiss();
//result
}
In doInBackground you don't assign a value after you search. You might try this:
#Override
protected Void doInBackground(String... strings) {
try {
search(strings[0], string[1]);
String name = string[0];
String location = string[1]
} catch(Exception e) {
}
}
Or something else that will assign value while it runs. As it is, it appears that you just search, and then nothing else.
The reason foundResults is null is because you don't ever assign it a value.
There is nothing wrong with your AsyncTask. Please include the search() method.

Can't set ListView Adapter from AsyncThread

I'm using a ListView on my Activity and it takes a while to load from a SQLite DB, so I wanted to show a ProgressDialog to the user to let them know something is loading. I tried to run the task on a separate thread but I'm getting a CalledFromWrongThreadException. Here's my main Activity code:
#Override
public void onCreate(Bundle savedInstanceState)
{
try
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.open_issues);
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);
//Set Window title.
final TextView title = (TextView) findViewById(R.id.customTitle);
if (title != null)
title.setText("Open Issues");
//Call Async Task to run in the background.
new LoadIssuesTask().execute();
}
catch (Exception e)
{
Errors.LogError(e);
}
}
And the LoadIssuesTask code:
private class LoadIssuesTask extends AsyncTask<Void, Void, Cursor> {
ProgressDialog pdDialog = null;
protected void onPreExecute()
{
try
{
pdDialog = new ProgressDialog(OpenIssues.this);
pdDialog.setMessage("Loading Issues and Activities, please wait...");
pdDialog.show();
}
catch (Exception e)
{
Errors.LogError(e);
}
}
#Override
protected Cursor doInBackground(Void... params) {
LoadIssues();
return null;
}
#Override
protected void onPostExecute(Cursor c) {
pdDialog.dismiss();
pdDialog = null;
}
}
And the LoadIssues code:
private void LoadIssues(){
//Set listview of Issues.
ListView lvIssues = (ListView)findViewById(R.id.lvIssues);
lvIssues.setOnItemClickListener(viewIssuesListener);
IssueCreator = new IssueInfoCreator(this, Integer.parseInt(AppPreferences.mDBVersion));
IssueCreator.open();
lvIssues.setAdapter(new IssueInfoAdapter(this, IssueCreator.queryAll()));
IssueCreator.close();
}
Constructor for IssueInfoAdapter:
public IssueInfoAdapter(Context c, List<IssueInfo> list){
mListIssueInfo = list;
//create layout inflater.
mInflater = LayoutInflater.from(c);
}
It's throwing the error on the .setAdapter method inside LoadIssues().
ERROR:
03-12 10:41:23.174: E/AndroidRuntime(11379): Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException:
Only the original thread that created a view hierarchy can touch its views.
You're trying to access the views in the doInBackground method that doesn't run on the main UI thread. You'll have to set your adapter in the method onPostExecute that runs on the UI thread:
#Override
protected void onPostExecute(List<IsueInfo> items) {
pdDialog.dismiss();
ListView lvIssues = (ListView)findViewById(R.id.lvIssues);
lvIssues.setOnItemClickListener(viewIssuesListener);
lvIssues.setAdapter(new IssueInfoAdapter(this, items));
}
and in your doInBackground method:
#Override
protected List<IssueInfo> doInBackground(Void... params) {
IssueCreator = new IssueInfoCreator(this, Integer.parseInt(AppPreferences.mDBVersion));
IssueCreator.open();
IssueCreator.close();
return IssueCreator.queryAll();
}
Also your AsyncTask should be:
private class LoadIssuesTask extends AsyncTask<Void, Void, List<IssueInfo>>
In private void LoadIssues method call handler.setMessage(0) and create a private Handler instance to call setAdapter method
Use Handler instead of Asynctask.

android asynctask update to the listview in postexecute

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().

Categories

Resources