一 android四大组件之activity

            --简介

--一.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。

```

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容