Firebase multiple query bottleneck - android

I have ViewPager which holds 2 different fragments. In both of the fragments, I'm trying to query firebase database with addValueEventListener. Here is the reference -
public static DatabaseReference getDatabase() {
if (mDatabase == null) {
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
mDatabase = FirebaseDatabase.getInstance().getReference();
}
return mDatabase;
}
In first Fragment there is something like 500+ items (takes about 8 seconds to load up), and in the second there is like 20-30. The problem is that second fragment always wait for 1st one to finish and just then queries the second one. Is there a way I can separate or do something about it? I really need the second Fragment to load up faster. Thanks in advance.

If I had to solve this problem, I would think the overall situation a bit differently like #Mark suggested in the comments. I would have implemented a paginated query on Firebase so that I would not have to get all 500+ rows at a time.
Besides that, I would like to keep an offline storage of the data which are fetched from Firebase. So that, each time the application loads, the data is being populated in both fragments which were fetched earlier. I would run the query in the background asynchronously and the user would not have to wait for the query to finish to see the data in both fragments. Once the query is finished loading, I would have updated the views accordingly with the newly fetched data along with updating the local cache which will be needed to serve the data next time you launch the application.
If you are not interested in loading data in the background and showing the initial items from the cache and stick to your current implementation, then I would like to suggest you, manage all these things from the activity that contains the ViewPager. Just get the queries executed one after one, or asynchronously and get the results published to the fragments once the results are received from Firebase using BroadcastReceiver or something like that.
Hope that helps!

Related

After DataSource.Invalidate() new PagedList has only one page

I have a list with pagination which I implemented using Paging library. Items on this list can be modified (changed/deleted).
According to official documentation, I'm first changing in-memory list cache from which my DataSource gets pages and after that calling datasource.invalidate() in order to create new pair PagedList/DataSource:
If you have more granular update signals, such as a network API signaling an update to a single item in the list, it's recommended to load data from network into memory. Then present that data to the PagedList via a DataSource that wraps an in-memory snapshot. Each time the in-memory copy changes, invalidate the previous DataSource, and a new one wrapping the new state of the snapshot can be created.
It works and looks WELL if user modifies items on first page.
However, if user is on page two or further during datasource.invalidate() he will be thrown at the end of the first page.
Debugging shows this happens because new PagedList has only first page when it's submitted to PagedListAdapter.submitList. Adapter compares old and new lists and removes all items not from first page. It happens always but not visible for user if he is on the first page.
So to me, it looks like new pair PagedList/DataSource have no idea about number of pages which fetched previous pair and datasource.invalidate() doesn't fit for the situation in docs. Behavior that I see acceptable for cases then user updates all list (like swipe-to-refresh) but not
an update to a single item in the list
Has anybody faced such issue or somehow archived things I want? Maybe I'm missing some trick which helps me to get new PagedList already with all pages.
For clarification: library version 2.1.0. Custom PageKeyedDataSource based on in-memory cache and remote servise (No Room)
I want to share my research in case anybody is interested:
Issue ("lack of feature") is known, at least I've found the couple related discussions on official tracker one two
If you are using PositionalDataSource or ItemKeyedDataSource you should dig into the direction of requestedStartPosition/requestedInitialKey from initial params as this answer says. I didn't have much time to build the whole solution but those params are indeed different for initial load after invalidation
About my case : PageKeyedDataSource. Here you can read that there is no similar to requestedInitialKey params in this type of data source. Still, I found a solution which fits me, very simple, although, feels like a dirty trick:
When loadInitial() is called after invalidate() in-memory cache returns all already loaded pages instead of just first one.
At first I was worry that something will break if, for example, requestedLoadSize is 5 but the result is 50 items list but turns out it's just a hint and it can be ignored. Just don't forget to pass nextPageKey which corresponds to the last cached page and not the first one.
Hope it will help
With observable method you will only get first page list items....if you want to edit other items you can get that list by adapter.currentlist method.
Example:
private fun list():MutableList<String>{
val list = mutableListOf<String>()
for (value in videosAdapter.currentList.orEmpty()) {
val abc = value.snippet.resourceId.videoId
list.add(abc)
}
return list
}

