關(guān)于Activity你必須要知道的一切

前言:Activity是Android四大組件之一,為用戶提供與系統(tǒng)交互的界面,每一個(gè)應(yīng)用都有一個(gè)或者多個(gè)Acticity,這樣會(huì)有各種各樣的細(xì)節(jié)問題需要查找,我將本人接觸到的知識(shí)點(diǎn)匯總到此篇文章。

1.Activity簡(jiǎn)單介紹

一個(gè)Activity是一個(gè)應(yīng)用程序組件,提供一個(gè)屏幕,用戶可以用來交互為了完成某項(xiàng)任務(wù),例如撥號(hào)、拍照、發(fā)送email、看地圖。每一個(gè)activity被給予一個(gè)窗口,在上面可以繪制用戶交互的畫面。窗口通常充滿屏幕,但也可以小于屏幕而浮于其它窗口之上。一個(gè)用戶交互界面對(duì)應(yīng)一個(gè)activity,相當(dāng)于是界面的容器setContentView(),activity 是Context的子類,同時(shí)實(shí)現(xiàn)了window.callback接口(里面方法如dispatchtouchevent可以分發(fā)事件)和keyevent.callback等, 可以處理與窗體用戶交互的事件。

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {}

2.Activity的創(chuàng)建

1.創(chuàng)建步驟:

  • 創(chuàng)建class類繼承Activity
  • 創(chuàng)建布局文件,作為Activity的顯示內(nèi)容
  • 在清單文件中注冊(cè)Activity

2.注意點(diǎn):

  • 需要在清單文件中為其配置一個(gè)activity標(biāo)簽,聲明你的activity在manifest文件為了它可以被系統(tǒng)訪問。否則如果系統(tǒng)找不到,在顯示時(shí)會(huì)直接產(chǎn)生ActivityNotFoundException。要聲明你的activity,打開manifest文件,添加一個(gè)activity元素作為application元素的子元素。
  • Activity 在調(diào)用的時(shí)候才會(huì)實(shí)例化,如果manifest沒有聲明這個(gè)activity,不使用則不會(huì)報(bào)錯(cuò)。
  • 如果Activity所在的包跟應(yīng)用包名同名,那么可以省略不寫。而用.xxxActivity來代替。
  • 標(biāo)簽中如果帶有這個(gè)子節(jié)點(diǎn),則會(huì)在系統(tǒng)中創(chuàng)建一個(gè)快捷圖標(biāo)
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
  • 聲明入口activity,就會(huì)生成快捷圖標(biāo),可以聲明多個(gè)入口activity,會(huì)產(chǎn)生多個(gè)Activity對(duì)應(yīng)的快捷圖標(biāo)。
  • activity的名稱label、圖標(biāo)icon可以和應(yīng)用程序application節(jié)點(diǎn)的名稱、圖標(biāo)不相同,但默認(rèn)使用application節(jié)點(diǎn)下的屬性。
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"

3.Activity的的跳轉(zhuǎn)

Activity的跳轉(zhuǎn)需要?jiǎng)?chuàng)建Intent對(duì)象,通過設(shè)置intent對(duì)象的參數(shù)指定要跳轉(zhuǎn)的activity。通過設(shè)置Activity的包名和類名實(shí)現(xiàn)跳轉(zhuǎn),稱為顯式意圖。
通過指定動(dòng)作實(shí)現(xiàn)跳轉(zhuǎn),稱為隱式意圖。

1. 顯式意圖

  • 跳轉(zhuǎn)至同一項(xiàng)目下的另一個(gè)Activity,直接指定該Activity的字節(jié)碼即可
Intent intent = new Intent();
intent.setClass(this, SecondActivity.class);
startActivity(intent);
  • 跳轉(zhuǎn)至其他應(yīng)用中的Activity,需要指定該應(yīng)用的包名和該Activity的類名
Intent intent = new Intent();
//啟動(dòng)系統(tǒng)自帶的撥號(hào)器應(yīng)用
intent.setClassName("com.android.dialer","com.android.dialer.DialtactsActivity");
startActivity(intent);

