I am new to android and I want to understand what is the best way to write clean code.
I have the following example:
ActivityA ---> FragmentA (main UI window the user sees)
then on a user's action
FragmentA --->starts---> ActivityB-->FragmentB (the next window the user sees and hides previous one)
then on a user's click:
FragmentB---> starts ---> ActivityC-->FragmentC (the next window the user see that hides the rest)
So at the last step the user sees the layout of FramentC.
In FragmentC in order to populate the widgets of the layout properly I need some data that are available in FragmentA.
What I do now is:
Pass the data as extras in the intent to FragmentB. Some of these are actually needed by FragmentB but others are not, and are passed to FragmentB so that subsequently they are passed to FragmentC via FragmentB (again by intent/extra) if the user actually presses the button that opens up FragmentC's layout
Question:
1) It works but I was wondering if the fact that I pass in the extras of intent to FragmentB data that it does not really need is wrong/hack and there is a better/standard solution
2) When passing data among fragments are these data copies or a single copy is passed arround? I am not clear on that. E.g. in my example if I have a really big object passed from FragmentA to FragmentB (does not need it) and then FragmentB passes it to FragmentC (does need it) do I eventually have occupied 3 x size of the object?
1)Intent is probably the right way to do it. The fact that you need to pass in unneeded data sounds like you may have some really tight coupling in your fragments that may be bad for flexibility later on. Since the data is (I assume) related it would make sense to abstract it into a class and make the class Parcable or Serializable in order to reduce that coupling.
2)Assuming you use parcable/serializable, they're actually copied. This is because an intent doesn't have to go to your app, so the system will turn your data into a form that can be read by a second application. (I'm not sure what format it actually uses, but imagine it as JSON. For all practical purposes it may as well be).
Intents are definitely the way to do it! And just as the other answered has posted, because data is copied, you want to only store as minimal data as possible in your intents. As a general personal rule of thumb, I design my activities to work with only 2~3 different extra values inside intents which usually store a key or an ID and states, and the receiving activity opens and initializes everything based on those few key values.
Think about this things in your solution:
1- to discuss between activity you need intent
2- to pass a big data between activity/fragment : use a pareceable/seriazable bundle passed in intent : http://blog.denevell.org/android-parcelable.html
3- to pass data from fragment to activity you need a communicator interface :
http://developer.android.com/training/basics/fragments/communicating.html
Related
I'm confused regarding with some basic android development concepts, my question is not pointing at a particular code, thats why I dont include any.
Let's say that I have an activity inside of which I have a container in which I load a couple fragments (they are multiple instances of the same fragment), now the activity is populated, and inside one fragment I press a button that opens a new activity, it doesn't matter what may happen in that activity, the thing is that when I press a button it should take me back to the previos activity, I know that pressing the back button or using .finish(); will take me back to my already-populated activity, but I want to know, if that is the correct thing to do, or should I finish the activity as soon as i leave to the next one and when I want to go back create a new instance and repopulate it, if so, where should i store the variables?
Let's say that the fragments that I mentioned are "alarms" for an alarm application, and when I create it I call AlarmFragment newAlarm = new AlarmFragment(); and then I add that alarm to an arrayList in my alarms activity (java class) getListOfAlarms().add(getAlarmsAmount(), frag); which remains on the activity that has the fragment container, the question is, are these variables created in the right place? Because I am leaving them inside the activity itself right? What could happen if the activity is destroyed? I've been told that I should create an SQL database for storing those variables. I am not talking about long term saving but variables that I will be using at runtime
Can someone explain me these concepts a little bit? A link to a place where it is explained will be great too.
Your question seems like it has many parts.
In Part 1, this is what I think you are talking about:
1) how you decide to allow the user to get back to the first activity is really up to you. And 2) what you leave in the Back Stack is also up to you and what you want to define for the users' capabilities within your app. For example, if you want them to only be able to use a button that you define inside Activity 2 container, that is fine. However, you are not required to provide a button in Activity 2, and you are certainly allowed to use the Up Action in the App Bar for navigation. If I were you, I would read more about the Tasks and the Back Stack http://developer.android.com/guide/components/tasks-and-back-stack.html
You also mention this idea of having to "finish an Activity" with .finish(). I don't think that is usually necessary, but it is available to you if you want to use it based on what you decide for your app's logic (and what the user should and shouldn't be able to do).
With the Back button, Activity 1 will appear as if just initialized when you get back from Activity 2. Try it out. Also run some Log statements based on the simple diagram I provided and the Lifecycle "callbacks" (put these methods in your Activities and throw a Log statement in each to get a better sense of where you are in the Lifecycle) http://developer.android.com/guide/components/activities.html#Lifecycle
As for Part 2 of your question, I would try/set-up some of the above first, then start to experiment with a single variable to see what happens to it between Activities. There are a lot of "what ifs" to your question. You don't necessarily have to create a DB to store your variables, but that could certainly be an option. Take a look at the Developer Guide for most of the Data Storage options: http://developer.android.com/guide/topics/data/data-storage.html
If you are concerned about losing data when the Activity is destroyed, I might consider creating a database. Read the following for more info on recreating an Activity when you have gone elsewhere and are returning: http://developer.android.com/training/basics/activity-lifecycle/recreating.html In particular Saving Your Activity State: http://developer.android.com/training/basics/activity-lifecycle/recreating.html#SaveState
There's also an SO post on this: Saving Android Activity state using Save Instance State
I have an android app with one "home" or "main" activity that relies on fragments to accomplish several tasks. This data relies on information retrieved from a server (mine, and presumably a substantial amount by google maps).
I would like to structure my code such that several other activities (ie. preferences) can temporarily take focus before returning to the main activity.
Currently android destroys and recreates the main activity, which means bandwidth is wasted every time.
There are several notable intent flags which 'solve' this problem (Intent.FLAG_ACTIVITY_REORDER_TO_FRONT and Intent.FLAG_ACTIVITY_CLEAR_TOP), however they only appear to be useful when transitioning back to the main activity, which means I have to #override the system behaviour for both onKeyDown() and onBackPressed(). I would really prefer not to do this in case it causes other issues or eventually becomes deprecated.
Is this safe? Or is the better solution to force my application to create a serialization (savedInstanceState) of the main activity and all fragments anytime another activity temporarily takes the foreground?
Using saved instance state is the proper approach here. That will let you persist the information retrieved from the server when the activity gets re-created.
You only need to implement code to save / restore the instance state in the fragment or activity that holds the data. If you want to share that data across all your fragments, you can place it in the activity and add code to save the instance state of the activity. Then you can access the data that's stored in the activity from your fragments with ((MainActivity)getActivity()).getData().
For code to save and restore instance state, take a look at:
http://developer.android.com/training/basics/activity-lifecycle/recreating.html#SaveState
http://developer.android.com/training/basics/activity-lifecycle/recreating.html#RestoreState
If the data you need to persist is really large, you can use a retained fragment instead, as explained here:
http://developer.android.com/guide/topics/resources/runtime-changes.html#RetainingAnObject
Another trick for large data objects is to use a singleton class to store the data while the orientation change takes place.
So, I'm trying to make something like an rpg for the android to practice programming. I have a hero object which I'm trying to pass using parcelable as recommended by others, but I'm not sure how to pass it back.
In one activity, I'll have
myIntent.putExtra("heroData", hero);
And then, in myIntent, the activity started in the original activity, I'll have
hero = (Protag) getIntent().getParcelableExtra("heroData");
note: Protag is the class of the hero object
So, the first activity does successfully pass the object to the second activity, but such that the second activity doesn't affect the object in the first activity. Like, if something happens to the object in the first activity, it'll preserve to the second activity, but if something happens to the object in the second activity, the object in the first activity is still the same.
How would I make an object that can be changed by any activity such that the changes are preserved through other activities?
you are now dealing with concurrency. Since you have 2 'activities' possibly acting upon a single 'data'. However, Parcelable is a serialization technique, which means that you will lose reference once you pass a parcelable object(via parcelable). which means that you can't use a parcelable object as a central point of synchronization (both updating it with data).
If you will ONLY EVER plan on having a single object of your class, then you can make the object have static values. Otherwise this is the wrong mechanism to do this.
An AIDL one-way ascync service that notifies each thread of changes to a 'registered' object's values, is viable. (AIDL one-way is actually not hard to write, just takes some practice) Here is a project I wrote that makes use of it, to show both sync and async service usage. (My PhD Adviser's REPO for the MOOC he teaches)
Update to explain why I say this is 'concurrency'.
First of all I take a 'wide breadth' approach to what is 'concurrent' vs what you might be thinking. Your working definition of 'concurrent' is a valid one. However, it is somewhat limited in scope compared to what I'm thinking about. (usually doesn't matter, but the nuance of Android's lifecycle actually makes it important)
Android Activities have 6 life-cycle states they could possibly be in.
Created
Started (Visible)
Resumed (Visible)
Paused (partially visible)
Stopped (hidden)
Destroyed
Now here is where the issue of concurrency comes up.... When you have 2 or more Activities that are not in the 'Destroyed' state.
Plus there are a lot of things that add unpredictability to your logic that you have to think out very carefully...
Android's non-deterministic behavior.. User could press power/home/back or some other button, a phone call comes in/AMBER Alert takes priority over the phone, and/or the Garbage Collector "magic divine/unholy ritualistically" decides who lives or dies (exaggerating, but you get the point).
But lets assume that A doesn't get killed (which is actually the 'problem' scenario here).
So A makes an X object (hero or whatever) and passes by value (parcelable+intent) to B the value of X. At this time, the value(and reference) of X is in A. but the value of X is in B. Therefore, we are already in concurrency. Because the life-cycles of A and B overlap. Not just the "visible" portions of the life-cycles. So this means... where do you put the logic on "when to pass back the value of X"? Do you pass it back on onPause()? (but how? an intent wouldn't work if the User presses the back button)
Short answer: There isn't a 'great' way to do this. (at least with only Activities and Intents/Parcelables.)
You need some concurrency mechanism in place that allows the changes to X in A to propagate to change the values of X in B. (And this needs to be done reliably, correctly, and efficiently.)
The base goals of concurrent programming include correctness, performance and robustness. (from WIKI:Concurrency(CS) )
Ideally you wouldn't pass the data by value, but instead by reference, and when one class updated X (be it A or B) there only ever existed one value of X. That way when A or B was re-started it would have a 'valid' value of X. But you can't do this 'nicely' through parcelable data.
Therefore I would use a service to be the authoritative keeper of the 'value of X', any time A or B wants to update X, then they have to go through synchronized methods to get/set X. (I've never really ran into this scenario where two activities want to have the same 'live data' like this, so perhaps there is a better solution, possibly making use of handlers(but I can't think of it offhand)) With a service you will have slight time delays from when A is telling Service that X is updated to when B can get that info. but its the best I can think of off the top of my head right now.
Also the SQLite DB is designed to promote data storage like this, and has built in support for monitors to block access to the DB when another thread is accessing it. (there is nuance here I won't get into, such as 'setLockingEnabled(boolean)')
Make it static on the top class
All the activity can call it. Like
MyObject hero = topActivity.Hero
but but but
I'm not sure at all it is the correct way or the best way to do it. So maybe wait for other response
Sincerely
A possibility is:
From Activity1 launch the Activity2 using the method startActivityForResult(). In the Activity2 you will do all the modification needed to the object "hero" and then you can put it in an intent and send it back using setResult (int resultCode, Intent data). This way, when Activity1 is resumed, using onActivityResult(int requestCode, int resultCode, Intent data), you will be able to catch the object "hero" modified.
Here you can find more information on this mechanism.
http://developer.android.com/training/basics/intents/result.html
You could write object data in shared prefs. That way there is no need for a service. You need to save the progress of the game anyway. I think changes like that should be saved in memory once the app closes the progress is back to where it was.
Edit:
Or you could save in Internal Storage. Save the Protag object as a file.
It all depends how often the object changes.
Tip: You should not be writing every time you make a change to the object. Save the data in some data structure and then when everything is finalized such as the level ends or something then you write in storage or shared prefs. That way you keep it efficient otherwise all that writing in storage will slow the app down.
my android app activity sequence is activity1->activity2->activity3->activity4.
I can pass activity1 X value to activity2 using intent.putExtra() its works fine.
But if i want to access my activity1 X value in activity4, i don't want to use X in activity2 and activity3. But can't change the sequence. do i have to pass one by one each activity or is there any other way?
There's always a 'path' of Activities that you can use to pass objects around. But that's cumbersome. As mentioned you can use Sharedpreferences, that's cumbersome too.
You can use global variables or the Singleton pattern, some consider this not very elegant but it's okay for some simple stuff.
I'd look into EventBus, especially the sticky events. It works, simply put, like the mail. Activity 1 puts something into the mailbox, Activity 2 either gets it delivered to it's front door (normal event, delivered instantly) or can go pick it up at the mailbox (sticky event).
This also has it's drawbacks, but I'd not bother about them until you run into them.
This may be in the RTFM category but I can’t seem to figure out the proper way to do this. One of my activities shows some random data from a database, it uses some user-defined search-criteria from a previous activity to filter out which data blocks to search from. But it’s main purpose is to display the data, and present the user with a UI to manipulate the data to his will.
The user can also bookmark this random data, and then access it later again (the bookmarks show up in a listview in another activity). Rather than creating a whole new activity with basically the same purpose, I want to reuse the one already created, and just tell it that I want to view some data, rather than search for some new. So what is the proper way of informing an activity of want you want to do? Should that be defined in the Intent extras bundle or is there another way?
Or would the proper way be to create a new activity for this?
You can extend the first Activity like this:
ActivityB extends ActivityA
and then the methods that need to be different in ActivityB can #override the methods in ActivityA, but methods that do the same thing, you don't have to dupe as long as they're protected.