码迷,mamicode.com
首页 > 移动开发 > 详细

android四大组件之activity

时间:2015-08-07 12:56:54      阅读:237      评论:0      收藏:0      [点我收藏+]

标签:

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;
        }
    }
}

android四大组件之activity

标签:

原文地址:http://www.cnblogs.com/ymczxy/p/4710331.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!