2. 隱式意圖

  • 隱式意圖跳轉(zhuǎn)至指定Activity
Intent intent = new Intent();
//啟動(dòng)系統(tǒng)自帶的撥號(hào)器應(yīng)用
intent.setAction(Intent.ACTION_DIAL);
startActivity(intent);
  • 要讓一個(gè)Activity可以被隱式啟動(dòng),需要在清單文件的activity節(jié)點(diǎn)中設(shè)置intent-filter子節(jié)點(diǎn)
    • action 指定動(dòng)作(可以自定義,可以使用系統(tǒng)自帶的,可以使任意字符串,但一般寫全類名)
    • data 指定數(shù)據(jù)(操作什么內(nèi)容,包括URI和數(shù)據(jù)類型)
    • category 類別 (默認(rèn)類別)
<intent-filter >
     <action android:name="com.chenqiao.second"/>
     <data android:scheme="chenqiao" android:mimeType="aa/bb"/>
     <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
  • 隱式意圖的屬性設(shè)置
//[1]創(chuàng)建意圖對(duì)象 意圖就是我要完成一件事
Intent intent = new Intent();
//[2] 設(shè)置跳轉(zhuǎn)的動(dòng)作
intent.setAction("com.chenqiao.testactivity");
//[3] 設(shè)置category
intent.addCategory("android.intent.category.DEFAULT");
//[4]設(shè)置數(shù)據(jù)
// intent.setData(Uri.parse("chenqiao:"+110));
//[5]設(shè)置數(shù)據(jù)類型
//intent.setType("aa/bb");
//[6]注意 如果setdata 方法和 settype 方法一起使用的時(shí)候 應(yīng)該使用下
//面這個(gè)方法
intent.setDataAndType(Uri.parse("chenqiao:"+110), "aa/bb");
//[4]開啟Activity
startActivity(intent);
  • 你要啟動(dòng)的那個(gè)Activity的意圖過濾器
<intent-filter>
<action android:name="com.itheima.testactivity" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="aa/bb" android:scheme="chenqiao" />
</intent-filter>
  • 隱式意圖啟動(dòng)Activity,需要為intent設(shè)置以上三個(gè)屬性,且值必須與該Activity在清單文件中對(duì)三個(gè)屬性的定義匹配。intent-filter節(jié)點(diǎn)及其子節(jié)點(diǎn)都可以同時(shí)定義多個(gè),隱式啟動(dòng)時(shí)只需與任意一個(gè)匹配即可。在啟動(dòng)效率上,隱式遠(yuǎn)低于顯示。
  • 如果系統(tǒng)中存在多個(gè)Activity的intent-filter同時(shí)與你的intent匹配,那么系統(tǒng)會(huì)顯示一個(gè)對(duì)話框,列出所有匹配的Activity,由用戶選擇啟動(dòng)哪一個(gè)。顯式意圖一般用于啟動(dòng)同一應(yīng)用中的Activity。隱式意圖一般用于啟動(dòng)不同應(yīng)用中的Activity,因?yàn)椴荒苣玫搅硪粋€(gè)應(yīng)用的activity類名,不能顯示啟動(dòng)。

3.意圖匹配原則

  • action行為檢測(cè):manifest文件中activity的一個(gè)intentfilter元素以子元素的形式列出了行為。例如:
<intent-filter . . . >
<action android:name="com.example.project.SHOW_CURRENT" />
<action android:name="com.example.project.SHOW_RECENT" />
<action android:name="com.example.project.SHOW_PENDING" />
. . .
</intent-filter>

