Android preserve variable state during minimize and re launch - android

i have a simple activity where i start a service in onCreate depending on checkbox state.
#Override
protected void onCreate(Bundle savedInstanceState) {
if(savedInstanceState == null)
Log.e(TAG,"savedInstanceState is null");
if(savedInstanceState != null){
Log.e(TAG,"savedInstanceState is not null");
}
super.onCreate(savedInstanceState);
on checkbox state checked starting the service and setting serviceStarted
toggleButton1.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// TODO Auto-generated method stub
if(isChecked){
Log.e(TAG,"starting service");
serviceStarted = true;
startService(new Intent(getApplicationContext(), MyService.class));
}else{
serviceStarted = false;
stopService(new Intent(getApplicationContext(), MyService.class));
}
}
});
i have overrided below functions
#Override
protected void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
Log.e(TAG,"onSaveInstanceState called");
super.onSaveInstanceState(outState);
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
Log.e(TAG,"onResume called : serviceStarted = "+String.valueOf(serviceStarted));
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
Log.e(TAG,"onPause called : serviceStarted = "+String.valueOf(serviceStarted));
}
Now in my logcat, on minimize and re-launching the app after starting service, i see below
04-22 12:34:42.150: E/MainActivity(19720): onPause called : serviceStarted = true
04-22 12:34:44.700: E/MainActivity(19720): onResume called : serviceStarted = false
Now my need is
I want to preserve state of serviceStarted without using sharedpreferences
Anyway there, or should i go with service binding model ? Please help Thank you

Well, just use the whole save instance state thing, that's what it's for:
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("serviceStarted", mServiceStarted);
}
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
mServiceStarted = state.getBoolean("serviceStarted", false);
}
But actually, if your activity gets destroyed for reals (by back button or by the OS), you'll still lose state, so the more reliable option is to use SharedPreferences.
But in your case, service can still get killed between the times your activity pauses and gets brought back -- then your serviceStarted will be obsolete. So just use a static member to know the state:
public MyService extends Service {
private static volatile boolean mIsRunning = false;
public void onCreate() {
MyService.mIsRunning = true;
}
public void onDestroy() {
MyService.mIsRunning = false;
}
public static boolean isRunning() {
return mIsRunning;
}
}

Related

Doing something when returning to an Activity from Background