How to properly use LiveData with RecycleView

I have a project that loads a list from the server. This data will eventually be stored into a database, but for now is stored in memory in a MutableLiveData. A RecyclerView's adapter is watching the data and displaying it. So far everything is working as expected, using a FAB the user can post a new entry which will go at the top of the list, on success I get a 200 and here's the main part where I'm getting lost...
When I want to add a single item to a list stored in a LiveData, the observer is unaware of the delta. I currently make a call to RecyclerView.Adapter.notifyDataSetChanged(), though the ideal in my case would be to call notifyItemInserted(0) or in other cases I can see various other notifications. What the best way to do this? The lifecycle architecture library appears to be very well thought of, I assume I'm missing something simple. I can't imagine having to manually perform a diff between the lists?

Updated RealmResults with time-based query

I'm starting to approach this wonderful world of Realm. I'm very happy of the results I'm getting and now I have one question to submit.
In my android app I've got a Fragment that displays data retrieved from Realm. The query condition is that the time this data refers to is in between the beginning and the end of today.
RealmResults<Appointment> results = realm
.where(MyObject.class)
.between("begin", rangeBegin, rangeEnd)
.between("end", rangeBegin, rangeEnd)
.findAllSorted("begin", Sort.ASCENDING);
This query is executed in the onStart() method helping me to exploit the live-update feature, which indeed works very well.
I've also added listeners for changes in order to optimize UI updates.
Now the question is: how does this live-update behave if the time conditions change? (Imagine I keep the app opened for more than one day without touching it or simply I keep the app opened for minutes around midnight)
From what I've seen it seems to do the same query done the very first time onStart() was executed.
Is there a way to have also live-updating query or should I re-run that query somewhere else outside onStart()?
Thank you in advance
Now the question is: how does this live-update behave if the time conditions change? (Imagine I keep the app opened for more than one day without touching it or simply I keep the app opened for minutes around midnight)
The query is pretty constant after you've set it up, so you'd need to execute a new query with different parameters for rangeStart and rangeEnd, and replace your other results.
This query is executed in the onStart() method helping me to exploit the live-update feature, which indeed works very well. I've also added listeners for changes in order to optimize UI updates.
Personally I'd advise to put the query in onCreateView() instead, and the Realm lifecycle management to onCreateView() and onDestroyView().
Also, you can avoid manually assigning RealmChangeListeners for displaying lists if you use RealmRecyclerViewAdapter (adapters 1.3.0 works with realm 1.2.0).
If you use RealmRecyclerViewAdapter, then just call adapter.updateData(newResults); and it'll update the view as needed.

Loading a BIG SQLiteDatabase in a ListActivity

