I'm developing an Android application and I need to open pdf files and return to same activity when back button is pressed.
Problem
I correctly open pdf file (from ActivityOne) using an intent and starting activity, but when I press back button, all data that I had in the ActivityOne (and previous activities) have been lost.
Here is my code of starting activity for showing pdf:
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
+"/"+ myApplication.getUsuarioActual().getFacturaActual().getPdf());
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.fromFile(file));
intent.setType("application/pdf");
startActivity(intent);
What do I have to do to solve that? Same occurs when I open another application and close it: when return to my app, it shows an error saying that all data is null.
EDIT
After reading that question, as #TheCharliemops recommended me, I know it is what I need, but I have another question related to that.
I have a class myApplicationthat extends Application to maintain global application state where I save all data that I read/write in different Activities.
My question is if I have to save all data I have in myApplication in every activity using onSaveInstanceState or there is some easiest manner to do it.
Firts of all, welcome to SO!!
Here, #reto-meier explains how to save the activity state in Android. I think that could fix your problem. I put his code here for future people with similar problem.
He says that you must override onSaveInstanceState(Bundle savedInstanceState) and onRestoreInstanceState(Bundle savedInstanceState) as following code shows:
Reto Meier said:
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
// Save UI state changes to the savedInstanceState.
// This bundle will be passed to onCreate if the process is
// killed and restarted.
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
savedInstanceState.putInt("MyInt", 1);
savedInstanceState.putString("MyString", "Welcome back to Android");
// etc.
}
The Bundle is essentially a way of storing a NVP ("Name-Value Pair") map, and it will get passed in to onCreate and also onRestoreInstanceState where you'd extract the values like this:
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
double myDouble = savedInstanceState.getDouble("myDouble");
int myInt = savedInstanceState.getInt("MyInt");
String myString = savedInstanceState.getString("MyString");
}
I hope this helps you.
Regards!
Related
I use the library zxing-android-embedded in my Android App. Before calling initiateScan() method to start the scanner from my activity, I set a class variable scanedItemId to know on which item I clicked to scan.
My issue is that when the scanner activity finished, it goes back to my activity, but in a new instance and not the initial one (I checked with a break point in the onCreate method). So, my class variable is null. What can I do to keep my initial activity instance live and be sure the scanner goes back to it?
public class MyActivity
[...]
scanedItemId = currentItem.id // The current item where we clicked on.
IntentIntegrator qrCodeScanner = new IntentIntegrator(this);
qrCodeScanner.setOrientationLocked(false);
qrCodeScanner.initiateScan();
[...]
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case IntentIntegrator.REQUEST_CODE:
// Here the scanedItemId is always null.
[...]
```
You asked:
What can I do to keep my initial activity instance live and be sure
the scanner goes back to it?
You can't. When your app goes to the background and the scanner app needs resources, Android will kill your app to make the resources available to the scanner app. This is normal and your app needs to be made robust enough to deal with this. If you need to keep track of your app's state so that you can continue when your app returns to the foreground, then you need to save that information somewhere persistent. You have choices:
SharedPreferences
SQLite database
Use a file
Implement onSaveInstanceState()
Following what #David said, I implemented the "onSaveInstanceState()" solution which works fine: I just added this in my activity:
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putInt("scanedItemIdKey", scanedItemId);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
scanedItemId = savedInstanceState.getInt("scanedItemIdKey");
}
Thanks
I'm making an android app that test if certain security features on your phone are enabled. For example, if you have password log in enabled or if your data is encrypted on your phone.
For some reason, the app has to be run twice to test and see if these security features are enabled on the phone or not, and this is the problem I'm trying to solve. I'd like it to test and see if the security features are enabled when the app is created and the first time the app is run, not the second time it is run.
I test if these features are enabled in the onStart() function in my MainActivity file. I included the functions code below:
#Override
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
#SuppressLint("NewApi")
public void onStart()
{
super.onStart();
//determine if phone uses lock pattern
//It returns 1 if pattern lock enabled and 0 if pin/password password enabled
ContentResolver cr = getBaseContext().getContentResolver();
lockPatternEnable = Settings.Secure.getInt(cr, Settings.Secure.LOCK_PATTERN_ENABLED, 0);//Settings.System
//returns 1 if pin/password protected. 0 if not
KeyguardManager keyguardManager = (KeyguardManager) getBaseContext().getSystemService(Context.KEYGUARD_SERVICE);
if( keyguardManager.isKeyguardSecure())
{
//it is pin or password protected
pinPasswordEnable=1;
}
else
{
//it is not pin or password protected
pinPasswordEnable=0;
}//http://stackoverflow.com/questions/6588969/device-password-in-android-is-existing-or-not/18716253#18716253
//determine if adb is enabled. works
adb=Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0);
//determine if bluetooth is enabled.works
bluetooth=Settings.Global.getInt(cr, Settings.Global.BLUETOOTH_ON, 0);
//Settings.System BLUETOOTH_DISCOVERABILITY
//determine if wifi is enabled. works
WifiManager wifi = (WifiManager)getSystemService(Context.WIFI_SERVICE);
if (wifi.isWifiEnabled())
{
//wifi is enabled
wifiInt=1;
}
else
wifiInt=0;
//determine if data is encrypted
getDeviceEncryptionencryption();
//determine if gps enabled
}//end of onStart() function
If any more code needs to be posted to answer this question, just let me know, and thanks for your help. Maybe the issue has something to do with the super.onStart();
Does anyone think that a splash loading screen might help solve the issue?
Here is good explanation how app lifecycle flows.
onStart() can be executed many times. You can keep counter how many times you had entered this method and act differently on each time:
static int counter=0;
public void onStart()
{
counter++;
Log.i("MyApp", "onStart() run "+counter);
switch (counter){
case 1: break; // first run
case 2: break; // second run
default: break;// other runs
}
}
To be more clear about life cycle and why your onStart() method is called twice I suggest to have counter and Log.i() in each important state of the cycle - at least in onCreate() and onRestart().
Keep in mind that app stays in memory when you click Home button. When you click app icon again it restarts already running app (calls onRestart() and then onStart() methods and no onCreate() ). When you really kill you app for real then sequence would be onCreate and onStart without onRestart. Having logcat records really helps you to understand app lifecycle flow and why your onStart() is called twice or more times.
Using a static variable to check how many times onStart has been called isn't a good idea, because an app can be killed if Android needs more memory for other apps while still allowing the user to navigate back to the app. That would be the path through the red box in the picture below (http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle):
A static variable would be 0 again after that and your app would run the security check again.
What you need to do is use an instance variable that you persist in onSaveInstanceState and restore in onCreate. In case the app is killed, onSaveInstanceState is called and you save your Activity's state. If the user goes back to the app, onCreate is called and the state would be restored. This works for all other cases too when the app isn't killed but the user just navigates away from the app and later re-opens it. Here's a simple example of an app saving and restoring:
public class MainActivity extends Activity {
private boolean mSecurityCheckDone;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mSecurityCheckDone = savedInstanceState.getBoolean("mSecurityCheckDone");
}
}
#Override
protected void onStart() {
super.onStart();
if (! mSecurityCheckDone) {
// run the security check
mSecurityCheckDone = true;
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("mSecurityCheckDone", mSecurityCheckDone);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
mSecurityCheckDone = savedInstanceState.getBoolean("mSecurityCheckDone");
}
}
}
how about using flag ?
singleTop
If an instance of the activity already exists at the top of the target
task, the system routes the intent to that instance through a call to
its onNewIntent() method, rather than creating a new instance of the
activity.
singleTask
The system creates the activity at the root of a new task and routes
the intent to it. However, if an instance of the activity already
exists, the system routes the intent to existing instance through a
call to its onNewIntent() method, rather than creating a new one.
singleInstance
Same as "singleTask", except that the system doesn't launch any other
activities into the task holding the instance. The activity is always
the single and only member of its task.
http://developer.android.com/guide/topics/manifest/activity-element.html
I'm not sure why you are using onStart(), if you want it to be run the first time the activity is created I would probably use onCreate() instead.
There is no API in Android that will tell you if the app has been run at least once so you will need to use some type of persistent storage for that, e.g. SharedPreferences could be used to persist a flag that would be set the first time your app is run and thereafter you can check it as shown here.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences settings = getSharedPreferences("Prefs", 0);
if (settings.getBoolean("first_time", true)) {
//the app is being launched for first time, do something
Log.d("Comments", "First time");
// first time task
// record the fact that the app has been started at least once
settings.edit().putBoolean("first_time", false).commit();
}
}
I've been looking for a way to have the blank detail side of my fragment layout host a welcome screen (or something - login perhaps) on start up. Afterwards, when a user presses one of the left side menu items, I'd like to eliminate the fragment for the remainder of the program run. I don't want to add it to the backstack, as that messes up my configuration changes. I've considered using shared prefs to host a boolean about whether the fragment has been displayed. The only concern with this method is where to safely reset the boolean value for the next run of the app. I'm of the impression that there's no gaurantee that the onStop, onDetach etc. will definitely get called upon closing of the app, so if the app got closed in the wrong state, it would be rendered useless ( the first fragment wouldn't display - crash )
Anyone have any ideas on how I could implement a filler for the right side of the app upon startup?
I've been trying to add something to the onCreate of my main activity thus far with no success.
Thanks in advance.
Ken
If your fragment can be part of its own Activity, you can use the android:noHistory="true" attribute to keep the Activity off of the backstack. If your user tries to navigate backwards, it'll hit the bottom of the backstack twice before exiting your application.
If you can't split your fragment into its own activity, noHistory may not work -- I can't say as I haven't tried it myself.
I was able to come up with a solution to creating a welcome or login screen which will display both fragments and activities from the main activity. Seems to be working fine as tested.
private boolean welcomeShown;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_list);
if (findViewById(R.id.item_detail_container) != null) {
mTwoPane = true;
((MainFragment) getSupportFragmentManager().findFragmentById(
R.id.item_list)).setActivateOnItemClick(true);
}
if (savedInstanceState != null){
welcomeShown = savedInstanceState.getBoolean("displayed");
}
if(!welcomeShown){
if (mTwoPane){
WelcomeFragment welcomeFragment = new WelcomeFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.item_detail_container, welcomeFragment)
.commit();
}
else{
Intent welcomeIntent = new Intent(this, WelcomeActivity.class);
startActivity(welcomeIntent);
welcomeShown = true;
}
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("displayed", true);
}
This app is simple and all I want it to do is just keep the text in the text field even after I close the app. I looked through some tutorials but I can't seem to figure out how to get it to save with onSaveInstanceState and onRestoreInstanceState. How can I do it?
Here is notes.java:
public class notes extends Activity{
/** Called when the activity is first created. */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notes);
Button wg = (Button) findViewById(R.id.button3);
wg.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent intent = new Intent();
setResult(RESULT_OK, intent);
finish();
}
});
}
}
How do I save text input into preferences with onSaveInstanceState?
You don't. onSaveInstanceState() is only for updating the supplied Bundle, with data that the user might like to keep if it works out (e.g., onSaveInstanceState() is called as part of flipping from portrait to landscape or back again).
all I want it to do is just keep the text in the text field even after I close the app
Then onSaveInstanceState() is not the right place. Either save it in onPause(), or perhaps on an explicit action by the user (e.g., clicking a Save button or menu option).
In my android app the user can enter the text in the EditView and the click on a button which takes him to an other activity where he can select a contact ... and then press a button which
brings him back to the first activity...
now the problem is I need to pass the selected contact to the first activity and display it (which i have done it using a bundle) but i am unable to retain already entered text in the EditView... which i should do (but the text should be retained with out passing it through the the bundle and getting it back)
thanks :)
The text in a view component is automagically saved by the OS, even after a soft kill (user changed phone orientation), but not after a hard kill, the user hit the back button while the parent activity was in focus. So, unless you are doing something non-standard, such as calling onSaveInstanceState without calling super.onSaveInstanceState, the data in the view state should persist.
One solution would be to save the text in the view component as a non view instance property before you launch the child activity, and just read this value back when the focus returns to the parent activity in the method onActivityResult.
JAL
EDIT: The Android Docs Activity page has been extensively updated. View state will not be saved if the widget does not have an ID.
EDIT: What I am saying is that the view state should be persisted by the OS. You should not need to save the view state manually. On a hard kill, you would need to save the state of your activity IF that is the expected behavior of the activity. So here is some code that saves the activity state. Given an instance variable:
String password;
Here we save state on a soft kill:
protected void onSaveInstanceState(Bundle outState){
password= editTextPassword.getText().toString();
outState.putString("password", password);
super.onSaveInstanceState(outState); // save view state
}
Here we save state on a hard kill
#Override
protected void onStop(){
super.onStop();
SharedPreferences prefs = getPreferences(MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("password",password);
editor.commit();
Here we restore state in onCreate(Bundle savedInstanceState):
if( savedInstanceState!= null){ // get saved state from soft kill after first pass
try {
password= savedInstanceState.getString("password");
Log.d(TAG,"RestoredState!");
}
catch(Exception e){
Log.d(TAG,"FailedToRestoreState",e);
}
}
else { // get saved state from preferences on first pass
SharedPreferences prefs = getPreferences(MODE_PRIVATE); // singleton
if (prefs != null){
this.password= prefs.getString("password","");
Log.d(TAG,"gettingPrefs");
}
}
Log.d(TAG,"onCreate");
Also given the fact that IF onSaveInstanceState is called it will be called before onStop, it is possible to use the flags isSavedInstanceState and isSavedPreferences to write to prefs ONLY on a hard kill if you reset the flags in onResume as:
protected void onResume() {
super.onResume();
Log.d(TAG,"onResume");
isSavedInstanceState= false;
isSavedPrefs= false;
}
Setting the flags in onCreate will not result in the desired outcome.