Object sharing between multiple activities - android

I wanted to share an object(has multiple states) between 3 activities and any of the activities can modify the object state and the same state should be accessible to all 3 activities.
I do not want to use singleton object as it will exist for whole application.
Shared object should only persist till the time these 3 activities are present.

hi #rahul you can implement parcelable in your object and pass it via Intent to all activities.In this way you can maintain states as well as avoid singleton.
Since making a object parcelable involves more boilerplate code you can use this parceler library which needs no boilerplate code.
After making a object parcelable you can pass that to any activity via Intent like this
Intent intent = new Intent(this, MyActivity.class);
intent.putParcelable("key", object_you_want_to_pass);
...
startActivity(intent);

If your object can be serialized, maybe you could try to use SharedPreferences to store your specific object, using one of the methods described here. You can then parse it and modify it from any activity.
And if you want it to be persistent only when all of your three activities are running, just clear your SharedPreferences entry.

In general designing your app you should avoid creating such shared states when it's possible. But if you really need it - see below.
You could create a singleton with kind of "reference counting" - Self releasing (reference counting) singelton
If you want to limit its visibility you can make it package private for instance (if your Activities are in the same package).
But this whole thing looks a bit overcomplicated to me, I'd probably go with normal singleton or a field in your Application class, maybe removing it manually if I know at some time that it's no longer needed.

