How to Properly Update TextViews in the UI in Android? - android

I am trying update text views while this loop is processing. Here is my code that I am using for this activity.
public class StartDayActivity extends Activity {
Data data_;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.startdayscreen);
Thread seperationTimer = new Thread(){
public void run(){
int seperationTimer = 5000;
int weatherAffect = 0;
int randomEventAffect = 0;
int locationTotalAvailable = 10;
//int servingTime = 3000;
int totalAvailableCustomers = (weatherAffect + randomEventAffect + locationTotalAvailable);
int dayTimer = (totalAvailableCustomers * seperationTimer);
int seperationTimerDay = 0;
int lemonadeSold = 11;
int totalDrinksSold = 0;
int pitcherSize = 12;
int totalLemonsUsed= 0;
int totalIceUsed =0;
int totalSugarUsed = 0;
int totalCupsUsed = 0;
double drinkPrice = 0.80;
try{
while(seperationTimerDay < dayTimer){
sleep(seperationTimer);
seperationTimerDay = seperationTimerDay + seperationTimer;
lemonadeSold = lemonadeSold + 1;
totalDrinksSold += 1;
data_.cups_ -= 1;
totalIceUsed += 2;
if(lemonadeSold == pitcherSize){
lemonadeSold = 0;
data_.lemons_ -= 4;
data_.sugar_ -= 2;
totalSugarUsed +=2;
totalLemonsUsed +=4;
}
if(data_.lemons_ == 0 || data_.ice_ == 0 || data_.sugar_ ==0 || data_.cups_ == 0){
break;
}
updateView();
Log.d("TAG", "for each : " + data_.ice_);
//calcMarketing();
}
data_.day_gross_profit_ = (totalDrinksSold * drinkPrice);
data_.cash_ += (totalDrinksSold * drinkPrice);
Intent i = new Intent("com.game.lemonade.PLAYSCREEN");
startActivity(i);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
finally{
finish();
}
}
};
seperationTimer.start();
}
#Override
public void onStart(){
super.onStart();
data_ = getData();
}
#Override
public void onResume(){
super.onResume();
data_ = getData();
//refreshDisplay();
}
#Override
public void onPause(){
super.onPause();
saveData();
//refreshDisplay();
}
#Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
//refreshDisplay();
}
private Data getData() {
GameData player_data = new GameData(this);
player_data.open();
Data game_state = new Data(player_data.getGameString());
player_data.close();
return game_state;
}
private void saveData() {
GameData player_data = new GameData(this);
player_data.open();
player_data.setGameString(data_.SerializeGame());
player_data.close();
}
private void updateView(){
((TextView)findViewById(R.id.lemonsLeftText2)).setText(
(data_.lemons_) );
}
}
Is there a better way to try to do this? And how would I want to do that? I get an error when I do the updateView() to try to update textview.

Error message already contains answer to your question. You can not update UI from different thread - use runOnUIThread() to do this. And for battery sake - get reference to textView in onCreate() and reuse it.

You cant access the UI using threads directly.Either use an AsyncTask or Handler for your longer thread operations which effect the UI.
Check these;
http://developer.android.com/resources/articles/painless-threading.html
http://developer.android.com/reference/android/os/Handler.html

You are not allowed to change the appearance of your Views from any thread that is not the main thread.
#Serdar has some great suggestions for how to handle that.
Also you should avoid calling findViewById() each time you need to call setText(). findViewById() is a fairly expensive call in terms of system resources. The fewer calls like that you make the better your app will run.
I suggest you make yourself a TextView reference and make your findViewById() call inside onCreate() and set your reference to what it returns to you. Then in your updateView() method call something like yourTxt.setText("");

Although the other answers have covered the answer to your question, I'll point you at this section in the documentation.
Threads
In particular the last part.
Additionally, the Andoid UI toolkit is not thread-safe. So, you must not manipulate your UI from a worker thread—you must do all manipulation to your user interface from the UI thread. Thus, there are simply two rules to Android's single thread model:
Do not block the UI thread
Do not access the Android UI toolkit from outside the UI thread
Two very simple rules indeed but if you stick to them you'll avoid a lot of problems.

You should use Handler class:
Where you call:
updateView();
replace with:
myHandler.sendEmptyMessage(0);
In the handler:
private Handler myHandler=new Handler(){
#Override
public void handleMessage(Message msg) {
if(msg.what==0){
updateView();
}
};
};

