--简介
--一.actiivty的生命周期
--二.activity的启动模式
--三.activity的启动方式
--四.activity的数据传递以及数据回传
--五.activity的横竖屏之间的转化
--六.一些特定方法的特定讲解
--七.activity保存数据和状态。
--八.activity在清单文件中的配置详解以及例子(一个应用多个入口):
--九actiivty面试中的一些问题:
--十 activity中的一些疑问
--十一如何安全推出已经打开了多个activit的application
简介:
activitiy是android四大组件之一,是我们在平常的运用到最多的组件。
一activity的生命周期
1.1总的来说activity的生命周期就是六个状态,7个方法。如图:
1.2上述所说的六个状态的特性:
注意:其中Paused这个状态有两种情况:
第一种:有一个半透明的activity覆盖在本activity。
第二种:activityB覆盖在activityA上,但是没有覆盖完全。
1.3各种情况下生命周期走的方法:
情况1:从acitivityA跳到activityB,首先activityA执行的是oncreate()--onstart()--onresume(),当从activityA跳转到activityB的时候,此时执行的方法activityA的onPause()--activityB的oncreate()----activity B的onstart()---activityB 的onresume()--activity A的onstop()方法,然后点击返回键,从activityB返回到activityA,此时执行的方法是activityB的onpause()--activityA的onrestsrt()--activityA的onstart()--activityA的onresume()--activityB的onstop()---activityB的ondestroy()方法。
情况2:当这个activity启动之后,退居后台,已经执行了onstop方法,但是此时系统内存不够的时候,杀死了该process,那么下次要执行的就是oncreate--onstart()--onresume(),而不是执行onrestart()--onstart()--onresume()方法。
二.activity的启动模式
2.0 Application, Task ,Process之间的区别
Application,在android中,总体来说就是一组组件的集合,众所周知,android是在因公层组件化程度非常高的系统,android开发其实就是写android四大组件中的我们需要的组件,并且在清淡文件中注册这些组件之后,把这些组件和组件使用到的资源打包称apk,然后这就是一个Application,其实Application和组件的关系可以在manifest文件中清晰体现出来,在apk安装的时候,系统会读取application中清淡文件manifest的信息,将所有的额组件解析出来,一边在运行时进行实例化。
Task是在程序运行的时候,只针对activity的概念,即Task是一组相互关联的正在运行的activity的集合。它是存在与framework层的一个概念,控制界面的跳转和返回,framework是以栈的数据结构方式管理用户开启的activty,也可以说activity是存在于back stack(回退栈)中的。而且task是可以跨应用的,即不同的app的activity是可以在同一个Task中的,主要是为了保证用户操作的连贯性。(比如在app中的activityA页面打开一个网页(使用第三方浏览器),当用户浏览完网页的信息之后,按back键,就会回到activityA页面。)
Process即进程是操作系统进行资源分配和调度的一个独立单位,默认情况夏,一个应用程序的所有组件会在同一个进程中,但是应用的不同组件也是可以运行在不同的进程中的,只需要在清单文件中进行注册的时候,声明process属性:
``` <activity android:name=".health.healthListActivity"
android:process="processb">
</activity>
```
2.1acitivty的四种启动模式
<1>standard--默认的启动模式,同一个任务中可以存在多个该activity的实例。不断的利用startActivity()或者 startActivityForResult()启动该activty,都会重新创建该activity的新实例。
<2>singleTop--(作用:防止activity被重复启动多个,一般来说在项目中,被用户多次点击从而导致activity被启动多个,这个不常见,主要是为了防止activity被隐士启动的时候,被开发者使用代词恶意多次启动,从而创建多个该activity实例)--同一任务栈中可以存在多个该activity的实例,但是当该actiivty已经处在任务栈的栈顶时,此时再利用startActivity()或者startActivityForResult()启动该activity,则会复用栈中已经存在的该activity实例,走的生命周期方法为本activity的onPause()-----》onNewIntent()---->onResume();如果该activity不在任务栈的栈顶时,此时就会重新创建新的该activity实例。
当用户在一个activityA中,因为手速过快,或者开发者使用隐士启动方式多次启动一个singleTop模式的activityB的时候,其流程如下:
(1)该app所在的进程会有一个主线程将这些代码(多次调用startActivity()方法的代码)保存下来,然后一个一个执行.
(2)首先是执行第一个startActivity()的代码,请注意,此时activityB还不在栈顶位置,其执行顺序如下:activityA的onPause()--->activityB的oncrate()---->activityB的onStart()--->activityB的onResume()--->activttyA的onStoP().
(3)接下来执行第二个到最后一个startActivity()的代码,此时activityB已经处在栈顶位置了,其执行的方法如下:activityB的onPause()------>activityB的onNewIntent()---->activityB的onResume().
<3>singleTask--(作用:保证任务栈中只有一个该activity实例)--同一任务栈中只能存在一个该activity的实例,当该activity不在任务栈的栈顶的时候,此时利用startActivity()或者startActivityForResult()启动该activity,则直接杀死该activity上边的所有activity实例,让该activiyt处在任务栈的顶端,详细过程看第十点;如果该activity在任务栈的顶端的时候,此时再利用startActivity()或者startActivityForResult()启动该actiivty,则走onPause()-----》onNewIntent()---->onResume()。
<4>singInstance--(作用:保证整个系统内存中都只有一个该activity实例)--会创建一个独立的任务栈中用于保存该activity的实例,并保证不让其他activity实例进入到这个栈中,匹配者隐士启动,从而让多个app共享这一个实例,例子就是接电话activity。(这句话是对的,在下面有详细例子)。并保证在该任务栈中只有该actiivty一个实例,不会被重复创建多次,此时利用startActity()或者startActivityForResult()启动已经存在的该activity的时候,该activity首先执行的都是onNewIntent()方法,)。
```
。以下a,b,c都是activity,其中a和c的启动模式是默认的,b的启动模式singleInstance,那么从a-->b--->c,然后在c中利用startActivity()启动b,此时c和b走的生命周期方法:
c的onPause()--->b的onNewIntent()--->b的onRestart()---b的onstart()----b的onresume()---c的onStop()
当从a--->b,在b中利用startActivity()方法,启动b,此时b走的生命周期方法:
b的onPuase()---->b的onNewIntent()----b的onResume()
```
2.2actiivty的四种启动模式在项目中的运用
<1>使用singInstance的activity只有一个---微信分享的回调类WXEntryActivity,此处的actiivyt使用singleinstance的原因是:要调用微信客户端里的界面,为了能够让多个应用共享这个activity实例,所以这样。
<2>使用singleTask的activity有很多,比如homeativity,loginactivity,loginguidectivity,因为在我们的app中只有一个activity用了singleinstance,所以说我们的activity基本都在同一个任务栈中,之所以让上述几个activity为singleTask,是为了保证在app的任务栈中只有一个实例存在,登陆页面,登陆引导页面,以及首页。
<3>使用singleTop的acitivty有几个,比如EvidentSignActivity,电子签名页面,主要是为了防止重复启动电子签名页面,比如(点击一个按钮,开始电子签名,然后点击多次,为了防止重复启动,就设置这个电子签名的类的启动模式为singleTop.)
2.3activity的启动模式的设置:
```
<activity
android:name=".SecondActivity"
//通过该标签lauhchMode来设置该activity的启动模式
android:launchMode="singleTop">
</activity>
```
三.activity的显示启动和隐士启动
1activity的显式启动
1.1有两种方式:
第一种方式:setClass(上下文,类名.class);
此时启动的是该类所在的包下的activity或者说同一个应用下的activity
第二种方式:setClassName(目标actiivty所在的应用包名,目标activity的类名(即该类的全路径地址));
使用的情况:可以启动同一应用的activity,也可以启动不同应用的acitvity,但是由于别的应用的activity我们不能控制,所以类名经常变,不利于开发,一般不用于启动不同应用的activity。
```
问题:但是当同一个应用下不同文件夹下有两个activity的名字相同的时候,此时怎么办?
此时activity需要使用全路径名,对于第一种和第二种方式,在这种情况下,均是使用activity的全路径名。
```
2activity 的隐士启动:
2.1隐士启动的运行过程:
当使用隐士意图启动activity的时候,此时系统会遍历所有应用的清单文件,寻找是否存在与隐士intent匹配的intent—filter,如果找到,那么就会把intent-filter对应的activity启动起来,如果找不到,就会抛出异常ActivityNotFoundException,这个是确定会抛出的。
2.2隐士启动的格式:
```
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="a.b.c"/>
<data android:scheme="xuexi" android:mimeType="text/name"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
注意: 当使用隐士启动启动某一个activity的时候,此时这个activity在清单文件中的配置时,其intent-filter中一定要添加有关categroy的东西,,这一句<category android:name="android.intent.category.DEFAULT"/>一定要记得添加,如果category是特定的,那么就选择特定的categroy,如果categroy没有指定,那么就要选择上边这个default类型的,否则就会失败,报异常。
当启动这个activity的时候,如果其categroy为android.intent.category.DEFAULT的时候,此时在代码中不必为intent设置categroy了,如下:
Intent intent=new Intent();
intent.setAction("a.b.c");
匹配category的内容,我们如果不写的花,那么系统会自动添加: intent.addCategory("android.intent.category.DEFAULT"),
SecondOneActivity.this.startActivity(intent);
```
```
intent-filter中属性:
action--动作(可以自定义,可以使用系统自带的)
data --数据(操作的内容)
scheme--数据的标签
mimeType--描述数据的类型,(描述传递的数据的类型) ,其实只有一些主流文件格式对应的确定mimetype类型,但是一般的字符串,我们就可以自己定义,比如说henansheng,这这个字符串。)(定义的标准一般是大类/小类,比如text/name).
为什么使用mimetype来描述数据的类型,而不用后缀名,因为有些数据是没有后缀名的
category--类别(默认类别,机顶盒,车载模式)
```
此时在启动这个activity的时候,就要调用setAction(),setData()方法,进行匹配。如果隐士意图的activity配置的categoty属性配置的如图所示(即默认),那么我们就不用使用addCategory()方法--系统会默认添加,如果隐士意图的activity 配置的categoty属性配置的不是如果所以,那么此时在启动activity的时候,就要手动的使用addCategory()方法进行匹配。
2.3注意:
<1>首先配置信息中的意图过滤器中的data数据是setdata(Uri.parse("标签:"))的标签,利用setData方法可以传递,在跳转后的actiivty通过getIntent().getData()获取intent中的数据。
<2>如果一个activity想要被隐士启动,就必须在清淡文件中给这个activity配置intent—filter,一个activity可以有多个intent-filter。
<3>在给一个activity配置intent-filter的时候,intent-filter有三个属性,《action》《date》《category》,一个intent-filter可以有多个action,多个data,也可以有多个category(scheme跳转策略中的category就是多个,但是我们一般正常写activity都是设置一个category)。
<4>当一个actiivty有多个intent-filter的时候,此时只需要匹配一个即可。
<5>当一个activity的intent-filter由多个action,多个data的时候,此时只需要匹配一个action和一个data即可,如果只匹配action或者data这两个其中一个,是不能启动这个activity 的。
<6>intent-filter的属性data,有很多属性,其中mimetype(描述数据的类型)是其中之一,当一个date同时schme和mimetype两个属性的时候,此时在启动这个activity的时候,要调用setDateAndType(Uri data,String type),而不是用setData()和setType()方法,因为这两个方法互斥。
2.4隐士启动的常见例子:
<1>启动拨号器
```
Intent intent=new Intent();
intent.setAction(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:")); //加上这一句直接来到拨号器界面(用户直接拨号),否则拨号的那个键盘没有跳出来。
startActivity(intent);
```
<2>打电话
```
Intent intent=new Intent();
intent.setAction(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:15138157556"));
startActivity(intent);
这个需要权限: <uses-permission android:name="android.permission.CALL_PHONE"/>
```
<3>利用浏览器打开一个网页url
```
Intent intent=new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
```
<4>当我们要使用定制版的浏览器来打开网页的话,就要使用显示启动:
intent.setClassName("应用报名","应用报名+类名");
这种方式我们一般不常用,原因如下:
《1》因为如果指定浏览器的话,其应用的包名以及类名有可能一次代码重构就都发生了改变,这样的话,我们就得手动进行改正了。
《2》如果指定浏览器,就会降低用户的选择性,从而使用户对应用的整体印象不佳。
2.5activity两种启动方式的使用场景
启动同一应用的activity,使用显式启动方式,显式的效率高于隐士。
启动不同应用的activity,使用隐士启动方式
(多个intent-filter的action相同,那么隐士启动时会弹出对话框询问启动哪一个;拨号器和浏览器均可显示和隐式启动,但是一般都是用隐式启动,这样系统会询问用户用哪一个,更好)
四。activity间数据传递
在actiivty之间进行数据传递,不管是正向传递还是回传,都是通过intent来进行传递数据的。
1.intent可以传递的数据类型:
*八种基本数据类型以及数组
*字符串以及数组
*两个序列化接口的实现类的对象及其数组
****parcelabel
****serializable
*Bundle对象
*四种类型的ArrayList()<> ,四种类型分别是String,Integer,charsequence,实现parcelable的对象。
其方法基本上是:
intent.putExtra("名字",内容);
intent.putExtras("bundle对象的名字",bundle对象);
intent.putCharSequenceArrayListExtra();
intent.putStringArrayListExtra();
intent.putIntegerArrayListExtra();
intent.putParcelableArrayListExtra();
2.intent数据传递的具体方式例子:
```
Intent intent=new Intent(this,SecondActivity.class);
//第一种类型:这是通过四种ArrayList类型来传递数据
intent.putCharSequenceArrayListExtra("charSequenceArraylist",charsequenceArrayList);
intent.putStringArrayListExtra("stringArraylist",stringArrayList);
intent.putIntegerArrayListExtra("IntegerArrayList",integerArrayList);
// intent.putParcelableArrayListExtra("parcelableArrayList",);
startActivity(intent);
Intent intent = getIntent();
//这是获取四种arryalist其中的三种
if (null != intent && null != intent.getCharSequenceArrayListExtra("charSequenceArraylist")) {
ArrayList<CharSequence> charSequenceArraylist = intent.getCharSequenceArrayListExtra("charSequenceArraylist");
tv_second_charSequenceArraylist.setText(getString(R.string.secondActivity_charSequenceArrayList, getArrayListData(charSequenceArraylist)));
}
if (null != intent && null != intent.getStringArrayListExtra("stringArraylist")) {
ArrayList<String> stringArraylist = intent.getStringArrayListExtra("stringArraylist");
tv_second_stringArraylist.setText(getString(R.string.secondActivity_stringArraylist, getArrayListData(stringArraylist)));
}
if (null != intent && null != intent.getIntegerArrayListExtra("IntegerArrayList")) {
ArrayList<Integer> integerArrayListExtra = intent.getIntegerArrayListExtra("IntegerArrayList");
tv_second_integerArrayList.setText(getString(R.string.secondActivity_IntegerArrayList, getArrayListData(integerArrayListExtra)));
}
```
```
Intent intent=new Intent(this,SecondActivity.class);
//第四种类型:这是通过实现了serialable或者paralable的对象以及继承的parable对象的数组来进行传递数据
intent.putExtra("serialable",xiaoming);
intent.putExtra("paralable",teacher);
startActivity(intent);
//取数据
Intent intent = getIntent();
if (null != intent && null != intent.getSerializableExtra("serialable")) {
Student student = (Student) intent.getSerializableExtra("serialable");
tv_second_serializable.setText(getString(R.string.secondActivity_serializable, student.getName() + student.getAge() + student.getGrade()));
}
if (null != intent && null != intent.getParcelableExtra("paralable")) {
Teacher teacher = intent.getParcelableExtra("paralable");
tv_second_parcelable.setText(getString(R.string.secondActivity_parcelzable, teacher.getName() + teacher.getAge()));
}
```
```
Intent intent=new Intent(this,SecondActivity.class);
//第三种类型:这是通过字符串及其数组传递数据
intent.putExtra("String","哈哈哈哈");
String[] strings=new String[]{"shanghai","beijing","zhengzhou","sanmenxia"};
intent.putExtra("String[]",strings);
startActivity(intent);
//取数据
Intent intent = getIntent();
if (null != intent) {
String dataone=intent.getStringExtra("String");
String[] datas=intent.getStringArrayExtra("String[]");
}
```
```
//第五种类型:这是通过bundle来进行传递数据
Intent intent=new Intent(this,ThirdActivity.class);
Bundle bundle=new Bundle();
bundle.putString("name","秦始皇");
bundle.putInt("age",2000);
bundle.putBoolean("living",false);
bundle.putFloat("height",165);
bundle.putDouble("weight",165.66);
intent.putExtras(bundle);
startActivity(intent);
Intent intent = getIntent();
if (null != intent && null != intent.getExtras()) {
Bundle bundle = intent.getExtras();
tv_name.setText(getString(R.string.thirdactivity_name,(String) bundle.get("name")));
tv_age.setText(getString(R.string.thirdactivity_age,(Integer) bundle.get("age")));
tv_height.setText(getString(R.string.thirdactivity_height,(Float) bundle.getFloat("height")));
tv_weight.setText(getString(R.string.thirdactivity_weight,(Double) bundle.getDouble("weight")+""));
tv_isliving.setText(getString(R.string.thirdactivity_isLiving,(Boolean) bundle.getBoolean("living") + ""));
}
```
3.activity使用intent正向数据传递
即从activityA跳到activityB,此时通过intent将数据从activityA携带到activityB,这就是正想数据传递;之后从activityA返回到activityB,携带者数据,此时就叫做数据回传。
4.activity使用intent进行数据回传
在进行数据会传递的时候,还是通过intent的五种携带数据的方式进行携带数据,总体来说是这样的:
当从actiityA跳到activityB时,然后从activityB返回到actiityA,而且需要actiivtyB向activityA回传一些数据,此时就要进行数据回传,在从actiivtyA跳转到activityB时,我们就要使用startActivityForResult(Intent intent,int requestCode)方法--其中requestCode是用于区分将来会传递的数据来自于哪个activity中,然后在activitya的onActivityResult(int RequestCode,int resultCode,Intent intent)方法中进行接受回传的数据,在activityb中的如果要回传数据的话,直接调用setResult(int resultCode,Intent intent),将数据直接封装在intent中,然后设置resultCode来区分是该activity中哪种条件下的数据(一个activity有可可能根据条件的不同,要回传不同的数。,)。
例子:
```
MainActivity:
//这是跳转的方法
private void startThirdActivity() {
//第五种类型:这是通过bundle来进行传递数据
Intent intent=new Intent(this,ThirdActivity.class);
Bundle bundle=new Bundle();
bundle.putString("name","秦始皇");
bundle.putInt("age",2000);
bundle.putBoolean("living",false);
bundle.putFloat("height",165);
bundle.putDouble("weight",165.66);
intent.putExtras(bundle);
startActivityForResult(intent,contants.activity_main_request_code_to_third);
}
private void startSecondActivity() {
Intent intent = new Intent(this, SecondActivity.class);
startActivityForResult(intent, contants.activity_main_request_code_to_second);
}
//这是接受回传数据的方法:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (contants.activity_main_request_code_to_second == requestCode) {
if (contants.activity_second_result_code == resultCode) {
tv_third_back_data.setText(data.getStringExtra("message"));
}
} else if (contants.activity_main_request_code_to_third == requestCode) {
if (contants.activity_third_result_code_one == resultCode) {
tv_second_back_data.setText(data.getStringExtra("锄禾") + "one");
} else if (contants.activity_third_result_code_two == resultCode) {
tv_second_back_data.setText(data.getStringExtra("春晓") + "two");
}
}
}
```
```
//这是ThirdActivity中回传数据的操作
bt_back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent();
intent.putExtra("锄禾","锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦!");
setResult(contants.activity_third_result_code_one,intent);
finish();
}
});
bt_back_one.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent();
intent.putExtra("春晓","春眠不觉晓,处处闻啼鸟。");
setResult(contants.activity_third_result_code_two,intent);
finish();
}
});
```
```
//这是SecondActivity中回传数据时的操作
bt_back_one.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent();
intent.putExtra("message","this is from second activity!");
setResult(contants.activity_second_result_code,intent);
finish();
}
});
```
四.activity的横竖屏之间的转换
手机中一般都有重力感应,从而让屏幕进行横竖屏之间的切换,但是我们一般不常用一般为:看视频,签名等。
1.设置activity的方向,有两种方式:
1.1第一种是在清淡文件中:
直接配置activity的属性:android:screenOrientation="portrait" 或者"landspace"
代码如下:
```
<activity android:name=".ThirdActivity"
android:screenOrientation="portrait"/>
```
2.2第二种是在actiity代码中:
```
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//这是设置竖屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//这是设置横屏
再setContentView()方法之前或者之后都行。
```
2. 在横竖屏切换的时候,会触发生命周期方法,onpause()--onstop()--ondestroy()--oncreate()--onstart()--onresume()方法,注意横屏状态下该activity执行所有生命周期方法,然后在执行竖屏状态下的activity的oncreate等一系列生命周期方法,所以说当我们想要在横竖屏之间进行数据的保存与传递的时候,此时我们应该是在onstop()方法中进行保存数据,在ondestroy()方法中进行释放资源。
横竖屏切换,会销毁重建,是为了加载另一套布局文件。
2.1有一种情况,在理论上是存在的,但是在实际的开发中没有遇到过这种情况,我们可以通过在清单文件中配置一些activity属性,从而让actiivty在进行横竖屏转换的时候,不走该activity的生命周期方法(包括onSaveInstanceState()和onRestoreInstanceState()方法),而是仅仅走一个特定的方法即onConfigurationChanged(Configuration newConfig),且此时已经横屏显示了,而在清单文件中的配置如下:
```
<activity android:name=".MainActivity"
//当屏幕的方向和长宽尺寸发生改变的时候,系统不去处理,也就是不走生命周期方法
android:configChanges="orientation|screenSize"/>
```
而在方法onConfigurationChanged(Configuration newConfig)中,做的事情呢,如下:
```
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Log.d("qindong","走了这个方法");
if(newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){
Log.d("qindong","this is landspace");
}
if(newConfig.orientation==Configuration.ORIENTATION_PORTRAIT){
Log.d("qindong","this is portrait");
}
}
```
3.横竖屏之间的屏幕适配:
当一个actiity在进行横竖屏之间进行切换的时候,此时该activity会使用不同的xml布局文件,当我们的项目中包含layout_land的时候,此时当横屏的时候,就会走该文件夹下的相应的布局文件;如果我们的项目文件中不包含这个layout_land的时候,此时就是使用原来的xml布局文件。
注意:在layout和layout_land中,相应的xml布局文件的名字必须是一样的。
五.一些特定方法的特定讲解
1.finish()方法执行了那些方法?
1.1 在onstop方法中调用system.exit(0) 方法,退出app,此时app就不会再走ondestroy方法;但是在onpause方法调用,app会瞬间启动
1.2 当我们在oncreate方法中调用finish()方法的时候,此时activity走的生命周期就是直接oncreate--ondestroy
1.3 当调用finish方法的时候,就是这个activity走向销毁的过程,一般性的会走onpause--onstop--ondestroy;但是当这个activyt已经处于后台了,也就是该actvity已经再onStop()方法,此时直接执行onDestroy()方法即可。
重点:其实finish()方法就相当于用户点击返回键,其过程和用户点击返回键的过程是一样的。
2.在各个生命周期方法,都应该做些什么?
2.1 oncreate()方法--做的是建立activity所需要的一些基础操作,例如:从intent中获取数据,声明ui变量,声明全局变量,设置ui控件的具体点击事件,以及一些访问网络获取数据。
2.2. onstart()方法--当系统调用activity的onstop方法之后,此app就会处在后台一段时间,之后走onrestart()--onstart()--onresume()方法,此时我们在onstart方法中,可以做的事情:
2.2.1会使一个比较好的验证某一些必须的系统特性是否可用的地方。
2.2.2在onpause(),onstop()方法中清除的资源,重新实例化。
2.3 onresume()方法--一般性不做,特殊情况下会做:
1.开始动画,或者只有在用户获取到焦点时才需要的组件
2.视频的开始播放等
2.4 onpause()方法--
当系统调用activity的onpause()方法的时候,从技术性将,activity所处的状态为可见但是不能交互的状态,在大多数情况,系统会紧接这调用activity的onstop方法,从而让activity进入到stoped状态,在这个方法中我们应该做的事:释放activity的资源,但是不做耗时的事情,耗时的事情放在onstop方法去做,如果在onpause()方法中做耗时操作的话,会使页面跳转变的很慢,不顺畅。
1.停止动画活着其他正在运行导致cpu消费的操作。
2.保存没有保存的改变。(例如邮件草稿)
3.释放系统资源,例如broadcast,sensors(比如gps),活着其他任何影响到电量的资源。
4.如果程序正在使用camera,在这个地方去做释放资源的操作。
5. 视频的暂停以及播放进度。
2.5 onstop()方法--
在该方法中可以做一些耗时操作,比如写数据到database等等。
2.6 ondestory()方法--
1 有可能导致内存泄漏的地方,做彻底的清除工作,释放资源(置空)。
六.activity保存数据和状态。
6.1在android中用于保存和恢复数据的功能有三种形式:
第一种:利用activity的周期方法,进行正常的数据数据的保存和恢复。
第二种:利用onSaveInstanceState()和onRestoreInstanceState()方法进行瞬时数据的保存和恢复。
第三种: onRetainCustomNonCoonfigurationInstance()()和getLastNonConfigurationInstance()()--主要用于屏幕的旋转操作是保存数据(这个有待研究,因为当横屏的时候,此时getLastNonConfigurationInstance()方法。)
6.2onSaveInstanceState( Bundle outInstance)和onRestoreInstanceState(Bundle oncreateInstance)方法:
这两个方法并不是activity的生命周期方法,所以只有一些特定的情况下才会执行这两个方法。其中onSaveInstanceState(Bundle outState)是用于保存要恢复的数据,而onRestoreInstanceState(Bundle oncreateState)则是用于恢复已经保存的数据,由于我们要保存的数据均保存在Bundle对象中,也就是基本上是键值对的形式来保存的,两个方法一般性都是保存一些瞬时数据。同时onSaveInstanceState( Bundle outInstance)中的bundle对象也是oncreate(Bundle bundle)方法,所以可以直接在oncreate方法进行数据的恢复性工作,但是最好的还是在onRestoreInstanceState(Bundle oncreateInstance)方法中做。
6.2.1###onSaveInstanceState()方法什么时候调用:
该方法执行的具体时间为:在onpause()之后,onStop()之前
当应用遇到一些情况,在这些情况下,该应用的activity有可能被系统杀死。那么这些情况下,系统会调用onSaveInstanceState()方法,例如:
<1>当用户按下home键的时候。
当用户按下home键的时候,此时一般手机会回到首页,然后用户有可能会运行其他别的程序,那切换到后台的程序A的activity就有可能因为系统内存不足,从而有可能被系统杀死。
<2>当用户长按home键的时候,选择其他正在运行的程序。
<3>关闭屏幕显示即锁屏(或者按下电源键)的时候
<4>从activityA跳转到别的activity的时候,此时activityA的onSaveInstanceState会执行。
<5>屏幕方向发生切换。例如从横竖屏进行切换的时候。
6.2.2 ###onRestoreInstanceState()方法什么时候调用:
切记:当用户按下back键,销毁一个activitiy的时候,此时这个activity是不会执行这个方法的。
这个方法执行的具体时间为:onStart() 之后 onResume()之前。
<1>横竖屏切换的时候
<2>当activityA被系统自己销毁(非用户自愿,比如系统内容不够时,杀死该activity)了,那么重建activityA的时候,就会执行。
6.2.3###onSaveInstanceState()方法的“默认实现”实现了了什么东西-“自定义实现”实现的是什么
当我们没有覆盖onsaveinsantcestate()方法,此方法的默认实现会自动保存activity的某些状态数据,比如activity中各种ui控件的状态,然后在需要恢复的时候,进行恢复保存的状态和数据,但是对此ui空间有要求:
<1>必须是android系统框架中的ui控件(因为他们都恰当的实现了onSaveInstanceState()方法)
<2>此ui控件必须指定id(系统保存ui控件状态和数据的时候,需要,否则不行的)
这些东西和我们的目的不向对,所以说我们只需要继承默认实现就行了。那么我们“自定义实现”实现的是什么呢?我们自定义的全局变量的值+自定义控件的状态和值等等。
6.2.4onSaveInstanceState()和 onRetainCustomNonCoonfigurationInstance()之间的区别
onRetainCustomNonCoonfigurationInstance还得研究研究
八.activity在清单文件中的配置详解以及例子:
例子1:一个application配置两个入口:
activity应用配置两个入口
一、这种情况出现的几率不大但是会出现,主要是一个app中的功能很多,为了方便,让一个app有多个入口,比如手机上的联系人和拨号就是同一个应用的两个入口。
二、说一下原理:
一般来说一个应用只有一个入口,在清淡文件中,<application>节点中,选择其一些参数进行配置应用的图标和名字:
```
<application
android:allowBackup="true"
//这是应用的icon图标
android:icon="@mipmap/ic_launcher"
//这时应用的名字
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"
android:screenOrientation="portrait"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
```
但是当我们需要一个应用有两个入口的时候,直接在app的清单文件中配置两个activity为应用的入口即可。
将一个activity配置成为应用的入口,只需要配置一个这个意图过滤器,需要指定date和category为特定的类型:
```
<activity android:name=".MainActivity"
//应用的名字
android:label="@string/app_name_one"
//应用的图标
android:icon="@drawable/ic_launcher_haofuli">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
```
当我们需要让两个入口显示不同的图标和名字的时候,直接配置activity的属性icon和label即可(此时在界面中出现的应用的图标和名字就是activity配置的图标和名字,但是在android的进程中该app显示的名字和图标是application的配置项中的名字和图标),如上图中activity的属性label和icon。
九actiivty面试中的一些问题:
```
1.Activity的状态有哪些?
有六种,其中三种是长状态resumed,stoped,paused,三种是短状态,created,started,destroyed.
2.如何将activity设置为一个窗口的样式?
android:theme="@android:style/theme.Dialog"
3.如何退出Activity?如何安全退出已调用多个activity的application?
<1>对于用户而言,退出activity,就是点击返回键;对于我们自己而言,就是直接调用finish()
```
十 activity中的一些疑问
```
1。以下a,b,c都是activity,其中a和c的启动模式是默认的,b的启动模式singleInstance,那么从a-->b--->c,然后在c中利用startActivity()启动b,此时c和b走的生命周期方法:
c的onPause()--->b的onNewIntent()--->b的onRestart()---b的onstart()----b的onresume()---c的onStop()
当从a--->b,在b中利用startActivity()方法,启动b,此时b走的生命周期方法:
b的onPuase()---->b的onNewIntent()----b的onResume()
2.在代码中设计横竖屏的时候,是在seContentView()方法之前还是之后
试验过,设置横竖屏,在setContentView()之前或者之后都行。
3.当activity的启动模式是singleTask的时候,在任务栈中,其上边还有很多的activity,那么此时当再次启动这个activity的时候,其他的activity是走什么方法呢?
当a,b,c,d均是activity,其中a的启动模式是singleTask,b,c,d的启动模式是默认的,当a-->b--->c---d,然后在d中,使用startActivity启动a,此时生命周期方法是如何走的?
b的ondestroy()--->c的ondestroy()--->d的onPause()----a的onNewIntent()----a的onrestare()----a的onStare()----->onResume()----d的Stop()---d的onDestroy()
当在a中利用startActivity()方法启动a的时候,此时a走的生命周期方法:
a的onPause()-->a的onNewIntent()----->a的onResume()
4.当activity的启动模式为singleTop,singleTask,singleInstance的时候,此时分为两种情况:
<1>第一种当在本acitivty利用startActivity()或者startActivityForResult()方法来再次启动本activity的时候或者该activity已经处在任务栈的栈顶位置的时候,那么此时走的生命周期方法均是本activity的:
onPause()-------->onNewIntent()------onResume();
<2>当该activity已经存在,然后再其他的activity中利用startActivity()或者startActivityForResult()方法来启动该activitiy的时候,如下:
1》当该activity的启动模式为singleTop的时候,此时系统就会再次重新创建一个该activity,因为singleTop允许一个task中存在多个该activity示例。
2》当该activity的启动模式为singleTask的时候,此时系统就会杀死task中处在该activity至上的activity,然后将该activity显示出来。如十中的3.
3》当该activity的启动模式为singleInstance的时候,此时就如十中的1.所描述的例子一样。
此时都会走要启动的那个已经存在的activity的onNewIntent()方法,在onrestart()方法之前。
5.在activity之间跳转的时候,从A--->B,再从B--》A,此时A和B之间的生命周期方法是如何进行的。
当从B--->A返回的时候,此时执行的方法: B的onPause()---->A的onRestare()--->A 的onStart()----> A的onResume()------B的onStop()----B的onDestroy()
6.以下a,b,c,d,e都是activity,其中a,c,e是默认启动模式,而b,d为singleInstance模式,此时依次跳转(即使用startActivity()方法进行跳转)a-->b--->c-->d-->e,然后点击返回键,此时页面的返回顺序是什么?
返回顺序为:e--->c-->a-->d-->b,返回所设计到生命周期方法还是页面回转是所应该跳转的方法。此事件说明的是singleInstance模式的activity,系统会专门创建一个栈用于存储该实例。
7.以下a,b,c,d,e都是activity,其中a,c,e是默认启动模式,而b,d为singleInstance模式,此时依次跳转(即使用startActivity()方法进行跳转)a-->b--->c-->d-->e-->d,然后点击返回键,此时页面的返回顺序是什么?
返回的顺序为:d-->e-->c-->a-->b,返回所设计到生命周期方法还是页面回转是所应该跳转的方法。这表示同为singleInstance模式的activityb和activityd,这两个实例的任务栈不是同一个,此事件说明的道理是:
<1>针对singleInstance模式的activity,系统会专门创建一个任务战用于存储该activity实例,并且不让其他activity实例进入到该栈中(即便其他activity的启动模式为singleInstance)
8.O你Configurationchanged(Configuration d)中的configuration是什么东西?
9当使用因示意图启动activity的时候,系统找不到匹配的intent-filter的时候,此时是会报ActivityNotFoundException异常的。
10.在自定义隐士启动的时候,这一句<category android:name="android.intent.category.DEFAULT"/>一定要记得添加,当我们需要指定activity的category类型的时候,此时我们肯定添加,这不用说;当我们不需要制定特定的category的时候,此时就添加<category android:name="android.intent.category.DEFAULT"/>代码,否则当其他人使用隐士启动启动该activity的时候,就会报异常ActivityNotFoundException().
11.什么情况下getIntent为null?
getIntent()返回的是启动这个activity的Intent, 一般情况下,是不为null的,具体再什么情况下为null,暂时还没有发现,将来发现后记上。
```
十一如何安全推出已经打开了多个activit的application?
1.有两种方法,方法一:
```
1.1在baseActivity中做的事情:
1.1 在baseActivity中,定义一个静态成员变量ArrayList对象arraylist。
1.2 在activity的哦那create()方法中,将当前activity实例保存到arrayList中,在ondestroy()方法中,移除当前的这个activity实例,并提供一个方法getArrayList(),返回这个arrayList;
2.在退出应用的时候:
当我们想要推出应用的时候,通过getArrayList()方法,获取到ArrayList对象,然后便利其中的activity,调用其finish()方法,至于这句话system.exit(0),可有可无,不过最好还是加上;
```
```
1.2代码:
*****这是baseActivity:
public class BaseActivity extends Activity {
private static ArrayList<Activity> arrayList=new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
arrayList.add(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
arrayList.remove(this);
}
public ArrayList getArrayList(){
return arrayList;
}
}
***这是具体的退出应用时的操作:
在某个activity的某个点击事件中:
private void exitApplication() {
ArrayList<Activity> arrayList = getArrayList();
for (Activity activity:arrayList){
activity.finish();
}
System.exit(0);
}
```
2.方法二:
```
2.1总体来说,是利用广播:
<1>首先创建一个类继承广播接收者BroadCastReceiver
创建一个有参构造函数,有参构造函数接受的参数就是activity的对象,然后赋值给本地成员变量,之后再onreceiver()方法中,调用activity的finish()方法即可。
<2>在BaseActivity中做的事情:
在oncreate()方法中,使用registerRecevier(),注册广播
在ondestroy()方法中,使用unregisterReceive()方法,注销广播。
<3>在要退出应用的时候,此时做的事情:
发送一个特定的广播即可。可以使用无顺广播或者有序广播都行。注意,在使用无序广播的时候,此时就不要加System.exit(0)了,否则只会将当前的activity关闭,其他的activity不能关闭;使用有序广播的时候,直接在结果接收者中,调用System.exit(0),即可。
```
```
2.2代码:
****<1>这是创建的继承BraodCastReceiver的类:
public class MyExitBroadCastReceiver extends BroadcastReceiver {
private Activity activity;
public MyExitBroadCastReceiver() {
super();
}
MyExitBroadCastReceiver(Activity activity) {
//这一句必须要写,父类中只有无参构造函数。
super();
this.activity = activity;
}
@Override
public void onReceive(Context context, Intent intent) {
mActivity.finish();
}
}
****<2>这是baseActivity的内容:
public class BaseActivity extends Activity {
private MyExitBroadCastReceiver myExitBroadCastReceiver=new MyExitBroadCastReceiver(this);
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//这一步可以考虑写在构造代码块中。
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction("a.b.c");
registerReceiver(myExitBroadCastReceiver,intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myExitBroadCastReceiver);
}
}
****<3>这是具体要关闭application的地方:
private void exitApplication() {
Intent intent = new Intent();
intent.setAction("a.b.c");
//这是发送无序广播,之后就不要添加System.exit(0)。
// sendBroadcast(intent);
//这是使用有序广播,在结果接收者中添加System.exit(0);
sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
System.exit(0);
}
}, null, 0, null, null);
}
```
```
2.3问题:
按照上述代码来实现发送广播关闭多个activity的操作的时候,会遇到一个问题,比如说现有五个activit,分别是a,b,c,d,e,然后这些activity均继承BaseActivity,并且依次打开a,b,c,d,e,然后再e中发送广播关闭,此时会遇到一个问题,即nullPointerException(空指针异常),通过打印log,得到一个东西:即当广播接收者接受到这个广播,并且执行onreceiver()方法的时候,对于a,b,c,d这四个activity都没有问题,但是对于e出现了问题,此时在执行onreceiver()方法的时候,该方法内的变量activity为null了,所以此时执行actiity.finish(),系统就会爆出空指针异常,对于这个问题的相关情况如下:
<1>出现这个问题的原因:
现在没有找到。
<2>解决这个问题的方法:
对于onreceiver()作如下的修改:
@Override
public void onReceive(Context context, Intent intent) {
if(null!=mActivity){
mActivity.finish();
}
}
此时就可以避免空指针异常,并且也可以实现关闭所有的activity。
```