目的:
1.Activity的启动模式有哪几种,分别用于什么场景?
2.清晰地描述下onNewIntent和onConfigurationChanged这两个生命周期方法的场景
从事Android的朋友都知道activity是什么,activity的启动方式。
activity的启动方式有四种:
1.standard
2.singleTop
3.singleTask
4.singlestance
下面一一介绍这四种启动模式的特点和应用场景:
那怎么配置其启动模式呢?
就是直接在AndroidManifest.xml配置的activity节点里配置:android:launchMode="xxxxx"即可,如下图所示:
1.standard
首先在布局界面的添加一个按钮和一个textview,按钮用来做点击跳转事件,textview用来显示当前activity实例的序号
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.nova.startupdemo.MainActivity">
<TextView
android:id="@+id/tv_taskid"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_centerInParent="true"
android:gravity="center"
/>
<Button
android:id="@+id/btn_gomain"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="跳转MainActivity"
android:layout_below="@id/tv_taskid"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:background="@drawable/bg_button"
android:textColor="#FFFFFF"
/>
</RelativeLayout>
MainActivity类的代码:
public class MainActivity extends Activity implements View.OnClickListener {
private Button btn_gomain;
private TextView tv_taskid;
private String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"-----进入onCreate");
setContentView(R.layout.activity_main);
btn_gomain = (Button)findViewById(R.id.btn_gomain);
tv_taskid = (TextView)findViewById(R.id.tv_taskid);
tv_taskid.setText(this.toString());
Log.d(TAG,this.toString());
btn_gomain.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_gomain:
startActivity(new Intent(MainActivity.this,MainActivity.class));
break;
}
}
@Override
protected void onResume(){
super.onResume();
Log.d(TAG,"-----进入onResume");
}
@Override
protected void onStart(){
super.onStart();
Log.d(TAG,"-----进入onStart");
}
}
并在AndroidManifest.xml配置启动模式。
下面点击按钮,并看打印结果:
发现虽然是跳转同一个Activity,但是他们的序列号不一样,也发现新跳转的Mainactivity的onCreate,onStart,onResume生命周期方法都会被调用,我们知道,android是通过栈来管理activity,通过上面例子可以初步确定,当启动模式设为standard,不管这个实例是否存在,都会重新去创建这个实例。当按back键返回时,会跳到原来之前的界面,也就是创建新的实例会放到栈顶。
2.singleTop
这次将布局文件修改一下,增加一个按钮,并且增多一个activity,叫SecondActivity,因为要验证
1.当activity在栈顶的情况下跳转自己的情况
2.当activity不在栈顶的情况
activity_main布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.nova.startupdemo.MainActivity">
<TextView
android:id="@+id/tv_taskid"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_centerInParent="true"
android:gravity="center"
/>
<Button
android:id="@+id/btn_gomain"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="跳转MainActivity"
android:layout_below="@id/tv_taskid"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:background="@drawable/bg_button"
android:textColor="#FFFFFF"
/>
<Button
android:id="@+id/btn_second"
android:layout_below="@id/btn_gomain"
android:layout_centerHorizontal="true"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="跳转到SecondActivity"
android:textColor="#FFFFFF"
android:background="@drawable/bg_button"/>
</RelativeLayout>
public class MainActivity extends Activity implements View.OnClickListener {
private Button btn_gomain;//跳自己
private Button btn_second;//跳其他界面
private TextView tv_taskid;
private String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"-----进入onCreate");
setContentView(R.layout.activity_main);
btn_gomain = (Button)findViewById(R.id.btn_gomain);
btn_second = (Button)findViewById(R.id.btn_second);
tv_taskid = (TextView)findViewById(R.id.tv_taskid);
tv_taskid.setText(this.toString());
Log.d(TAG,this.toString());
btn_gomain.setOnClickListener(this);
btn_second.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_gomain:
startActivity(new Intent(MainActivity.this,MainActivity.class));
break;
case R.id.btn_second:
startActivity(new Intent(MainActivity.this,SecondActivity.class));
}
}
@Override
protected void onResume(){
super.onResume();
Log.d(TAG,"-----进入onResume");
}
@Override
protected void onStart(){
super.onStart();
Log.d(TAG,"-----进入onStart");
}
}
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_second"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_centerVertical="true"/>
<Button
android:id="@+id/btn_second"
android:layout_width="200dp"
android:layout_height="40dp"
android:background="@drawable/bg_button"
android:layout_centerHorizontal="true"
android:text="跳转到MainActivity"
android:textColor="#FFFFFF"
android:layout_marginTop="10dp"
android:layout_below="@id/tv_second"/>
</RelativeLayout>
SecondActivity:
public class SecondActivity extends Activity implements View.OnClickListener{
private TextView tv_second;
private Button btn_second;
private String TAG ="SecondActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"-----进入onCreate");
setContentView(R.layout.activity_second);
btn_second = (Button)findViewById(R.id.btn_second);
tv_second = (TextView)findViewById(R.id.tv_second);
tv_second.setText(this.toString());
Log.d(TAG,this.toString());
btn_second.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_second:
startActivity(new Intent(SecondActivity.this,MainActivity.class));
break;
}
}
@Override
protected void onResume(){
super.onResume();
Log.d(TAG,"-----进入onResume");
}
@Override
protected void onStart(){
super.onStart();
Log.d(TAG,"-----进入onStart");
}
}
最后在AndroidManifest.xml配置启动模式:MainActivity和SecondActivity节点下:android:launchMode="singleTop"
step1:自己跳转自己,也就是当所需要跳转的activiity已经在栈顶的情况
发现:
可发现,当 如果新的Activity已经位于任务栈的栈顶,那么Activity不会重建,onCreate和onStart方法不会被调用,但是onResume会被调用
step 2:当要跳转的activity不再栈顶,步骤:先跳转到SecondActivity,然后SecondActivity在跳转到MainActivity,这时候MainActivity不在栈顶,看看打印的结果:
发现,如果所要跳转的activity不在栈顶,系统会重新创建实例并于栈顶。按back键,先返回SecondActivity,再返回MainActivity
因此可总结:
1.当如果启动模式是singleTop,当所要跳转的activity已经在栈顶,那么系统不会创建新的实例,并回调onResume方法
2.当如果启动模式singleTop,当所要跳转的activity不在栈顶,系统还是重新创建实例放到栈顶
3.singleTask
这次只改启动模式:android:launchMode=singleTask
这里只讨论新activity的任务栈已经存在:
1.step:跳自己
发现如果在栈顶不会创建新的实例
1.step:跳转SecondActivity再跳转MainActivity
发现:
按back键,直接跳回手机界面。
发现:如果所要跳转的activity在栈内,就不会创建新的实例,但是会回调onStart和onResume方法,注意,并且将所要跳转activity上面的activity全部出栈,最终自己置于栈顶。
总结:
1.当如果启动模式是singleTask,当所要跳转的activity已经在栈顶,那么系统不会创建新的实例,并回调onResume方法
2.当如果启动模式singleTask,当所要跳转的activity不在栈顶,系统会将栈内所有在这activity上面的activity全部出栈
2.当如果启动模式singleTask,当所要跳转的activity不在栈顶,系统会将栈内所有在这activity上面的activity全部出栈
4.singleInstance
这种情况比较特殊:
首先在AndroidMainifest下改变启动模式:
MainActivity的launchMode=”standard”,SecondActivity的launchMode=”singleInstance
然后再每一个activity中打印栈信息id
Log.d(TAG+"MainActivity",this.getTaskId()+"");
Log.d(TAG+"SecondActivity",this.getTaskId()+"");
发现:
两个Activity的实例放到两个不同的栈中、
原理图如下:
MainActivity跳转到SecondActivity时,重新启用了一个新的栈结构,来放置SecondActivity实例,然后按下后退键,再次回到原始栈结构;图中下半部分显示的在SecondActivity中再次跳转到MainActivity,这个时候系统会在原始栈结构中生成一个MainActivity实例,然后回退两次,注意,并没有退出,而是回到了SecondActivity,为什么呢?是因为从SecondActivity跳转到MainActivity的时候,我们的起点变成了SecondActivity实例所在的栈结构,这样一来,我们需要“回归”到这个栈结构
四种模式的应用场景:
1.singleTop适合接收通知启动的内容显示页面。例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很烦人的。聊天的对话窗口,再例如QQ接受到消息后弹出Activity,如果一次来10条消息,总不能一次弹10个Activity。
2.singleTask适合作为程序入口点。例如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。之前打开过的页面,打开之前的页面就ok,不再新建。 singleTask:a界面购物,b界面确认订单,c界面付款,如果付款成功会跳到a,如果不付款则返回b,这时候重启a就会用到singleTask.
3.singleInstance适合需要与程序分离开的页面。例如闹铃提醒,将闹铃提醒与闹铃设置分离。singleInstance不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,在此启动,首先打开的是B。
4.standard 标准的启动模式,也是默认的启动模式。
2.清晰地描述下onNewIntent和onConfigurationChanged这两个生命周期方法的场景
1.启动模式是singleTop栈顶复用的情况下:如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,它的onNewIntent会被调用
2.启动模式是singleTask栈内复用的情况下:只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,和singleTop一样,会调用onNewIntent.
3.启动模式是singleInstance单实例的模式下:只要Activity存在就会回调onNewIntent这个方法
因此如果Activity的启动模式是:singleTop, singleTask, singleInstance,在复用这些Acitivity时就会在调用onStart方法前调用onNewIntent方法
当一些系统配置发生改变时,但是不想重新创建activity,通过给activity设定configChanges属性,onConfigurationChanged就会调用。