Singleton is still the best way to manage this.
class Singleton {
static private Singleton _instance = null;
static private boolean _activity1 = false;
static private boolean _activity2 = false;
static private boolean _activity3 = false;
static public register(Activity activity) {
_instance = null;
if (activity instanceOf Activity1) _activity1 = true;
if (activity instanceOf Activity2) _activity2 = true;
if (activity instanceOf Activity3) _activity3 = true;
}
static public unregister(Activity activity) {
_instance = null;
if (activity instanceOf Activity1) _activity1 = false;
if (activity instanceOf Activity2) _activity2 = false;
if (activity instanceOf Activity3) _activity3 = false;
}
static public Singleton getInstance() {
if (_activity1 && _activitiy2 && _activity3) {
if (_instance == null)
_instance = new Singleton();
return _instance;
}
return null;
}
}
class Activity1 {
#Overrride
public void onCreateView() {
Singleton.register(this);
}
#Override
public void onDestroy() {
Singleton.unregister(this);
}
Apply same to Activity2 and 3. Above are only pseudo codes.

Related

Is it necessary to declare Singleton to access a single object in my all activities?

I am building a menu from which the user can select items. They can edit their selections whenever necessary. The singleton would be a class containing the list of all selectable items. Whenever new activities are opened, the singleton would have the correct state of all items.
The reason I am asking this is because implementing Serializable creates a new instance (albeit almost identical) of the item.
Yes you could use a singleton for this. It would be something like:
public class MenuData {
private static MenuData instance;
private boolean isItemASelected;
public static MenuData getInstance() {
if (instance == null) {
instance = new MenuData();
}
return instance;
}
public boolean isItemASelected() {
return isItemASelected;
}
public void setItemASelected(boolean itemASelected) {
isItemASelected = itemASelected;
}
}
However I wouldn't recommend this. This data will only be around for as long as your Application is in memory. As soon as your app gets killed by Android all the variables will be cleared and the state will have been lost.
If your menu items are constant then I'd recommend using SharedPreferences to store the state. If they are dynamic then use the SQL database. This way the options are persisted even if your app gets killed.

RxJava as event bus?

I'm start learning RxJava and I like it so far. I have a fragment that communicate with an activity on button click (to replace the current fragment with a new fragment). Google recommends interface for fragments to communicate up to the activity but it's too verbose, I tried to use broadcast receiver which works generally but it had drawbacks.
Since I'm learning RxJava I wonder if it's a good option to communicate from fragments to activities (or fragment to fragment)?. If so, whats the best way to use RxJava for this type of communication?. Do I need to make event bus like this one and if that's the case should I make a single instance of the bus and use it globally (with subjects)?
Yes and it's pretty amazing after you learn how to do it. Consider the following singleton class:
public class UsernameModel {
private static UsernameModel instance;
private PublishSubject<String> subject = PublishSubject.create();
public static UsernameModel instanceOf() {
if (instance == null) {
instance = new UsernameModel();
}
return instance;
}
/**
* Pass a String down to event listeners.
*/
public void setString(String string) {
subject.onNext(string);
}
/**
* Subscribe to this Observable. On event, do something e.g. replace a fragment
*/
public Observable<String> getStringObservable() {
return subject;
}
}
In your Activity be ready to receive events (e.g. have it in the onCreate):
UsernameModel usernameModel = UsernameModel.instanceOf();
//be sure to unsubscribe somewhere when activity is "dying" e.g. onDestroy
subscription = usernameModel.getStringObservable()
.subscribe(s -> {
// Do on new string event e.g. replace fragment here
}, throwable -> {
// Normally no error will happen here based on this example.
});
In you Fragment pass down the event when it occurs:
UsernameModel.instanceOf().setString("Nick");
Your activity then will do something.
Tip 1: Change the String with any object type you like.
Tip 2: It works also great if you have Dependency injection.
Update:
I wrote a more lengthy article
Currently I think my preferred approach to this question is this to:
1.) Instead of one global bus that handles everything throughout the app (and consequently gets quite unwieldy) use "local" buses for clearly defined purposes and only plug them in where you need them.
For example you might have:
One bus for sending data between your Activitys and your ApiService.
One bus for communicating between several Fragments in an Activity.
One bus that sends the currently selected app theme color to all Activitys so that they can tint all icons accordingly.
2.) Use Dagger (or maybe AndroidAnnotations if you prefer that) to make the wiring-everything-together a bit less painful (and to also avoid lots of static instances). This also makes it easier to, e. g. have a single component that deals only with storing and reading the login status in the SharedPreferences - this component could then also be wired directly to your ApiService to provide the session token for all requests.
3.) Feel free to use Subjects internally but "cast" them to Observable before handing them out to the public by calling return subject.asObservable(). This prevents other classes from pushing values into the Subject where they shouldn't be allowed to.
Define events
public class Trigger {
public Trigger() {
}
public static class Increment {
}
public static class Decrement {
}
public static class Reset {
}
}
Event controller
public class RxTrigger {
private PublishSubject<Object> mRxTrigger = PublishSubject.create();
public RxTrigger() {
// required
}
public void send(Object o) {
mRxTrigger.onNext(o);
}
public Observable<Object> toObservable() {
return mRxTrigger;
}
// check for available events
public boolean hasObservers() {
return mRxTrigger.hasObservers();
}
}
Application.class
public class App extends Application {
private RxTrigger rxTrigger;
public App getApp() {
return (App) getApplicationContext();
}
#Override
public void onCreate() {
super.onCreate();
rxTrigger = new RxTrigger();
}
public RxTrigger reactiveTrigger() {
return rxTrigger;
}
}
Register event listener wherever required
MyApplication mApp = (App) getApplicationContext();
mApp
.reactiveTrigger() // singleton object of trigger
.toObservable()
.subscribeOn(Schedulers.io()) // push to io thread
.observeOn(AndroidSchedulers.mainThread()) // listen calls on main thread
.subscribe(object -> { //receive events here
if (object instanceof Trigger.Increment) {
fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) + 1));
} else if (object instanceof Trigger.Decrement) {
if (Integer.parseInt(fabCounter.getText().toString()) != 0)
fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) - 1));
} else if (object instanceof Trigger.Reset) {
fabCounter.setText("0");
}
});
Send/Fire event
MyApplication mApp = (App) getApplicationContext();
//increment
mApp
.reactiveTrigger()
.send(new Trigger.Increment());
//decrement
mApp
.reactiveTrigger()
.send(new Trigger.Decrement());
Full implementation for above library with example -> RxTrigger

android new activity can't load singleton

I am trying to use a Singleton to share a large data object between Activities. But when I open the new Activity, the singleton comes up as empty. It seems to me that the Singleton should be the same no matter where in the Application I call if from.
It seems like the Scope of the Singleton is being limited to the individual Activity. Working around this is making my App very complicated. I must be doing something wrong. I even tried instantiating them in an extended Application class... Google says I should not have to use that though...
Can someone please point out where I am going wrong? i.e. Why does this singletom not contain the same data in each Activity?
I call it from an Activity with...
DataLog dataLog = DataLog.getInstance(this);
I have...
public class DataLog extends ArrayList<String> implements Serializable {
private static final long serialVersionUID = 0L;
private static DataLog sInstance;
private static Context mContext;
public static DataLog getInstance(Context context) {
mContext = context.getApplicationContext();
prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
if (sInstance == null) {
sInstance = new DataLog();
}
return sInstance;
}
private DataLog() {
}
public boolean add(String entry) {
super.add(entry);
return true;
}
public void add(int index, String entry) {
if (index > 0)
super.add(index, entry);
else
super.add(entry);
}
public void clear() {
super.clear();
}
...
}
Its highly advisable to avoid singleton for sharing large data sets in android.
Singletons are used for short life-cycle objects.
Switch to SharedPrefferences, SQLite DB's or file storing. You are not the only to have experienced this behavior, and the reason lies in the nature of android Activities and the system itself(managing activities and its data).
Here is an example why singleton is bad for your case:
You stored important data in it. The user knows that he can close the app on home button to call someone or whatever)maybe someone called him when he was in your app), and that when he opens your app he will come back at the same place with everything in order. (this is expected behavior from users and android apps). The system can easily kill your process and all static variables in it for memory maintenance, app inactivity etc...result=data lost. Thus its not safe to use it.

