I hope that we can pass data between android application components
by following ways.
1.we can pass data using intent object,
2.we can implement serializable , parcelable interface and pass objects by using intent,
3.we can create a new class by extending Application class, to access global members from anywhere
the android application,
4.sharedpreference ,
5.sqlite.
Are there any other mechanism to send data between android application components?
Another option is create ApplicationPool.
Follow the below steps:-
Initiate the ApplicationPool :-
ApplicationPool pool = ApplicationPool.getInstance();
modify the data on details page and add to pool
pool.put("key", object);
get the modified data on list page from pool
Object object = (Object) pool.get("key");
important notes:- notify the listview or gridview after getting the data
ApplicationPool class file
public class ApplicationPool {
private static ApplicationPool instance;
private HashMap<String, Object> pool;
private ApplicationPool() {
pool = new HashMap<String, Object>();
}
public static ApplicationPool getInstance() {
if (instance == null) {
instance = new ApplicationPool();
}
return instance;
}
public void clearCollectionPool() {
pool.clear();
}
public void put(String key, Object value) {
pool.put(key, value);
}
public Object get(String key) {
return pool.get(key);
}
public void removeObject(String key) {
if ((pool.get(key)) != null)
pool.remove(key);
}
}
Another way is to use static elements, wether it be:
Static fields (with public access for example)
Static properties (meaning private fields with getter and/or setter)
Singletons
Possibly nested classes
While the use of static variables in OOP is debatable, they introduce global state and therefore are a way to accomplish sharing of data inbetween activities too.
1) HashMap of WeakReferences, for example:
public class DataHolder {
Map<String, WeakReference<Object>> data = new HashMap<String, WeakReference<Object>>();
void save(String id, Object object) {
data.put(id, new WeakReference<Object>(object));
}
Object retrieve(String id) {
WeakReference<Object> objectWeakReference = data.get(id);
return objectWeakReference.get();
}
}
Before launching the activity:
DataHolder.getInstance().save(someId, someObject);
From the launched activity:
DataHolder.getInstance().retrieve(someId);
2) Or strange method: store data on server O_o
Related
I have abstracted the functionality of my app into lots of different POJO's. Now some POJO down the line needs access to the Context, SharedPreferences or what not. How do they get that access?
More specifically, consider this example:
Activity {
B b;
}
B {
C c;
}
C {
method() {
SharedPreferences.readSomeValue();
}
}
My Activity uses a POJO B, which in turn uses a POJO C, which needs to read a value from SharedPreferences. How would I give C access to SharedPreferences?
The obvious solution would be to pass it down from Activity through B to C. That however would require to clutter class B with SharedPreferences for the single purpose of passing it down to C. B itself doesn't need access to SharedPreferences. I find this approach extremely ugly.
Another solution I tinkered with was to have a public static variable somewhere to store the SharedPreferences and access them from anywhere. This solution is not only equally ugly, it might lead to NullPointerExceptions if C is accessed in a different hierarchy from a different Activity.
Is there another way?
How about a singleton class that handles POJO classes ?
In this case you can mutable the same object which is in Map not in your Activity.
public class AppVariables{
private static AppVariables instance = new AppVariables();
private Map<String,Object> map;
public static AppVariables getInstance(){
return instance;
}
private AppVariables(){
map = new HashMap<>();
}
public void add(String key, Object value){
map.put(key,value);
}
public Object get(String key){
return map.get(key);
}
//In your A class
AppVariables.getInstance().add("AVariable",A);
//In your C class
Object obj = AppVariables.getInstance().get("AVariable");
//Now you can receive your variable in Activity C or any other java class
}
Andrew Sun's comment gave me the right direction. I now have a class called Initializer that handles initialization:
public class Initializer {
public static void init(Context context) {
A.init(context.getResources());
B.init(context);
C.init(PreferenceManager.getDefaultSharedPreferences(context));
}
}
It's called in the Activity's onCreate() method:
Initializer.init(this);
In my application i have to share various java-beans class among the activities.
In order to do that, i extended the Application class, in which i create an HashMap filled with all the java-beans. Each java-beans has its own Key.
Example code:
public class MyApplication extends Application {
public static final String CLASSROOM_KEY = "Classroom";
private HashMap<String, Object> myObjects;
#Override
public void onCreate() {
myObjects = new HashMap<String, Object>();
myObjects.put(CLASSROOM_KEY, new Classroom());
}
public HashMap<String, Object> getMyObjects() {
return myObjects;
}
}
This is usable in all the activities, and this is ok. BUT, i have two problems:
1) I need to get myObjets also in non-activity classes, like utils classes, but in these classes i can't do "getApplicationContext()" because they don't extend Activity.
For example, from my main activity i start a service (but it is in a normal class), and the service calls a query that in turn is in another normal class.
The query needs an object that is in myObjects!
I can't make myObjects public static i think.
2) In MyApplication i have to create all my java-beans in advance.
What if in the future i wanted to create a new classroom object in addition to the already present one?
I should create a new key for it, but it is impossible!
Thanks for your help.
UDPATE
I change the question:
In this class:
public class ClassroomUtils {
private static String result = null;
private static String studentObjectID = null;
public static void queryClassroom(String UUID, final Classroom classroom) {
ParseQuery<ParseObject> query = ParseQuery.getQuery("Classroom");
query.whereEqualTo("BeaconUUID", UUID);
query.getFirstInBackground(new GetCallback<ParseObject>() {
public void done(ParseObject object, ParseException e) {
if (e == null) {
try {
result = object.getString("Label");
} catch(Exception e1){
System.out.println("Vuota");
}
if(result != null) {
Log.i("Classroom", "Retrieved " + result );
classroom.setClassroom(result);
sendNotification(result);
addStudentClassroomRelation(object);
}
} else {
Log.e("Classroom", "Error: " + e.getMessage());
}
}
});
}
i want to avoid to pass the classroom to this method (called from another normal class). How can i access to global objects from this class?
I can't make myObjects public static i think.
Why not? myObjects is effectively global in scope already. There is nothing to be gained, from a memory management standpoint, by having myObjects be a private data member of Application. If you want Classroom to be a Java singleton, do so. You just have to watch your memory management, as you do with your current implementation.
In MyApplication i have to create all my java-beans in advance
No, you do not.
What if in the future i wanted to create a new classroom object in addition to the already present one?
Then create another one. Perhaps the right singleton is a School, which holds onto a collection of Classroom objects. Again, your primary near-term issue is one of memory management, so you do not run out of memory because you are trying to keep these objects around all of the time.
1) I need to get myObjets also in non-activity classes, like utils classes, but in these classes i can't do "getApplicationContext()" because they don't extend Activity.
The best way, I think, is to create the MyApplication class as a singleton. There you can retrieve the data from anywhere by calling getInstance and the corresponding getter/setter for your attributes.
Short example:
public class MyApplication extends Application {
private static MyApplication mInstance;
public MyApplication getInstance(){
// this means you have only one existing instance of this class.
if(mInstance == null){
// set the context to this MyApplication instance
mInstance = this;
}
// return the instance of this class
return mInstance;
}
// here your stuff for MyApplication
public HashMap<String, Object> getMyObjects() {
return myObjects;
}
}
Then you can call it from another class like this:
public class CFoo{
public CFoo(){
//retrieve myObjects from MyApplication
MyApplication.getInstance().getMyObjects();
}
}
I'm using Gson to deserialise Json into a model ApplicationModel. I want this Model to be a singleton so I can access it elsewhere in my application.
Now as Gson creates an instance of this class, I'm creating the singleton instance in a rather unconventional way. See below:
public class ApplicationModel {
private static ApplicationModel instance;
private GeneralVO general;
protected ApplicationModel() {
instance = this;
}
public static ApplicationModel getInstance() {
return instance;
}
public String getVersionDate() {
return general.getVersionDate();
}
}
This is the way I create it and then reuse it later in the application:
InputStreamReader reader = new InputStreamReader(is);
ApplicationModel model1 = new Gson().fromJson(reader,ApplicationModel.class);
Log.i("MYTAG", "InputStream1 = "+model1.toString());
Log.i("MYTAG", "Date: "+model1.getVersionDate());
ApplicationModel model2 = ApplicationModel.getInstance();
Log.i("MYTAG", "InputStream1 = "+model2.toString());
Log.i("MYTAG", "Date: "+model2.getVersionDate());
This works as the getInstance() returns the same model but somehow this just doesn't seem right.
My question is 'is this a good way of going about it or is there a better solution???'
EDIT
A much better way of doing singletons is to use an enum with one INSTANCE element.
See this post for an explanation
I suggest to instantiate your singleton instance on your Model, rather than instantiating it using constructor.
public class ApplicationModel {
private static ApplicationModel instance; //= new ApplicationModel();
//instantiating here is called an "Eagerly Instantiated"
private GeneralVO general;
private ApplicationModel() {
}
public static ApplicationModel getInstance() {
//instantiating here is called "Lazily Instantiated", using :
//if (instance==null) { --> Check whether 'instance' is instantiated, or null
// instance = new ApplicationModel(); --> Instantiate if null
//}
return instance; //return the single static instance
}
public String getVersionDate() {
return general.getVersionDate();
}
}
By setting the constructor to private, you prevent the object from being re-instantiated by another class, to use the object, you will have to call the object with ApplicationModel.getInstance().
So if you want to set values, call ApplicationModel.getInstance().setterMethod(value), Why this is useful? if you want to track the change, you will only need to track the setter method. If you used constructors, you will have to track the constructors too.
Example :
// To SET the value:
// instead of ApplicationModel model1 = new Gson().fromJson(reader,ApplicationModel.class);
ApplicationModel.getInstance.setValue(new Gson().fromJson(reader,ApplicationModel.class);
// To GET the value :
ApplicationModel.getInstance.getValue();
The "Eager Instantiation" vs "Lazy Instantiation" :
Eager Instantiation is useful if you want an easy way to deal with
Threads
Lazy Instantiation has better memory footprints
There's more than that, you can google it for more info, but I think this should be enough for you right now.
Hope this helps, and good luck ^^
Regards,
Reid
I am trying to pass the complete arraylist from one activity to another.
i have tried like this way..
arraylist=new ArrayList<HashMap<String,Object>>();
Intent i= new Intent(ListActivity.this,search.class);
i.putExtra("arraylist", arraylist);
startActivity(i);
Could somebody help me out #thanks
This will not work because the Object class in Java is not serializable. See this question for an explanation as to why.
The Intent.putExtra() method requires a type that implements the serializable interface, Object does not implement this so consequently it will not work. I would suggest rather than having a HashMap<String,Object> you replace the Object with a more specific type that implements the Serializable interface. See this tutorial for how to do this.
UPDATE
If the data you are passing is large there could be a fairly significant overhead associated with serializing and deserializing. Consequently it might be worth using a Static Singleton class to store the arraylist. The code sample below shows how you could implement this:
public class DataStore {
private static final DataStore instance = new DataStore ();
private arraylist = new ArrayList<HashMap<String,Object>>();
//Private constructor
private DataStore () {}
//Class is only accessible through this method
public static Singleton getInstance() {
return instance;
}
//Accessors for your data
private ArrayList<HashMap<String,Object>> getArrayList()
{
return arraylist;
}
private void setArrayList(ArrayList<HashMap<String,Object>> value)
{
arraylist = value;
}
}
For reference here is a tutorial on static singletons.
I have a map between string and a class object. I populate this map in Activity1 and i would like to pass it to activity2.
public class NEW extends Activity {
public class data {
String name;
float value;
.... etc }
......
static Map<String, data> data_map = new HashMap<String, data>();
..... }
The best way to do this is if you can express your data in the primitives supported by Bundle, so it can be placed in the Intent you are sending through the Intent.putExtra() methods. (EXCEPT for the use of Serializable, which is extremely slow and inefficient.)
However you can't do this because (a) you are using a Map and (b) your map contains a custom data type.
The formally correct solution to this exact problem is to write a custom Parcellable class that takes care of marshalling/unmarshalling your data structure. I'll sketch out the code here, though it may not be exactly correct:
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable.Creator;
public class MyData implements Parcelable {
HashMap<String, data> data_map = new HashMap<String, data>();
public MyData() {
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int parcelableFlags) {
final int N = data_map.size();
dest.writeInt(N);
if (N > 0) {
for (Map.Entry<String, data> entry : data_map.entrySet()) {
dest.writeString(entry.getKey());
data dat = entry.getValue();
dest.writeString(dat.name);
dest.writeFloat(dat.value);
// etc...
}
}
}
public static final Creator<MyData> CREATOR = new Creator<MyData>() {
public MyData createFromParcel(Parcel source) {
return new MyData(source);
}
public MyData[] newArray(int size) {
return new MyData[size];
}
};
private MyData(Parcel source) {
final int N = source.readInt();
for (int i=0; i<N; i++) {
String key = source.readString();
data dat = new data();
dat.name = source.readString();
dat.value = source.readFloat();
// etc...
data_map.put(key, dat);
}
}
}
Note that when you have a custom data structure like your "data" class, it can be cleaner to also make that Parcellable, so it knows how to read/write its contents in a Parcel, and the code here would just call .writeToParcel(...) and a .readFromParcel(...) method on it instead of knowing the details of its contents. That way when you add new fields to "data" you don't forget to also update this other marshalling code to know about them.
I am assuming you own both Activities (call them A and B). In which case just put the map in a public static variable and access it from B via A.data_map.
[update]
For all of the downvotes take a peek at the Android Application Framework FAQ section "How do I pass data between Activities/Services within a single application?". The solution I recommend is exactly the same as...
A public static field/method
An alternate way to make data
accessible across Activities/Services
is to use public static fields and/or
methods. You can access these static
fields from any other class in your
application. To share an object, the
activity which creates your object
sets a static field to point to this
object and any other activity that
wants to use this object just accesses
this static field.
Yes, there are caveats to this solution but with the limited info presented by the OP we can not assume this method will not work.
I would suggest using Intents, which work for both static and arbitary objects (as long as they implement Serializable). Create a custom Intent for your application and then pass on your HashMap (not a Map, which doesn't implement Serializable!) as extra data:
Intent act2 = new Intent(Activity2.SHOW_ME);
act2.putExtra("data", data_map);
Then in Activity2, you can call getIntent() and check via Intent.getAction().equals(Activity2.SHOW_ME) whether you were the one calling your Activity. If so, you can access your extra data by
Intent caller = getIntent();
if (caller.getAction().equals(Activity2.SHOW_ME)) {
Map<String, NEW.data> data_map = (Map<String, NEW.data>)caller.getExtras().get("data");
}
Hope I typed everything correctly ;) This assumes, that your Intent action-string is stored as static final string SHOW_ME = "yourpackage.yourname"; in Activity2.
If you need further clarification, add a comment.
You can encapsulate your data in a Bound Service as described in this document:
http://developer.android.com/guide/topics/fundamentals/bound-services.html
Don't forget to add your server to the manifest file.
this solution has the advantage of decoupling your data from your UI, which will ultimately lead to a more maintainable design.
If the need arrises you can add a messaging wrapper to your service's API to allow it to be called from other processes / applications
Create a new java file which will be global for whole application.
Step1:
public class GlobalClass extends android.app.Application
{
public static Map<String, NEW.data> data_map = new Map<String, NEW.data>();
}
Step2:
After doing that, Register this "GlobalClass" in AndroidManifest.xml
<application
android:name=".GlobalClass"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
</application>
Step3:
now you can use this Map anywhere in your application.
#Override
protected void onCreate(Bundle icicle)
{
GlobalClass global = (GlobalClass)getApplication();
Map<String, NEW.data> my_map_data = global.data_map;
}
These steps maybe helpful for you...