一個(gè)Intent對(duì)象只命名一個(gè)單獨(dú)的行為,而一個(gè)過濾器可以列出多個(gè)行為。列表不能為空;一個(gè)過濾器至少要包含一個(gè)元素,否則它將屏蔽所有的意圖。要通過這個(gè)檢測(cè),在Intent對(duì)象中指定的行為必須與過濾器所列出的行為之一相匹配。如果該對(duì)象或是過濾器沒有指定一個(gè)行為,就會(huì)有下面的結(jié)果:如果過濾器沒有列出任何行為,就沒有行為可以與意圖相匹配,所有的意圖都無法通過檢測(cè)。沒有意圖可以通過過濾器。另一方面,如果Intent對(duì)象沒有指定任何的行為,它將自動(dòng)通過檢測(cè)——只要過濾器含有一個(gè)或以上的行為。

  • category類別檢測(cè):intent-filter也會(huì)將類別作為子元素列出。例如:
<intent-filter . . . >
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
. . .
</intent-filter>

注意之前描述過的表示行為和類型的常量不在mainfest文件中被使用。而是使用完整的字符串值。例如,在范例中的“android.intent.category.BROWSABLE”字符串和文檔之前提到的CATEGORY_BROWSABLE常量相對(duì)應(yīng)。類似地,字符串“android.intent.action.EDIT”和ACTION_EDIT常量相對(duì)應(yīng)。
一個(gè)意圖要通過類別檢測(cè),Intent對(duì)象中的每一個(gè)類別都必須與過濾器中的某個(gè)類別相匹配。過濾器可以列出其他更多的類別,但不能省略任何一個(gè)意圖中含有的類別。原則上,所以說,一個(gè)不含有類別的Intent對(duì)象,無論過濾器中有哪些類別,總是能夠通過這項(xiàng)檢測(cè)。這通常是正確的。不過,有一個(gè)例外,Android把所有傳遞給startActivity()的隱式意圖視為它們包含了至少這樣一個(gè)類別:“android.intent.category.DEFAULT”(CATEGORY_DEFAULT常量)。因此,要接受隱式意圖的活動(dòng)必須在其意圖過濾器中包含有“android.intent.category.DEFAULT”。(設(shè)有“android.intent.action.MAIN”和“android.intent.category.LAUNCHER”的過濾器不在此范圍之中。它們將活動(dòng)標(biāo)記為了新任務(wù)的開始,并顯示在應(yīng)用啟動(dòng)器(launcher)屏幕上。它們可以在類別列表中包含“android.intent.category.DEFAULT”,不過這并不是必須的)

  • data數(shù)據(jù)檢測(cè):如同行為和類別,意圖過濾器的數(shù)據(jù)類型也是作為子元素保存的。而且和它們一樣,這些子元素可以出現(xiàn)多次或完全不出現(xiàn)。
<intent-filter . . . >
<data android:mimeType="video/mpeg" android:scheme="http" . . . />
<data android:mimeType="audio/mpeg" android:scheme="http" . . . />
. . .
</intent-filter>