Related

How show ProgressBar update with Volley Android

I have a problem with showing progress on ProgressBar.
I have function that calls my server with Volley, when return result I have implemented a Callback that return data to my activity.
Something like:
public static void saveData(Data data, final VolleyCallbackJsonObject callback){
...
callback.onSuccessResponse(new JSONObject());
...
}
Supposing that the function is called on cycle like:
for(int i = 1; i<=5; i++){
final int copyIndex = i;
MyClass.saveData(new Data(i), new VolleyCallbackJsonObject() {
#Override
public void onSuccessResponse(JSONObject result) {
callFunctionForShowAndUpdateProgress(copyIndex);
}
})
}
}
I would like show a ProgressBar that show a bar with a progress from 1/5 to 5/5
so I have my Function in my activityClass:
public Class Test extends Activity{
private ProgressBar pb;
public void callFunctionForShowAndUpdateProgress(int index){
if(index == 1){
pb = (ProgressBar) findViewById(R.id.pbLoading);
pb.setMax(5);
pb.setIndeterminate(false);
pb.setVisibility(View.VISIBLE);
}
if(pb != null){
pb.setProgress(index);
}
if(index == 5){
pb.postDelayed(new Runnable() {
#Override
public void run() {
pb.setVisibility(View.GONE);
}
}, 3000);
}
}
}
My problem is that progress bar not set progress, but it show only at the end so with 5/5 and not show the progress step (1/5, 2/5, 3/5, 4/5) and then it will hide after 3 seconds how specified on postDelayed.
So How can I show all the step? what is wrong?
Try to postDelay the method 'callFunctionForShowAndUpdateProgress' in the method 'onSuccessResponse' instead the postDelay you are already making because the delay you are making happens only when i = 5, so technically the progress goes through 2, 3, and 4 but you don't see it.
try this inside 'onSuccessResponse':
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
callFunctionForShowAndUpdateProgress(copyIndex);
}
}, 3000);
By the way, When the method 'callFunctionForShowAndUpdateProgress' is called in the
'onSuccessResponse' the index initially will be 1 and that is fine, the compiler then will go to second if statement
if(pb != null){
pb.setProgress(index);
}
and will execute it because the condition is correct there, it will not cause a problem but leaving it like this is not a good practice, to solve this prevent the compiler from getting into this if statement by adding else.
I hope that helps
Change your cycle calling function as like below and then check,
int copyIndex = 1;
callFunctionForShowAndUpdateProgress(copyIndex);
for(int i = 1; i<=5; i++)
{
MyClass.saveData(new Data(i), new VolleyCallbackJsonObject()
{
#Override
public void onSuccessResponse(JSONObject result)
{
copyIndex++;
callFunctionForShowAndUpdateProgress(copyIndex);
}
})
}

android: set visibility from AsyncTask

