4.1 问题
在旋转过程中,默认会将Activity销毁,然后再重新创建,这会严重影响应用程序的性能。
如果没有自行修改的话,在配置变化时,Android会结束当前的Activity实例,然后重新创建一个适用于新配置的Activity实例,这会带来性能上的损失,因为这需要先保存UI状态,然后再完全重新构建UI。
4.2 解决方案
(API Level 1)
利用清单文件中的android:configChanges参数,告诉Android某个Activity在处理旋转事件时不需要运行时进行干预,这不仅能降低Android的工作量,即销毁和重建Activity实例,也会降低应用程序的工作量。保持Activity实例不变,应用程序就不必为了针对用户维持一致性而花费时间保存和还原应用程序的当前状态。
注册了一个或多个配置变动的Activity可以通过Activity.onConfigurationChanged()回调方法收到通知,在该方法中可以执行各种跟配置变动相关的必要手动操作。
要完全以手动方式处理旋转,Activity至少要注册三个配置变动参数:orientation、keyboardHidden和screenSize。
- orientation : 为设备方向变动的事件注册Activity。
- screenSize : 为设备屏幕长宽比变动的事件注册Activity。在每次方向变动时才会发生此事件。
- keyboardHidden : 为用户滑开或关闭物理按键的事件注册Activity。
尽管后者看上去跟屏幕旋转没有直接关系,但如果不注册这些事件的话,Android就会在这些事件发生时重建Activity,这会使前面手动处理旋转的努力付之东流。
4.3 实现机制
将这些参数添加到AndroidManifest.xml的任意一个<activity>元素中,如下所示:
<activity android:name = ".MyActivity"
android:configChanges = "orientation|keyboardHidden|screenSize"/>
在一条赋值语句中可以注册多种变动,用“|”符号将它们分开即可。因为这些参数都不能应用于<application>元素,所以每个Activity都必须在AndroidManifest.xml中注册。
注册Activity后,配置发生改变时就会调用Activity的onConfigurationChanged()方法。以下三段代码到一个简洁的Activity定义,用于处理变动发生时产生的回调。
res/layout/activity_manual.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="Rotate the device, Activity will remain"/>
<CheckBox
android:id="@+id/override"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Check to Force Reload View"/>
<EditText
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
res/layout-land/activity_manual.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBox
android:id="@+id/override"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Check to Force Reload View"/>
<EditText
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
手动管理旋转的Activity
public class MyActivity extends Activity {
//引用视图元素
private EditText mEditText;
private CheckBox mCheckBox;
@Override
protected void onCreate(Bundle savedInstanceState) {
//必须调用超类
super.onCreate(savedInstanceState);
//加载视图资源
loadView();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
//必须调用超类
super.onConfigurationChanged(newConfig);
//如果选中此复选框,则只重新加载新配置下的元素
if (mCheckBox.isChecked()) {
final Bundle uiState = new Bundle();
//保存重要的UI状态
saveState(uiState);
//重新加载视图资源
loadView();
//恢复UI状态
restoreState(uiState);
}
}
//实现持久化UI状态的代码
private void saveState(Bundle state) {
state.putBoolean("checkbox", mCheckBox.isChecked());
state.putString("text", mEditText.getText().toString());
}
//恢复重新加载之前保存的任何元素
private void restoreState(Bundle state) {
mCheckBox.setChecked(state.getBoolean("checkbox"));
mEditText.setText(state.getString("text"));
}
//设置内容视图并获得视图引用
private void loadView() {
setContentView(R.layout.activity_manual);
//我们必须在设置新的布局时重新视图引用
mCheckBox = (CheckBox) findViewById(R.id.override);
mEditText = (EditText) findViewById(R.id.text);
}
}
注意:
Google并不推荐这样处理旋转,除非应用程序的性能能确实有此要求。在响应各个变动事件时,所有跟配置有关的资源都必须手动加载。
Google建议允许默认的Activity旋转行为重建,除非应用程序的性能能确实有此要求。这主要是因为,阻止默认的Activity重建会导致Android无法加载在资源条件目录中存放的备选资源(例如res/layout-land/目录中的横屏布局资源)。
在实例的Activity中,所有用于处理视图布局的代码都被抽象成在onCreate()和onConfigurationChanged()中调用的私有方法loadView()中。在该方法中,类似setContentView()这样的代码可以确保加载与配置相匹配的布局。
调用setContentView()会重新加载视图,所以一定要在没有诸如onSaveInstanceState()和onRestoreInstanceState()等生命周期回调函数的协助下,保存所有的UI状态。为了达到该目的,示例中实现了saveState()和restoreState()方法。
为了在不添加视图重载代码的情况下演示此人Activity的行为,我们在布局中关联了一个复选框,用于确定在配置改变时视图是否再次加载,选中此复选框时,Activity仍然会旋转在新的方向重绘其内容。然而,相反的配置布局(横向或纵向)将不会重新加载。
网友评论