How to pass a value to an activity from another WITHOUT starting it?

I want to pass a value from activity A to activity B without actually starting the activity B (therefore this rules out the use of Intents and putExtra). The activity B may or may not be started but when it does it needs to display the value passed to it by activity A.
I searched high and low but couldn't find any relevant solution to this seemingly simple question. Any help will be appreciated!
You can't find a solution, because it's something that goes against any logic.
Activity B can't do anything if not started and visible. Activity B might even already be destroyed by the system without you knowing.
You can use a lot of things to set some variables, which your Activity B can read such as in your Application, somewhere in your XML or simply any random class.
Still, you can not actually DO anything with any of these options, until you start Activity B and when you can, it will just effectively be the same as giving the extra data in the Intent.
Use global class like :
public class AppConfig {
Context context;
public AppConfig(Context mxt) {
context = mxt;
}
public static String filepath = null;
Then, in activity A before onCreate() :
AppConfig config;
Context context;
and in onCreate() method put this :
context = getApplicationContext();
config = new AppConfig(context);
And, in second Activity B before onCreate() :
AppConfig config;
Context context;
And, in onCreate() method put this :
context = getApplicationContext();
config = new AppConfig(context);
And set the value where you want. Hope this will help you.
You can use shared Preferences. Using this one Activity can set Value into it, and other activity can read from it.
So you need to keep a value for an activity . If it starts it means you have to use those values, otherwise you will discard those values. For this you can use a separate class with a variable of datatype that you want and you can create getter setter for that and you can use it.
Make use of the classes
public class GetSet {
public GetSet() {
super();
}
private static boolean passwordSet = false;
public static boolean isPasswordSet() {
return passwordSet;
}
public static void setPasswordSet(boolean passwordSet) {
GetSet.passwordSet = passwordSet;
}
}
You can access ths using GetSet.setPasswordSet(false);
and Boolean flag = GetSet.isPasswordSet();
Set the value as Global:
public class Global {
public static int mValue;
public static int getValue() {
return mValue;
}
public static void setValue(int mValue) {
Global.mValue = mValue;
}
}
I was looking for answers to that but I couldn't find it. So I found a way to do that.
First Activity
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("Flag" , true);
startActivity(intent);
Second Activity
boolean flag;
flag = getIntent().getBooleanExtra("Flag" ,false);
if(flag == true)
{
this.finish();
}
So now you may send any data you want it will open the Second Activity and then immediately close it after you wouldn't even realize it. You may use Shared prefences to save your data for after usage.

How to reference the current or main activity from another class

I often find myself needing to access methods that require referencing some activity. For example, to use getWindowManager, I need to access some Activity. But often my code for using these methods is in some other class that has no reference to an activity. Up until now, I've either stored a reference to the main activity or passed the context of some activity to the class. Is there some better way to do this?
If you already have a valid context, just use this:
Activity activity = (Activity) context;
Passing context is better way for refrence Activity.
You can pass Context to another class.
IN Activity ::
AnotherClass Obj = new AnotherClass(this);
IN Another Class
class AnotherClass{
public AnotherClass(Context Context){
}
}
You can implement the necessary methods in your activity and implement a Handler. Then, simply pass a handler instance to your classes, where you can obtain a message for handler and send it to target.
You can make you application instance a singleton, and use it when you need a Context
An example is in this question:
Android Application as Singleton
This way, when you need a Context, you can get it with
Context context = MyApplication.getInstance()
This might not be the cleanest solution, but it has worked well for me so far
I found a way to get the Activity to a non-activity class that I have not seen discussed in forums. This was after numerous failed attempts at using getApplicationContext() and of passing the context in as a parameter to constructors, none of which gave Activity. I saw that my adapters were casting the incoming context to Activity so I made the same cast to my non-activity class constructors:
public class HandleDropdown extends Application{
...
public Activity activity;
...
public HandleDropdown() {
super();
}
public HandleDropdown(Activity context) {
this.activity = context;
this.context = context;
}
public void DropList(View v,Activity context) {
this.activity = context;
this.context = context;
...
}
After doing this cast conversion of Context to Activity I could use this.activity wherever I needed an Activity context.
I'm new to android so my suggestion may look guffy but what if you'll just create a reference to your activity as private property and assign that in OnCreate method? You can even create your CustomActivity with OnCreate like that and derive all your activities from your CustomActivity, not the generic Activity provided by android.
class blah extends Activity{
private Activity activityReference;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityReference = this;
}
}
after that you could use that the way you want, i.e. in
Intent i = new Intent(activityReference, SomeOtherActivity.class)
etc
There are many ways for Activities communication.
you can use:
the startActivityForResult method
a system of broadcast message and receiver (you can broadcast an event from the actual activity, and register a receiver in the target activity. Remember that the target activity must be previously initialized and non finished)
as you say, store a reference of the target activity wherever you need.
We built a framework for this. We have a BaseActivity class that inherits from Activity and it overrides all the lifecycle methods and has some static (class) variables that keep track of the activity stack. If anything wants to know what the current activity is, it just calls a static method in BaseActivity that returns the activity on top of our privately-managed stack.
It is kinda hacky, but it works. I'm not sure I would recommend it though.
Handle the Intent in the class you want to do these methods, and send your information to it in a Bundle like so:
Intent i = new Intent("android.intent.action.MAIN");
i.setComponent(new ComponentName("com.my.pkg","com.my.pkg.myActivity"));
Bundle data = new Bundle();
i.putExtras(data);
startActivityForResult(i);
Then use an OnActivityResultListener to grab the new data.
I solved this by making a singleton class has an instance of the class below as a member.
public class InterActivityReferrer <T> {
HashMap<Integer, T> map;
ArrayList<Integer> reserve;
public InterActivityReferrer() {
map = new HashMap<>();
reserve = new ArrayList<>();
}
public synchronized int attach(T obj) {
int id;
if (reserve.isEmpty()) {
id = reserve.size();
}
else {
id = reserve.remove(reserve.size() - 1);
}
map.put(id, obj);
return id;
}
public synchronized T get(int id) {
return map.get(id);
}
public synchronized T detach(int id) {
T obj = map.remove(id);
if (obj != null) reserve.add(id);
return obj;
}
}
This class can get a T object and return a unique integer assigned to the object by attach(). Assigned integers will not collide with each other unless HashMap fails. Each assigned integer will be freed when its corresponding object is detached by detach(). Freed integers will be reused when a new object is attached.
And from a singleton class:
public class SomeSingleton {
...
private InterActivityReferrer<Activity> referrer = new InterActivityReferrer<>();
...
public InterActivityReferrer<Activity> getReferrer() {return referrer;}
}
And from an activity that needs to be referred:
...
int activityID = SomeSingleton.getInstance().getReferrer().attach(this);
...
Now with this, a unique integer corresponding to this activity instance is returned. And an integer can be delivered into another starting activity by using Intent and putExtra().
...
Intent i = new Intent(this, AnotherActivity.class);
i.putExtra("thisActivityID", activityID);
startActivityForResult(i, SOME_INTEGER);
...
And from the another activity:
...
id refereeID = getIntent().getIntExtra("thisActivityID", -1);
Activity referredActivity = SomeSingleton.getInstance().getReferrer().get(refereeID);
...
And finally the activity can be referred. And InterActivityReferrer can be used for any other class.
I hope this helps.
public static Activity getLaunchActivity()
{
final Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
final Method methodApp = activityThreadClass.getMethod("currentApplication");
App = (Application) methodApp.invoke(null, (Object[]) null);
Intent launcherIntent = App.getPackageManager().getLaunchIntentForPackage(App.getPackageName());
launchActivityInfo = launcherIntent.resolveActivityInfo(App.getPackageManager(), 0);
Class<?> clazz;
try
{
clazz = Class.forName(launchActivityInfo.name);
if(clazz != null)
return Activity.class.cast(clazz.newInstance());
}
catch (Exception e)
{}
return null;
}
Just a guess since I haven't done this but it might work.
1) Get your applicationContext by making your Android Application class a Singleton.
2) Get your ActivityManager class from the context.
3) Get a list of RunningTaskInfos using getRunningTasks() on the ActivityManager.
4) Get the first RunningTaskInfo element from the list which should be the most recent task launched.
5) Call topActivity on that RunningTaskInfo which should return you the top activity on the activity stack for that task.
Now, this seems like a LOT more work than any of the other methods mentioned here, but you can probably encapsulate this in a static class and just call it whenever. It seems like it might be the only way to get the top activity on the stack without adding references to the activities.

Categories

Resources