zoukankan      html  css  js  c++  java
  • android四大组件之activity

    Activity是四大组件中最重要的一个组件,以下将从activity生命周期,任务栈及启动模式/设置flag,数据/对象传递及startactivtyforresult的使用,安全退出,Title及基类等几个方面来详述activity的特性。

    [转]http://blog.csdn.net/lijianli9/article/details/19072623

    一、activity的生命周期:

    1. Activity状态

    激活状态 : Activity出于前台 , 栈顶位置;

    暂停状态 : 失去了焦点 , 但是用户仍然可以看到 , 比如弹出一个对话框 , 后面的Activity出于暂停状态;

    停止状态 : 被其它的Activity覆盖 , 用户不可见 , 但是仍然存在;

    2.Activity操作生命周期的方法

    onCreate() : 初始化一些成员变量 , 如View等 , 此时进入停止状态;

    onStart() : 被用户可见之前调用 , 调用之后进入暂停状态 , 如果不满足条件我们不想让用户进入应用 , 可以在这里进行限制 , finish掉该Activity;

    onResume() : 在与用户交互之前调用 , 调用之后进入激活状态;

    onPause() : 激活另一个Activity时调用 , 调用之后进入暂停状态 , 界面可见 , 失去焦点; 该操作用来保存当前Activity数据;

    onStop() : Activity被覆盖前调用 , 调用之后该Activity不可见; 该方法用来关闭旧的Activity , 注意是完全不可见的时候才会调用这个方法;

    onDestroy() : Activity被销毁前调用 ;

    注意 : 在暂停状态 和 停止状态 , 如果内存紧张 , 该Activity会被系统回收;也就是除了onResume()和onDestroy(),其他几个方法中都有可能被回收

    3.Activity运作流程

    (1)第一次启动Activity A  

    首先调用onCreate()方法创建Activity进入停止状态 -> 调用onStart()方法进入暂停状态 -> 调用onResume()方法进入激活状态; 

    (2)从Activity A 跳转到 Activity B 

    A先执行onPause()方法进入暂停状态 -> B执行onCreate()方法进入停止状态 -> B执行onStart()方法进入暂停状态 -> B执行onResume()方法进入激活状态 -> A被完全覆盖执行onStop()方法进入停止状态;

    (3)从ActivityB 回到 Activity A

    B执行onPause()方法进入暂停状态 -> A调用onRestart()方法进入停止状态 -> A调用onStart()方法进入暂停状态 -> A调用onResume()方法进入激活状态 -> B被覆盖调用onStop()方法进入停止状态 -> B执行onDestroy()方法销毁

    这里注意Activity的栈是不可逆的 , 只能后退 , 不能前进 , 回退后 , 原来的栈顶的Activity就马上销毁了.

    [转]http://blog.csdn.net/vipzjyno1/article/details/25463457

    二.Activity启动模式、任务栈及标志位flag

    1.什么是栈
    2.Activity栈
    3.Task
    4.Activity启动模式
    5.Activity栈和Task联系
    6.Intent Flags 
    7.Activity相关属性taskAffinity

    1.什么是栈

    栈是一种常用的数据结构,栈只允许访问栈顶的元素,栈就像一个杯子,每次都只能取杯子顶上的东西,而对于栈就只能每次访问它的栈顶元素,从而可以达到保护栈顶元素以下的其他元素.”先进后出”或”后进先出”就是栈的一大特点,先进栈的元素总是要等到后进栈的元素出栈以后才能出栈.递归就是利用到了系统栈,暂时保存临时结果,对临时结果进行保护.
    
    

    定义栈(Stack)

    栈的定义栈(Stack)是限制仅在表的一端进行插入和删除运算的线性表。(1)通常称插入、删除的这一端为栈顶(Top),另一端称为栈底(Bottom)。 (2)当表中没有元素时称为空栈。(3)栈为后进先出(Last In First Out)的线性表,简称为LIFO表。栈的修改是按后进先出的原则进行。每次删除(退栈)的总是当前栈中"最新"的元素,即最后插入(进栈)的元素,而最先插入的是被放在栈的底部,要到最后才能删除。

    栈的操作:压栈、弹栈

    2.Activity中的栈

    Android的管理主要是通过Activity栈来进行,当一个Activity启动时,系统会根据其配置将它压入到一个特定的栈中,系统处于运行状态。
    当用户点击返回或则FINISH()了该Activity,那么它便会被从栈中压出,随之摧毁。
    按照Activity的生命周期可以知道,如果当前显示的栈中Activity没有被摧毁,那么打开新的Activity时候,会将新打开的压入到栈,原来的根据其显示情况选择状态变化(原Activity依旧可见,变为暂停状态(Paused),如果被完成遮住了,转变为停止状态(Stopped))。

    3.Task

     
    Task是与Activity相关的一个重要概念,它密切联系着Activity栈,它简单的说,就是一组以栈的模式聚集在一起的Activity组件集合。(这里只提它和Activity的启动模式来讲)
     

    4.Activity启动模式

    属性:android:launchMode  
    作用:用于指示Activity如何启动。
    描述:这里有四种模式,与Intent对象中的Activity Flags的属性(FLAG_ACTIVITY_*变量)共同作用,来决定Activity如何启动来处理Intent。
    四种模式
        "standard"  --默认模式
        "singleTop"
        "singleTask"
        "singleInstance"
     

    以下举例说明它们的区别:

    standard:Activity的默认加载方法,该方法会通过跳转到一个新的activity,同时将该实例压入到栈中(不管该activity是否已经存在在Task栈中,都是采用new操作)。例如: 栈中顺序是A B C D ,此时D通过Intent跳转到A,那么栈中结构就变成 A B C D A ,点击返回按钮的 显示顺序是 D C B A,依次摧毁。

     

    singleTop:singleTop模式下,当前Activity D位于栈顶的时候,如果通过Intent跳转到它本身的Activity (即D),那么不会重新创建一个新的D实例,所以栈中的结构依旧为A B C D,如果跳转到B,那么由于B不处于栈顶,所以会新建一个B实例并压入到栈中,结构就变成了A B C D B。

     

    singleTask:singleTask模式下,Task栈中只能有一个对应Activity的实例。例如:现在栈的结构为:A B C D。此时D通过Intent跳转到B,则栈的结构变成了:A B。其中的C和D被栈弹出销毁了,也就是说位于B之上的实例都被销毁了。

     

    singleInstance:singleInstance模式下,会将打开的Activity压入一个新建的任务栈中。例如:Task栈1中结构为:A B C ,C通过Intent跳转到了D(D的模式为singleInstance),那么则会新建一个Task 栈2,栈1中结构依旧为A B C,栈2中结构为D,此时屏幕中显示D,之后D通过Intent跳转到D,栈2中不会压入新的D,所以2个栈中的情况没发生改变。如果D跳转到了C,那么就会根据C对应的launchMode的在栈1中进行对应的操作,C如果为standard,那么D跳转到C,栈1的结构为A B C C ,此时点击返回按钮,还是在C,栈1的结构变为A B C,而不会回到D。

     

    5.Activity栈和Task联系

     
    Task简单的就是一组以栈的模式聚集在一起的Activity组件集合,类似于一个填充了Activity的容器,最先加入的Activity会处于容器最下面,最后加入的处于容器最上面,而从Task中取出Activity是从最顶端先取出,最后取出的是最开始添加Activity,这就是后进先出(Last In First Out)模式,而Activity在Task中的顺序是可以控制的,在Activity跳转时用到Intent Flag可以设置新建activity的创建方式(这里就涉及到了Intent Flag的使用)。
     

    6.Intent Flags 

     
    Flags: 表示Intent的标志位,常用于Activity的场景中,它和Activity的启动模式有着密切的联系。
    下面列举的是和本文主题相关的Flags属性:
     
    Intent.FLAG_ACTIVITY_NEW_TASK (默认)
     
    默认的跳转类型,它会重新创建一个新的Activity,不过与这种情况,比如说Task1中有A,B,C三个Activity,此时在C中启动D的话,如果在AndroidManifest.xml文件中给D添加了Affinity的值和Task中的不一样的话,则会在新标记的Affinity所存在的Task中压入这个Activity。如果是默认的或者指定的Affinity和Task一样的话,就和标准模式一样了启动一个新的Activity.
     
    FLAG_ACTIVITY_SINGLE_TOP
     
    这个FLAG就相当于启动模式中的singletop,例如:原来栈中结构是A B C D,在D中启动D,栈中的情况还是A,B,C,D。
     
    FLAG_ACTIVITY_CLEAR_TOP
     
    这个FLAG就相当于启动模式中的SingleTask,这种FLAG启动的Activity会把要启动的Activity之上的Activity全部弹出栈空间。例如:原来栈中的结构是A B C D ,从D中跳转到B,栈中的结构就变为了A B了。(这个方法可以用来关闭多个Activity,之后的一篇博文里面会提到)
     
    FLAG_ACTIVITY_BROUGHT_TO_FRONT
     
    这个网上很多人是这样写的。如果activity在task存在,拿到最顶端,不会启动新的Activity。这个有可能会误导大家! 他这个FLAG其实是这个意思!比如说我现在有A,在A中启动B,此时在A中Intent中加上这个标记。此时B就是以FLAG_ACTIVITY_BROUGHT_TO_FRONT方式启动,此时在B中再启动C,D(正常启动C,D),如果这个时候在D中再启动B,这个时候最后的栈的情况是 A,C,D,B。如果在A,B,C,D正常启动的话,不管B有没有用FLAG_ACTIVITY_BROUGHT_TO_FRONT启动,此时在D中启动B的话,还是会变成A,C,D,B的。
     
    FLAG_ACTIVITY_NO_USER_ACTION
     
    onUserLeaveHint()作为activity周期的一部分,它在activity因为用户要跳转到别的activity而要退到background时使用。比如,在用户按下Home键,它将被调用。比如有电话进来(不属于用户的选择),它就不会被调用。
    那么系统如何区分让当前activity退到background时使用是用户的选择?
     
    它是根据促使当前activity退到background的那个新启动的Activity的Intent里是否有FLAG_ACTIVITY_NO_USER_ACTION来确定的。
     
    注意:调用finish()使该activity销毁时不会调用该函数
     
    FLAG_ACTIVITY_NO_HISTORY
     
    意思就是说用这个FLAG启动的Activity,一旦退出,它不会存在于栈中,比方说!原来是A,B,C这个时候再C中以这个FLAG启动D的,D再启动E,这个时候栈中情况为A,B,C,E。
     

    7.Activity相关属性taskAffinity

     
    Activity 中的 android:taskAffinity 这个属性介绍:

       Activity为Task拥有的一个affinity。拥有相同的affinity的Activity理论上属于相同的Task(在用户的角度是相同的“应用程序”)。Task的affinity是由它的根Activity决定的。 
       affinity决定两件事情——Activity重新宿主的Task(参考allowTaskReparenting特性)和使用FLAG_ACTIVITY_NEW_TASK标志启动的Activity宿主的Task。
        默认情况,一个应用程序中的所有Activity都拥有相同的affinity。捏可以设定这个特性来重组它们,甚至可以把不同应用程序中定义的Activity放置到相同的Task中。为了明确Activity不宿主特定的Task,设定该特性为空的字符串。
        如果这个特性没有设置,Activity将从应用程序的设定那里继承下来(参考<application>元素的taskAffinity特性)。应用程序默认的affinity的名字是<manifest>元素中设定的package名。
     
    注:以上的android:taskAffinity只有通过标志位为FLAG_ACTIVITY_NEW_TASK的Intent启动Activity时,该Activity的这个属性才会生效,系统才会将具有相同Task亲和力的Task切换到前台,然后启动该Activity,否则该Activity仍然运行在启动它的Task中。
     
     
     
    三.activity数据传递

          这一节介绍一下activity之间参数传递。我们知道用intent可以实现activity之间相互跳转,在跳转的同时我们不免也需要传递一些参数,下面就介绍一下如何在一个activity里传递参数,在另一个activity里接受参数。

          activity之间有两种参数传递形式:一种是一个activity启动另一个activity的时候传递数据到另一个activity,然后在activity中接受到数据,做相应处理。另一种是一个activity启动另个一activity传递数据,当另个一activity窗口关闭后,在传递数据到启动它的那个activity。

             我们先看一下第一种方式

             首先先建立一个ActivityDemo项目:

         

    1. public class MainDemoActivity extends Activity {  
    2.    private Button button1,button2;  
    3.      @Override  
    4.     protected void onCreate(Bundle savedInstanceState) {  
    5.         // TODO Auto-generated method stub  
    6.         super.onCreate(savedInstanceState);  
    7.         setContentView(R.layout.main);  
    8.           
    9.         button1=(Button) this.findViewById(R.id.button1);  
    10.         button2=(Button) this.findViewById(R.id.button2);  
    11.           
    12.         button1.setOnClickListener(new MyListener());  
    13.         button2.setOnClickListener(new MyListener());  
    14.     }  
    15.      private class MyListener implements View.OnClickListener{  
    16.   
    17.         public void onClick(View v) {  
    18.             // TODO Auto-generated method stub  
    19.             Intent intent=new Intent();  
    20.             if(v==button1){  
    21.              intent.setClass(MainDemoActivity.this, FirstDemoActivity.class);  
    22.             }else if(v==button2){  
    23.              intent.setClass(MainDemoActivity.this, ResultDemoActivity.class);  
    24.             }  
    25.             startActivity(intent);  
    26.         }  
    27.            
    28.      }  
    29. }  

       接着给出main.xml

      

    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     android:layout_width="fill_parent"  
    4.     android:layout_height="fill_parent"  
    5.     android:orientation="vertical" >  
    6.   
    7.     <Button  
    8.         android:id="@+id/button1"  
    9.         android:layout_width="wrap_content"  
    10.         android:layout_height="wrap_content"  
    11.         android:text="传递参数不得到返回值" />  
    12.   
    13.     <Button  
    14.         android:id="@+id/button2"  
    15.         android:layout_width="wrap_content"  
    16.         android:layout_height="wrap_content"  
    17.         android:text="传递参数得到返回值" />  
    18.     
    19. </LinearLayout>  

        看一下第一种是怎么传递参数的:
    1. public class FirstDemoActivity extends Activity {  
    2.    private Button button3;  
    3.    private EditText editText;  
    4.      @Override  
    5.     protected void onCreate(Bundle savedInstanceState) {  
    6.         // TODO Auto-generated method stub  
    7.         super.onCreate(savedInstanceState);  
    8.         setContentView(R.layout.first);  
    9.           
    10.         button3=(Button) this.findViewById(R.id.button3);  
    11.         editText=(EditText) this.findViewById(R.id.editText1);  
    12.         button3.setOnClickListener(new View.OnClickListener() {  
    13.               
    14.             public void onClick(View v) {  
    15.                 // TODO Auto-generated method stub  
    16.                 String content=editText.getText().toString();  
    17.               Intent intent=new Intent(FirstDemoActivity.this,SecondDemoActivity.class);  
    18.               //可以把要传递的参数放到一个bundle里传递过去,bundle可以看做一个特殊的map.  
    19.               Bundle bundle=new Bundle();  
    20.               bundle.putString("result", "第一个activity的内容");  
    21.               bundle.putString("content",content);  
    22.               intent.putExtras(bundle);  
    23.               //也可以用这种方式传递.  
    24.               //intent.putExtra("result", "第一个activity的内容");  
    25.                 
    26.               startActivity(intent);  
    27.             }  
    28.         });  
    29.     }  
    30. }  
       

          看一下在SecondDemoActivity中怎么接受参数:
        

    1. public class SecondDemoActivity extends Activity {  
    2.     private TextView textView;  
    3.      @Override  
    4.     protected void onCreate(Bundle savedInstanceState) {  
    5.         // TODO Auto-generated method stub  
    6.         super.onCreate(savedInstanceState);  
    7.         setContentView(R.layout.second);  
    8.           
    9.         textView=(TextView) this.findViewById(R.id.textView3);  
    10.         Intent intent=getIntent();  
    11.         String result=intent.getStringExtra("result");  
    12.         String content=intent.getStringExtra("content");  
    13.         textView.setText(result+" : "+content);  
    14.     }  
    15. }  
        
          启动,看一下运行效果:

         

         点击发送按钮:

        

          OK!  成功。 

          接下来看看第二种方式:

        先看一下页面的布局文件:

      

    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     android:layout_width="fill_parent"  
    4.     android:layout_height="fill_parent"  
    5.     android:orientation="vertical" >  
    6.   
    7.     <TextView  
    8.         android:id="@+id/text"  
    9.         android:layout_width="fill_parent"  
    10.         android:layout_height="wrap_content"  
    11.         android:text="@string/hello" />  
    12.   
    13. </LinearLayout>  

         核心代码:

      

    1. public class ResultDemoActivity extends Activity {  
    2.     /** Called when the activity is first created. */  
    3.     private TextView text;  
    4.     @Override  
    5.     public void onCreate(Bundle savedInstanceState) {  
    6.         super.onCreate(savedInstanceState);  
    7.         setContentView(R.layout.result);  
    8.           
    9.         text=(TextView)this.findViewById(R.id.text);  
    10.         pickContact();  
    11.     }  
    12.     private void pickContact(){  
    13.         Intent intent=new Intent(Intent.ACTION_PICK,ContactsContract.Contacts.CONTENT_URI);  
    14.           //在这里也可以传递一些参数  
    15.         //intent.putExtra(name, value)  
    16.         //第二个参数就是onActivityResult 里得第一个参数。  
    17.         startActivityForResult(intent,1);  
    18.     }  
    19.   
    20.     @Override  
    21.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
    22.         // TODO Auto-generated method stub  
    23.         if(resultCode==Activity.RESULT_OK && requestCode==1){  
    24.             Cursor cusor=this.getContentResolver()  
    25.                 .query(data.getData(), new String[]{ContactsContract.Contacts.DISPLAY_NAME},  
    26.                         null, null, null);  
    27.             if(cusor.moveToFirst()){  
    28.                 String str=cusor.getString(cusor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));  
    29.                 text.setText(str);  
    30.             }  
    31.         }  
    32.     }  
    33. }  

       代码我想大家都看的懂吧!不懂得看我以前的使用Content Provider得到联系人信息这一节

        看一下运行效果:

        

       

        这是我通讯录的联系人。点击listview:

        

         

        返回到了ResultDemoActivity并得到了联系人的名字。

        xml序列化传递对象:

       

    Activity之间传递对象,或者通过Bundle传递对象的两种方式。 

    1:Serializable方式 
      传递一个对象 

    2:Parcelable方式 
      传递一个对象、传递多个对象(ArrayList<Object>) 


    方式一:Serializable 

         传递类: 
        
    Java代码  
    1. public class CustomeClass implements Serializable{  
    2.       
    3.     /** 
    4.      *  
    5.      */  
    6.     private static final long serialVersionUID = -7060210544600464481L;  
    7.     private String name;  
    8.     private String id;  
    9.     private int age;  
    10.     private String sex;  
    11.       
    12.     public String getName() {  
    13.         return name;  
    14.     }  
    15.     public void setName(String name) {  
    16.         this.name = name;  
    17.     }  
    18.     public String getId() {  
    19.         return id;  
    20.     }  
    21.     public void setId(String id) {  
    22.         this.id = id;  
    23.     }  
    24.     public int getAge() {  
    25.         return age;  
    26.     }  
    27.     public void setAge(int age) {  
    28.         this.age = age;  
    29.     }  
    30.     public String getSex() {  
    31.         return sex;  
    32.     }  
    33.     public void setSex(String sex) {  
    34.         this.sex = sex;  
    35.     }  
    36.   
    37. }  



           发送部分: 
          
    Java代码  
    1. CustomeClass cc = new CustomeClass();  
    2. cc.setAge(21);  
    3. cc.setId("123456");  
    4. cc.setName("mingkg21");  
    5. cc.setSex("男");  
    6.   
    7. Intent intent = new Intent(this, PersonInfo.class);  
    8. intent.putExtra("PERSON_INFO", cc);  
    9. startActivity(intent);  


            
           接收部分: 
          
    Java代码  
    1.        Intent intent = getIntent();  
    2. CustomeClass cc = CustomeClass)intent.getSerializableExtra("PERSON_INFO");  
    3. setTextView(R.id.id, cc.getId());  
    4. setTextView(R.id.name, cc.getName());  
    5. setTextView(R.id.sex, cc.getSex());  
    6. setTextView(R.id.age, String.valueOf(cc.getAge()));  



    方式二:Parcelable 

         传递类: 
        
    Java代码  
    1. public class CustomeParcelable implements Parcelable {  
    2.       
    3.     private String name;  
    4.     private String id;  
    5.     private int age;  
    6.     private String sex;  
    7.   
    8.     public String getName() {  
    9.         return name;  
    10.     }  
    11.   
    12.     public void setName(String name) {  
    13.         this.name = name;  
    14.     }  
    15.   
    16.     public String getId() {  
    17.         return id;  
    18.     }  
    19.   
    20.     public void setId(String id) {  
    21.         this.id = id;  
    22.     }  
    23.   
    24.     public int getAge() {  
    25.         return age;  
    26.     }  
    27.   
    28.     public void setAge(int age) {  
    29.         this.age = age;  
    30.     }  
    31.   
    32.     public String getSex() {  
    33.         return sex;  
    34.     }  
    35.   
    36.     public void setSex(String sex) {  
    37.         this.sex = sex;  
    38.     }  
    39.       
    40.     public static final Parcelable.Creator<CustomeParcelable> CREATOR = new Creator<CustomeParcelable>(){  
    41.   
    42.         public CustomeParcelable createFromParcel(Parcel source) {  
    43.             // TODO Auto-generated method stub  
    44.             CustomeParcelable cus = new CustomeParcelable();  
    45.             cus.name = source.readString();  
    46.             cus.id = source.readString();  
    47.             cus.age = source.readInt();  
    48.             cus.sex = source.readString();  
    49.             return cus;  
    50.         }  
    51.   
    52.         public CustomeParcelable[] newArray(int size) {  
    53.             // TODO Auto-generated method stub  
    54.             return new CustomeParcelable[size];  
    55.         }  
    56.           
    57.     };  
    58.   
    59.     public int describeContents() {  
    60.         // TODO Auto-generated method stub  
    61.         return 0;  
    62.     }  
    63.   
    64.     public void writeToParcel(Parcel dest, int flags) {  
    65.         // TODO Auto-generated method stub  
    66.         dest.writeString(name);  
    67.         dest.writeString(id);  
    68.         dest.writeInt(age);  
    69.         dest.writeString(sex);  
    70.     }  
    71.   
    72. }  


           发送部分: 
          
    Java代码  
    1. CustomeParcelable cc = new CustomeParcelable();  
    2. cc.setAge(21);  
    3. cc.setId("123456");  
    4. cc.setName("mingkg21");  
    5. cc.setSex("男");  
    6.           
    7. Intent intent = new Intent(this, PersonInfo.class);  
    8. intent.putExtra("PERSON_INFO", cc);  
    9. startActivity(intent);  


            接受部分: 
           
    Java代码  
    1. Intent intent = getIntent();  
    2. CustomeParcelable cc = intent.getParcelableExtra("PERSON_INFO");  
    3. setTextView(R.id.id, cc.getId());  
    4. setTextView(R.id.name, cc.getName());  
    5. setTextView(R.id.sex, cc.getSex());  
    6. setTextView(R.id.age, String.valueOf(cc.getAge()));  



    以上为Parcelable传递一个对象,若要实现传递多个对象, 
    传递部分: 
    Java代码  
    1. Bundle bundle = new Bundle();  
    2.         bundle.putParcelableArrayList("mP3TagForNetDTOs",mP3TagForNetDTOs);  
    3.         msg.setData(bundle);  
    4.         endDocNotice.sendMessage(msg);  


    接受部分: 
    Java代码  
    1. Bundle bundle =  msg.getData();  
    2.                 mP3TagForNetDTOs = bundle.getParcelableArrayList("mP3TagForNetDTOs");  

    四.多个activity的安全退出

    在android的用户交互中,按钮触发的意图(Intent)跳转会为你重新打开新的一个界面活动(Activity),对于之前的界面根据需求进行摧毁(Finish())或则保留。

    如果一个交互流程中,是从A开始,按照A - B - C - D - A这样的顺序进行的话,那么B,C,D这3个活动界面会根据你D中最后的操作来进行保留或是摧毁,例如

    (1)注册流程中,在A界面点击注册,通过B,C,D界面完成注册后,B,C,D就随之摧毁,而如果D中注册不成功没跳转会A的话,那么B,C,D就不能摧毁,之前所填的内容也必须保留。

    (2)客户端交互中,返回首页按钮,由于在频繁的点击打开过多的界面(如微信查看朋友圈),返回首页就必须一个一个back回去,所有有的客户端为了优化用户体验,便会加入一个按钮返回首页(之前打开的全部关闭)。

    以上几个例子都涉及到了   ---   如何安全退出多个ACTIVITY    这个问题。

    其实,这个问题的解决方案有好多种,并且各有各的优缺点,下面就罗列出多个方案以及各个方案的优缺点所在,以便用户根据需求选择。

    知识结构

    首先,通过大致的思维导图罗列出了以下几个知识点,来帮助你去分析学习:

    1.Activity的启动模式                        

    2.intent:  Flags属性,以及其显、隐式        

    3.Application : 全局的使用

    4.Activity:  onActivityResult(int requestCode, int resultCode, Intent data)方法

    5.栈的概念:Last-In/First-Out(LIFO)   ---  后进先出的原则 

    6.BroadcastReceiver 广播

    7.栈的引申的知识点:(1)ArrayList和LinkedList的区别  (2)android 栈和队列

    以上的 (1)Activity的启动模式  (2)intent:  Flags属性  (3)栈的概念         

    我通过一篇文章写明了他们3者的联系可以点击以下链接查看

    Activity启动模式 及 Intent Flags 与 栈 的关联分析

    具体方案

     

    方案1

    方法:采用FLAG_ACTIVITY_CLEAR_TOP退出整个程序(多activity)

    思路:通过Intent的Flags来控制堆栈去解决

    android中,每打开一个Activity,便会在栈中加入一个Activity,当该Activity被摧毁后,栈中便移除了它,并且栈中的Activity是按照开打的先后顺序依次排排列的。

    Android的窗口类提供了历史栈,我们可以通过stack的原理来巧妙的实现,这里我们在A窗口打开B窗口时在Intent中直接加入标 志 Intent.FLAG_ACTIVITY_CLEAR_TOP,这样开启B时将会清除该进程空间的所有Activity。

    代码:

    在注册流程最后的FourthStep.class中,点击完成注册点击事件

    1. btn_finish.setOnClickListener(new OnClickListener() {  
    2.   
    3.     @Override  
    4.     public void onClick(View v) {  
    5.         // TODO Auto-generated method stub  
    6.         Intent intent = new Intent(INTENT_METHOD_FIRST_SINGUP);  
    7.         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  
    8.         startActivity(intent);  
    9.     }  
    10. });  

    其中的 INTENT_METHOD_FIRST_SINGUP 是登录界面的Intent隐式Action。

    优缺点:

    优:使用对栈的巧妙利用,不会赞成内存无故占用等问题,个人认为这个方法是首选。

    方案2

    方法:通过堆栈管理器去管理

    思路:通过堆栈管理器,对Stack进的存储Activity进行操作(推入,推出,弹出)

    代码:

    1. public class StackManager {  
    2.     /** 
    3.      * Stack 中对应的Activity列表  (也可以写做 Stack<Activity>) 
    4.      */  
    5.     private static Stack mActivityStack;  
    6.     private static StackManager mInstance;  
    7.   
    8.     /** 
    9.      * @描述 获取栈管理工具 
    10.      * @return ActivityManager 
    11.      */  
    12.     public static StackManager getStackManager() {  
    13.         if (mInstance == null) {  
    14.             mInstance = new StackManager();  
    15.         }  
    16.         return mInstance;  
    17.     }  
    18.   
    19.     /** 
    20.      * 推出栈顶Activity 
    21.      */  
    22.     public void popActivity(Activity activity) {  
    23.         if (activity != null) {  
    24.             activity.finish();  
    25.             mActivityStack.remove(activity);  
    26.             activity = null;  
    27.         }  
    28.     }  
    29.   
    30.     /** 
    31.      * 获得当前栈顶Activity 
    32.      */  
    33.     public Activity currentActivity() {  
    34.         //lastElement()获取最后个子元素,这里是栈顶的Activity  
    35.         if(mActivityStack == null || mActivityStack.size() ==0){  
    36.             return null;  
    37.         }  
    38.         Activity activity = (Activity) mActivityStack.lastElement();  
    39.         return activity;  
    40.     }  
    41.   
    42.     /** 
    43.      * 将当前Activity推入栈中 
    44.      */  
    45.     public void pushActivity(Activity activity) {  
    46.         if (mActivityStack == null) {  
    47.             mActivityStack = new Stack();  
    48.         }  
    49.         mActivityStack.add(activity);  
    50.     }  
    51.   
    52.     /** 
    53.      * 弹出指定的clsss所在栈顶部的中所有Activity 
    54.      * @clsss : 指定的类  
    55.      */  
    56.     public void popTopActivitys(Class clsss) {  
    57.         while (true) {  
    58.             Activity activity = currentActivity();  
    59.             if (activity == null) {  
    60.                 break;  
    61.             }  
    62.             if (activity.getClass().equals(clsss)) {  
    63.                 break;  
    64.             }  
    65.             popActivity(activity);  
    66.         }  
    67.     }  
    68.       
    69.     /** 
    70.      * 弹出栈中所有Activity 
    71.      */  
    72.     public void popAllActivitys() {  
    73.         while (true) {  
    74.             Activity activity = currentActivity();  
    75.             if (activity == null) {  
    76.                 break;  
    77.             }  
    78.             popActivity(activity);  
    79.         }  
    80.     }  
    81. }  

    之后在注册流程中的对应步骤的Activity的onCreate()中把当前Activity推入栈列表,完成注册流程后,弹出栈列表中流程所涉及的Activity。
    优缺点:

    缺:如果处理不当,容易造成不在当前界面的Activity被全局引用而摧毁不掉,内存得不到释放,从而无故占用不必要的内存。

    方案3:

    方法:全局记录打开的Activity或通过一个自定义的类去管理打开的Activity

    思路:通过在Application中用一个列表来记录当前所打开的Activity,根据需求去遍历finish()。

    描述和方案2有点类似。

    代码:

    1. public class AppApplication extends Application {  
    2.     private static AppApplication mAppApplication;  
    3.     /** 当前打开的activity列表 */  
    4.     public ArrayList<Activity> activityList;  
    5.   
    6.     @Override  
    7.     public void onCreate() {  
    8.         // TODO Auto-generated method stub  
    9.         super.onCreate();  
    10.         mAppApplication = this;  
    11.     }  
    12.   
    13.     /** 获取Application */  
    14.     public static AppApplication getApp() {  
    15.         if (mAppApplication == null) {  
    16.             mAppApplication = new AppApplication();  
    17.         }  
    18.         return mAppApplication;  
    19.     }  
    20.   
    21.     /** 添加当前Activity 到列表中 */  
    22.     public void addActivity(Activity acitivity) {  
    23.         if(activityList == null){  
    24.             activityList = new ArrayList<Activity>();  
    25.         }  
    26.         activityList.add(acitivity);  
    27.     }  
    28.       
    29.     /** 清空列表,取消引用*/  
    30.     public void clearActivity(){  
    31.         activityList.clear();  
    32.     }  
    33.   
    34.     /** 遍历退出所有Activity */  
    35.     public void exit() {  
    36.         for (Activity activity : activityList) {  
    37.             activity.finish();  
    38.         }  
    39.         clearActivity();//千万记得清空取消引用。  
    40.         System.exit(0);  
    41.     }  

    使用流程和方法2类似。

    优缺点:

    缺:如果处理不当,容易造成不在当前界面的Activity被全局引用而摧毁不掉,内存得不到释放,从而无故占用不必要的内存。

    方案4

    方法:使用广播机制解决

    思路:通过Activity创建的时候,设置监听广播,在注册流程最后步完成注册时候,发送广播进行遍历finish().

    描述:这里我把这些广播的初始化都写在了基类BaseActivity里面,便于维护。

    代码:

    1. /** 
    2.  * 初始化退出广播 
    3.  */  
    4. public void initFinishReceiver() {  
    5.     IntentFilter filter = new IntentFilter();  
    6.     filter.addAction(INIENT_FINISH);  
    7.     registerReceiver(mFinishReceiver, filter);  
    8. }  
    9.   
    10. /** 
    11.  * 监听是否退出的广播 
    12.  */  
    13. public BroadcastReceiver mFinishReceiver = new BroadcastReceiver() {  
    14.   
    15.     @Override  
    16.     public void onReceive(Context context, Intent intent) {  
    17.         if (INIENT_FINISH.equals(intent.getAction())) {  
    18.             finish();  
    19.         }  
    20.     }  
    21. };  

    在流程中的每步Activity中,初始化广播,之后在点击完成注册时候,发送广播

    1. btn_finish.setOnClickListener(new OnClickListener() {  
    2.   
    3.     @Override  
    4.     public void onClick(View v) {  
    5.         // TODO Auto-generated method stub  
    6.         getApplicationContext().sendBroadcast(new Intent(INIENT_FINISH));  
    7.     }  
    8. });  

    优缺点:

    缺:开启过多的广播监听,觉得会浪费资源。

    方案5:

    方法:通过Activity跳转中传递requestCode的之后根据onActivityResult(int requestCode, int resultCode, Intent data)中返回的resultCode遍历关闭Activity

    思路:使用startActivityForResult(intent, requestCode)方法跳转,并且通过

    描述:这里我把这些广播的初始化都写在了基类BaseActivity里面便于查看。

    代码:

    1. /** 关闭时候的requestCode请求码 */  
    2.     public final static int FINISH_REQUESTCODE = 1;  
    3.     /** 关闭时候的resultCode请求码 */  
    4.     public final static int FINISH_RESULTCODE = 1;  
    5.     /** 
    6.      * 方法5通过回调关闭的时候用到 
    7.      */  
    8.     @Override  
    9.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
    10.         // TODO Auto-generated method stub  
    11.         if(requestCode == FINISH_REQUESTCODE ){  
    12.             if(resultCode == FINISH_RESULTCODE){  
    13.                 setResult(FINISH_RESULTCODE);  
    14.                 finish();  
    15.             }  
    16.         }  
    17.         super.onActivityResult(requestCode, resultCode, data);  
    18.     }  

    之后在流程的Activity中调用带请求码的Intent跳转意图。

    1. startActivityForResult(new Intent(getApplicationContext(), SecondStep.class),FINISH_REQUESTCODE);  

    在最后完成注册流程的时候通过以下方式返回:

    1. btn_finish.setOnClickListener(new OnClickListener() {  
    2.   
    3.     @Override  
    4.     public void onClick(View v) {  
    5.         // TODO Auto-generated method stub  
    6.         setResult(FINISH_RESULTCODE);  
    7.         finish();  
    8.     }  
    9. });  

    优缺点:

    方案6(不推荐)

    方法:方法有人说可以使用抛出异常来退出,可是这样会影响到用户体验,所以不推荐

    五、title管理及基类

    常见的Android应用每个Activity或者Fragment都会带有一个Title栏。最普通的就是每个Activity的布局文件中都写一个title。但是这样管理起来比较麻烦,因为每个activity的title栏其实看起来都差不多,基本都包含有返回功能,一个textview描述当前页面,最右侧会是一个更多或者搜索之类的功能按钮。这样我们其实可以使用一个通用的布局,在所有的activity的父类中直接控制title的样式。

    1.首先是BaseActivity作为所有Activity的父类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    public abstract class BaseActivity extends Activity  {
     
        public LayoutInflater inflater;
        public Resources mResources;
        public Context mContext;
        public View rootView;
        private View titleView;
        private LinearLayout.LayoutParams params;
        public int layoutId;
     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
     
            inflater = getLayoutInflater();
            layoutId = getContentView();
            rootView = inflater.inflate(layoutId, null);
            mContext = this;
            mResources = mContext.getResources();
            params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) mResources.getDimension(R.dimen.common_title_height));
            setContentView(rootView);
            findViewById();
            initTitleView();
            initData();
        }
     
     
        private void initTitleView() {
            titleView = TitleManager.getInstance(this).initTitleView(layoutId);
            ((ViewGroup) rootView).addView(titleView, 0, params);
        }
     
        /**
         * 绑定控件id
         */
        protected abstract void findViewById();
     
        protected abstract void initData();
     
        /**
         * 初始化控件
         */
        protected abstract int getContentView();
     
     
    }

    2.接着是titlemanager类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    public class TitleManager implements View.OnClickListener {
     
     
        private static TitleManager uniqueInstance = null;
        private Activity context;
        private View titleView;
        private LayoutInflater inflater;
     
        private TitleManager(Activity context) {
            this.context = context;
            inflater = context.getLayoutInflater();
        }
     
        public static TitleManager getInstance(Activity context) {
            if (uniqueInstance == null) {
                uniqueInstance = new TitleManager(context);
            }
            return uniqueInstance;
        }
     
        public ImageView mCommonTitleLeft;
        public TextView mCityNameTextView;
        public TextView mTitleTextView;
        public ImageView mSearch;
        public ImageView mFavorite;
        public ImageView mShare;
     
     
        public View initTitleView(int layoutId) {
     
            titleView = inflater.inflate(R.layout.view_product_common_title, null);
            mCommonTitleLeft = (ImageView) titleView.findViewById(R.id.common_title_left);
            mCommonTitleLeft.setOnClickListener(this);
            mCityNameTextView = (TextView) titleView.findViewById(R.id.activity_product_home_left);
            mCityNameTextView.setOnClickListener(this);
            mTitleTextView = (TextView) titleView.findViewById(R.id.common_title_center);
            mSearch = (ImageView) titleView.findViewById(R.id.common_title_search);
            mSearch.setOnClickListener(this);
            mFavorite = (ImageView) titleView.findViewById(R.id.details_product_favorite_img);
            mFavorite.setOnClickListener(this);
            mShare = (ImageView) titleView.findViewById(R.id.details_product_shear_img);
            mShare.setOnClickListener(this);
     
            switch (layoutId) {
                case R.layout.main:
                    mCityNameTextView.setVisibility(View.VISIBLE);
                    mSearch.setVisibility(View.VISIBLE);
                    mTitleTextView.setText(R.string.title_activity_main);
                    break;
                case R.layout.main1:
                    mCommonTitleLeft.setVisibility(View.VISIBLE);
                    mTitleTextView.setText(R.string.title_activity_main1);
                    break;
                case R.layout.main2:
                    mCommonTitleLeft.setVisibility(View.VISIBLE);
                    mFavorite.setVisibility(View.VISIBLE);
                    mTitleTextView.setText(R.string.title_activity_main2);
                    break;
                case R.layout.main3:
                    mCommonTitleLeft.setVisibility(View.VISIBLE);
                    mShare.setVisibility(View.VISIBLE);
                    mTitleTextView.setText(R.string.title_activity_main3);
                    break;
     
                default:
                    mCityNameTextView.setVisibility(View.GONE);
                    mSearch.setVisibility(View.GONE);
                    mCommonTitleLeft.setVisibility(View.VISIBLE);
                    mFavorite.setVisibility(View.GONE);
                    mShare.setVisibility(View.GONE);
                    break;
            }
            return titleView;
        }
     
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.common_title_left:
                    context.finish();
                    break;
                case R.id.common_title_search:
                    Toast.makeText(context, "search", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }
  • 相关阅读:
    XmlNode中Value和InnerText的区别
    C# 后台POST和GET 获取数据
    XDocument简单入门
    一个基于jQuery的简单树形菜单
    使用C#的HttpWebRequest模拟登陆网站
    JS中offsetTop、clientTop、scrollTop、offsetTop各属性介绍
    height、clientHeight、scrollHeight、offsetHeight区别
    使用httpwebrequest Post数据到网站
    sql语句中left join、inner join中的on与where的区别
    SQL Join的一些总结
  • 原文地址:https://www.cnblogs.com/ymczxy/p/4710331.html
Copyright © 2011-2022 走看看