I have two activities in my Android 2.1 application.
The first is the Main activity, with a view offering touch interaction.
The second is the Settings activity, offering settings to adjust parameters which are used within Main.
I currently have a Settings activity within my Main class, as a class instance. I read settings from this instance within Main.
public class Main extends Activity implements View.OnClickListener, View.OnTouchListener {
protected Settings settings;
}
public class Settings extends Activity implements ListAdapter {
}
I've discovered how to reuse my Settings activity if it has already been created, ensuring only one persistent instance:
// within Main.java :
Intent intent = new Intent(this,Settings.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); // reuse existing Settings
this.startActivity(intent);
My issue is that I cannot discover how to tie this in with the local 'settings' member in Main.
Would someone be kind enough to give me a quick example of how I can start my local 'settings' instance as a reusable activity?
Many thanks
I currently have a Settings activity within my Main class, as a class instance.
Delete this immediately. One activity should not be holding onto another activity, as this results in memory leaks. I guarantee you that there are better solutions for whatever problem you think that you are solving this way.
My issue is that I cannot discover how to tie this in with the local 'settings' member in Main.
By deleting "the local 'settings' member in Main", this problem goes away.
Also, please use SharedPreferences and a PreferenceActivity for collecting "settings" wherever possible. For example, if the point behind "the local 'settings' member in Main" is to allow Main to access the settings, the right answer for that is for the settings to be stored in a SharedPreferences object and for Main to be using those SharedPreferences. Using a PreferenceActivity has the benefit of giving the user a look and feel that they expect when providing settings to an application.
Related
I am working on the PreferencesActivity of an App:
https://developer.android.com/reference/android/preference/PreferenceActivity
Pref. activity looks something like this :
http://i.imgur.com/qnHbJX9.png
There are some actions we need to take when the user selects or deselects options in this Pref. Activity (which involve turning services on/off, making a connection to DB etc.)
Currently, most of the logic to take this action is written in the activity itself (the activity is huge) and some static classes.
We now want to save these settings on the server and perform these actions on user login (without launching the pref. activity). How should I move the logic?
To completely static classes? To individual broadcast receivers? Something else?
Image taken from here:
Preference Activity on Preference Click Listener
Create PreferencesStorage class, which load asynchronously settings from server, and send changes in preferences to the server.
Then use this class in your PreferenceActivity and SplashActivity (if you use di - then inject this class as singleton, if no - use instance of this class).
If some of settings need to be readed in every activity - yes, I think BroadcastReceiver will be best solution.
If you want to use clean architecture approach, then you can create PreferencesInteractor, which includes PreferencesRepository, and inject this interactor to SplashPresenter and PreferencesPresenter.
How can I save activity state (B) when moving to previous activity (A) and how to restore it's previous state when moving back (to activity B)?
SCENARIO
Content of activity B can be changed by user. After finishing work, performed changes can be persisted (e. g. saved to local DB). Before persisting, user may want to navigate back to previous activity. At this point, "work session" should be somehow saved temporary in memory. After returning back to activity B "work session" should be restored, so user can continue in work.
APPROACHES
Standard methods onCreate(Bundle) and onSaveInstanceState(Bundle) are used to restore/save activity state when device configuration changes (e. g. on rotate). As onSaveInstanceState(Bundle) is not part of activity's life-cycle, this method does not get called on "back-press" when activity is destroyed (by default finish() is called). The same applies for onRestoreInstanceState(Bundle). Obviously these methods alone are not sufficient to restore activity state after lefting an activity.
Transfering bundle state between activities
One way I can think of is to override onBackPressed() in such way it will start an Intent to previous activity A with bundle state Intent#putExtras(Bundle) of current activity B. When moving back, starting an Intent to activity B, this bundle state will be delivered back Intent#putExtras(Bundle) to restore the state of activity B. However this also require to override onCreate(Bundle) and onSaveInstanceState(Bundle) in A so bundle state of B would not be lost on configuration change before navigating back to B.
Not finishing an activity on back-press + non-default launchMode
Another way (more cleaner) is to override onBackPressed() so that it would start Intent to previous activity A without calling finish() of current activity B so activity B will hang in memory in paused state (waiting for resume). To resume activity B, manifest configuration android:launchMode="singleInstance" is required so android will use existing activity (paused B) instead of creating new one (B2) when navigating back to activity B (startIntent(B.class)).
Details: launchMode singleInstance creates singleton activity in new task (task = set of activities with the same group id i. e. affinity, normally app activities have the same affinity i. e. app = single task).
Drawback: transition animations does not work with singleInstance mode. It seems that singleInstance task may not be fully initialized at animation time. For more details: Custom animation doesnt work on SingleInstance Activity.
Saving activity state to SharedPreferences
Saving activity state to SharedPreferences on "back-press", restoring activity state from preferences in onCreate(Bundle) like in following link Save activity state to SharedPreferences.
Drawback: unable to save Bundle state (only primitives: putInt, putString, ...).
Others
Some of the methods listed in Share data between activities can be used. Answers from this link refers to developer.android.com/guide/appendix/faq/framework.html which is unfortunately broken. Here is an alternative source http://wing-linux.sourceforge.net/guide/appendix/faq/framework.html.
About sharing data via Application object:
Don't Store Data in the Application Object,
Using the Android Application class to temporary persist data.
Based on Application class documentation, using static singletons over Application is more preferable.
Base class for maintaining global application state. ...
Note: There is normally no need to subclass Application. In most situations,
static singletons can provide the same functionality in a more modular
way. If your singleton needs a global context (for example to register
broadcast receivers), include Context.getApplicationContext() as a
Context argument when invoking your singleton's getInstance() method.
It seems that putting data in Application object or static singleton is so far the best solution for this problem.
QUESTION
Is there any build-in solution for this (without need of overriding onBackPressed())? For example saving activity on back-stack with it's state. If not, what is the common pattern to save activity state in such situation?
RELATED POSTS (just to link them with this post)
How do I save temporary data on android?
For an alternative way to handle this, you can map your view's states to a data structure and save it as an object notation (such as json) to your local and when your activity is created / re-created you can read your state from local and bind them with your views.
For more information and example, you can check Spotify's presentations and open source project which contains information about how they manage application's ui from apis. (I know it's not exactly what you're trying to do but you may find some tricks.)
If you need to save your data in your activities, you can use Local Db or in-memory cache.
When you back out of an activity, the activity is not just destroyed programmatically, but also conceptually. In other words, the user expects it to be gone. We talk about saving activity state in situations where the activity object is destroyed but the user perceives that it still exists, such as during configuration changes or when it's in the background or backstack.
What you're trying to save is properly thought of as application state, not activity state. As such, SharedPreferences is an appropriate place for it. Whether it's the best solution is a matter of opinion and depends on your use case.
I have written an activity A, when users press a button, it will do MyConfig.doSomething() where MyConfig is simple class with activity A passed to it.
public class A extends PreferenceActivity {
private MyConfig mMyConfig;
/* pseudo code, when button clicked, call */
mMyConfig.doSomething();
}
In mMyConfig, it accesses SharedPreferences for some configuration. Thus, I can do this to pass the activity to mMyConfig for calling getSharedPreferences().
mMyConfig = new MyConfig ( this );
Here comes my request:
I want to do something that MyConfig.doSomething() already does, but except when users click some button to invoke it, I want to invoke it when Android Boots-Up.
I can write another class to extend BroadcastReceiver and then starts activity A by calling startActivity(A.class), and then in A, do some tricks to make mMyConfig.doSomething() happen. It works but the Application will be shown on screen when Android Boots-Up.
I want to make mMyConfig.doSomething() happen implicitly without letting users be aware of it. I suppose two possible solutions but I don't know how to do it.
Solution A:
Write a class that extends BroadcastReceiver, start a service (instead of activity A) that reads the SharedPreferences of A and create MyConfig object to do doSomething(). However, I don't if this can work if activity itself is never launched and how could I do this (read SharedPreferences from a service)?
Solution B:
Write a class that extends BroadcastReceiver, start activity A without showing it, put it to activity stack by calling startActivity(A.class) in onReceive. Is this possible?
Instead of Activity, which are meant to be visible to the user, you can make your BoardcastReceiver to start a Service instead. It is meant to perform tasks in the background without disturbing the user. The official guide is a nice place to start with.
Edited:
To access the SharedPreference of your application, simply call this line inside your service:
SharedPreferences pref = PreferenceManager.getSharedPreferences();
can I use activiy one time(register activity) and switch the main launcher after using to different activity?
another question if I may,
If I create parameter x in one of the activities in my application, can I use this parameter in other activities?...If yes, how I can do that?
thanks :)
You cannot dynamically change the launcher activity once it has to be only 1 activity that is defined in the manifest file.
I would recommend having something like a landing or splash activity which checks a shared preference variable, to decide which activity to launch, for example either a login activity or another activity.
You should not access a variable in an activty from another activity, you should store these in data holding classes. however if you want to do it, for a good reason, simply make it static.
You cannot adjust the manifest after running your application. What you can do is have your default launcher activity write to SharedPreferences once it has been run once. Inside of that activity check to see if that preference has been set and if it has just finish that activity and launch your new activity, the user will not see anything if you do this in the onCreate of the launcher activity.
As for passing params between activities you should use intent extras. For example to pass a string use putExtra(String key, String value), and to get that parameter inside of the new activity use getStringExtra("Key").
For global variables accessable from different activities you can also extend Application class and then access it via getApplicationContext().
1. One time activity launch
You can't change the main launcher. It's a static information. What you could do is following:
// in the beginning of onCreate
// first launch could be loaded from shared preferences
// see 2. for more
if (!firstLaunch) {
// start another activity
finish();
return;
}
2. Use data in another activity
One way is to persist the data and load it somewhere else. You will find all information you need in the Data Storage article.
If your data is primitive you could try to pass it by intent to another activity. See Using integer from one class in another Android.
If it is complex you could try to implement an own Application class and use helper methods to access global data. See Android: Accessing resources without an Activity or Context reference.
Be careful with that, please read the Avoiding Memory Leaks article then.
I am using the InstrumentationTestCase class in order to unit test some things within an activity.
I need to be able to check the SharedPreferences's contents and edit them, before this activity is launched.
I cannot use the setUp method to create the Activity and access it's SharedPreferences object to edit it, and then close that activity before finishing the setUp method because it apparently is locking the tests processing.
I also cannot access the SharedPreferences after I have launched the activity inside the test because as soon as the Activity is launched, it will already change the SharedPreferences object and act according to it, before I had the chance to get it's reference.
I apparently cannot access the SharedPreferences before either, because I have no Activity object... and as soon as I do, it is already executing code and being launched...
So, my question is, is there any way to access the SharedPreferences (and any other Activity information) of this Activity before I have the Activity actually created through an Intent?
I cannot change it to an ActivityInstrumentationTestCase2 because my test uses a second activity in it's process, so I can't just change to this class and use it's setUp() method to access the SharedPreferences.
I found the best simpler way to do this through the instrumentation only, without having to edit the application's architecture or any of the access attributes.
I achieved it through this:
Instrumentation instrumentation = getInstrumentation();
instrumentation.getTargetContext().getSharedPreferences(..);
This way I can access the SharedPreferences before any Activity is launched by the instrumentation.
Thanks for all the help, hints and other alternatives anyway.
Well... To tell you frankly.. I am not able to visualize your scenario. But is checking for info in application is doable ?
Create a class which extends android.app.Application and specify class name in Manifests child application element.
Sample Code:
import android.app.Application;
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
//try and access activity info here.
}
}
When your application is launched first class method to execture is onCreate of your application and has all the lifecyle events of that of any activity..
You must define extended application class in manifest by:
<application
android:name=".MyApplication"
android:label="#string/application_name">
I hope this ca give you some overview.
I haven't tried it, but if you set the mode to MODE_WORLD_READABLE and possibly MODE_WORLD_WRITEABLE instead of MODE_PRIVATE, I would think you could access the shared preferences from another application before the activity under test starts.
You could probably also use a different activity or service within the apk, or another apk that establishes a shared user ID and has the same certificate, to do the access without changing the access mode.