I want to execute a function only when returning to the Application from the background.
I have included the method in onResume, and this does it to a certain extent. Problem is since onResume is fired even on creating the Activity and when returning to the activity from another activity (Ex: From pressing the back button), and the function is executed there as well.
How to avoid this and execute the function only when returning from background?
Ps: My application already has multiple places using startActivity so changing to startActivityForResult is a tedious task.
Also all my Activities are extending from a common BaseAppCompactActivity class and it's where my method is located, so this will apply to the whole application.
Edit 2:
My BaseAppCompactActivity is as below with LifecycleObserver implemented now. This doesn't seem to work though.
public class BaseAppCompactActivity extends AppCompatActivity implements LifecycleObserver {
private String TAG = BaseAppCompactActivity.class.getSimpleName();
#Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
#Override
protected void onPause() {
super.onPause();
stopService();
}
#Override
protected void onPostResume() {
super.onPostResume();
startService();
}
// #Override
// protected void onResume() {
// super.onResume();
//// updateLastAccessedDate();
// }
private void startService() {
startService(new Intent(this, BusinessCacheService.class));
}
private void stopService() {
stopService(new Intent(this, BusinessCacheService.class));
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
private void updateLastAccessedDate() {
//Do something
}
}
Although its a duplicate . Here is a Java implementation i am sharing for sake of help ..
public class MyApplication extends MultiDexApplication implements LifecycleObserver {
private boolean previouslyInBackground;
#Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void onAppBackgrounded() {
previouslyInBackground=true;
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
void onAppForegrounded() {
if(previouslyInBackground){
// Do your stuff Here
}
previouslyInBackground=false;
}
}
Add the Gradle dependency from Lifecycle-aware components Documentation
You can use startActivityForResult instead of startActivity.
Then you can catch the returning inside onActivityResult method.
first set a global boolean variable like this:-
boolean isPaused = false;
now set a methods in your activity :-
#Override
protected void onUserLeaveHint() {
isPaused = true;
super.onUserLeaveHint();
}
or in your onResume method:-
#Override
protected void onResume() {
if(isPaused){
isPaused = false;
}
super.onResume();
}
Do like this
add these variable in your main activity
public static boolean isAppWentToBg = true;
public static boolean isWindowFocused = false;
public static boolean isBackPressed = false;
and also add these methods
private void applicationWillEnterForeground() {
if (isAppWentToBg) {
isAppWentToBg = false;
// Toast.makeText(getApplicationContext(), "App is in foreground", Toast.LENGTH_SHORT).show();
}
}
public void applicationdidenterbackground() {
if (!isWindowFocused) {
isAppWentToBg = true;
// Toast.makeText(getApplicationContext(), "App is Going to Background", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onBackPressed() {
isBackPressed = true;
Log.d(TAG, "onBackPressed " + isBackPressed + "" + this.getLocalClassName());
super.onBackPressed();
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
isWindowFocused = hasFocus;
if (isBackPressed && !hasFocus) {
isBackPressed = false;
isWindowFocused = true;
}
super.onWindowFocusChanged(hasFocus);
}
#Override
protected void onStart() {
Log.d(TAG, "onStart isAppWentToBg " + isAppWentToBg);
applicationWillEnterForeground();
super.onStart();
}
#Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop ");
applicationdidenterbackground();
}
What I would suggest is create a new boolean variable which say if that is created for the first time in resume and work on it.
Boolean isForeGround = true;
#Override
public void onPause() {
super.onPause();
isForeGround = false;
}
#Override
public void onResume() {
super.onPause();
if(!isForeGround){
isForeGround = true;
// write your code here
}
}

Saving the state of a button onSaveInstanceState

Ok, this one is really tricky to me. The idea of this is a time log for EMS. You press a button by the action taken, and it logs the time for later use. I managed to figure out how to save the TextView into a string. Now, how on earth do I save the logTime1.setEnabled(false); so that on rotation or on leaving the activity, it restores it disabled? Eventually I will have another button beside it that will allow you to edit which will unlock the button. Here is the code.
public class TimeLog extends Activity{
boolean logTimeDis1=true;
Button logTime1;
String time1;
TextView ivTimeStamp;
int counter=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.time_log);
logTime1 = (Button) findViewById(R.id.logTime1);
ivTimeStamp = (TextView) findViewById(R.id.ivTimeStamp);
if(savedInstanceState != null) logTime1.setEnabled(savedInstanceState.getBoolean("logTimeDis1", true));
logTime1.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
// TODO Auto-generated method stub
time1 = new SimpleDateFormat("HH:mm", Locale.US).format(new Date());
ivTimeStamp.setText(time1);
logTime1.setEnabled(false);
logTimeDis1 = false;
return false;
}
});
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
counter++;
}
#Override
protected void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
super.onSaveInstanceState(outState);
outState.putString("ivTimeStamp", ivTimeStamp.getText().toString());
outState.putBoolean("logTimeDis1", logTimeDis1);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onRestoreInstanceState(savedInstanceState);
ivTimeStamp.setText(savedInstanceState.getString("ivTimeStamp"));
}
}
So, in this line
if(savedInstanceState != null) logTime1.setEnabled(savedInstanceState.getBoolean("logTmeDis1", false));
you don't check if savedInstanceState contains logTmeDis1. Say, savedInstanceState is not null, but does not have logTmeDis1, you'll get false and your button locked. Actually, you don't have to check it in this case, just change false to true, and it should work correctly.

Difference between oncreate and onRestoreInstanceState method in activity