每一個(gè)元素可以指定一個(gè)URI和數(shù)據(jù)類型(MINE媒體類型)。URI的每一個(gè)部分由不同的屬性——模式(scheme)、主機(jī)(host)、接口(port)和路徑(path)。當(dāng)一個(gè)Intent對(duì)象中的URI和過濾器中的URI相比較時(shí),僅比較過濾器中所包含的URI部分。例如,如果一個(gè)過濾器僅指定了一個(gè)模式,那所有具有這個(gè)模式的URI就與過濾器相匹配。如果過濾器指定了一個(gè)模式及一個(gè)授權(quán)但是沒有指定路徑,那么無論是什么路徑,具有相同模式和授權(quán)的URI將得到匹配。如果過濾器指定了模式、授權(quán)和路徑,那就只有具有相同模式、授權(quán)和路徑的URI得到匹配。不過,過濾器中指定的路徑可以包含通配符以僅僅限定部分路徑。一個(gè)data元素的類型(type)屬性指定了數(shù)據(jù)的MINE類型。在過濾器中這比URI更為常見。Intent對(duì)象和過濾器都可以用””通配符作為子類別域來標(biāo)識(shí)子類別匹配,例如“text/”或“audio/*”。

數(shù)據(jù)檢測(cè)同時(shí)將Intent對(duì)象中的URI和數(shù)據(jù)類型與過濾器中的URI和數(shù)據(jù)類型相比較。該過程遵循以下規(guī)則:

1.不包含URI和數(shù)據(jù)類型的Intent對(duì)象只有在過濾器也不指定任何URI及數(shù)據(jù)類型時(shí)才能通過檢測(cè)。
2.包含URI但不包含數(shù)據(jù)類型(且數(shù)據(jù)類型不能通過URI推斷出來)的Intent對(duì)象只有在其URI與過濾器中的URI相匹配且過濾器同樣沒有指定類型時(shí)才能通過檢測(cè)。這是僅僅在類似mailto:和tel:這樣沒有指向?qū)嶋H數(shù)據(jù)的URI時(shí)才會(huì)出現(xiàn)的情況。
3.包含數(shù)據(jù)類型但不包含URI的Intent對(duì)象只有在過濾器列出了相同的數(shù)據(jù)類型 而沒有指定URI時(shí)才能通過檢測(cè)。
4.同時(shí)包含有URI和數(shù)據(jù)類型(或URI可以推導(dǎo)出數(shù)據(jù)類型)的Intent對(duì)象在其數(shù)據(jù)類型與過濾器中列出的類型相匹配時(shí)通過檢測(cè)的數(shù)據(jù)類型部分。當(dāng)其URI與過濾器中的URI相匹配,或,其具有一個(gè)content:或file:URI并且過濾器沒有指定URI時(shí),通過測(cè)試的URI部分。換言之,如果過濾器僅列出了數(shù)據(jù)類型,一個(gè)組件將被假定支持content:和file:數(shù)據(jù)。

4.Activity跳轉(zhuǎn)時(shí)的數(shù)據(jù)傳遞

Activity之間的數(shù)據(jù)傳遞,常見的有4中,Intent傳遞簡(jiǎn)單數(shù)據(jù),Bundle傳遞數(shù)據(jù)包,傳遞值對(duì)象,獲取Activity的返回參數(shù)。

1. Intent傳遞簡(jiǎn)單數(shù)據(jù)

  • Activity通過Intent啟動(dòng)時(shí),可以通過Intent對(duì)象攜帶數(shù)據(jù)到目標(biāo)Activity
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("maleName", maleName);
intent.putExtra("femaleName", femaleName);
startActivity(intent);
  • 在目標(biāo)Activity中取出數(shù)據(jù)數(shù)據(jù)
Intent intent = getIntent();
String maleName = intent.getStringExtra("maleName");
String femaleName = intent.getStringExtra("femaleName");

2. Bundle傳遞數(shù)據(jù)包
MainActivity:

Intent intent = new Intent(MainActivity.this,OtherActivity.class);

 Bundle bundle = new Bundle();

 bundle.putString("name","MirGao");
 bundle.putString("age","24");

 intent.putExtras(bundle);
 startActivity(intent);

OtherActivity:

Intent intent = getIntent();
Bundle b = intent.getExtras();
tv.setText(b.getString("name") + "  " + b.getString("age"));

3.傳遞值對(duì)象

所謂的值對(duì)象,就是我們通常在項(xiàng)目中自定義的,有數(shù)據(jù)類型的javabean。
那我們就先自定義一個(gè)數(shù)據(jù)類型。

public class UserBean implements Serializable{
        
            private String name;
            private String age;
        
            public UserBean(String name, String age) {
                this.name = name;
                this.age = age;
            }
        
        
            public void setName(String name) {
                this.name = name;
            }
        
            public void setAge(String age) {
                this.age = age;
            }
        
            public String getName() {
                return name;
            }
        
            public String getAge() {
                return age;
            }
        }

在這里我們定義了兩個(gè)都是String類型的參數(shù),并且實(shí)現(xiàn)了Serializable接口。使我們的自定義數(shù)據(jù)類型進(jìn)行數(shù)據(jù)序列化。

在MainActivity中直接進(jìn)行自定義對(duì)象的傳遞,并賦予兩個(gè)參數(shù):

Intent intent = new Intent(MainActivity.this,OtherActivity.class);
intent.putExtra("UserBean",new UserBean("MirGao","24"));
startActivity(intent);

OtherActivity中獲取值對(duì)象數(shù)據(jù):

UserBean userBean ;
userBean = (UserBean) getIntent().getSerializableExtra("UserBean");
tv.setText(userBean.getName() + "  " + userBean.getAge());

使用UserBean對(duì)象獲取序列化后的對(duì)象并進(jìn)行強(qiáng)制轉(zhuǎn)換,并通過獲取的對(duì)象,進(jìn)行操作。使用序列化很簡(jiǎn)單,方便的可以進(jìn)行復(fù)雜,大量數(shù)據(jù)的傳遞。但是,Serializable與Parcelable相比而言 效率比較低 ,所以Android平臺(tái)又給我們提供了Parcelable,他是一個(gè)專門針對(duì)移動(dòng)工具上數(shù)據(jù)序列化的接口,那么,下面我們就來進(jìn)行學(xué)習(xí)。

public class UserBean implements Parcelable{

    private String name;
    private String age;

    public UserBean(String name, String age) {
        this.name = name;
        this.age = age;
    }

    protected UserBean(Parcel in) {
        name = in.readString();
        age = in.readString();
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public String getAge() {
        return age;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(getName());
        dest.writeString(getAge());
    }

    public static final Creator<UserBean> CREATOR = new Creator<UserBean>() {
        @Override
        public UserBean createFromParcel(Parcel in) {
            return new UserBean(in.readString(),in.readString());
        }

        @Override
        public UserBean[] newArray(int size) {
            return new UserBean[size];
        }
    };
}

我們實(shí)現(xiàn)了Parcelable接口,并且實(shí)現(xiàn)了3個(gè)方法,writeToParcel這個(gè)方法中是需要我們?nèi)ナ謩?dòng)讀取的,我們把我們要傳遞的變量保存,以供其他的成員變量或者組件使用。

Other中獲取數(shù)據(jù):

UserBean userBean = getIntent().getParcelableExtra("UserBean");
tv.setText(userBean.getName() + "  " + userBean.getAge());

總結(jié):Serializable和Parcelable都是序列化接口,因?yàn)镾erializable簡(jiǎn)單,方便,Android系統(tǒng)對(duì)他有自動(dòng)完成序列化的操作,所以速度是比較慢的。而Parcelable中都是手動(dòng)去添加數(shù)據(jù)類型,讀取數(shù)據(jù),Android系統(tǒng)并沒有給我們提供序列化機(jī)制,所以Parcelable的數(shù)據(jù)是相對(duì)復(fù)雜的,但是速度是比較快的。所以在使用時(shí),注意優(yōu)缺點(diǎn)選擇。

5.啟動(dòng)Activity并獲取返回值

從A界面打開B界面, B界面關(guān)閉的時(shí)候,返回一個(gè)數(shù)據(jù)給A界面:

  • 1.開啟activity并且獲取返回值
//第一個(gè)參數(shù)為請(qǐng)求碼,即調(diào)用startActivityForResult()傳遞過去的值
//第二個(gè)參數(shù)為結(jié)果碼,可以根據(jù)業(yè)務(wù)需求自己編號(hào),結(jié)果碼用于標(biāo)識(shí)返回?cái)?shù)據(jù)來自哪個(gè)新Activity
startActivityForResult(intent, 0);
  • 2.在新開啟的界面里面實(shí)現(xiàn)設(shè)置數(shù)據(jù)的邏輯
Intent data = new Intent();
data.putExtra("phone", phone);
//設(shè)置一個(gè)結(jié)果數(shù)據(jù),數(shù)據(jù)會(huì)返回給調(diào)用者
setResult(0, data);
finish();//關(guān)閉掉當(dāng)前的activity,才會(huì)返回?cái)?shù)據(jù)
  • 3.在開啟者activity里面實(shí)現(xiàn)方法
//通過data獲取返回的數(shù)據(jù)
onActivityResult(int requestCode, int resultCode, Intent data) 
  • 4.根據(jù)請(qǐng)求碼和結(jié)果碼確定業(yè)務(wù)邏輯
    請(qǐng)求碼:用來區(qū)分?jǐn)?shù)據(jù)來自于哪一個(gè)Activity
    結(jié)果碼:用來區(qū)分,返回的數(shù)據(jù)時(shí)屬于什么類型

5. Activity的生命周期

一張圖看清Activity的生命周期圖:

Activity的生命周期
  • void onCreate():Activity已經(jīng)被創(chuàng)建完畢,當(dāng)Activity第一次啟動(dòng)的時(shí)候,觸發(fā)該方法,可以在此時(shí)完成Activity的初始化工作。onCreate 方法有一個(gè)參數(shù),該參數(shù)可以為空( null ),也可以是之前調(diào)用onSaveInstanceState()方法保存的狀態(tài)信息。

  • void onStart():該方法的觸發(fā)表示所屬Activity將被展現(xiàn)給用戶。Activity已經(jīng)顯示在屏幕,但沒有得到焦點(diǎn)。

  • void onResume():Activity得到焦點(diǎn),可以與用戶交互。

  • void onPause():Activity失去焦點(diǎn),無法再與用戶交互,但依然可見。當(dāng)一個(gè)正在前臺(tái)運(yùn)行的Activity因?yàn)槠渌腁ctivity需要前臺(tái)運(yùn)行而轉(zhuǎn)入后臺(tái)運(yùn)行的時(shí)候,觸發(fā)該方法。這時(shí)候需要將Activity的狀態(tài)持久化,比如正在編輯的數(shù)據(jù)庫記錄等。

  • void onStop():Activity不可見,進(jìn)入后臺(tái)。當(dāng)一個(gè)Activity不再需要展示給用戶的時(shí)候,觸發(fā)該方法。如果內(nèi)存緊張,系統(tǒng)會(huì)直接結(jié)束這個(gè)Activity,而不會(huì)觸發(fā)onStop 方法。所以保存狀態(tài)信息是應(yīng)該在onPause 時(shí)做,而不是onStop 時(shí)做。Activity如果沒有在前臺(tái)運(yùn)行,都將被停止或者Linux 管理進(jìn)程為了給新的Activity預(yù)留足夠的存儲(chǔ)空間而隨時(shí)結(jié)束這些Activity。因此對(duì)于開發(fā)者來說,在設(shè)計(jì)應(yīng)用程序的時(shí)候,必須時(shí)刻牢記這一原則。在一些情況下,onPause 方法或許是Activity觸發(fā)的最后的方法,因此開發(fā)者需要在這個(gè)時(shí)候保存需要保存的信息。

  • void onDestroy():Activity被銷毀。當(dāng)Activity銷毀的時(shí)候,觸發(fā)該方法。和onStop 方法一樣,如果內(nèi)存緊張,系統(tǒng)會(huì)直接結(jié)束這個(gè)Activity而不會(huì)觸發(fā)該方法。
    ·onSaveInstanceState :系統(tǒng)調(diào)用該方法,允許Activity保存之前的狀態(tài),比如說在一串字符串中的光標(biāo)所處的位置等。通常情況下,開發(fā)者不需要重寫覆蓋該方法,在默認(rèn)的實(shí)現(xiàn)中,已經(jīng)提供了自動(dòng)保存Activity所涉及到的用戶界面組件的所有狀態(tài)信息。

  • void onRestart():當(dāng)處于停止?fàn)顟B(tài)的Activity需要再次展現(xiàn)給用戶的時(shí)候,觸發(fā)該方法,即從不可見變成可見時(shí)會(huì)執(zhí)行此方法

  • 完整生命周期(entire lifetime)
    onCreate-->onStart-->onResume-->onPause-->onStop-->onDestory

    可視生命周期(visible lifetime)
    onStart-->onResume-->onPause-->onStop

    前臺(tái)生命周期(foreground lifetime)
    onResume-->onPause

6.Activity數(shù)據(jù)的保存

場(chǎng)景:當(dāng)Activity由于系統(tǒng)配置等發(fā)生改變,會(huì)導(dǎo)致Activity被殺死而重新創(chuàng)建。即會(huì)調(diào)用onDestroy銷毀Activity,再重新onCreate開啟新Activity,系統(tǒng)通過調(diào)用onSaveInstanceState和onRestoreInstanceState分別保存和恢復(fù)視圖(View)狀態(tài)。

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//這里,當(dāng)Acivity第一次被創(chuàng)建的時(shí)候?yàn)榭?//所以我們需要判斷一下
if( savedInstanceState != null ){
savedInstanceState.getString("anAnt");
}
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);

outState.putString("anAnt","Android");

}

onSaveInstanceState

onSaveInstanceState在onStop之前調(diào)用,但不一定在onPause之前或者之后執(zhí)行。onRestoreInstanceState在onStart之后調(diào)用。需要注意的是,onSaveInstanceState方法只會(huì)在Activity被異常終止,在Activity即將被銷毀且有機(jī)會(huì)重新顯示的情況下才會(huì)調(diào)用。具體有以下幾種情形:

1、當(dāng)用戶按下HOME鍵時(shí)。
這是顯而易見的,系統(tǒng)不知道你按下HOME后要運(yùn)行多少其他的程序,自然也不知道activity A是否會(huì)被銷毀,故系統(tǒng)會(huì)調(diào)用onSaveInstanceState,讓用戶有機(jī)會(huì)保存某些非永久性的數(shù)據(jù)。以下幾種情況的分析都遵循該原則。

2、長(zhǎng)按HOME鍵,選擇運(yùn)行其他的程序時(shí)。

3、按下電源按鍵(關(guān)閉屏幕顯示)時(shí)。

4、從activity A中啟動(dòng)一個(gè)新的activity時(shí)。

5、屏幕方向切換時(shí),例如從豎屏切換到橫屏?xí)r。(如果不指定configchange屬性) 在屏幕切換之前,系統(tǒng)會(huì)銷毀activity A,在屏幕切換之后系統(tǒng)又會(huì)自動(dòng)地創(chuàng)建activity A,所以onSaveInstanceState一定會(huì)被執(zhí)行。

總而言之,onSaveInstanceState的調(diào)用遵循一個(gè)重要原則,即當(dāng)系統(tǒng)“未經(jīng)你許可”時(shí)銷毀了你的activity,則onSaveInstanceState會(huì)被系統(tǒng)調(diào)用,這是系統(tǒng)的責(zé)任,因?yàn)樗仨氁峁┮粋€(gè)機(jī)會(huì)讓你保存你的數(shù)據(jù)(當(dāng)然你不保存那就隨便你了)。此外,由于默認(rèn)的onSaveInstanceState()方法的實(shí)現(xiàn)幫助UI存儲(chǔ)它的狀態(tài),所以如果你需要覆蓋這個(gè)方法去存儲(chǔ)額外的狀態(tài)信息時(shí),你應(yīng)該在執(zhí)行任何代碼之前都調(diào)用父類的onSaveInstanceState()方法(super.onSaveInstanceState())。 既然有現(xiàn)成的可用,那么我們到底還要不要自己實(shí)現(xiàn)onSaveInstanceState()?這得看情況了,如果你自己的派生類中有變量影響到UI,或你程序的行為,當(dāng)然就要把這個(gè)變量也保存了,那么就需要自己實(shí)現(xiàn),否則就不需要。

onRestoreInstanceState

onRestoreInstanceState方法在activity確定被銷毀以后,重建activity時(shí)調(diào)用,注意是確定activity被銷毀,例如,當(dāng)正在顯示activity A的時(shí)候,用戶按下HOME鍵回到主界面,然后用戶緊接著又返回到activity A,這種情況下activity A一般不會(huì)因?yàn)閮?nèi)存的原因被系統(tǒng)銷毀,故activity A的onRestoreInstanceState方法不會(huì)被執(zhí)行。

另外,onRestoreInstanceState的bundle參數(shù)也會(huì)傳遞到onCreate方法中,你也可以選擇在onCreate方法中做數(shù)據(jù)還原。 還有onRestoreInstanceState在onstart之后執(zhí)行。

至于這兩個(gè)函數(shù)的使用,給出示范代碼(留意自定義代碼在調(diào)用super的前或后):

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
savedInstanceState.putInt("MyInt", 1);
savedInstanceState.putString("MyString", "Welcome back to Android");
// etc.
super.onSaveInstanceState(savedInstanceState);
}

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);

boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
double myDouble = savedInstanceState.getDouble("myDouble");
int myInt = savedInstanceState.getInt("MyInt");
String myString = savedInstanceState.getString("MyString");
}

7. Activity的啟動(dòng)模式

一個(gè)應(yīng)用一般包含很多Activity,它們按照各自打開的順序排列在返回棧(Back Stack)中,這些Activity統(tǒng)稱為Task。返回棧中的Activity永遠(yuǎn)不會(huì)重新排列,遵循先進(jìn)后出的原則。

通過在AndroidManifest.xml配置Activity的啟動(dòng)模式。

<activity
    android:name=".aidldemo.BindingActivity"
    android:launchMode="standard"
    ... />

在代碼中向Intent添加相應(yīng)標(biāo)志。

Intent intent = new Intent(this, MyActivity.class);  
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
startActivity(intent); 
  • standard標(biāo)準(zhǔn)啟動(dòng)模式
    默認(rèn)的啟動(dòng)模式,新啟動(dòng)的Activity放入返回棧棧頂,遵循先進(jìn)后出原則,同一個(gè)Activity可以被實(shí)例化多次。

  • singleTop 單一頂部模式
    如果任務(wù)棧的棧頂存在這個(gè)要開啟的activity,不會(huì)重新的創(chuàng)建activity,而是復(fù)用已經(jīng)存在的activity。保證棧頂如果存在,不會(huì)重復(fù)創(chuàng)建。
    應(yīng)用場(chǎng)景:瀏覽器的書簽

  • singeTask 單一實(shí)例模式
    當(dāng)開啟activity的時(shí)候,就去檢查在任務(wù)棧里面是否有實(shí)例已經(jīng)存在,如果有實(shí)例存在就復(fù)用這個(gè)已經(jīng)存在的activity,并且把這個(gè)activity上面的所有的別的activity都清空,復(fù)用這個(gè)已經(jīng)存在的activity。保證整個(gè)任務(wù)棧里面只有一個(gè)實(shí)例存在.
    應(yīng)用場(chǎng)景:瀏覽器的activity。從不同地方打開瀏覽器使用的都是同一個(gè)實(shí)例。
    如果一個(gè)activity的創(chuàng)建需要占用大量的系統(tǒng)資源(cpu,內(nèi)存)一般配置這個(gè)activity為singletask的啟動(dòng)模式。

  • singleInstance單一任務(wù)棧模式
    singleInstance啟動(dòng)模式非常特殊, activity會(huì)運(yùn)行在自己的任務(wù)棧里面,并且這個(gè)任務(wù)棧里面只有一個(gè)實(shí)例存在。如果你要保證一個(gè)activity在整個(gè)手機(jī)操作系統(tǒng)里面只有一個(gè)實(shí)例存在,使用singleInstance
    應(yīng)用場(chǎng)景: 電話撥打界面。

8.結(jié)束語

以上就是我在學(xué)習(xí)的過程當(dāng)中遇到的關(guān)于Activity的有關(guān)問題總結(jié), 個(gè)人眼界有限, 可能還不是很全面, 我會(huì)在以后的學(xué)習(xí)過程當(dāng)中不斷補(bǔ)充。純粹為了自己溫故而知新,不斷的學(xué)習(xí)。┭┮﹏┭┮

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容