The philosophy of Activity/View destruction
- Rotating device screen.
- Change the location or change preferred language.
- Expand or hide a physical keyboard on the device which has sliding keyboard.
Now, considering with this example. Firstly, we have a simple
Activity
layout with only Button
and TextView
:activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Click Me!" />
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_horizontal_margin"
android:gravity="center"
android:text="This is a TextView"
android:textStyle="bold" />
</LinearLayout>
And this is programmatically code:
MainActivity.java
When running app, we will have this output screen:package info.devexchanges.handlingrotation;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView textView;
private View button;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
textView.setText("Button clicked!");
}
});
}
}
When click the
Button
:
But when you rotate device, it's will reset to it's original state:
Saving your Activity state
onSaveInstanceState()
that you want regardless of your activity is destroyed by any reason. That means you must provide sufficient information to Bundle
object, it will help you return to the current state of activity (state before destroyed). Then, in the method onCreate()
(or methods onRestoreInstanceState()
if you want), get data from objects Bundle
up and use them to restore your activity as before.Rewrite the above Java code like this:
MainActivity.java
By this way, the package info.devexchanges.handlingrotation;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView textView;
private View button;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text);
if (savedInstanceState != null) {
textView.setText(savedInstanceState.getString("message"));
}
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
textView.setText("Button clicked!");
}
});
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("message", textView.getText().toString());
}
}
TextView
content is preserved when you rotate the device!Now With More Savings!
onSaveInstabceState()
that you are limited to only one object Bundle
. Because this callback method is also used in situations where your all processes is terminated, so the data that needs to be saved must be something might sequentially and not depend on your running processes.One way to get past this is to use
onRetainNonConfigurationInstance()
instead of onSaveInstanceState()
for “light” changes like a rotation. Your activity’s onRetainNonConfigurationInstance()
callback can return an Object, which you can retrieve later via getLastNonConfigurationInstance()
. The Object
can be just about anything you want - typically, it will be some kind of “context” object holding activity state, such as running threads, open sockets, and the like. Your activity’s Object
can call getLastNonConfigurationInstance()
- if you get a non-null response, you now have your sockets and threads and whatnot. The biggest limitation is that you do not want to put in the saved context anything that might reference a resource that will get swapped out, such as a Drawable
loaded from a resource.Rewrite above code with this way:
MainActivity.java
Important Note: In order to use package info.devexchanges.handlingrotation;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView textView;
private View button;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text);
//check if data saved
final String data = (String) getLastNonConfigurationInstance();
if (getLastNonConfigurationInstance() != null) {
textView.setText(data);
}
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
textView.setText("Button clicked!");
}
});
}
@Override
public Object onRetainNonConfigurationInstance() {
return textView.getText().toString();
}
}
onRetainNonConfigurationsInstance()
, your activity must extend Activity
class, not FragmentActivity
or it's subclass.However, even approaches using
onRetainNonConfigurationInstance()
in handling screen rotation can still cause many problems for your application. Moreover, this method is now is deprecated, So I do not recommend using this way, with the much complicated data, you should use other official solutions in saving data (Preferences, Files or database) and managing the loading process. Another way of handling rotation is use onConfigurationChanged()
method, but Google is not recommended it, too. So, I wouldn't like to present it!Prevent screen rotation
android:screenOrientation="portrait"
(or landscape
if you want) in AndroidManifest.xml
file as follows:
AndroidManifest.xml
Since it only applies to each activity, so you will need to decide what their activity should enable this feature. With settings in <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="info.devexchanges.handlingrotation">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:screenOrientation="portrait"
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
AndroidManifest.xml
file as above, now your application will always remain way under value you specify (in this example, it will always be portrait) regardless of whatever you can do.