ListFragment with a Loader and an EndlessCursorAdapter - Nightmare - android

I will start with what I am trying to accomplish.
I have a ListFragment, with LoaderCallbacks associated to retrieve data from a DB. The data is downloaded using an AsyncTask, and inserted into the DB. When the user gets to the bottom of the list, using the CWAC-Endless widget the AsyncTask is kicked off and downloads more data.
I am facing a couple of issues here, and I have tried to sort this out over many a night, and I have decided to come here to ask for help.
The first issue is configChanges. When the user rotates the device, the Activity is destroyed, and then recreates all of the Fragments. I know I can use setRetainInstance to true, but this does not help as the AsyncTask is still running when the Activity gets torn down!
The second issue is to do with the Loader. If data is downloaded, and the AsyncTask completes fine, then the items appears in the List fine. Lets say there are 20 items in the DB. When the user rotates the device, and the Fragment is recreated, the Loader needs to be associated again. When this happens, the data is not loaded into the list straight away, and instead the AsyncTask for the download is kicked off because the CWAC-Endless adapter thinks its at the last item in the list!
Both of these issues have exhausted me. I need a fresh look on this, as im getting no where.
Any suggestions will do, and I can post up source code if needed.
EDIT
Ok here are a few more details to help with some suggestions.
I am downloading data from the internet, which will only return a set number of items at a time. I then have to request more data when I want it (pagination).
I decided to use a database, as the new Loader functionality makes it so simple to make sure the data is loaded efficiently and consistant, without any threading issues.
If it would make sense to ditch the Loader approach, and use a standard Adapter to render the data, I am more than happy to ditch this approach and use that. I just wanted to see if someone could offer an insight into why this solution is so difficult.
Thanks,
Adam

When the user gets to the bottom of the list, using the CWAC-Endless widget the AsyncTask is kicked off and downloads more data.
FWIW, I have not tried EndlessAdapter with this combination of stuff (cursors, loaders, and AsyncTask). In particular, quoting the docs:
Note that this has been tested with ArrayAdapter extensively but may not work with other adapter types
I am not even quite certain what the use case would be for an EndlessAdapter applied to a local database. If you have a crazy long list (e.g., thousands of rows), the answer isn't "load it progressively" but "provide a different UX to avoid the long list". For shorter lists, just load the whole thing and be done with it. EndlessAdapter is for cases where the loading is expensive (e.g., Internet access).
That being said, I will add "play with EndlessAdapter and Loader" to my to-do list.
I know I can use setRetainInstance to true, but this does not help as the AsyncTask is still running when the Activity gets torn down!
So? onPostExecute() will not be invoked until the new activity has gotten through onCreate(). Moreover, in a fragment-based model, your task should be talking to the fragment, and if that fragment is retained via setRetainInstance(true), it's the same fragment instance in both the old and the new activity.
When this happens, the data is not loaded into the list straight away
It should be loaded in fairly quickly, though asynchronously. Moreover, I don't see why this is any different from when the activity is created in the first place.
and instead the AsyncTask for the download is kicked off because the CWAC-Endless adapter thinks its at the last item in the list
You should not be creating the EndlessAdapter until after you have data.

Related

Design of RecyclerView program in Android

