I know there are lots of quetions regarden onSaveInstanceState but I wasn't able to find an answer to my problem.
I have an activity that extends AppCompatActivity; this activity uses 3 fragments and it has a variable 'int currentStep' to track which fragment is being displayed. In the onSavedInstanceState method I store the currentStep variable in the bundle, and then in onCreate method I restore it.
public class MainActivity extends AppCompatActivity {
private final String CURRENT_STEP_TAG = "current_step";
private int currentStep;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pedido);
if(savedInstanceState == null) {
loadFirstFragment();
} else {
currentStep = savedInstanceState.getInt(CURRENT_STEP_TAG);
if(currentStep == 2){
//Do some stuff...
}
}
}
#Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
Log.d("deubg", "--------onsaveinstancestate--------");
outState.putInt(CURRENT_STEP_TAG, currentStep);
super.onSaveInstanceState(outState, outPersistentState);
}
...
}
The thing is that onSavedInstanceState won't get called when screen orentation changes, and according to google documentation, it should. The message "--------onsaveinstancestate--------" doesn't show in the console.
However the Bundle savedInstanceState in the method onCreate is non null after screen rotation, but it doesn't have the int 'currentStep'.
I've tried many things: changed AppCompatActivity for ActionBarActivity, moved the call for super.onSavedInstanceState to different locations, tried to store other variables; but no matter what I do the method doesn't execute.
Also I DO NOT have android:configChanges in my manifest.
My application is being compiled against sdk version 22, buildToolsVersion 22.0.1
Unless your app is running on Lollipop (API21) version of Android or newer, your
public void onSaveInstanceState (Bundle outState, PersistableBundle outPersistentState);
will NOT be called as it simply does not exist on earlier versions of the platform than 21. To support pre API 21 devices you must, instead of the above, override the following method:
public void onSaveInstanceState (Bundle outState);
This will work on API 21+ as well, so you do not need to override both methods, of course (unless you know you need to deal with PersistableBundle the new one offers).
See docs.
Related
im sorry for the long question, but i could really use the help
so I've been trying to make a camera app for this school project that i have. i'm really new to coding in general, and i don't really know much about Java. i decided to use the CameraKit library by Furgle to help me with this. they say all i have to do is include
protected void onResume() {
super.onResume();
CameraView.start();
and
#Override
protected void onPause () {
super.onPause();
CameraView.stop();
}
i should be able to start and stop the camera preview im trying to create.
however, when i added this code to my main activity, i got the following:
non static method 'stop()' / 'start()' cannot be referenced from a static context
I've tried a few things like trying to create an object of the class and calling the method from that (i'm not completely sure if i said that right or not)
#Override
protected void onResume() {
super.onResume();
CameraView main = new CameraView()
main.start();
when i try to run that, i get:
cannot resolve constructor CameraView()
I also tried to create instances of the class called "CameraView" which is where the method "start();" and "stop();" are. sadly i have not been able to get anywhere with that.
the point is i tried everything that i could understand but any help would be greatly appreciated.
after looking into the code for the library, i saw that neither the start method or the stop method within the CameraView class are declared "static". so i really don't see where the problem is coming from and how to overcome it
Assuming the tutorial you're following is this one https://github.com/gogopop/CameraKit-Android#usage ...
When they say that "all you have to do" is add this code:
#Override
protected void onResume() {
super.onResume();
cameraView.start();
}
#Override
protected void onPause() {
cameraView.stop();
super.onPause();
}
They're speaking to more-experienced developers. The part they're leaving out is where does cameraView come from?
Well, the first step is to include a <CameraView> in your layout. But even after that, you need to find it and assign it to a cameraView variable. So really, you need all this:
public class MainActivity extends AppCompatActivity {
private CameraView cameraView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // `activity_main.xml` must have a `<CameraView>` tag with id `camera`
cameraView = (CameraView) findViewById(R.id.camera);
}
#Override
protected void onResume() {
super.onResume();
cameraView.start();
}
#Override
protected void onPause() {
cameraView.stop();
super.onPause();
}
}
I'd like to implement an update checker in an application, and I obviously only need this to show up once when you start the application. If I do the call in the onCreate() or onStart() method, it'll be shown every time the activity is created and this is not a viable solution.
So my question is: Is there a way to do something, like check for updates, just once per application start / launch?
I'm sorry if it's a bit hard to understand, I'm having difficulties explaning myself on this one.
SharedPreferences seems like ugly solution to me. It's much more neat when you use application constructor for such purposes.
All you need is to use your own Application class, not default one.
public class MyApp extends Application {
public MyApp() {
// this method fires only once per application start.
// getApplicationContext returns null here
Log.i("main", "Constructor fired");
}
#Override
public void onCreate() {
super.onCreate();
// this method fires once as well as constructor
// but also application has context here
Log.i("main", "onCreate fired");
}
}
Then you should register this class as your application class inside AndroidManifest.xml
<application android:label="#string/app_name" android:name=".MyApp"> <------- here
<activity android:name="MyActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
You even can press Back button, so application go to background, and will not waste your processor resources, only memory resource, and then you can launch it again and constructor still not fire since application was not finished yet.
You can clear memory in Task Manager, so all applications will be closed and then relaunch your application to make sure that your initialization code fire again.
looks like you might have to do something like this
PackageInfo info = getPackageManager().getPackageInfo(PACKAGE_NAME, 0);
int currentVersion = info.versionCode;
this.versionName = info.versionName;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
int lastVersion = prefs.getInt("version_code", 0);
if (currentVersion > lastVersion) {
prefs.edit().putInt("version_code", currentVersion).commit();
// do the activity that u would like to do once here.
}
You can do this every time, to check if the app has been upgraded, so it runs only once for app upgrade
The shared preferences approach is messy, and the application class has no access to an activity.
Another alternative I've used is to have a retained fragment instance, and within that instance, a lot more stuff can be done especially if you need access to the main activity UI.
For this example, I've used asynctask within the retained fragment. My AsyncTask has callbacks to the parent activity. It is guaranteed to run only once per application because the fragment is never destroyed-recreated when the same activity is destroyed-recreated. It is a retained fragment.
public class StartupTaskFragment extends Fragment {
public interface Callbacks {
void onPreExecute();
void onProgressUpdate(int percent);
void onCancelled();
void onPostExecute();
}
public static final String TAG = "startup_task_fragment";
private Callbacks mCallbacks;
private StartupTask mTask;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (Callbacks) activity;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true); // this keeps fragment in memory even if parent activity is destroyed
mTask = new StartupTask();
mTask.execute();
}
#Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
private class StartupTask extends AsyncTask<Void, Integer, Void> {
#Override
protected void onPreExecute() {
if (mCallbacks != null) {
mCallbacks.onPreExecute();
}
}
#Override
protected Void doInBackground(Void... ignore) {
// do stuff here
return null;
}
#Override
protected void onProgressUpdate(Integer... percent) {
if (mCallbacks != null) {
mCallbacks.onProgressUpdate(percent[0]);
}
}
#Override
protected void onCancelled() {
if (mCallbacks != null) {
mCallbacks.onCancelled();
}
}
#Override
protected void onPostExecute(Void ignore) {
if (mCallbacks != null) {
mCallbacks.onPostExecute();
}
}
}
}
Then, in main (or parent) activity where you want this startup task fragment to run once.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getFragmentManager();
StartupTaskFragment st = (StartupTaskFragment) fm.findFragmentByTag(StartupTaskFragment.TAG);
if(st == null) {
fm.beginTransaction().add(mStartupTaskFragment = new StartupTaskFragment(), StartupTaskFragment.TAG).commit();
}
...
}
Ideas for retained fragment came from here: http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html. I just figured out its other uses aside from config changes.
Yes you can do it Using SharedPrefernce concept of android. Just create a boolean flag and save it in SharedPrefernce and check its value in your onCreate() method .
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (!prefs.getBoolean("onlyonce", false)) {
// <---- run your one time code here
// mark once runned.
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("onlyonce", true);
editor.commit();
}
}
This continues on #Vitalii's answer.
After having setup the Application class, if access to the Activity is required, we can use the aptly named android library "Once" https://github.com/jonfinerty/Once.
In the Application class's onCreate method
Once.initialise(this)
In the Activity / Fragment class's onCreate / onViewCreated method.
val helloTag = "hello"
if (!Once.beenDone(Once.THIS_APP_SESSION, helloTag)) {
//Do something that needs to be done only once
Once.markDone(helloTag) //Mark it done
}
I do this the same way as described in the other answer. I just have a global variable in the first activity which matches the release number from the manifest. I increment it for every upgrade and when the check sees a higher number, it executes the one-time code.
If successful, it writes the new number to shared preferences so it wont do it again until the next upgrade.
Make sure you assign the default to -1 when you retrieve the version from shared preferences so that you error on the side of running the code again as opposed to not running it and not having your app update correctly.
Use SharedPreference for this-
If you are not restarting your launcher activity again once your app is active then in that case you case use it.
Use this in a Splash screen if you are implementing it in the app.
If you are not using any splash screen then you need to create a activity with no view set and on it's oncreate call you can do start updation and start your main activity.
you can use counter value or boolean for this.
Here is SharedPreference doc:
http://developer.android.com/reference/android/content/SharedPreferences.html
try {
boolean firstboot = getSharedPreferences("BOOT_PREF",MODE_PRIVATE)
.getBoolean("firstboot", true);
if(firstboot){
//place your code that will run single time
getSharedPreferences("BOOT_PREF",MODE_PRIVATE).edit().
putBoolean("firstboot", false)
.commit();
}
I just solved doing this myself, I reopen my main activity multiple times throughout the application's execution. While the constructor is a valid approach for some things it doesn't let you access the current Application context to write toasts among other things.
My solution was to create a simple 'firstRun' boolean set to true in the class of my MainActivity, from there I run the contents of the if statement then set it to true. Example:
public class MainActivity extends AppCompatActivity
{
private static boolean firstRun = true;
#Override
protected void onCreate(Bundle savedInstanceState)
{
if(firstRun)
{
Toast.makeText(getApplicationContext(), "FIRST RUN", Toast.LENGTH_SHORT).show();
//YOUR FIRST RUN CODE HERE
}
firstRun = false;
super.onCreate(savedInstanceState);
//THE REST OF YOUR CODE
}
I'm having a problem restoring the state of nested fragments. Here's the hierarchy:
MainActivity
----> AlarmOverviewFragment (doesn't really hold any important data)
--------> AlarmFragment (holds data)
The problem I am running into is when I minimize the app (press home button) and then reopen the app, it crashes every time when trying to get data from an array stored as one of Fragment's instance variables inside of onCreateView().
The frustrating bit is that I'm saving the instance variables in onSaveInstanceState(), but savedInstanceState is always null in onCreateView(), onCreate(), and onActivityCreated().
Clearly I am missing something huge here because I've tried many solutions found on stackoverflow to no avail. Here's some relevant code bits:
Saving instance data:
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt("hour", this.hour);
outState.putInt("window", this.window);
outState.putInt("minutes", this.minutes);
outState.putBooleanArray("days", this.days);
super.onSaveInstanceState(outState);
}
Unsuccessfully trying to restore data
#Override
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
/* Never happens */
hour = savedInstanceState.getInt("hour");
window = savedInstanceState.getInt("window");
minutes = savedInstanceState.getInt("minutes");
days = savedInstanceState.getBooleanArray("days");
}
super.onCreate(savedInstanceState);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
if (savedInstanceState != null) {
/* Also never happens */
}
super.onActivityCreated(savedInstanceState);
}
And just in case this is helpful, here's the stack trace:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: group12.wakemeup, PID: 22756
java.lang.RuntimeException: Unable to resume activity {group12.wakemeup/group12.wakemeup.MainActivity}: java.lang.NullPointerException: Attempt to read from null array
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3121)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3152)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1400)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5525)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:730)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)
Caused by: java.lang.NullPointerException: Attempt to read from null array
at group12.wakemeup.AlarmFragment.onCreateView(AlarmFragment.java:113)
at android.support.v4.app.Fragment.performCreateView(Fragment.java:2087)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1113)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1295)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:801)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1682)
at android.support.v4.app.FragmentController.execPendingActions(FragmentController.java:388)
at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:607)
at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:181)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1237)
at android.app.Activity.performStart(Activity.java:6288)
at android.app.Activity.performRestart(Activity.java:6334)
at android.app.Activity.performResume(Activity.java:6339)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3110)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3152)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1400)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5525)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:730)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)
EDIT
Extra information
Each AlarmFragment is added dynamically and given a unique ID via View.generateViewId(), so I've been unsure of how to save each fragment in MainActivity. Also worth mentioning that I do use a FragmentPagerAdapter to handle tabs/swiping (there are other Overview fragments in this app, but I left them out as they aren't causing me problems). I've heard there is a way to use a ViewPager to save state, perhaps there is one with a FragmentPagerAdapter as well?
Since your problem seems different from the start, actually you don't need to re-create your fragments inside onNewIntent every time.
You can do this instead:
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if ("some_action".equals(intent.getAction())) {
//your action, in your case create and assign fragment
//remove the action
intent.setAction(null);
}
}
Try to save your fragments in your activity
private YourFragment yourFragment;
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Save the fragment instances
getFragmentManager().putFragment(outState, "any string you want as key", yourFragment);
//or use code below if you use android.support.v4.app.Fragment
//getSupportFragmentManager().putFragment(outState, "any string you want as key", yourFragment);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
//retrieve your fragment that saved from onSaveInstanceState()
yourFragment = (YourFragment) getFragmentManager().getFragment(savedInstanceState, "any string you want as key");
//or
//yourFragment = (YourFragment) getSupportFragmentManager().getFragment(savedInstanceState, "any string you want as key");
}
else {
//create your fragment if it is first time
yourFragment = new YourFragment();
}
}
Well I guess my problem came from something completely different. MainActivity was creating a new AlarmFragment using onNewIntent() when it returned from a series of activities to define the values for each AlarmFragment. Evidently, minimizing the app and then reopening it calls onNewIntent() every time, so my solution was to make sure that I was actually creating new AlarmFragments with data. Stupid problem, stupid solution
Have some problem with android finish() methods.
I have one parent-class activity. Lets call it ParentActivity. All other activities in my project extends ParentActivity. Each time on ParentActivity.onCreate there are some statement, and I want to stop activity from executing if it fails. But when I call finish() in parent, I cant stop executing onCreate method on its child. Something like that:
public class ParentActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!someStatement) finish();
}
public class Test extends ParentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("TAG", "I dont want this code!");
}
}
Surely, I can just verify in parent activity its status each time, but I dont think its a good idea.
public class Test extends RexActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isFinishing()) return; /// It works - but it bad :(((
Log.d("TAG", "I dont want this code!");
}
}
Can I somehow stop executing onCreate method on child activity from its parent? Many thanks for any help!
I'm not sure if I got your question right. As you have some grammatical issues.
The onCreate statements are always executed. You can either have a Boolean in ParentActivity to stop the code from executing in ChildActivity#onCreate().
You can try making your onCreate() code more modular by dividing it into functions so that it's not called.
Let me know what works for you.
Best option is to Use finish() in your splash screen just before you create your second activity,
I'm currently writing on a Bluetooth application only allowing the user to work with it in the portait mode. However, there are some other reasons causing confguration changes forcing me to show the user that the Bluetooth connection has been lost.
While connecting to a device I show a dialogue prompting a password. If a configuration change occurs while this dialogue is visible I want to dismiss it. Therefore I call a helper method in onSaveInstanceState, onDestroy as well as onCreate (if the saved instance state is not null).
#Override
public void onCreate(Bundle savedInstanceState)
{
if (savedInstanceState != null)
{
this.tryDismiss();
}
super.onCreate(savedInstanceState);
}
#Override
public void onSaveInstanceState(Bundle outState)
{
System.out.println("Saving instance state!");
this.tryDismiss();
super.onSaveInstanceState(outState);
}
#Override
public void onDestroy()
{
System.out.println("Destroying dialogue!");
this.tryDismiss();
super.onDestroy();
}
public void tryDismiss()
{
try
{
System.out.println("DIE BART, DIE!");
this.dismissAllowingStateLoss();
}
catch (Exception closingException)
{
}
}
However, the dialogue is still displayed to the user. I have read a similar questions, but couldn't figure out a solution to this issue.
I appreciate any help. Thanks in advance!
You can easily dismiss() method
http://developer.android.com/reference/android/app/Dialog.html
Using a strong reference in the activity displaying the dialogue and invoking the dismiss() method in the activitie's onSaveInstanceStatemethod fixed this issue for me. I know, it's not the most optimal solution, but it works.
#Override
protected void onSaveInstanceState(Bundle persistantState)
{
if (this.connectionPasswordDialogue != null)
{
this.connectionPasswordDialogue.dismiss();
}
}
With strong reference I mean a private attribute used internally by the activity:
private DialogueUserModePassword connectionPasswordDialogue;