I'm working on an Android project I need to finish very fast.
One of the app's features is loading a SQLite database content and listing it in a ListView inside a ListActivity.
The database contains a few tables, among which 2 are very large.
Each item in the database has many columns, out of which I need to display at least 2 (Name, Price), although preferably is 3.
This might seem a pretty easy task, as all I need to do in this part of the app is read a database and list it. I did this without any problems, testing the app versus a small sample database.
In my FIRST version, I used a Cursor to get the query, then an ArrayAdapter as the list's adapter, and after the query I simply loop the cursor from start to end, and for each position I add the Cursor's content to the adapter.
The onItemClickListener queries the database again versus other parameters (basically I open categories) so it clears the adapter, then loops the Cursor and adds its content to the adapter all over again.
The app worked like a charm, but when I used a real-life, big database (>300MB) I suddenly got my app taking very long to display the contents, and sometimes even blocking.
So I did some research and started using a SimpleCursorAdapter that automatically links the contents of a Cursor to the ListView using the usual parameters (String[] from, int[] to etc., where I used android.R.layout.simple_list_item_2 and android.R.id.text1 and text2).
Problem is, is doesn't change much the time to load.
I've came across some suggested solutions on different web sites and tutorials, most of them using, in one way or another, the AsyncTask class. I tried implementing this manually myself but it's hard to keep track of multiple threads and I failed.
Tutorials keep telling how to do this with content providers, but I found nothing clear bout my specific situation: very big SQLite database -> read to ListView.
Now my head is filled in with notions like LoaderManager, LoaderAdapter etc, all mixed up and confused in my head.
Can anybody please provide me a complete, nice, clean solution to do this "simple" task?
Again: I want to read a BIG SQLiteDatabase and display it in a ListView. I want the app NOT to block.
I need a class that has a member function that takes as parameter a query and the ListActivity's context and takes itself care of displaying the result of the query in the view.
Please don't provide me abstract answers. I'm running out of time and I'm very confused right now and I need a clean complete solution.
You're my only hope.
If you query such large database it will take tym, you need to find a smart way,
Like limit you database query to get first 10 or 30 items and then maintain,once last item is reached query rest 30 items and bind them
Refer this tutorial, it will teach you how to add data dynamically in a list view
http://p-xr.com/android-tutorial-dynamicaly-load-more-items-to-the-listview-never-ending-list/
The above list has expired chk this
http://mobile.dzone.com/news/android-tutorial-dynamicaly
If you query large database it will take time to fetch data and show it on List View. So it is better to populate data at run time. You can use Lazy Adapter concept to load data . This link1 may be useful for You.
Thanks
you can also use :
public class TodosOverviewActivity extends ListActivity implements
LoaderManager.LoaderCallbacks<Cursor>
check this link for more details.

Downloading data in separate thread before initializing a ListView

I'm having issues with multithreading in my application. I know there are many posts on Threads/AsyncTasks/etc, but none seem to address my specific problem.
Basically, I get a query string in my search Activity, then send it to my results Activity, where the string is used as a SQL query, the results are returned as an array of JSON objects, then I display these objects in a ListView (which is part of the results Activity). All of my SQL connection and retrieval is done in a separate class that I call at the start of the results Activity.
MySQLRetrieve data = new MySQLRetrieve();
ArrayList<Tile> tiles = data.getResults(nameValuePairs, isLocationSearch);
The above code is how I get the SQL response and convert into an ArrayList, which I then use the populate my ListView with. getResults() takes care of all of this.
I already have separate threads working to download images into the ListView, but what I can't get to work is getting the SQL query and result to run in it's own Thread. What I want to achieve is this:
User enters search query in search Activity.
Intent is sent to results Activity, and it starts immediately.
ProgressDialog (just the animated spinner thing, not a loading bar) displays while the SQL query is taking place.
ListView populates with objects from the JSON array, lazy loading images as they come.
I have steps 1,2, and 4 working well, but 3 is the problem. I've looked up AsyncTasks, which seem to be the answer, but I just can't get them to work. Does anyone have a solution to this problem? I need to do this, so when starting the results Activity, the UI changes immediately to the results Activity and doesn't have to wait until the SQL response is returned.
And yes, I've already read the painless-threading post.
Thank you.
I would recommend against creating that ArrayList<Tile> to reduce memory consumption (and code size) and instead directly bind the SQLite Cursor to the ListView using a CursorAdapter.
That alone might just increase the performance enough that you don't need to do any async loading.
If you still want async loading, check out the LoaderManager framework (available since Android 3.0/ API level 11, with Android support package down to 1.6/4) which will automagically do asynchronous loading of your Cursor -- either using the built-in CursorLoader (if you happen to have a ContentProvider), or the SimpleCursorLoader created by a fellow SO user (if you don't).

Categories

Resources