活动的生命周期与启动
实验内容
-
首先建立一个新项目,在项目中添加一个新的Activity,这样就有了两个Activity;
-
修改两个Activity的布局,为他们添加按钮用于跳转;为活动1添加按钮button_1,添加按钮text:"Goto Activity2"以及TextView:“this is activity1”;为活动2添加按钮button_2,添加text按钮:"Goto Activity1"以及TextView:“this is activity2”;
-
下一步就是在对应的activity中添加按钮点击的事件;
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button=(Button) findViewById(R.id.button_1); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent=new Intent(MainActivity.this,Main2Activity.class); //活动1向活动2的跳转; startActivity(intent); } }); }
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); Button button=(Button) findViewById(R.id.button_2); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent=new Intent(Main2Activity.this,MainActivity.class); //活动2向活动1的跳转; startActivity(intent); } }); }
-
之后在活动1中添加代码来记录onCreate, onStart, onResume, on Pause, onStop,onDestroy以及onRestart函数的调用情况。
package com.example.test3; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity { public static final String TAG="main"; @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG,"onDestory"); } protected void onPause(){ super.onPause(); Log.d(TAG,"onPause"); } protected void onResume(){ super.onResume(); Log.d(TAG,"onResume"); } protected void onStop(){ super.onStop(); Log.d(TAG,"onStop"); } protected void onRestart(){ super.onRestart(); Log.d(TAG,"onRestart"); } protected void onStart(){ super.onStart(); Log.d(TAG,"onStart"); } public String tmpData="I am original"; @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("IS",tmpData); } protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG,"onCreat"); setContentView(R.layout.activity_main); if(savedInstanceState!=null) { tmpData=savedInstanceState.getString("IS"); Log.d(TAG,tmpData); } Button button=(Button) findViewById(R.id.button_normal); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { tmpData="I am changed"; Log.d(TAG,tmpData); Intent intent=new Intent(MainActivity.this,normal.class); startActivity(intent); } }); Button button1=(Button) findViewById(R.id.button_dialog); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d(TAG,tmpData); Intent intent=new Intent(MainActivity.this,dialog.class); startActivity(intent); } }); } }
-
做好这些后,还需要在logcat界面添加过滤器来使得我们对onCreate, onStart, onResume, on Pause, onStop,onDestroy以及onRestart函数的调用情况有一个更直观的了解,否则log记录太过杂乱,很难得到我们需要的log信息;
-
接下来就是运行调试APP,打开APP,单击按钮实现活动1跳转到活动2;之后再按返回,依次关闭活动2,活动1;
-
跳转之后我们来看一下所作的Log记录;可以看出,APP运行的一开始需要执行onCreat,onStart,onResume来使得活动1开始运行;在我们点击了按钮跳转到活动2时,调用了函数onPause和onStop来暂停停止函数;之后当我们返回,由活动2返回至活动1时,调用了onRestart,onStart,onResume来使得活动1恢复运行,在最后关闭活动1时,则是调用了onPause,onStop,onDestory来结束这个活动;从这里我们看出,当活动出现跳转时会暂停原先的活动,并不会调用onDestory来结束这个活动,返回时则会使用onRestart,onStart,onResume来唤醒它,onPause,onStop往往组合使用,onStart,onResume也往往组合使用;
-
接下来我并不是通过返回来回到活动1,而是通过活动2的按钮来跳转到活动1,之后再按返回依次关闭活动,来看一下log有什么不同;前半段与之前相同,直到onStop之后出现了onCreat,这与之前的onRestart不同,说明出现了一个新的活动1,之后按返回它一样onPause,onStop,onDestory结束了这个活动,后面就和之前相同返回结束了活动;这说明了活动2的跳转并不是回到原来的活动1,而是创建了一个新的活动1,这与我们之后探讨的活动启动模式相关联;
-
接下来就是通过实验来证明系统可能会自行对活动运行onDestroy;首先打开APP,之后将它横屏变竖屏,观察log记录;
-
通过Logcat我们可以看到,在屏幕方向变化的这个过程,系统调用了onPause,onStop,onDestory,之后又重新onCreat创建了,这里就可以说明系统可能自行对活动运行onDestroy;
-
接下来我们就是通过实验了解利用saveinstantstate处理系统自行运行onDestroy而导致数据丢失的情况;首先我为活动1添加了一个新的按钮,用来提示Log信息,来用于对比;并取消了button_1的跳转功能,改为修改数据信息并记录;
-
之后对活动1的代码进行了修改,从而来调用saveinstantstate,以及对数据内容的记录;
package com.example.test; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity { public String TAG="main"; protected void onDestroy() { super.onDestroy(); Log.d(TAG,"onDestory"); } @Override protected void onStart() { super.onStart(); Log.d(TAG,"onStart"); } @Override protected void onResume() { super.onResume(); Log.d(TAG,"onResume"); } @Override protected void onPause() { super.onPause(); Log.d(TAG,"onPause"); } protected void onStop(){ super.onStop(); Log.d(TAG,"onStop"); } protected void onRestart(){ super.onRestart(); Log.d(TAG,"onRestart"); } public String tmpData="I am original"; //原始数据 @Override protected void onSaveInstanceState(Bundle outState) { //非用户调用onDestory时调用,保存数据 super.onSaveInstanceState(outState); outState.putString("IS",tmpData); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG,"onCreat"); setContentView(R.layout.activity_main); if(savedInstanceState!=null) //如果存有之前保存的信息,就读取 { tmpData=savedInstanceState.getString("IS"); Log.d(TAG,tmpData); } Button button=(Button) findViewById(R.id.button_1); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { tmpData="I am changed"; //点击按钮会改变tmpData的内容 Log.d(TAG,tmpData); } }); Button button1=(Button) findViewById(R.id.button_3); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d(TAG,tmpData); //弹出当前tmpData的内容 } }); } }
-
打开APP,首先点击ORIGINAL,之后再点击CHANGE TMPDATA,再点击ORIGINAL;接下来再翻转屏幕点击ORIGINAL,查看logcat内容;
-
通过logcat我们可以看出,一开始tmpData的内容时**“I am original”,之后点击了CHANGE TMPDATA,内容变为了"I am changed";而ORIGINAL按钮的功能是记录当前tmpData的值,所以再次点击还是"I am changed";之后我们翻转屏幕,可以看到onDestory的执行,再点击ORIGINAL,记录的仍是"I am changed",之所以会有两个"I am changed"**是因为从savedInstanceState读取内容时也记录了依次tmpData;
-
接下来我们将savedInstanceState相关读取和保存的代码注释掉,再来执行之前的操作,通过logcat的内容来进行对比;
-
从这段log我们就可以看出前面半段的内容是相同的,重点是翻转之后的内容,即执行了onDestory之后的内容,执行了之后我们再次点击ORIGINAL,可以看到logcat记录的内容是**“I am original”**,说明数据内容没有保存,这也和先前的实验形成了对比,也说明了saveinstantstate处理系统自行运行onDestroy而导致数据丢失的功能;
-
接下来就是通过实验说明活动的standard, singleTop, singleTask和singleInstance四种启动模式之间的区别点;首先我们对活动一的代码进行修改,让它变回原来的跳转到活动2的功能,同时添加代码来记录活动本身的相关信息;并追加一个新的按钮,来实现活动1跳转到他自身;在Manifest中也对启动模式进行修改,首先设置为standard来进行实验;
package com.example.test; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity { public String TAG="main"; protected void onDestroy() { super.onDestroy(); Log.d(TAG,"onDestory"); } @Override protected void onStart() { super.onStart(); Log.d(TAG,"onStart"); } @Override protected void onResume() { super.onResume(); Log.d(TAG,"onResume"); } @Override protected void onPause() { super.onPause(); Log.d(TAG,"onPause"); } protected void onStop(){ super.onStop(); Log.d(TAG,"onStop"); } protected void onRestart(){ super.onRestart(); Log.d(TAG,"onRestart"); } protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG,"onCreat"); setContentView(R.layout.activity_main); Log.d("main",this.toString()); //对活动相关信息进行记录 Button button=(Button) findViewById(R.id.button_1); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent=new Intent(MainActivity.this,Main2Activity.class); //跳转到活动2; startActivity(intent); } }); Button button1=(Button) findViewById(R.id.button_3); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent=new Intent(MainActivity.this,MainActivity.class); //跳转到活动1自身; startActivity(intent); } }); } }
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".Main2Activity"></activity>
<activity android:name=".MainActivity"
android:launchMode="standard"> //设置启动模式
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
-
在standard模式下打开APP,这种模式下,当Intent发送的时候,Activity总是被创建一个新的出来单独工作,也就是说每实现一次跳转,都会产生新的Activity;我们连续地点击Goto Myself来实现活动1跳转到它本身,同时我们对logcat的内容进行观察;
-
通过logcat我们可以看到在我点击Goto Myself时,每点击一次他都会产生一个新的Activity,这里我点击了三次,也产生了三个新的Activity;
这样我们就对standard模式有了一个直观的了解,每次Intent发送的时候,总是会创建一个新的Activity出来;
-
接下来来试一下singleTop,它的表现几乎和standard模式一模一样,一个singleTop Activity 的实例可以无限多,唯一的区别是如果在栈顶已经有一个相同类型的Activity实例,Intent不会再创建一个Activity,而是通过onNewIntent()被发送到现有的Activity。
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".Main2Activity"></activity> <activity android:name=".MainActivity" android:launchMode="singleTop"> //设置启动模式 <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
-
同样,我们也采用连续地点击Goto Myself来实现活动1跳转到它本身,同时我们对logcat的内容进行观察;可以看到当我点击Goto Myself时,出现了onPause和onResume却并没有新的Activity产生,这是因为活动1就在栈顶,而singleTop模式下如果在栈顶已经有一个相同类型的Activity实例,Intent不会再创建一个Activity;而接下来我点击Goto Activity2,再在活动2中点击Goto Activity1来实现两个活动的相互跳转,这是logcat的内容就不一样了,我实现了两次相互跳转,同时它也生成了两个新的Activity1,这是因为跳转到活动2时,活动1并不位于栈顶,所以发送intent后会产生一个新的Activity;
-
而singleTask与之前两个就不太相同,singleTask模式的Activity只允许在系统中有一个实例。如果系统中已经有了一个实例,持有这个实例的任务将移动到顶部,同时intent将被通过onNewIntent()发送。如果没有,则会创建一个新的Activity并置放在合适的任务中。也就是说,无论活动在栈中的哪个位置,都只会存在一个Activity实例,并不会重复产生新的Activity实例;
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".Main2Activity"></activity> <activity android:name=".MainActivity" android:launchMode="singleTask"> //设置启动模式 <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
-
对singleTask验证的方法就是采取活动1和活动2之间的相互跳跃,来查看是否有新的Activity实例产生;从logcat中可以看出至始至终都没有新的Activity产生,这也体现出了singleTask模式下的特点;
-
最后就是singleInstance模式(单实例模式),是一种加强的singleTask模式,它除了具有singleTask模式的所有特性以为,还加强了一点:具有此种模式的activity只能单独地位于一个任务栈中,比如activity A是singleInstance模式,当A启动后,系统会为它创建一个新的任务栈,然后A独自在这个新的任务栈中,由于栈内复用的特性,后续的请求均不会创建新的activity,除非这个独特的任务栈被系统销毁了;activity会启动一个新的任务栈来管理这个activity,singleInstance模式加载activity时,无论从哪个任务栈中启动该activity,只会创建一个activity实例,并且会使用一个全新的任务栈来装载该activity实例
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".Main2Activity"></activity>
<activity android:name=".MainActivity"
android:launchMode="singleInstance"> //设置启动模式
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
- 因为singleInstance模式下会启动一个新的任务栈来管理activity,所以无论是在活动1中点击Goto Myself还是在活动2中点击Goto Activity1都不会产生新的Activity实例,它会运行在自己单独,独立的任务栈里面,并且任务栈里面只有这一个实例存在;也就是说整个系统中只会存在一个这样的实例,因为它拥有着自己独立的任务栈,与其他Activity分离;