Android CursorAdapters, ListViews and background threads - java

This application I've been working on has databases with multiple megabytes of data to sift through. A lot of the activities are just ListViews descending through various levels of data within the databases until we reach "documents", which is just HTML to be pulled from the DB(s) and displayed on the phone. The issue I am having is that some of these activities need to have the ability to search through the databases by capturing keystrokes and re-running the query with a "like %blah%" in it. This works reasonably quickly except when the user is first loading the data and when the user first enters a keystroke. I am using a ResourceCursorAdapter and I am generating the cursor in a background thread, but in order to do a listAdapter.changeCursor(), I have to use a Handler to post it to the main UI thread. This particular call is then freezing the UI thread just long enough to bring up the dreaded ANR dialog. I'm curious how I can offload this to a background thread totally so the user interface remains responsive and we don't have ANR dialogs popping up.
Just for full disclosure, I was originally returning an ArrayList of custom model objects and using an ArrayAdapter, but (understandably) the customer pointed out it was bad memory-manangement and I wasn't happy with the performance anyways. I'd really like to avoid a solution where I'm generating huge lists of objects and then doing a listAdapter.notifyDataSetChanged/Invalidated()
Here is the code in question:
private Runnable filterDrugListRunnable = new Runnable() {
public void run() {
if (filterLock.tryLock() == false) return;
cur = ActivityUtils.getIndexItemCursor(DrugListActivity.this);
if (cur == null || forceRefresh == true) {
cur = docDb.getItemCursor(selectedIndex.getIndexId(), filter);
ActivityUtils.setIndexItemCursor(DrugListActivity.this, cur);
forceRefresh = false;
}
updateHandler.post(new Runnable() {
public void run() {
listAdapter.changeCursor(cur);
}
});
filterLock.unlock();
updateHandler.post(hideProgressRunnable);
updateHandler.post(updateListRunnable);
}
};

I find it difficult to believe that listAdapter.changeCursor() alone would take up sufficient time to cause an ANR, assuming you created the Cursor in a background thread. There just should not be that much work that needs to happen to repaint a handful of list rows. I would double-check to see what work you are doing in your Handler is as limited as you think. Perhaps consider using an AsyncTask, which makes it easier to separate the background work (doInBackground()) from the on-UI-thread post-processing (onPostExecute()).
You can try just replacing the adapter outright, by calling setAdapter() again with the new Cursor wrapped in a new adapter.
You can look to see how AutoCompleteTextView handles this scenario, since it does on-the-fly filtering with a SpinnerAdapter. Perhaps some of its techniques can apply in your case.

Just posted an answer here: Android: Update Listview after Thread loads data from the net
Short: AsyncTask's method onProgressUpdate can touch the view: http://developer.android.com/reference/android/os/AsyncTask.html#onProgressUpdate(Progress...)

Related

Android recycle view update sort list with concurrency issue

I have a sorted list I would like to use to hold objects for maintaining a recycleview as usual.
The api I am interfacing with passes me updates through an interface callback using multiple threads. This api can add, remove or update object state. When the app opens there is a flurry of adds, removes and updates. Then it slowes down to some degreee. This is a reverse of the typical database paradigm where the UI updates faster than the data to one where the data updates faster than the UI
This seems to be an issue with the sortedList in android for several reasons.
adding an item to sortedlist must be done on UI thread if updates to list are going to fully execute without simply adding to the list and swallowing the error in a background thread when trying to show the update
to change an object state you must first get the index. then get the item. if your multithreading then you get index errors due to race conditions and pull the wrong object
you can affect a list item object's state in a background thread but this still relies on a call to recalculate() or updateitem() type calls. this leads to reycleview is computing layout errors
Question: Which design pattern would you use to solve this issue? Below are some of my ideas
solutions that seem bad
Swallow errors and use a timer to call notifydatasetchanged(). This really eliminates the purpose of using the sorted list.
Collect updates in another list and do batch updates when size() > someNumber or some timer. why bother using sortedlist
solution that seems good
add all object into a concurrent hashmap. This solves the threading and update issues. But I still need to present a sorted list to the UI. This means batching add and remove for the sorted list. Then also calling notifydatasetChanged() from time to time to handle object state updates
My fantasy solution that maybe exists already is to use an object that holds a concurrent sorted list where I can add, remove and update from a background thread and also bind to a recycleview adpater.
My solution is to hold updates in a buffer which can only fire after a time delay. Then putting the update objects into both a concurrent hashmap for easy object retrieval as well as a sortedlist for easy ui display
The api calls this method. I have another version that can run off the ui thread by simply detaching the adapter in sortedlist callbacks.
public void onResult(Object object) {
if ( !currentlyProcessing && System.currentTimeMillis() > (lastBatch + delay)) {
currentlyProcessing = true;
runOnUiThread(new Runnable() {
#Override
public void run() {
easyChatList.beginBatchedUpdates();
while (batchBuilder.size() > 0){
performBatch(batchBuilder.remove(0));
}
easyChatList.endBatchedUpdates();
lastBatch = System.currentTimeMillis();
currentlyProcessing = false;
}
});
}
}
The performbatch() handles a single update. If the object is new it puts it into a concurrent hashmap with its id as a key. Then adds it to the sortedlist. When an update occurs I pull the object from the hashmap, do the update and add it to the sortedlist. The sortedlist api then replaces the old object and updates the sort order.
edit:this does have a slight drag according to androids skipped frames warning. You can detach the adapter from the sortedlist and do updates on a worker thread but I'm not sure if that has any value as this is working alright for now.

