A strange behavior I'm tearing my eyes on since early this afternoon, I'm givin up understanding but perhaps someone has an idea (yeah, I'm a beginner, some PROBABLY has an idea ^^).
Situation :
MainActivity.java (first one to be called, only one of interest here)
public ExpandableListView listClubs;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listClubs = (ExpandableListView) findViewById(R.id.expLstMainClubs);
AppGlobal.CreateDistricts() ;
ELVAdapterDistrictsClubs adapter = new ELVAdapterDistrictsClubs(this);
listClubs.setAdapter(adapter);
}
it's the only code in the class. Basically, I'm filling an ExpandableList with an adapter that relies on what's created in the CreateDistricts() method (creates business objects, Districts containing Clubs containing Members, Events, etc.).
Basically (again ^^), everything runs fine, on first start the ExpList works as expected, as well as the rest of the app. If I hit the back button from the MainActivity, however, and rerun the app, a "strange" (to me at least) behavior occurs :
The ExpList is loaded twice in a row - first one with the data, works fine, and just below a duplicate (with the same 2 Groups, i.e. here the only two Districts in my sample data), which doesn't work at all (if you try to expand it, it crashes the app).
Frankly, I'm lost - I've tried some things on the various on[Pause/Stop/Destroy/Start/Resume] etc. to no avail (therefore, there is yet nothing done on this side, no override, as it seems not to bring anything good).
As the onCreate will after the onDestroy load the bundle, it should load the ExpList as it were, and in this case there might be a conflict between the "old" groups/children and the actual ones (currently, data is simulated, but will afterward come from a remote data source), and I haven't decided yet what the best "save" behavior is, i.e. if I should find a way to obliterate the ExpList onDestroy in order to be sure it's 100% recreated anew (and how does one do that ?) and start from scratch, or if I could use the ExpList as is, while updating it's content, in order not to lose the previous selection. It may be irrelevant to complicate matters in order to do that, the first list being not so big and quite quickly used to reach the second activity...
Anyway, I suspect it comes from around that part, the bundle load after destroy and rerunning, does it reload the ExpList as is and then I add things instead of first deleting ? What I found confusing is that the next activity (reached simply by clicking one of the items of the ExpList) displays a similar ExpList (filled with Months and Events per Month for the selected Club), and doesn't present the same strange behavior after a destroy... and both have an almost identical ExpList declaration in their original xml layout so... well, perhaps tomorrow morning I'll see the light, but if anyone has an idea, feel free :D
The more I write, the more I think I should first clear the ExpList but... I don't see how exactly. I've tried
listClubs.removeViewsInLayout(0, listClubs.getCount()) ;
but it just doesn't seem to do anything.
Thanks in advance
Nothing related to Android behavior - I was recreating everytime the whole set of BO behind if the base ArrayList of Districts was not null... instead of simply getting out of the method, but it raises another question - how was the state of this ArrayList, which is declared in a subclass of Application serving as Application in my manifest ? I'll have to make some tests about that...
Related
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.
I have heard things about how it is bad to use setContentView()
Pattern "One activity, multiple views": Advantages and disadvantages
However I was wondering, would it be unlikely that my application will cause memory leaks, if I use setContentView() once in the onResume() method of my activity?
Whenever the user opens my app, it checks to see if something has been enabled in settings. If it has been enabled then the app uses a different screen compared to the original screen.
Therefor my code looks like this:
#Override
protected void onResume() {
super.onResume();
InputMethodManager im = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
String list = im.getEnabledInputMethodList().toString();
if(Stuff is true){
setContentView(R.layout.activityscreen_enabled);
}
}
}
Would using setContentView() be unlikely to cause memory leaks and other such problems? Or is there a better solution?
I'm doing Android since few years now and I have never done that because I like to stick to the pattern which is almost always having the setContentView in the onCreate.
However, I do not believe that you would have big troubles doing that (for the memory leaks I mean).
Nevertheless, I do not see the point of doing such a thing, the pattern of the Activity (or how I understood it) is more:
I create a view in the onCreate and I update its data in the onResume and if the data are A then add/remove this view and if the data are B add/remove this other view.
To be complete, I read your (really good) link and I think you maybe misunderstood how you can apply what Commonsware is saying: you can have multiply views without having different setContentView: your view structure needs, in this case, to be really modular and you will be able to load all the subviews dynamically (or, at least, it's how my colleague and I are doing ;) ).
For your example, I would have an empty layout for the base of the activity (let's say a blue background) and then for every view I want to have (every case), I would have a dynamic layout that I load at some point in the life cycle (probably at onResume). I do not believe that what you're doing is particularly bad but I doubt that it was thought like this ^^
This link agrees with me
If you need multiple screens use a Fragment or even create a new Activity inside of messing around with the view for some reasons
It's not good to have single Activity for the whole app or it will be so long and complicated.
Your onResume() would need to handle the new views and their ids, onClickListeners... etc.
onResume() is called many times unlike onCreate() so it would be a waste of time and memory to load the views over and over.
According to android doc in activity life cycle about onPause() and onResume()
Because this state can transition often, the code in these two methods should be fairly lightweight in order to avoid slow transitions that make the user wait.
After user inputs parameters in MainActivity for my app (shown below), he taps Search, which calls MatchesActivity, which generates output on a new screen (shown further below), which is exited via tapping back.
But with MatchesActivity active, every time the device is rotated, Search is again executed because the Activity restarts. In the screenshot below, I rotated device from vertical to horizontal to vertical to horizontal back to vertical.
It looks silly.
The output is generated in MatchesActivity that is invoked in onCreate in MainActivity like so:
Intent matchesIntent;
matchesIntent = new Intent(MainActivity.this, MatchesActivity.class);
startActivity(matchesIntent);
Here's the essence of onCreate for MatchesActivity:
#Override protected void onCreate(Bundle savedInstanceState)
{
MainActivity.dbc.setDbProcesslistener(this); // to know txaMatches has been defined
MainActivity.dbc.findDBMatches(); // generate output
}
I did research. I found some complicated ways of preventing an activity from restarting when the device is rotated. For example .
I'm hoping for a simpler solution. Any ideas?
As you have found, one option is to prevent the activity from being recreated on configuration changes all together. This is not always the best option, as this will prevent other things depending on the configuration from being recreated/reloaded too (e.g. resources overridden with the "-land" qualifier).
Another option is to cache the result of the DB search somehow. This could be done by adding a wrapper around your database that memorizes the term and results of the last search. Another way to cache the results would be to use a fragment, and reuse that fragment across activity recreations. Whether a fragment is recreated along with its activity is controlled by this method:
http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean).
My solution was simple.
Introduce boolean variable outputIsShowing, set it to true in onCreate as MatchesActivity terminates, set it for false when onCreate or onResume are executed in MainActivity (i.e., when MatchesActivity terminates), and return immediately in onCreate for MatchesActivity if outputIsShowing is true.
So if MatchesActivity is active when device is rotated, outputIsShowing will be true, so don't execute again.
It may not be best practice, but I've extensively tested it under normal conditions and am happy enough so far. Not sure if anything is lurking out there as a "gotcha".
I plan to go back and study the suggestions made so far since the more general situation is definitely important. And I will have to do so if someone finds fault with what I've done.
#Override protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// usual details prior to asking for matches
if(outputIsShowing)
return;
MainActivity.dbc.setDbProcesslistener(this); // to know matches was defined
MainActivity.dbc.findDBMatches();
outputIsShowing = true;
}
* EDIT *
Strangely, after embedding TextView txaMatches in a ScrollView to accomplish smooth, accelerated scrolling, I had to remove the references to outputIsShowing in order to see output after two device orientation changes.
And now, maybe I'll submit another question to address the fact that, very infrequently after screensaver forces waking the device, the output does NOT show if that is where the focus was when screensaver became active. Tapping 'back' to get to user input screen and then immediately tapping Search restores all to normal until about 100 (give or take) screensaver instances later, the output is again missing.
Such a bug makes me think I ought to follow the advice above.
If I do, or when I figure out the problem, I'll edit this again.
I have a MainActivity that extends SherlockActivityFragment. This activity holds some fragments and a menu. Each menu item leads to new SherlockActivities. One particular SherlockActivity when it is loaded contains a carousel, that moves extremely slow.
However, once the orientation changes this carousel performs perfectly. I know that the Activity is reloaded on orientation change. But I have no idea what could start this to begin with, or even where to start looking.
Has anyone come across any similar issues? What did you do to profile the issue and what was your fix?
If you can think of relevant parts of the code (eg onCreate etc) please ask and I'll post it. Each activity has about 150 - 200 LoC)
Edit:
Here is the intent that I am using to start the SherlockActivity in question:
Intent rankIntent = new Intent(context, RankActivity.class);
rankIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(rankIntent);
break;
I've read that on orientation change onDestory() is called followed by onCreate. This makes me think that maybe some resource is freed that wasn't initially available. But the flag in the intent is intended to clear the backstack and any resources with it.
I've also tested with using the finish() method just before loading the new activity with no improvements.
This seems like a far reach but I recently had a problem I'd say could be similar.
I learnt that Android doesn't handle scaling images particularly well - could it be that the images are scaled before you change the orientation and not scaled once you do?
Answering the 2nd part of your question: since my app doesn't perform any resource-needy logic operations and is based on its UI, what I did was comment out some of its elements one at a time to see if it helps; that's how I found out an ImageView that was scaled up in my case.
So after a while of programming android apps(1 uploaded on market, have 3k+ active installs with a 4,7 rating), I started to wonder, how can I make my app even more awesome. I realized, that I couldnt really add any really new, and world changing features, so I started to inspect the performance, and how to optimize just about everything, how to find the best cpu/memory usage ratio, and so on.
Anyway, I found out that onCreate will run in the case of rotating the screen, which is quite logic, but there are some(big) calculations, that I surely dont need to redo after every rotate. One of this is iterating through a csv with 6500 rows, having 4 columns, 2 of it always contains some data, 2 of it not always. The 2 column with datas will be used for autocomplete adapter, the another 2 is optional for a feature, but it still need to be initialized. Currently, this is running in an asynctask, triggered at the end of the onCreate, and takes about 3 seconds on my HTC Desire S, which has a quite good CPU, so lower budget devices will have a longer initialize time after every rotate which is surely not I want... It wont crash the UI, but there won't be any autocomplete until thoose seconds are over.
SO: my question is, can I do this in some separate method, for example a constructor(like in standard java), or is it a bad practise, because the special lifecycle of activities? I mean, I instantiate my activity the way the "constructor" will run, and just after that, my onCreate will run. In case of rotating, my "constructor" won't run again, but the onCreate will. Stability will still be my nr1 goal. Or, is there any good way to do this? Something that is created for exactly like this, which im unaware of? I really want to improve a lot in this matter, and I would really appreciate some help in this, preferrably from ones with experience in this, but any help is welcome! :)
For example, if I want to make a new activity this way, I would do it something like this:
new MyActivity(some parameters);
so the constructor runs, which ends something like this:
startActivity(new Intent(context, MyActivity.class));
So this way, the constructor runs, my variables will be initalized(not connecting to any view etc), and after that, my activity can run its onCreate variable anytime it has to.
Pardon me if Im wrong the syntax, I just fasttyped it :)
You should decouple this logic from your activity. There are many ways to do this, but the end goal is to have your csv parsing done in a different class, and this class should expose information about whether or not the data has already been parsed. So, in onCreate, you call your class to get the data. If it already exists, you get your cached data immediately. If this is the first time the method is called or for some reason your cache has been cleaned up, you parse your csv file and do whatever calculations you need.
you could take a look at onRetainNonConfigurationInstance which can return an object which you can access after your activity has been recreated.. so you would simply return an object containing all your processed results of onCreate and the next time around you check if there is a getLastNonConfigurationInstance() - and don't recalculate everything