Guys I am stuck in a problem with these two methods:-
When I change the orientation of device and set the text in edit text after retriving the text from bundle,it does't work.But the same code is working in onrestoreStoreInstante method.
Please have a look at my code:-
public class LifeCycleActivity extends Activity implements OnClickListener {
/** Called when the activity is first created. */
EditText user;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
user=(EditText)findViewById(R.id.et_user);
if(savedInstanceState!=null){
String s =savedInstanceState.get("Key").toString();
user=(EditText)findViewById(R.id.et_user);
user.setText(savedInstanceState.get("Key").toString());
Toast.makeText(this, s,Toast.LENGTH_SHORT).show();
}
Toast.makeText(this, "onCreate",Toast.LENGTH_SHORT).show();
Button b=(Button)findViewById(R.id.button1);
b.setOnClickListener(this);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Toast.makeText(this, "onSaveInstanceState",Toast.LENGTH_SHORT).show();
outState.putString("Key", "Deepak");
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
//String s =savedInstanceState.get("Key").toString();
//user.setText(s);
Toast.makeText(this, "onRestoreInstanceState",Toast.LENGTH_SHORT).show();
}
#Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
Toast.makeText(this, "onStart",Toast.LENGTH_SHORT).show();
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
Toast.makeText(this, "onResume",Toast.LENGTH_SHORT).show();
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
Toast.makeText(this, "onPause",Toast.LENGTH_SHORT).show();
}
#Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
Toast.makeText(this, "onStop",Toast.LENGTH_SHORT).show();
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Toast.makeText(this, "onDEstroy",Toast.LENGTH_SHORT).show();
}
#Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
Toast.makeText(this, "onRestart",Toast.LENGTH_SHORT).show();
}
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
startActivity(new Intent(LifeCycleActivity.this,SecondActivity.class));
}
}
when I set the text in edit text in oncreate method after getting the value from Bundle,it doesn't work.But the same code works in onRestoreInstanceState() method.
According to me , it should work for oncreate also as we can get the Bundle class object there.
Please help me to sort out this problem..
EditText and most of other views have their own methods to save/restore their own data. So in general it's not necessary to save/restore them on your code.
You can see this here on the Android TextView source code (remember that EditText extends from TextView) on line 3546:
if (ss.text != null) {
setText(ss.text);
}
so the reason you can't set it onCreate and can do it onRestoreInstanceState it's because during your activity super.onRestoreInstanceState(savedInstanceState) the activity calls the EditText.onRestoreInstanceState and the EditText restore it self to the previous value.
You can see it happening on the Activity source code on line 940
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}
hope it helps.
Overview
onSaveInstanceState() is used to put the data to bundle
onRestoreInstanceState() is used to set the data available in
bundle to your activity
So do as follows:
i Guess until the activity is created onorientation change the
bundle is not available, that's way you are not able to retrieve it
in oncreate
Let the activity create in oncreate
Then restore the instance in onRestoreInstanceState
Hope this helps !

Android creates new process rather than returning to background activity