How to handle all UI changes in a different class?

I am developing an android application that has a lot of UI changes. I know in order to alter a view, I must do the following:
runOnUiThread(new Runnable() {
#Override
public void run() {
//alter views
}
});
I have found that I have used this method several times throughout my code and it seems cluttered. How might I create a class that will handle all UI changes for an activity?
Well, I am supposing that you are doing this from inside a different thread (Of course. Why else you would need to call runOnUiThread specifically?) My 2 cent is that have you considered using AsyncTask for handing UI change after/before your background operation starts? This will clear things a bit for you and that's a better/proper way of handling your interacition between UI/Non-UI thread.

Firing Android Activity methods on separate process at set interval

I am writing my first Android app(allot of fun so far!) and have run into a roadblock. I am using SDK tools 21.1 targeting Android 4.2.
I am trying to set up a system that allows activities to register to invoke a method at set time intervals. I wanted to do this in such a way that the processing of the request would be handled on it's own process to avoid making the UI unresponsive.
I have been doing some reading and have explored a few avenues. First I thought that a service would be a good way to go about this but found a fair bit of information suggesting that was not a good course of action due to the OS being able to kill services indiscriminately.
I'm now looking at using a ScheduledThreadPoolExecutor. I've created this simple class with an overridable method to pass methods around:
public abstract class BaseEvent implements EventListener {
public abstract void onFire(Object... params);
}
I've created this runnable task invoke the method:
public class HeartBeatTask implements Runnable {
private BaseEvent mCallback;
private Object mParams;
public HeartBeatTask(BaseAioEvent callback,Object... params){
mParams = params;
mCallback = callback;
}
#Override
public void run() {
Log.d(LOG_TAG,"Run called");
if(mCallback != null)
{
mCallback.onEvent(mParams);
}
}
}
I'm going to use it like this (inside an Activity)
ScheduledThreadPoolExecutor threadPool = new ScheduledThreadPoolExecutor(1);
BaseEvent callback = new BaseEvent() {
public void onFire(Object... params){
if(params[0] !=null)
{
Context context = (Context)params[0];
Toast toast = Toast.makeText(context, "Task ran", Toast.LENGTH_SHORT);
toast.show();
}
}
};
threadPool.scheduleAtFixedRate(new HeartBeatTask(callback,(this)),0, 5, TimeUnit.SECONDS);
This will execute the task every 5 seconds, although the callback method is only being run once.
I'm thinking that this may not be a good way to do things. My feeling is that I'm overcomplicating things. What I really need is the ability to have something that will execute a method, on a process other than the main thread, and at a set interval, that activities can bind multiple actions to. So, for instance, I may want to have a UI component update after a call is made to a database, and would want that to happen every minute.
Could any tell me if I am on the right track here? Is what I am trying to do a viable way to accomplish my goal? Is there a better approach I could be taking? Any advice or suggestions would be very much appreciated. Thanks!
a few suggestions for an Android beginner.
Don't call it a separate process. Process is a different thing (Google 'Linux process'), you want to call them on a separate thread inside the same process.
ScheduledThreadPoolExecutor IS better than anything else people will suggest you here such as Timers or PostDelayed.
But I think you do have a philosophical error here as to UI updates shouldn't be running on a timed manner but on an event base instead. Once your Db, Disk or Network operation finishes from a background thread you callback to the UI thread to update it immediately.
There`re several tools for that and I'll list a few, point the one I like the best, but let you do some research on each one
Handler: That's basic java way
AsyncTask: Nice framework but doesn't handle screen rotation
Loader: That's my preferred way
I think your approach is a bit complicated. Consider you example
So, for instance, I may want to have a UI component update after a call is made
to a database, and would want that to happen every minute.
I think I will do it this way.
Create a AsyncTask which will update the UI component.
Create a thread which will execute a new AsyncTask and sleep one minute in a while loop.
Start the thread in step 3. at some point.
Interrupt the thread if you don't want the component to be updated.
Example of step 2
while (true) {
try {
new updateTask.execute();
Thread.sleep(60000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
Android is a mobile platform and in all honesty each time you want something run, you better consider it separately. You may be killing the battery or using resources for no reason. I begrudge Zedge app everyday for running for no reason in the background at all times. Especially since on CyanogenMod kernel puts WiFi to sleep, while apparently it is currently on.
I am guessing this is more of an exercise and for running tasks at a specified intervals. One-offs, most universal can be done via AlarmManager class. But this may not be the best solution.
For some system wide events there is the BroadcastReceiver class.
While addressing
So, for instance, I may want to have a UI component update after a call is made to a database, and would want that to happen every minute.
Easier done via an Interface really.

Combining Handler and AsyncTask in Android - Obvious Flaws?

I have a simple Android app which uses AsyncTasks for I/O. A frequent pattern:
User clicks a button
In response, an onClick handler instantiates and .execute()s an AsyncTask
Once the AsyncTask completes, the UI should be updated in some way
According to the documentation for AsyncTask, the correct way to accomplish the UI updates is to override onPostExecute in the AsyncTask class - this will be invoked back on the UI thread after execution and thus can touch the widgets, etc.
However, it seems wrong to me that onPostExecute should have any sort of hard reference to a UI element. I would prefer to keep my I/O tasks and UI code separate. Instead, this seems the obvious situation where I should pass an opaque callback to the AsyncTask - the callback retains a reference to the UI elements and thus we maintain isolation and reusability in the code. A classic delegate pattern (or perhaps listener, event, etc, many options here).
As an example, the code below seems wrong to me:
class QueryJobsDBTask extends AsyncTask<Void, Void, ArrayList<ContentValues>> {
#Override
protected void onPostExecute(ArrayList<ContentValues> freshJobsData) {
someList.clear();
someList.addAll(freshJobsData);
// BUG why does my DB query class hold UI references?
someAdapter.notifyDataSetChanged();
}
After some research, it looks like the Handler class is the most straightforward and lightweight way to accomplish a delegate pattern here. I can write reusable AsyncTasks for I/O and specify contextual UI update callbacks on a per-instance basis via Handler instances. So I have implemented this new Handler-enabled base class
public abstract class HandlerAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
private Handler preExecuteHandler, postExecuteHandler;
public void setPreExecuteHandler(Handler preExecuteHandler) {
this.preExecuteHandler = preExecuteHandler;
}
public void setPostExecuteHandler(Handler postExecuteHandler) {
this.postExecuteHandler = postExecuteHandler;
}
#Override
protected void onPreExecute() {
if (preExecuteHandler != null) {
preExecuteHandler.sendMessage(Message.obtain());
}
}
#Override
protected void onPostExecute(Result result) {
if (postExecuteHandler != null) {
Message msg = Message.obtain();
msg.obj = result;
postExecuteHandler.sendMessage(msg);
}
}
}
And voila, all of my I/O tasks are now properly partitioned from the UI - and I can still specify simple UI update callbacks when needed via Handler instances. This seems straightforward, flexible, and superior to me ... so of course I wonder what I'm missing.
How is the current framework solution superior? Is there some major pitfall to this approach? To my knowledge the topology of code execution and threads is the exact same at runtime, just code coupling is looser (and a few extra frames on the stack).
This is an elegant solution for segregating UI/Background tasks in small projects, although passing Runnables is even more elegant. Keep in mind that the AsyncTask is a wrapper around Thread/Handler, so you're doubling up on the thread-messaging that's already going on behind the scenes. The flaw here is that if you design the AsyncTasks to be reusable, you'll need to make sure that the IO you're running are all thread-safe, as there's no communication between the various AsyncTasks as to who is active or accessing which resources. An IntentService might be more appropriate if you need to queue background tasks rather than just fire them.
It's not so much a matter of superiority as purpose & use-case. AsyncTasks are usually written as private classes (or declared anonymously inline) within Activities, and as such inherit the Activity's references to various UI elements that need updating anyway.
If an AsyncTask is of sufficient size and/or complexity that it should be pulled out into its own class, and can be re-used by other classes, than using Handlers for better decoupling is a great idea. It's just that it's often not necessary, as the AsyncTask is accomplishing something specific to the Activity in which it was defined, and for simple ones, the corresponding handler code could even be larger than the entire AsyncTask itself.

How do you have the code pause for a couple of seconds in android?

Basically I need a pause (based on just a few seconds) to be put into one action so that the user can see what happens before the next action is taken. So for blackjack, when it's the dealer's turn and he decides to hit, he hits, a card is added, and then he decides what to do next. So before he decides on what to do next, I want the code to pause so it can be "seen" as to what the dealer is doing this way the dealer doesn't complete his actions in less than a second and the player only sees the results.
Thanks in advance!
I should note I have tried using wait(insert number here); but i am told by eclipse that it causes a stack interception error or something of the sort and throws an exception, thus doing nothing : (
Well this is interesting, (the way I've programed the things is "interesting" to say the least) I did the Thread.sleep(5000) and threw it under a try catch, it does sleep for 5 seconds and then continues doing the code. However my updates to views don't show until after I press a button(Is really hating event driven programming).
Learning to think in terms of events is indeed the key here. You can do it. :)
The first rule is: never stall the UI thread. The UI thread is responsible for keeping your app feeling responsive. Any work you do there should not block; do what you need to do and return as quickly as possible. Definitely avoid doing I/O on the UI thread. (There are some places where you can't really help it due to lifecycle requirements, for example saving app state in onPause.) If you ever call Thread.sleep on the UI thread you are doing it wrong.
Android enforces this with the "Application not responding" (or "ANR") error that the user sees. Whenever you see this in an Android app it means the developer did something that caused the UI thread to stall for too long. If the device is really bogged down for some reason this error might not actually be the app developer's fault, but usually it means the app is doing something wrong.
You can use this model to your advantage by posting your own events. This gives you an easy way to tell your app, "do this later." In Android the key to posting your own events is in the Handler class. The method postDelayed lets you schedule a Runnable that will be executed after a certain number of milliseconds.
If you have an Activity that looks something like this:
public class MyActivity extends Activity {
private Handler mHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler.postDelayed(new Runnable() {
public void run() {
doStuff();
}
}, 5000);
}
private void doStuff() {
Toast.makeText(this, "Delayed Toast!", Toast.LENGTH_SHORT).show();
}
}
Then 5 seconds after the activity is created you will see the toast created in doStuff.
If you're writing a custom View it's even easier. Views have their own postDelayed method that will get everything posted to the correct Handler and you don't need to create your own.
The second rule is: Views should only be modified on the UI thread. Those exceptions you're getting and ignoring mean something went wrong and if you ignore them your app will probably start misbehaving in interesting ways. If your app does most of its work in other threads you can post events directly to the view you want to modify so that the modifications will run correctly.
If you have a reference to your Activity from that part of your code you can also use Activity#runOnUIThread, which does exactly what the name implies. You might prefer this approach if posting to a single view doesn't really make sense in context.
As for updates to views not appearing until you hit a button, what kind of views are these? Are they custom views that are drawing these updates? If so, are you remembering to call invalidate after data changes to trigger the redraw? Views only redraw themselves after they have been invalidated.

Categories

Resources