after this thread, i tried to make a variable status bar with this code:
private int[] loadingElementIDs;
private void initLoadingBar() {
final DisplayMetrics displayMetrics=getResources().getDisplayMetrics();
final float screenWidthInDp = displayMetrics.widthPixels/displayMetrics.density;
final int elementAmount = (int) (Math.floor(screenWidthInDp * 0.5f / 30) * 5);
//set margins
LinearLayout container = (LinearLayout)findViewById(R.id.loading_outer);
...
container.requestLayout();
//declare length
loadingElementIDs = new int[elementAmount];
LayoutParams LLParams = new LayoutParams(0, LayoutParams.MATCH_PARENT);
LLParams.weight = 1f;
LinearLayout element;
for (int i=0; i<elementAmount; i++) {
int id = generateViewId(); //creates unique id
element = new LinearLayout(this);
element.setVisibility(View.INVISIBLE);
element.setLayoutParams(LLParams);
element.setBackgroundDrawable(getResources().getDrawable(R.drawable.loading_inner));
element.setId(id);
element.requestLayout();
container.addView(element);
loadingElementIDs[i] = id;
}
}
this is working fine for me, but now i want to calculate sth with an asynctask and make the elements visible (code within my activity class):
private class PrefetchData extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#SuppressWarnings("static-access")
#Override
protected Void doInBackground(Void... arg0) {
try {
int step = 0;
float totalSteps = 100f;
while (...) {
step++;
// ...................
//show status
setLoadingStatus( step / totalSteps);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
Intent i = new Intent(SplashScreen.this, MainActivity.class);
startActivity(i);
finish();
}
}
public void setLoadingStatus(float percentage) {
int max = (int) Math.min( Math.floor(percentage * loadingElementIDs.length),
for (int d=0; d<max; d++ ) {
((LinearLayout)findViewById(loadingElementIDs[d])).setVisibility(View.VISIBLE);
LinearLayout el = (LinearLayout)this.findViewById(loadingElementIDs[d]);
el.setVisibility(LinearLayout.VISIBLE);
}
}
And this does not work. if i call setLoadingStatus(20f); from onCreate it works perfectly, but not in the AsyncTask. Of course i do start initLoadingBar(); and new PrefetchData().execute(); in activities onCreate().
do you know what i'm doing wrong?
Use UI thread for update UI components. If you need to update task progress, you can use publishProgress(xxx) and onProgressUpdate(xxx). For more dateils: http://developer.android.com/reference/android/os/AsyncTask.html
I don't know how this got merged, the comment is all borked, but the requested code snippet is below for managing this with a Handler:
define a handler in your activity:
Handler handler = new Handler(){
handleMessage(Message msg)
{
if (msg.what == STATUS)
{
//do something if it's a message form your AsyncTask
}
else
//other messages..
}
};
when creating your AsyncTask, give it your handler. define a constructor to accept this and keep a local reference to it.
new PrefetchData(handler).execute(...);
and then inside your AsyncTask: (STATUS would be a constant setup as the message code.. )
while (...) {
step++;
// ...................
//show status
handler.obtainMessage(STATUS, step / totalSteps).sendToTarget();
}
thank you guys, i solved it with onProgressUpdate. instead of setLoadingStatus i call this:
private class PrefetchData extends AsyncTask<Void, Float, Void> {
....
protected void onProgressUpdate(Float... values) {
for (int d=0; d<Math.min( Math.floor(values[0] * loadingElementIDs.length), loadingElementIDs.length); d++ ) {
LinearLayout el = (LinearLayout)findViewById(loadingElementIDs[d]);
el.setVisibility(LinearLayout.VISIBLE);
}
}
}
You can runOnUiThread method to call Ui functions from any thread
runOnUiThread(new Runnable() {
public void run() {
// some code #3 (Write your code here to run in UI thread)
}
}); // enter code here

Android TableLayout with over 1000 rows loading very slowly

I'm looking for a way to speed up the creation of a TableLayout with over 1000 rows. Is there a way to create a TableLayout entirely on a separate thread or a way to speed it up?
Here is my method that is creating the table:
private void setTable()
{
final Activity activity = this;
final Handler handler = new Handler();
new Thread(new Runnable()
{
#Override
public void run()
{
for (int x = 0; x < rooms.size(); x++)
{
final int inx = x;
handler.post(new Runnable()
{
#Override
public void run()
{
Methods.createRow(table, rooms.get(inx), null, activity);
TableRow row = (TableRow)table.getChildAt(inx);
row.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View arg0)
{
if (arg0.getTag() != null && arg0.getTag().getClass() == Integer.class)
select((Integer)arg0.getTag());
}
});
}
});
}
}
}).start();
}
I was hoping that using a Handler would at least allow the new Activity to appear before the table was created. The application seems to freeze up for a few seconds when creating tables with a lot of rows. setTable() is being run in my Activity's onStart() method.
Methods.createRow adds a row to the end of the TableView that is passed in.
Edit:
After deciding to try out a ListView, I got much better results with a lot less code.
private void setTable()
{
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, roomNames);
table.setAdapter(adapter);
table.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3)
{
select(arg2);
}
});
}
First things first.
Why your app freezes:
Handler works like a queue, it queues every post you made and than execute it serially in your main thread.
But the main problem is the amount of data you are trying to show at once, but it is easily solved with an Adapter, you probably can use some default Component for solve this, like ListView or GridView, you can make your custom rows to work around the columns maybe.
Just from guessing on the method name, it seems this line may be slow to run on the main/UI thread:
Methods.createRow(table, rooms.get(inx), null, activity);
I would suggest separating all the heavy database work and UI work with AsyncTask, so it might look something like this:
private OnClickListener rowClickListener = new OnClickListener()
{
#Override
public void onClick(View arg0)
{
if (arg0.getTag() != null && arg0.getTag().getClass() == Integer.class)
select((Integer)arg0.getTag());
}
};
private void setTable()
{
final Activity activity = this;
final Handler handler = new Handler();
new AsyncTask<Void, Void, List<TableRow>>() {
#Override
protected List<TableRow> doInBackground(Void... params) {
// Do all heavy work here
final List<TableRow> rows = new ArrayList<TableRow>(rooms.size());
for (int x = 0; x < rooms.size(); x++)
{
Methods.createRow(table, rooms.get(x), null, activity);
rows.add((TableRow)table.getChildAt(x));
}
return rows;
}
#Override
protected void onPreExecute() {
// maybe show progress indication
}
#Override
protected void onPostExecute(List<TableRow> result) {
// Do all UI related actions here (maybe hide progress indication)
for (final TableRow row : result) {
row.setOnClickListener(rowClickListener);
}
}
};
}
Since I can't tell what is in some methods, you'll just need to ensure you've tried to optimize as best as possible in Methods.createRow(...) and move all the UI related work to the onPostExecute(...) method.
You are stacking the post with all the actions at once. So it same as not using a thread at all as your main thread doing all the work. Try changing to postDelay, and for each room index give it 1 millisecond or more.

