Android Activity的一些要点

qq54728811 8年前
   <p>Activity是安卓四大组件之一,也是使用最频繁的组件,这里就来记录下在使用Activity的过程中应该注意的一些小细节</p>    <h2><strong>一、Activity的生命周期</strong></h2>    <ol>     <li>"onStart与onResume"、"onPause与onStop"在描述上来看都是类似的,当Activity开始创建调用了onCreate方法后,会依次调用"onStart与onResume",当Activity切换时到另一个界面时,会依次调用"onPause与onStop"<br> 那么,它们之间有什么不同呢?可不可以只保留其中之一呢?<br> 其实,onStart与onResume的差异在于要启动的Activity是否可见,两者都是Activity从创建到显示在前台这之间被调用的,不过onStart方法被调用时Activity还在后台,onResume被调用时表示Activity已经可见并开始活动了<br> onPause与onStop在用户切换Activity或者打开一个Dialog时调用。当用户直接切换到一个非透明Activity时,onPause与onStop会依次调用,如果下一个Activity是透明主题,或者打开的是一个Dialog(只遮挡了一部分当前Activity的界面)时,只会调用onPause,而不会调用onResume</li>     <li>当前台Activity A切换到Activity B时,两者之间的回调函数调用顺序是怎样的呢?<br> 首先新建一个MainActivity,重写其各个回调函数,添加一句Log输出语句,类似如下所示 <pre>  <code class="language-java">private final String TAG = "MainActivity";  @Override  protected void onStart() {      super.onStart();      Log.e(TAG, "onStart被调用");  }</code></pre> 再在主布局文件中添加一个按钮,用于启动第二个Activity——Main2Activity<br> 也重写Main2Activity的各大回调函数,与MainActivity类似 <pre>  <code class="language-java">private final String TAG = "第二个Activity";   @Override  protected void onStart() {      super.onStart();      Log.e(TAG, "onStart被调用");  }</code></pre> 启动MainActivity,输出语句如下: <pre>  <code class="language-java">com.czy.myapplication E/MainActivity: onCreate被调用  com.czy.myapplication E/MainActivity: onStart被调用  com.czy.myapplication E/MainActivity: onResume被调用</code></pre> 点击按钮切换Activity <pre>  <code class="language-java">com.czy.myapplication E/MainActivity: onPause被调用  com.czy.myapplication E/第二个Activity: onCreate被调用  com.czy.myapplication E/第二个Activity: onStart被调用  com.czy.myapplication E/第二个Activity: onResume被调用  com.czy.myapplication E/MainActivity: onStop被调用</code></pre> 点击back键回退到MainActivity <pre>  <code class="language-java">com.czy.myapplication E/第二个Activity: onPause被调用  com.czy.myapplication E/MainActivity: onRestart被调用  com.czy.myapplication E/MainActivity: onStart被调用  com.czy.myapplication E/MainActivity: onResume被调用  com.czy.myapplication E/第二个Activity: onStop被调用  com.czy.myapplication E/第二个Activity: onDestroy被调用</code></pre> 可以看出来,当启动新的Activity或回退到前一个Activity时,执行顺序是:之前的Activity的onPause方法被调用——要切换到的Activity的onResume方法被调用——之前的Activity的onStop方法被调用<br> 因此,原Activity的onPause不能用于执行耗时操作,否则会影响新的Activity的显示</li>    </ol>    <h2><strong>二、Activity异常情况下的数据保存</strong></h2>    <ol>     <li> <p>当Android系统资源相关的配置发生变化(如横竖屏切换)时,默认情况下当前Activity就会被销毁并重新创建</p> <p>此时Activity是由于异常情况而销毁的,所以系统会调用如下方法来保存当前数据</p> <pre>  <code class="language-java">protected void onSaveInstanceState(Bundle outState)</code></pre> <p>即我们可以向Bundle中存入需要恢复的数据</p> <p>当Activity被重新创建后,以下方法会被自动调用</p> <pre>  <code class="language-java">protected void onRestoreInstanceState(Bundle savedInstanceState)</code></pre> <p>该Bundle就是在onSaveInstanceState中所使用的,我们可以从中取出数据,用于恢复Activity的状态</p> </li>    </ol>    <p>此外,该Bundle同时也会传递给</p>    <pre>  <code class="language-java">protected void onCreate(Bundle savedInstanceState)</code></pre>    <p>因此我们可以选择在两个方法其中之一来恢复数据,一般是在onRestoreInstanceState(Bundle savedInstanceState)当中执行恢复操作比较合理</p>    <p>且onRestoreInstanceState方法只在Bundle不为空时才会调用,而在onCreate方法中还需要再次判断Bundle的值</p>    <p>例如,如果当前Activity中有一个EditText,当屏幕发生旋转时,可以将其文本内容存入Bundle中,当重新创建Activity时再将其从Bundle取出并显示在EditText当中</p>    <pre>  <code class="language-java">@Override      protected void onSaveInstanceState(Bundle outState) {          super.onSaveInstanceState(outState);          EditText editText = (EditText) findViewById(R.id.editText);          if (!TextUtils.isEmpty(editText.getText())) {              outState.putString("TextContent", editText.getText().toString());          }      }        @Override      protected void onRestoreInstanceState(Bundle savedInstanceState) {          super.onRestoreInstanceState(savedInstanceState);          String content = savedInstanceState.getString("TextContent", "");          EditText editText = (EditText) findViewById(R.id.editText);          editText.setText(content);      }</code></pre>    <p>View本身也包含有该两个方法,某些View的数据保存与恢复操作系统已经自动为我们完成了,这也是我们旋转屏幕重新创建Activity后某些状态依然一样的原因</p>    <p>从Activity第一次创建到旋转屏幕,各回调函数的执行顺序如下所示:</p>    <pre>  <code class="language-java">com.czy.myapplication E/MainActivity: onCreate被调用  com.czy.myapplication E/MainActivity: onStart被调用  com.czy.myapplication E/MainActivity: onResume被调用    com.czy.myapplication E/MainActivity: onPause被调用  com.czy.myapplication E/MainActivity: onSaveInstanceState被调用  com.czy.myapplication E/MainActivity: onStop被调用  com.czy.myapplication E/MainActivity: onDestroy被调用    com.czy.myapplication E/MainActivity: onCreate被调用  com.czy.myapplication E/MainActivity: onStart被调用  com.czy.myapplication E/MainActivity: onRestoreInstanceState被调用  com.czy.myapplication E/MainActivity: onResume被调用</code></pre>    <p>2 . 禁止因系统配置改变而重新创建Activity</p>    <p>如果不想在系统配置被改变后而导致Activity重新创建,可以在Activity声明时为之指定configChanges属性</p>    <pre>  <code class="language-java">android:configChanges="orientation|screenSize"</code></pre>    <p>这样,当屏幕旋转时Activity就不会重新创建也不会去进行数据保存和数据恢复操作了,此时系统会改为去调用以下方法</p>    <pre>  <code class="language-java">public void onConfigurationChanged(Configuration newConfig)</code></pre>    <p>重写之</p>    <pre>  <code class="language-java">@Override  public void onConfigurationChanged(Configuration newConfig) {      super.onConfigurationChanged(newConfig);      Log.e(TAG, "onConfigurationChanged被调用,屏幕方向:" + newConfig.orientation);  }</code></pre>    <p>这样,当多次旋转屏幕时,onSaveInstanceState和onRestoreInstanceState均不会被调用,调用的只是onConfigurationChanged方法,且Activity并没有销毁重建</p>    <pre>  <code class="language-java">com.czy.myapplication E/MainActivity: onCreate被调用  com.czy.myapplication E/MainActivity: onStart被调用  com.czy.myapplication E/MainActivity: onResume被调用  com.czy.myapplication E/MainActivity: onConfigurationChanged被调用,屏幕方向:2  com.czy.myapplication E/MainActivity: onConfigurationChanged被调用,屏幕方向:1  com.czy.myapplication E/MainActivity: onConfigurationChanged被调用,屏幕方向:2  com.czy.myapplication E/MainActivity: onConfigurationChanged被调用,屏幕方向:1</code></pre>    <h2><strong>三、Activity的启动模式</strong></h2>    <p>Android共有四种Activity启动模式,分别为:standard、singleTop、singleTask、singleInstance</p>    <p>Activity位于任务栈当中,栈是一种“后进先出”的结构,当点击back键时,栈顶的Activity会一一出栈,直到栈为空后退出程序</p>    <p>Activity的默认栈名为包名,可以通过taskAffinity属性来设置,该属性必须包含有包名分隔符“.”,而不能仅仅是一个单词</p>    <pre>  <code class="language-java"><activity          android:name=".AnotherActivity"          android:launchMode="singleTask"          android:taskAffinity="com.custom.task" /></code></pre>    <p>taskAffinity属性主要和singleTask模式和allowTaskReparenting属性配合使用,在其他情况下没有作用</p>    <p>(1) standard</p>    <p>此为标准模式,即Activity的默认启动模式</p>    <p>在该启动模式下,每启动一个Activity,不管该Activity是否已存在,均会创建一个新的Activity实例,并将该Activity压入启动它的Acivity所在的任务栈的栈顶(singleInstance模式除外),不管该栈的栈名和新启动的Activity在AndroidManifest中指定的taskAffinity属性是否相同</p>    <p>(2) singleTop</p>    <p>此为栈顶复用模式</p>    <p>如果要启动的Activity已位于任务栈的栈顶,则此Activity不会被重新创建</p>    <p>不过,此时该Activity的以下方法会被调用</p>    <pre>  <code class="language-java">protected void onNewIntent(Intent intent)</code></pre>    <p>可以通过该intent来传输数据</p>    <p>例如,在启动Activity时,向intent传入文本信息</p>    <pre>  <code class="language-java">public void startAnotherActivity(View view) {          Intent intent = new Intent(this, AnotherActivity.class);          Bundle bundle = new Bundle();          bundle.putString("Content", "要传输的内容");          intent.putExtras(bundle);          startActivity(intent);      }</code></pre>    <p>然后在onNewIntent方法中再来获取数据</p>    <pre>  <code class="language-java">@Override      protected void onNewIntent(Intent intent) {          super.onNewIntent(intent);          Log.e(TAG, "onNewIntent被调用");          Bundle bundle = intent.getExtras();          if (bundle != null) {              String text = bundle.getString("Content", "默认文本");              Toast.makeText(AnotherActivity.this, text, Toast.LENGTH_SHORT).show();          }      }</code></pre>    <p>需要注意的是:如果要启动的Activity并不位于栈顶,则会创建该Activity实例,且onNewIntent方法不会被调用</p>    <p>(3) singleTask</p>    <p>即栈内复用模式</p>    <p>这是一种单实例模式,在该模式下,只要Activity存在于栈中,不管是否位于栈顶,均不会重新创建Activity实例,且系统也会回调其onNewIntent方法</p>    <p>如果该Activity存在但不位于栈顶,则系统会弹出该Activity之上的所有Activity,将该Activity置于栈顶</p>    <p>(4)singleInstance</p>    <p>即单实例模式</p>    <p>该模式与singleTask模式类似,不过该模式下的Activity只能单独地位于一个任务栈中</p>    <p>例如,如果Activity A是singleInstance模式,当A第一次启动时,系统会为之创建一个任务栈,然后将A单独置于该栈中,除非A被销毁了,否则之后每次启动A,都不会重新创建实例</p>    <p>指定启动模式有两种方法:</p>    <p>1.在AndroidManifest中直接为Activity指定启动模式</p>    <pre>  <code class="language-java"><activity          android:name=".AnotherActivity"          android:launchMode="singleTask"          android:taskAffinity="com.custom.task" /></code></pre>    <p>2.在Intent中设置标志位Flags来为Activity指定启动模式</p>    <pre>  <code class="language-java">Intent intent = new Intent(this, AnotherActivity.class);      intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);      startActivity(intent);</code></pre>    <p>在优先级上,第二种方式的优先级要高于第一种,如果两者同时存在,则以第二种为准</p>    <p>两者在限定范围上有所差别,第一种方式无法设定“Intent.FLAG_ACTIVITY_CLEAR_TOP”模式,第二种方式无法设定“singleInstance”模式</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/872a87fc5734</p>    <p> </p>