I'm working on my first app that uses RecyclerView and while I am making great progress, I strongly suspect that my design needs some changes.
I've asked in several places over recent weeks for complete examples of a RecyclerView that read, update, insert and delete and have come up empty so I'm guessing my way along based on various passing remarks. First, let me explain concisely how I have designed this app. I imagine this will make my mistakes self-evident.
My RecyclerView is based around sales in the small company where I work. Each sale consists of a client code, an order number, an order date, a number of tickets sold, and a seller name. My design uses a listener to react to a FAB (FloatingActionBar) and, if pressed, goes to an Add activity that prompts the user for the information needed to create a new sale. No information is passed to this activity because none is needed. The Add activity validates the data and, when the user presses an Add button, that new data is passed back to my main activity. The main activity inserts a row into a remote MySQL table via a PHP script invoked from an AsyncTask in my app. Upon returning from the database, if the insert worked (it could fail if it was a duplicate record), I add the information to my ArrayList and notifyItemInserted in the onPostExecute() method. That all seems to work okay although I always get two copies of the new sale in the RecyclerView. The next time the screen refreshes though - when I do another Add, Update or Remove - the duplicate sale disappears.
Each item in my RecyclerView contains all of the information for one sale, plus two clickable images, one intended for Editing (it's a blue pencil), and one intended for Remove (it's a red garbage can). If the user wants to edit that sale, he clicks on the blue pencil; if he wants to delete it, he clicks on the red garbage can. I use listeners to determine which image got clicked.
When someone clicks on the Edit graphic, I launch an Edit activity, passing the information from the existing sale to that activity. The activity displays the information and the user can modify any of the five fields. All changes are validated and, if all the validations are successful, the changed information is passed back to the Main activity which updates the existing row in the database in the doInBackground() method of an AsyncTask. If the update is successful, I try to change the information in my ArrayList and notifyItemChanged in onPostExecute(). That always fails.
When someone clicks on the Remove graphic, I launch a Remove activity, passing the information from the existing sale to that activity. The activity displays the information and the user can only press Remove to confirm that the sale should be removed or press Cancel to abort the removal. If Remove was pressed, the information from the sale is passed back to MainActivity which removes the existing row in the database in the doInBackground() method of an AsyncTask. If the database delete is successful, I try to delete the item from the ArrayList and then notifyItemRemoved in onPostExecute(). That always fails.
As you can see, the Edit and Remove both fail every time. The reason is that there is an indexOutOfBoundsException with respect to the position used in attempting to modify or remove the item from the ArrayList.
Now that I've set out an understanding of the situation, I can ask my specific questions:
Is it reasonable/appropriate to use Activities to do the work I've described for Add, Edit and Remove processes in the first place or would I be better making them fragments?
Is it appropriate to be doing the database activity and the adjustment of the ArrayList in the main activity or should I be letting the Add, Edit, and Remove activities (or fragments if that's better) do the work?
I think my fundamental problem is that my code can't "see" everything it needs to see at the point where it is doing its work. I think I need to redesign things a bit to make the app work better.
RecyclerView seems to be pretty fundamental so I want to make sure I write everything correctly. In fact, given the absence of good examples that show a RecyclerView that has all the functionality I'm describing, I'd like to write a tutorial or make a video series showing my finished app with all the critical parts explained so that others can learn from it.

How to correctly manage ASynkTaks in Tabs which have the same fragment class

I have a FragmentActivity with 7 tabs, and all of them refers to the same fragment, the only difference is a parameter, that makes them to load throught an ASyncTask the data to show from a PHP that returns a JSON. My problem is that when I swipe from one tab to another, if the task from the first tab is still loading, it loads in the new tab, or crash, or doesnt do anything. However, the activity load two tabs, so the task is launched twice and is the same problem. Any idea?
While AsyncTasks are wonderful to have, they are intended to be procedures that are independent of any UI (e.g. saving information). For the longest time I was in the same boat and used AsyncTasks for work that would end up changing the UI (since hey, they have an onPost method).
What you should be using for any work that will affect the UI is called a Loader which will pay attention to the UI state of the Fragment. In your case the AsyncTask is probably attempting to access a UI element that no longer exists (View Pagers only keep the previous, current, and next views in memory). The Loader will pay attention to this and not attempt to change the UI.
There are plenty of examples out on the web, but in short you will need to create (extend) a Loader for each of your AsyncTasks (I recommend AsyncTaskLoader, if you do pay attention to forceLoad) and add the callbacks (LoaderManager.LoaderCallbacks) to your Fragment. Then when you are ready to load call getLoaderManager().restartLoader(LOADER_ID, bundle_args, loader_callback);
Keep a reference of your AsyncTask. I assume you have a callback which let's you know when the tabs have changed. When you get notified that tabs have changed you can check if your AsyncTask is null or not finished yet, if it isn't you call it's cancel() method.
if(asyncTask!=null && asyncTask.getStatus()!=AsyncTask.Status.FINISHED) {
asyncTask.cancel(false);
asyncTask = null;
}

How do I get adapter to update views after data changes when using fragments?

I have a list of orders in one fragment. In a second fragment, I display the detail of the order, and I use a third fragment to display the buttons that change the status of the order.
In the list, each order is displayed with a background color that indicates its status, for example green for a completed delivery.
When in landscape mode, both the detail and list are shown. In portrait mode I use two separate activities.
This all works fine, up until I change the status of an order. I can't find a way to get the list to update.
As I understand it, what needs to happen is the adapter needs to have its notifyDateChanged() method called. I've tried calling it directly from the method that processes the button click, I've tried an asynctask, and I've tried a handler. My debug methods show that the call is happening, but the list doesn't get updated.
It's possible I'm doing something completely bone-headed, but I've double and triple checked things. I suspect there is some key element I don't understand. I hope someone else does and will tell me what I'm missing.
I had some code posted, but it was clearly wrong. Not sure what code to post, since I think this is more a conceptual than coding issue.
If you want to update a view, try to use :
Thread + Handler
AsyncTask
What I discovered, through the helpful comments of other posters, is that my problem wasn't that the adapter wasn't getting the notifyDataChanged() call on the correct thread. I put in debug code that proved that. The problem was that the background of the listview item was based on a change in the underlying data itself, so the real answer was to get the Adapter to refresh the Cursor. I made some changes. I modified the AsyncTask I was using to notify the adapter. Now the AsyncTask gets a new cursor and calls adapter.changeCursor(cursor) with the result. The AsyncTask is called from the MainActivity (which hosts both the list and detail fragments) when the status is changed. It's also called in the onResume() portion of the ListFragment code, so the list will be updated properly when coming back from the detail fragment. Works great.

Update ListView of previous activity in background

I have question regarding my previous ListView activity.
I have my ListView which is continue updating using socket connection, now when I click on any of the list row i can go to my next screen now when i come back to my previous ListView screen my ListView is start updating again but i want to keep updating my ListView in a background when i am on my nextscreen.
Something like i want to keep my previous screen alive when i am on my nextscreen.
Sounds to me like your the code you are using to load the data for your ListView is tied to your Activity.
For instance you have an AsyncTask or Thread in your Activity that cointains your ListView and you use it to download data, or do whatever is needed to get the data to populate the list. I also assume you start it in one of the Activity lifecycle methods e.g. onCreate().
If that is the case then you should consider seperating the code used for getting the data for the list from your activity code.
One way to do this is to use a Service which will be able to run independantly of the rest of your application and do all the heavy lifting involed with fetching the data for your list. You can communicate with the service from anywhere in your application. The Service can save the data to a database. Then all you have to do in your Activity is query the database and populate the adapter to get the latest data without much delay.
You can read more about services here:
http://developer.android.com/reference/android/app/Service.html
You could (and probably should) do what Paul suggested or you could change to way you switch your screens by using Fragments or a ViewFlipper. This way you still run your AsyncTask or Thread while doing something else on a different page.

When to load database data into an adapter

I have a simple personal application I'm working on that queries some records in an SQL Database and populates an adapter for a listview and is basically working fine... but I've began to wonder if I'm doing certain things at the right point of the framework.
Currently I'm loading everything up during onCreate(). In theory, I could be loading up quite a bit of data, so I wanted to possibly throw up a ProgressDialog while the information is being added to the adapter, but I ran into some odd threading issues with the Cursor. Ultimately, I launched a Progress Dialog near the end of onCreate(), followed by sleeping on another thread and calling a method to load my data with runOnUiThread() following the short sleep time, having the end of that method dismiss the Progress Dialog.
This works, but it's brought me to whether or not I should be loading database data during onCreate... or whether it should be moved to onStart() or onResume(), adding in code to clear the close and open the database, clear and repopulate the adapter as necessary as other Activity's are started and finished. Or would all that be unnecessary and I should just keep the adapter populated during onCreate()?
Reto Meier's suggestion to use an Application may suit your needs. Take a look at Activity restart on rotation Android
Move it to onResume, as if you stop the activity you can destroy the adapter and fill it back when to resume the activity.
It helps to save memory and also helps to update the adapter if data has changed.

Categories

Resources