Appending ListView from onScrollListener

I am trying to update my listview to make an "endless scroll". What happens is that the first 40 results load fine, when i get to the bottom of the scroll, next 40 results replace the first 40...
What I want is for second set of 40 results to add to the first 40 so I have an endless list and ability to scroll back to the beginning of the list.
I am posting my code below. Thank you!
public class SearchResults extends Activity implements BannerAdListener, OnScrollListener{
private LinearLayout bottomNav;
private ListView ringtoneList;
private int start = 0, num = 40, curPage = 1;
private Handler handler = new Handler();
private ProgressDialog progressDialog = null;
private ArrayList<Ringtone> ringtones;
private MoPubView moPubView;
private String searchString;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
if (extras == null) {
// no search string defined
finish();
} else {
searchString = extras.getString("search_string");
}
setContentView(R.layout.search_results);
ringtoneList = (ListView)findViewById(R.id.ringtone_list);
ringtoneList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> av, View view, int position, long id) {
Intent i = new Intent(SearchResults.this, RingtoneView.class);
i.putExtra("ringtone", ringtones.get(position));
startActivity(i);
}
});
performSearch();
moPubView = (MoPubView) findViewById(R.id.adview);
moPubView.setAdUnitId(Utils.MoPubBannerId);
moPubView.loadAd();
moPubView.setBannerAdListener(this);
ringtoneList.setOnScrollListener(this);
}
public void onScroll(AbsListView view, int firstVisible, final int visibleCount, int totalCount) {
Log.i("List", "firstVisible="+firstVisible+" visibleCount="+visibleCount+" totalCount="+totalCount);
boolean loadMore = firstVisible + visibleCount >= totalCount;
if(loadMore) {
Log.i("List", "Loading More Results");
curPage++;
start = num * (curPage-1);
new Thread() {
public void run() {
ringtones = Utils.search(start, num, searchString);
if (ringtones != null && ringtones.size() > 0) {
handler.post(new Runnable() {
public void run() {
ringtoneList.setAdapter(new RingtoneRowAdapter(SearchResults.this, ringtones));
}
});
} else {
handler.post(new Runnable() {
public void run() {
new AlertDialog.Builder(SearchResults.this)
.setTitle(getResources().getString(R.string.context_info)).setMessage(getResources().getString(R.string.context_noresult))
.setPositiveButton(getResources().getString(R.string.context_ok), null).show();
}
});
}
}
}
.start();
}
}
public void onScrollStateChanged(AbsListView v, int s) { }
private void performSearch() {
progressDialog = ProgressDialog.show(SearchResults.this, getResources().getString(R.string.loading_message), getResources().getString(R.string.loading_search), true);
new Thread() {
public void run() {
ringtones = Utils.search(start, num, searchString);
if (ringtones != null && ringtones.size() > 0) {
updateList();
} else {
handler.post(new Runnable() {
public void run() {
new AlertDialog.Builder(SearchResults.this)
.setTitle(getResources().getString(R.string.context_info)).setMessage(getResources().getString(R.string.context_noresult))
.setPositiveButton(getResources().getString(R.string.context_ok), null).show();
ringtoneList.setVisibility(View.INVISIBLE);
bottomNav.setVisibility(View.INVISIBLE);
}
});
}
progressDialog.dismiss();
}
}.start();
}
private void updateList() {
handler.post(new Runnable() {
public void run() {
//Log.d("search", "ringtones.size() " + ringtones.size());
ringtoneList.setVisibility(View.VISIBLE);
ringtoneList.setAdapter(new RingtoneRowAdapter(SearchResults.this, ringtones));
}
});
}
}
Please help! Thank you!
While I can't be 100% sure, I think your problem has to do with the fact that you're setting a new adapter that only has the section of ringtones that in loads. It probably has to do with this snippet:
ringtones = Utils.search(start, num, searchString);
if (ringtones != null && ringtones.size() > 0) {
handler.post(new Runnable() {
public void run() {
ringtoneList.setAdapter(new RingtoneRowAdapter(SearchResults.this, ringtones));
}
});
}
Instead of putting an entirely new array of ringtones, you should add on to the one you already have. Your ringtones variable is already an instance variable, so I'm sure if you changed this line:
ringtones = Utils.search(start, num, searchString);
to the following:
ringtones.addAll(Utils.search(start, num, searchString));
It might fix your problem.
Your code is a little bit messy, but your updateList method is creating a NEW RingtoneRowAdapter. You should ADD items to the list and call
mRingtoneRowAdapter.notifyDatasetChanged();
That will tell the adapter to get new views (if needed) along with a lot of internal stuff happening at Adapter's level. So it's kinda:
private void updateList() {
handler.post(new Runnable() {
public void run() {
if ( mAdapter == null ) {
mAdapter = new RingtoneRowAdapter(SearchResults.this, ringtones);
ringtoneList.setAdapter(mAdapter);
} else {
mAdapter.notifyDataSetChanged();
}
ringtoneList.setVisibility(View.VISIBLE);
}
});
}
And of course, as already suggested, don't init your ringtones array all the time. Just add data to it.
You don't need (most of the times) to hide the list, you can add in your layout, any View (including a full Layout!) with an id of:
android:id="#android:id/empty"
And if your ListView has an id of android:id="#id/android:list", and you're in a ListFragment or ListActivity, then when the list is empty, the "empty" layout will be shown (which can be a dummy view if you don't want to see it).
Just suggestions :)
UPDATE: For your null, I see that the logic is a little bit weird and since we don't know the requirements for your app (i.e. what to do if there are no results, what to do if the results are invalid, etc.) I'll assume you just want to make sure it works and deal with that later.
So with that in mind, I see two problems.
I assume your Utils.search method can return null, because you're checking for it. To me that feels strange, I'd rather return an empty array indicating that the search produced no results; that little remark aside, you are not checking (or we don't know because we haven't seen the source for your search method), if the searchString is null or not.
You haven't provided a stack trace, so we can't tell where the null is happening (either inside search or ?)
A quick solution would be to check for null before searching… I would personally do this INSIDE the search function.
Something like:
public ArrayList<Ringtone> search(final int start, final int num, final String searchString) {
if ( searchString == null ) {
//DECIDE WHAT YOU WANT TO DO, EITHER:
return null;
// OR YOU CAN RETURN AN EMPTY ARRAY
return new ArrayList<Ringtone>();
}
// You should check for these (change according to your rules)
if (start < 0 ) {
start = 0; // protect yourself from bad data.
}
if ( num < 0 ) {
num = 0;
}
/// THE REST OF YOUR search FUNCTION
return <your array>
}
Now another thing is that you may want the search to return incremental results (so you can ADD them to your array (instead of returning a new array every time). For that, as already suggested, use the addAll trick, but then, DON'T return null, return an new empty array, so there's no harm done if there's nothing else to add.

Android: Running thread preventing animation from starting

I currently have an Android activity which manages some locally-stored RSS feeds. In this activity, these feeds are updated in their own thread via a private class. I'm also trying to include an "updating" icon that rotates with a RotateAnimation while this thread is running.
The animation works by itself, but doesn't work while the thread is running despite the log entries stating the code is being executed. I suspect this is due to the thread not being entirely safe, and taking up most of the CPU time. However I'd just like to know if there's a better way of achieving this.
The function updateAllFeeds() is called from a button press. Here's the relevant code:
/**
* Gets the animation properties for the rotation
*/
protected RotateAnimation getRotateAnimation() {
// Now animate it
Log.d("RSS Alarm", "Performing animation");
RotateAnimation anim = new RotateAnimation(359f, 0f, 16f, 21f);
anim.setInterpolator(new LinearInterpolator());
anim.setRepeatCount(Animation.INFINITE);
anim.setDuration(700);
return anim;
}
/**
* Animates the refresh icon with a rotate
*/
public void setUpdating() {
btnRefreshAll.startAnimation(getRotateAnimation());
}
/**
* Reverts the refresh icon back to a still image
*/
public void stopUpdating() {
Log.d("RSS Alarm", "Stopping animation");
btnRefreshAll.setAnimation(null);
refreshList();
}
/**
* Updates all RSS feeds in the list
*/
protected void updateAllFeeds() {
setUpdating();
Updater updater = new Updater(channels);
updater.run();
}
/**
* Class to update RSS feeds in a new thread
* #author Michael
*
*/
private class Updater implements Runnable {
// Mode flags
public static final int MODE_ONE = 0;
public static final int MODE_ALL = 1;
// Class vars
Channel channel;
ArrayList<Channel> channelList;
int mode;
/**
* Constructor for singular update
* #param channel
*/
public Updater(Channel channel) {
this.mode = MODE_ONE;
this.channel = channel;
}
/**
* Constructor for updating multiple feeds at once
* #param channelList The list of channels to be updated
*/
public Updater(ArrayList<Channel> channelList) {
this.mode = MODE_ALL;
this.channelList = channelList;
}
/**
* Performs all the good stuff
*/
public void run() {
// Flag for writing problems
boolean write_error = false;
// Check if we have a singular or list
if(this.mode == MODE_ONE) {
// Updating one feed only
int updateStatus = channel.update(getApplicationContext());
// Check for error
if(updateStatus == 2) {
// Error - show dialog
write_error = true;
}
}
else {
// Iterate through all feeds
for(int i = 0; i < this.channelList.size(); i++) {
// Update this item
int updateStatus = channelList.get(i).update(getApplicationContext());
if(updateStatus == 2) {
// Error - show dialog
write_error = true;
}
}
}
// If we have an error, show the dialog
if(write_error) {
runOnUiThread(new Runnable(){
public void run() {
showDialog(ERR_SD_READ_ONLY);
}
});
}
// End updater
stopUpdating();
} // End run()
} // End class Updater
(I know the updateStatus == 2 bit is bad practice, that's one of the next things I plan to tidy up).
Any help is greatly appreciated, many thanks in advance.
Run the updater runnable in a separate thread. Do the following changes.
protected void updateAllFeeds() {
setUpdating();
new Thread( new Updater(channels)).start();
}
Call stopUpdating() in runOnUiThread block.
private class Updater implements Runnable {
.........
.........
.........
public void run() {
.........
.........
// End updater
runOnUiThread(new Runnable(){
public void run() {
stopUpdating();
}
});
} // End run()
} // End class Updater
Move anything that affects the UI off into its own Runnable and then post it with your button
btnRefreshAll.post(new StopUpdating());
I managed to get this working last night using Android's AsyncTask class. It was surprisingly easy to implement, though the downside was I had to write one class for updating an individual feed, and another for updating all feeds. Here's the code for updating all feeds at once:
private class MassUpdater extends AsyncTask<ArrayList<Channel>, Void, Void> {
#Override
protected Void doInBackground(ArrayList<Channel>... channels) {
ArrayList<Channel> channelList = channels[0];
// Flag for writing problems
boolean write_error = false;
// Iterate through all feeds
for(int i = 0; i < channelList.size(); i++) {
// Update this item
int updateStatus = channelList.get(i).update(getApplicationContext());
if(updateStatus == FileHandler.STATUS_WRITE_ERROR) {
// Error - show dialog
write_error = true;
}
}
// If we have an error, show the dialog
if(write_error) {
runOnUiThread(new Runnable(){
public void run() {
showDialog(ERR_SD_READ_ONLY);
}
});
}
return null;
}
protected void onPreExecute() {
btnRefreshAll.setAnimation(getRotateAnimation());
btnRefreshAll.invalidate();
btnRefreshAll.getAnimation().startNow();
}
protected void onPostExecute(Void hello) {
btnRefreshAll.setAnimation(null);
refreshList();
}
}
Thanks for your answers guys. userSeven7s' reply also makes a lot of sense so I may use that as a backup should I run into any problems with AsyncTask.

Categories

Resources