I am creating an application which requires login. I created the main and the login activity.
In the main activity onCreate
method I added the following condition:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
...
loadSettings();
if(strSessionString == null)
{
login();
}
...
}
The onActivityResult
method which is executed when the login form terminates looks like this:
@Override
public void onActivityResult(int requestCode,
int resultCode,
Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode)
{
case(SHOW_SUBACTICITY_LOGIN):
{
if(resultCode == Activity.RESULT_OK)
{
strSessionString = data.getStringExtra(Login.SESSIONSTRING);
connectionAvailable = true;
strUsername = data.getStringExtra(Login.USERNAME);
}
}
}
The problem is the login form sometimes appears twice (the login()
method is called twice) and also when the phone keyboard slides the login form appears again and I guess the problem is the variable strSessionString
.
Does anyone know how to set the variable global in order to avoid login form appearing after the user already successfully authenticates?
This question is related to
android
singleton
global-variables
state
Like there was discussed above OS could kill the APPLICATION without any notification (there is no onDestroy event) so there is no way to save these global variables.
SharedPreferences could be a solution EXCEPT you have COMPLEX STRUCTURED variables (in my case I had integer array to store the IDs that the user has already handled). The problem with the SharedPreferences is that it is hard to store and retrieve these structures each time the values needed.
In my case I had a background SERVICE so I could move this variables to there and because the service has onDestroy event, I could save those values easily.
On activity result is called before on resume. So move you login check to on resume and your second login can be blocked once the secomd activity has returned a positive result. On resume is called every time so there is not worries of it not being called the first time.
class GlobaleVariableDemo extends Application {
private String myGlobalState;
public String getGlobalState(){
return myGlobalState;
}
public void setGlobalState(String s){
myGlobalState = s;
}
}
class Demo extends Activity {
@Override
public void onCreate(Bundle b){
...
GlobaleVariableDemo appState = ((GlobaleVariableDemo)getApplicationContext());
String state = appState.getGlobalState();
...
}
}
you can use Intents , Sqlite , or Shared Preferences . When it comes to the media storage, like documents , photos , and videos, you may create the new files instead.
Just a note ..
add:
android:name=".Globals"
or whatever you named your subclass to the existing <application>
tag. I kept trying to add another <application>
tag to the manifest and would get an exception.
The approach of subclassing has also been used by the BARACUS framework. From my point of view subclassing Application was intended to work with the lifecycles of Android; this is what any Application Container does. Instead of having globals then, I register beans to this context an let them beeing injected into any class manageable by the context. Every injected bean instance actually is a singleton.
Why do manual work if you can have so much more?
The suggested by Soonil way of keeping a state for the application is good, however it has one weak point - there are cases when OS kills the entire application process. Here is the documentation on this - Processes and lifecycles.
Consider a case - your app goes into the background because somebody is calling you (Phone app is in the foreground now). In this case && under some other conditions (check the above link for what they could be) the OS may kill your application process, including the Application
subclass instance. As a result the state is lost. When you later return to the application, then the OS will restore its activity stack and Application
subclass instance, however the myState
field will be null
.
AFAIK, the only way to guarantee state safety is to use any sort of persisting the state, e.g. using a private for the application file or SharedPrefernces
(it eventually uses a private for the application file in the internal filesystem).
What about ensuring the collection of native memory with such global structures?
Activities have an onPause/onDestroy()
method that's called upon destruction, but the Application class has no equivalents. What mechanism are recommended to ensure that global structures (especially those containing references to native memory) are garbage collected appropriately when the application is either killed or the task stack is put in the background?
Create this subclass
public class MyApp extends Application {
String foo;
}
In the AndroidManifest.xml add android:name
Example
<application android:name=".MyApp"
android:icon="@drawable/icon"
android:label="@string/app_name">
Just you need to define an application name like below which will work:
<application
android:name="ApplicationName" android:icon="@drawable/icon">
</application>
If some variables are stored in sqlite and you must use them in most activities in your app. then Application maybe the best way to achieve it. Query the variables from database when application started and store them in a field. Then you can use these variables in your activities.
So find the right way, and there is no best way.
You can have a static field to store this kind of state. Or put it to the resource Bundle and restore from there on onCreate(Bundle savedInstanceState). Just make sure you entirely understand Android app managed lifecycle (e.g. why login() gets called on keyboard orientation change).
You can do this using two approaches:
Using Shared Preferences
Using Application class
Example:
class SessionManager extends Application{
String sessionKey;
setSessionKey(String key){
this.sessionKey=key;
}
String getSessisonKey(){
return this.sessionKey;
}
}
You can use above class to implement login in your MainActivity as below. Code will look something like this:
@override
public void onCreate (Bundle savedInstanceState){
// you will this key when first time login is successful.
SessionManager session= (SessionManager)getApplicationContext();
String key=getSessisonKey.getKey();
//Use this key to identify whether session is alive or not.
}
This method will work for temporary storage. You really do not any idea when operating system is gonna kill the application, because of low memory. When your application is in background and user is navigating through other application which demands more memory to run, then your application will be killed since operating system given more priority to foreground processes than background. Hence your application object will be null before user logs out. Hence for this I recommend to use second method Specified above.
Using shared preferences.
String MYPREF="com.your.application.session"
SharedPreferences pref= context.getSharedPreferences(MyPREF,MODE_PRIVATE);
//Insert key as below:
Editot editor= pref.edit();
editor.putString("key","value");
editor.commit();
//Get key as below.
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
String key= getResources().getString("key");
DO N'T Use another <application>
tag in manifest file.Just do one change in existing <application>
tag , add this line android:name=".ApplicationName"
where, ApplicationName
will be name of your subclass(use to store global) that, you is about to create.
so, finally your ONE AND ONLY <application>
tag in manifest file should look like this :-
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:name=".ApplicationName"
>
You could create a class that extends Application
class and then declare your variable as a field of that class and providing getter method for it.
public class MyApplication extends Application {
private String str = "My String";
synchronized public String getMyString {
return str;
}
}
And then to access that variable in your Activity, use this:
MyApplication application = (MyApplication) getApplication();
String myVar = application.getMyString();
I couldn't find how to specify the application tag either, but after a lot of Googling, it became obvious from the manifest file docs: use android:name, in addition to the default icon and label in the application stanza.
android:name The fully qualified name of an Application subclass implemented for the application. When the application process is started, this class is instantiated before any of the application's components.
The subclass is optional; most applications won't need one. In the absence of a subclass, Android uses an instance of the base Application class.
Source: Stackoverflow.com