I have a simple activity that starts and stops a background service that logs GPS data. I then place the process in the background by clicking on the home button.
When I return to the App by clicking on it's icon it creates a new process rather than reverting to the original activity. If I then close this process (by clicking the back button) it calls onDestroy() and then reverts back to my original activity screen showing the service is still recording. This is very inconvenient for the intended use of the App.
It is my understanding the original activity should of been popped from the activity stack rather than a new process being created.
Included below is my activity code. I would appreciate anybody's explanation of this.
public class MainActivity extends Activity {
GPSTracker gps;
private static final String TAG = "MEDIA";
private String tv;
private boolean record = false;
private TextView status = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
record = savedInstanceState.getBoolean("Status");
Toast.makeText(getApplicationContext(),"onCreate",Toast.LENGTH_LONG).show();
setContentView(R.layout.activity_main);
}
public void StartRecord(View v) {
Button btn = (Button) findViewById(R.id.record);
status = (TextView) findViewById(R.id.status);
if (record == false) {
btn.setText("Stop Record");
status.setText("... Recording ...");
record = true;
ComponentName comp = new ComponentName(getPackageName(), LogService.class.getName());
ComponentName service = startService(new Intent().setComponent(comp));
}
else {
record = false;
status.setText("... Standing By ...");
stopService(new Intent(MainActivity.this,LogService.class));
btn.setText("Start Record");
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
Toast.makeText(getApplicationContext(), "onSaveInstanceState",Toast.LENGTH_LONG).show();
savedInstanceState.putBoolean("Status", record);
// etc.
}
#Override
public void onResume() {
super.onResume(); // Always call the superclass method first
Button btn = (Button) findViewById(R.id.record);
Toast.makeText(getApplicationContext(), "onResume",Toast.LENGTH_LONG).show();
if (record == true)
btn.setText("Stop Record");
else
btn.setText("Start Record");
}
#Override
public void onStart() {
super.onStart(); // Always call the superclass method first
Toast.makeText(getApplicationContext(), "onStart",Toast.LENGTH_LONG).show();
}
#Override
public void onRestart() {
super.onRestart(); // Always call the superclass method first
Toast.makeText(getApplicationContext(), "onRestart",Toast.LENGTH_LONG).show();
}
#Override
public void onDestroy() {
super.onDestroy(); // Always call the superclass method first
Toast.makeText(getApplicationContext(), "onDestroy",Toast.LENGTH_LONG).show();
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
Toast.makeText(getApplicationContext(), "onRestoreInstanceState",Toast.LENGTH_LONG).show();
record = savedInstanceState.getBoolean("Status");
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Try this and override below to functions, It should work fine in your case.
onResume() -> set record=true
onPause() -> set record=false

How to check if my activity is the current activity running in the screen

I used Toast to make notification, but it seems it will appear even its activity is not in the current screen and some other activity has been started.
I want to check this situation, when the activity is not the current one, I'd not send the Toast notification. But how to do ?
When your Activity comes to the foreground, its onResume() method will be invoked. When another Activity comes in front of your Activity, its onPause() method will be invoked. So all you need to do is implement a boolean indicating if your Activity is in the foreground:
private boolean isInFront;
#Override
public void onResume() {
super.onResume();
isInFront = true;
}
#Override
public void onPause() {
super.onPause();
isInFront = false;
}
ArrayList<String> runningactivities = new ArrayList<String>();
ActivityManager activityManager = (ActivityManager)getBaseContext().getSystemService (Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> services = activityManager.getRunningTasks(Integer.MAX_VALUE);
for (int i1 = 0; i1 < services.size(); i1++) {
runningactivities.add(0,services.get(i1).topActivity.toString());
}
if(runningactivities.contains("ComponentInfo{com.app/com.app.main.MyActivity}")==true){
Toast.makeText(getBaseContext(),"Activity is in foreground, active",1000).show();
}
This way you will know if the pointed activity is the current visible activity.
I prefer not to handle the state by myself, so I have implemented a class that does this for me.
package mypackage;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
// Mine extends AppCompatActivity - your's might need to extend Activity, depending on whether
// you use the support library or not.
public class StateTrackingActivity extends AppCompatActivity {
public enum ActivityState {
CREATED, RESUMED, STARTED, PAUSED, STOPPED, DESTROYED
}
private ActivityState _activityState;
protected ActivityState getActivityState() { return _activityState; }
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_activityState = ActivityState.CREATED;
}
#Override
protected void onResume() {
super.onResume();
_activityState = ActivityState.RESUMED;
}
#Override
protected void onStart() {
super.onStart();
_activityState = ActivityState.STARTED;
}
#Override
protected void onPause() {
super.onPause();
_activityState = ActivityState.PAUSED;
}
#Override
protected void onStop() {
super.onStop();
_activityState = ActivityState.STOPPED;
}
#Override
protected void onDestroy() {
super.onDestroy();
_activityState = ActivityState.DESTROYED;
}
}
Then your activity can extend this one and you can get the state by calling getActivityState().
This is my ultimate isActivityVisible function.
protected boolean isActivityVisible() {
if (this.mActivity != null) {
Class klass = this.mActivity.getClass();
while (klass != null) {
try {
Field field = klass.getDeclaredField("mResumed");
field.setAccessible(true);
Object obj = field.get(this.mActivity);
return (Boolean)obj;
} catch (NoSuchFieldException exception1) {
// Log.e(TAG, exception1.toString());
} catch (IllegalAccessException exception2) {
// Log.e(TAG, exception2.toString());
}
klass = klass.getSuperclass();
}
}
return false;
}
if (BaseActivity.this instanceof Faq)
{
Toast.makeText(BaseActivity.this, "You are in the Same Page", Toast.LENGTH_SHORT).show();
}else {
Intent intent = new Intent(BaseActivity.this, Faq.class);
startActivity(intent);
drawer.closeDrawer(GravityCompat.START);
}
//// here am All my activities are extending on Activity called BaseActivity
There is Activity#isTaskRoot() method
if ( getActivity() instanceof ManageCardActivity){
// your code
}

Categories

Resources