I got two kind of ArrayAdapter in my Application which load both (more or less) dynamically data.
The first one is bound to a thread which fires each second to refresh the data.
The second one refresh the data with a onClick action.
Now when i run my application i can see in with dumpsys meminfo that the View counter
is constantly increasing (while on the fragment with the thread).
Obviously this causes a huge memory leak after a few seconds/minutes. The same
does happen for the onClick one.
My assumption is that those "old" data are still stored inside those view and won't
get freed. Is there a way to remove each old view?
My adapter is located here; https://github.com/Blechd0se/android_packages_apps_AeroControl/blob/master/AeroControl/src/com/aero/control/adapter/AeroAdapter.java
The main method which use the adapter is here (createList()); https://github.com/Blechd0se/android_packages_apps_AeroControl/blob/master/AeroControl/src/com/aero/control/fragments/AeroFragment.java
Or is there another way to just free up those unneeded Views?
EDIT:
I found a solution if anybody is interested;
As described i need to use the notifyDataSetChanged()-Method whenever i change the data.
This is only possible if working with List. In my example its List<adapterInit> which alows
me to use the mentioned method as well as clear(). Also setAdapter on each second
is a very bad idea. Now i change only the data and the result is as expected. The View-Count
went down from ~15.000 to 100-200.
A patch can be found on my github.
It appears (from the code below) that you are refreshing the data by creating a new adapter rather than telling the adapter that the data has changed. Try passing the data to the adapter and then calling notifyDataSetChanged();
This will keep you from spamming a dozen new views every second.
Second, you may want to see if the garbage collector is cleaning these up at some point. Since you were creating multiple views a second, you were probably creating views faster than the gc could reasonably keep up. It might have paused your program if it reached a critical point (not sure). See if you will actually crash due to out-of-memory to see if it is actually a memory leak.
private RefreshThread mRefreshThread = new RefreshThread();
private Handler mRefreshHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (msg.what >= 1) {
if (isVisible() && mVisible) {
createList();
mVisible = true;
} else {
// Do nothing
}
}
}
};
public void createList() {
// Default Overview Menu
adapterInit overview_data[] = new adapterInit[]
{
// First Value (0) is for loadable images.
new adapterInit(getString(R.string.kernel_version), AeroActivity.shell.getKernel()),
new adapterInit(getString(R.string.current_governor), AeroActivity.shell.getInfo(GOV_FILE)),
new adapterInit(getString(R.string.current_io_governor), AeroActivity.shell.getInfo(GOV_IO_FILE)),
new adapterInit(getString(R.string.current_cpu_speed), getFreqPerCore()),
new adapterInit(getString(R.string.current_gpu_speed), AeroActivity.shell.toMHz((AeroActivity.shell.getInfo(gpu_file).substring(0, AeroActivity.shell.getInfo(gpu_file).length() - 3)))),
new adapterInit(getString(R.string.available_memory), AeroActivity.shell.getMemory(FILENAME_PROC_MEMINFO))
};
listView1 = (ListView) root.findViewById(R.id.listView1);
adapter = new AeroAdapter(getActivity(),
R.layout.overviewlist_item, overview_data);
listView1.setAdapter(adapter);
}
Related
I am programming an app for Android. I uploaded it to GitHub: app
I have a ViewPager (MainActivity.java) controlling two Fragments. On the first Fragment (FirstFragment.java) you can add People (People.java) which appears on the RecyclerView (also on FirstFragment.java). When you click one of the list items on the RecyclerView its details (name and id) appear on the second fragment (SecondFragment.java). The SecondFragment.java also contains a button you can delete the selected People with.
To store the People objects I used a List of People and managed it with the methods in PeopleLab.java. The program was working fine: I could add/remove People objects to the list and it appeared on the RecyclerView fine.
After that, I decided to replace the List with a database. It only meant creating the database (the 3 files in database folder) and editing the already existing and two new methods in PeopleLab.java. The other files remained untouched.
The database is working as expected (checked it with sqlite3), I can add/remove People like before and the queries work. My only problem is that the changes don't appear on the RecyclerView. But if I close and reopen the app, the changes appear.
It's like the RecyclerView doesn't care about the database in runtime, only do when the app starts (or closes, not sure).
Do you have any idea what could cause the problem? My only guess is I miss something about how Android apps handle databases.
P.S.: sorry for my English.
You do call notifyDataSetChanged() on the adapter but you don't provide any new data for that adapter.
In your FirstFragment :
private void updateUI() {
PeopleLab peopleLab = PeopleLab.get(getActivity());
List<People> peoples = peopleLab.getPeople();
if(mAdapter == null) {
mAdapter = new PeopleAdapter(peoples);
mRecyclerView.setAdapter(mAdapter);
} else {
// You actually have to change your dataset
mAdapter.changeDataSet(peoples);
}
}
And in your Adapter :
public void changeDataSet(List<People> people) {
this.mPeoples = people;
notifyDataSetChanged();
}
This some brutal way to do it though.
It would be better to notify your adapter on insertion / removal calling notifyItemInserted(int itemPosition) or notifyItemRemoved(int itemPosition). (And refreshing your dataset, by the way)
It will not work automatically.You can either use to notify the adapter the underlying data has been changed so that adapter can fetch and reload the data.It can be done using adapter.notifyDataSetChanged()
Or use can use CursorLoader to achieve the same
I'm struggling with this issue since some days ago and I'm not able to find a solution.
I have a listener which receives market data (orders at bid and ask). If market is quiet (pre-market or post-market (low volatility)) everything works fine. But once the market is open the listener receives events too fast. So after a couple of minutes my app freezes.
Right now the listener only assigns the received data to a var.
orderBookBid.getBuyOrders().addListener(new ObservableListModelListener<Order>() {
#Override
public void modelChanged(final Change<? extends Order> change) {
System.out.println("bid event");
bidChange = change.getSource();
}
});
The program only freezes when uses real data. When market is closed and uses test data from a local file works fine.
Is there any way to set the maximum number of events per second? Or any way to ignore events for a short time period?
Any idea on how can I handle this would be very appreciated.
Thanks.
You could put a load balancer in your application, that way it will create a queue and will not freeze the application.
If you want to let go some events, in the logic of your listener, you should have something that check if it's been X time since the last time you managed the event.
private long timeSinceLastEventManaged = 0;
private final static long MINIMUM_TIME = 2000; //2 seconds
In your listener
public void modelChanged(final Change<? extends Order> change) {
long timeSystem = System.currentTimeMillis();
if(timeSystem - timeSinceLastEventManaged > MINIMUM_TIME){
//do your stuff
timeSinceLastEventManaged = timeSystem;
}
}
First of all you should get rid of the println as it is really slow
The rest depends on what you are doing. Right now it seems that you are just getting the value and writing it to a variable. You will only see the latest change that way and if that is what you want the solution #TyMarc suggested will work fine.
If what you showed us is just an example and you really need every change things get a bit more complicated. Your modelChanged method should be changed to add the current value to a queue (e.g a LinkedList or Stack).
public void modelChanged(final Change<? extends Order> change)
{
syncronized(syncObject)
{
//add to your preffered queue
syncObject.notifyAll()
}
}
This frees your listener from the real work and it can keep collecting data.
I added a syncronized as someone has to do the work. For this you can use a Thread that runs something like this:
Order current;
while(keeprunning)
{
syncronized(syncObject)
{
if(queue.hasNext())
{
current = queue.getNext()
}
else
{
Thread.wait()
}
}
//do the real work here
}
Now someone else has the problem. Literally. If the Thread can't handle the inflow of data the queue will grow in size until you run out of memory or hit some other limit. But that's another story.
And yes, nothing of this will compile as I only wanted to show an example
I have a normal listview/listadapter and I am loading a large amount of data from the network using an AsyncTask. It takes around 5 seconds to completely download and parse all the data.
Rather than have the listview empty for 5 seconds while I am processing the data, I would like to update it as data is processed so the user can see and scroll through the list items as they are ready. It takes around 20-50ms to process each item and have it ready for display.
Can someone please give me a hand with some ideas on how I can accomplish this.
I tried this code to only refresh every 500ms. I call onNewItemReady() every 20 to 50ms from the AsyncTask when a new item has finished being processed:
public void onNewItemReady(Object item) {
mData.add(item);
long diff = SystemClock.elapsedRealtime()-mLastRefresh;
if (diff > 500) {
mAdapter.setData(mData);
mAdapter.notifyDataSetChanged();
mLastRefresh = SystemClock.elapsedRealtime();
}
}
but if i scroll the listview before all the data has loaded, I receive the following error:
> 05-04 19:22:59.638: E/AndroidRuntime(24349):
> java.lang.IllegalStateException: The content of the adapter has
> changed but ListView did not receive a notification. Make sure the
> content of your adapter is not modified from a background thread, but
> only from the UI thread. Make sure your adapter calls
> notifyDataSetChanged() when its content changes. [in
> ListView(16908298, class android.widget.ListView) with Adapter(class
> MyAdapter)]
Thanks!
EDIT: I am not refreshing the adapter from the background thread. onNewItemReady() is being called from onProgressPublished from the AsyncTask.
As logcat message says you are trying to refresh the listview (which is on UI thread) from the background thread (probably AsyncTask ), so all you need to do is after the completion of execution of doInbackground() of the AsyncTask, you should refresh the listview in onPostExecute() of asynctask.
To avoid user interaction while your downloading process is going on, you should show a progress dialog in onPreexcecute().
So your Asynctask should look like:
class YourAsyncTask extends AsyncTask{
ProgressDialog pd;
onPreExecute(){
// show progress dialog here..
}
doInBackground(){
// perform network operation here and collect collect data
}
onPostExecute(){
// dismiss dialog box.
// refresh listview
}
}
Note this is just a blueprint for your code, for correct syntax refer this
Alternative
Also as you are saying that you are loading large amount of data and it is taking almost 5 second ( this is lot of time ), it is a recommended to use Pull-to-refresh, as it will load data on demand. You can google it and lot of stuff is available there.
I was using an "endless adapter" and displaying an added "loading" view at the bottom of my listview when the user scrolled to the bottom. I took the code that I was using from the Google i/o 2013 app. Apparently this code was causing the problem. I have since removed it and replaced it with a much simpler footerview and the problem has gone away.
this is the code I was using in my adapter:
#Override
public int getCount() {
if (mData != null) {
return mData.size() + (
((mDataTask.isLoading()) // we are already loading
|| mDataTask.hasMoreResults() // or there are more results
|| mDataTask.isErrored()) // or there's an error
? 1 : 0);
}
return 0;
}
Instead of this code, I just add a footerview when i start loading, and i remove it when onPostExecute completes.
I've been trying to fix an error causing an intermittent ConcurrentModificationException. What's happening is that I have tons of geopoints being displayed with an ItemizedOverlay. When the map is moved, however, I'm trying to clear out all current overlay items (the geopoints) and replace them with new points appropriate to the new view window.
I therefore have a callback function that clears out the old overlays and replaces them with new ones. I think my bug stems from multiple threads trying to do this simultaneously. The relevant sections are below. I have a very limited understanding of how the overlays and such work on a low level, so I was wondering if anyone could confirm (or refute) that this could be causing issues.
//first clear out the old overlays
List<Overlay> mapOverlays = mapView.getOverlays();
mapOverlays.clear();
//create the new overlays, each initialized with appropriate Drawables
MenuOverlay lowOverlay = new MenuOverlay(this, lowRisk);//(all valid Drawables)
MenuOverlay medOverlay = new MenuOverlay(this, medRisk);
MenuOverlay highOverlay = new MenuOverlay(this, highRisk);
//populate the overlays
//add the new overlays into the list of overlays
mapOverlays.add(lowOverlay);
mapOverlays.add(medOverlay);
mapOverlays.add(highOverlay);
//make the map refresh; this operation has to be pushed to another thread
Runnable runnable = new Runnable() {
public void run() {
mapView.invalidate();
}
};
runOnUiThread(runnable);
I tried making this method synchronized, but the error still occurred. Could this arise from the new runnable being pushed to the UI thread before the previous runnable terminates maybe? I've seen mention that populate is a better way than invalidate, although I'm not entirely sure how they're different. Any ideas how to resolve this?
Modifying the set of overlays should always be done on the UI thread. The List that getOverlays() returns is owned by the MapView, and it can decide to view or manipulate the list at any time.
Since you're working with a lot of geopoints, it's likely that your background thread is clearing (or adding) overlays while the MapView is iterating over them on the UI thread. That would trigger a ConcurrentModificationException because the iterator is invalidated when its underlying set of overlays changes. Sometimes the changes are not immediately visible to the UI thread, so the crash is intermittent.
Setting up the overlays is usually the slow part of this type of workflow. To avoid the concurrent modification, you could set up your overlays in the background thread and then make all of your calls to clear() and add() inside of the Runnable. (Another option is to use an AsyncTask.)
For example:
// Slow setup steps
final MenuOverlay lowOverlay = new MenuOverlay(this, lowRisk);
final MenuOverlay medOverlay = new MenuOverlay(this, medRisk);
final MenuOverlay highOverlay = new MenuOverlay(this, highRisk);
Runnable runnable = new Runnable() {
public void run() {
// Anything that touches UI widgets (map, overlay set, views, etc.)
List<Overlay> mapOverlays = mapView.getOverlays();
mapOverlays.clear();
mapOverlays.add(lowOverlay);
mapOverlays.add(medOverlay);
mapOverlays.add(highOverlay);
mapView.invalidate();
}
};
runOnUiThread(runnable);
TLDR:
I'm setting myListView.setVisibility(View.GONE);, but it's not dissappearing until later... do I need to let it know somehow that I've changed it's visibility? Or do I need to also hide it's inner elements or something?
Description of Problem:
I have a normal news app. You see a list of articles for the "main" section, then you can click the options to select a new section.
When the user clicked, the section title changed, but the articles in the list would just sit there with "old" content until the new content is loaded, then it would flash to the new content.
This isn't ideal obviously. I'd like the list to disappear, show a loading animation, then, after the new data is retrieved (either from DB or online, then DB), it shows the new content.
I found this SO question which seemed like what I want, but...
I'm setting GONE immediately upon selection of the menu, then VISIBLE after it import the articles and loads the new ones... but it's not disappearing at all during that. I know the GONE code works, because if I remove my VISIBLE code, the articles never reappear.
Do I need to say "View.GONE", then tell it to update it's visibility or something?
My Code (MainActivity):
public static void sectionSelected()
{
String selectedText = sectionsSpinner.getSelectedItem().toString();
String[] selectedSection = Section.stringToSection(selectedText);
//check if it was already the current section
if(!Section.isEqual(Section.currentSection, selectedText))
{
//hides list of articles
articleEntryListView.setVisibility(View.GONE);
//sets new currentSection
Section.currentSection = selectedSection; // Section.stringToSection(sectionsSpinner.getSelectedItem().toString());
//imports articles (if it's been more than an hour since last import)
articlesDataSource.importArticles(Section.currentSection, true, false);
//loads article from database to the list
loadArticlesIntoList(Section.currentSection);
}
}
public static void loadArticlesIntoList(String[] section)
{
//clears the list
//articleEntryAdapter.clear(); //don't think I need this now that I'm just going to hide it
//articleEntryAdapter.notifyDataSetChanged();
//POPULATES THE LIST OF ARTICLES, THROUGH THE ADAPTER
for(final Article a1 : articlesDataSource.getArticles(section))
{
articleEntryAdapter.add(a1);
}
articleEntryAdapter.notifyDataSetChanged();
//shows list of articles
articleEntryListView.setVisibility(View.VISIBLE);
}
ADDITION: here is my importAricles() code: http://pastebin.com/8j6JZBej
You have to invalidate the view anytime you make a change to its appearance, so make a call to articleEntryListView.invalidate() after setting the visibility.