Activity及其生命週期

 

一、Activity初步認識:【創建、配置、啓動Activity

 

()、創建Activity

· 創建Activity:繼承於Activity或其子類,重寫onCreate()方法。

 

()Activity註冊配置:

作爲四大組件之一的Activity,必須要在AndroidManifest清單文件中進行註冊。如果沒有配置,而又在程序中啓動了該Activity,將拋出異常(ActivityNotFoundException)。

 

註冊配置的核心代碼:


<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme">

<activity

android:name=".MainActivity"

android:label="@string/app_name">

<intent-filter>

<actionandroid:name="android.intent.action.MAIN"/>

<categoryandroid:name="android.intent.category.LAUNCHER"/>

</intent-filter>

</activity>

<activity

android:name=".HeadpicActivity"

android:label="@string/title_activity_headpic">

</activity>

</application>

()啓動Activity

1. Android應用裏各組件之間通信使用Intent。一個Activity通過Intent來表達自己的意圖

2. 常用有兩種方式:

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

o Intent intent = new Intent();

§  intent.setClass(MainActivity.this , NextActivity.class);

3. 常用啓動方式有兩種:

· startActivity();

· startActivityForResult();

()Activity之間交換數據

· 第一種方式:

· 

甲頁面利用intent對象的putExtra()方法傳值,乙頁面利用getStringExtra()getIntExtra()等系列方法來一一接收值;

· 第二種方式:【常用】

· 

甲頁面利用bundle對象來保存所有需要傳遞的數值,通過intentputExtras()方法傳值,乙頁面利用intent對象的getExtras()方法返回bundle對象,將所有接收的數據放在bundle對象中,利用bundleget系列方法一一取出數據。

        【備註:】如果甲頁面使用putExtras()方法傳值,則乙頁面使用getExtras()接收值。如果甲頁面使用putExtra()方法傳值,則乙頁面要使用getBundleExtra()方法接收值。

 

· Activity之間是否可以傳遞對象呢?

· 答案是可以的

· 【掌握Serializable的用法

· 

Serializable的用法

Parcelable的用法

 

1、傳遞一般數據的核心代碼:

 

1)、MainActivity頁面:


publicvoid clickButton(View view) {

switch (view.getId()) {

case R.id.button_main_tonext:

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

Bundle bundle = new Bundle();

bundle.putString("myname","思想的頓悟");

bundle.putInt("age", 16);

intent.putExtras(bundle);

startActivity(intent);

break;
}
}

 

2)、NextActivity頁面:


@Override
protectedvoid onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_next);

// 接收第一個頁面的傳值

Intent intent = getIntent();

Bundle bundle = intent.getExtras();

String name = bundle.getString("myname");

int age = bundle.getInt("age");

this.setTitle(name +":" + age);
}

 

 

2、傳遞對象數據的核心代碼:

 

1)、Person對象核心代碼:


public classPerson  implements Serializable {
private StringuserName;
private int age;
private int sex;

private List<String>infoList;

public List<String> getInfoList() {

returninfoList;
}

publicvoid setInfoList(List<String> infoList) {

this.infoList = infoList;

}

public String getUserName() {

returnuserName;
}

publicvoid setUserName(String userName) {
this.userName = userName;
}

publicint getAge() {
returnage;
}
publicvoid setAge(int age) {
this.age = age;
}

publicint getSex() {
returnsex;
}

publicvoid setSex(int
this.sex = sex;
}
}

2)、MainActivity頁面:


publicclass MainActivityextends Activity {

private Personperson =null;

@Override

protectedvoid onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

List<String> list = new ArrayList<String>();

list.add("one");

list.add("two");

list.add("three");

person =new Person();

person.setUserName("sixiangdedunwu");

person.setAge(40);

person.setSex(1);

person.setInfoList(list);

}




publicvoid clickButton(View view) {

switch (view.getId()) {

case R.id.button_main_submit:

Intent intent = new Intent(MainActivity.this,

ReceiverActivity.class);

Bundle bundle = new Bundle();

bundle.putSerializable("person",person);

intent.putExtras(bundle);

startActivity(intent);

break;

}
}




@Override

publicboolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present.

getMenuInflater().inflate(R.menu.main, menu);

returntrue;

}

}

 

3)、MainActivity頁面:


publicclass ReceiverActivityextends Activity {

@Override

protectedvoid onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_receiver);

Intent intent = getIntent();

Bundle bundle = intent.getExtras();

// 接收Serializable對象

Person data = (Person) bundle.getSerializable("person");

setTitle(data.getUserName() + ":" + data.getAge() +":"

+ data.getInfoList());
}
@Override

publicboolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present.

getMenuInflater().inflate(R.menu.receiver, menu);

returntrue;

}
}

 

二、Activity——調用另一個Activity返回結果

 

()、概念:

甲頁面調用乙頁面,當用戶在乙頁面完成任務後,程序自動返回到甲頁面,而甲頁面必須能夠獲取到用戶在完成乙頁面後傳遞的數據結果。

 

()、做法:

與普通的頁面交換數據不同的是,要使用startActivityForResult()方法來啓動另一個Activity

 

()、示例代碼:

 

1、MainActivity頁面:


publicclass MainActivityextends Activity {

private ImageView imageView_main_headpic;

@Override

protectedvoid onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

imageView_main_headpic = (ImageView) findViewById(R.id.imageView_main_headpic);

}




@Override

publicboolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present.

getMenuInflater().inflate(R.menu.main, menu);
returntrue;
}




publicvoid clickButton(View view) {

switch (view.getId()) {

case R.id.button_main_selectpic:
Intent intent = new Intent(MainActivity.this, HeadpicActivity.class);

startActivityForResult(intent, 0);
break;
}
}




@Override

protectedvoid onActivityResult(int requestCode,int resultCode, Intent data) {

super.onActivityResult(requestCode, resultCode, data);

if (requestCode == 0 && resultCode == 1) {

Bundle bundle = data.getExtras();

int imgid = bundle.getInt("imgId");

imageView_main_headpic.setImageResource(imgid);

}

}

}

 

2、NextActivity頁面:


publicclass HeadpicActivityextends Activity {

private GridView gridView_headpic_show;

// 定義數據源

privateint[]imgIds =newint[] { R.drawable.img01, R.drawable.img02,

R.drawable.img03, R.drawable.img04, R.drawable.img05,

R.drawable.img06, R.drawable.img07, R.drawable.img08,

R.drawable.img09 };




@Override

protectedvoid onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_headpic);

gridView_headpic_show = (GridView) findViewById(R.id.gridView_headpic_show);
List<Map<String, Object>> list =new ArrayList<Map<String, Object>>();

for (int i = 0; i < imgIds.length; i++) {

Map<String, Object> map = new HashMap<String, Object>();

map.put("headpic",imgIds[i]);

map.put("picname","頭像—" + i);

list.add(map);
}




SimpleAdapter adapter = new SimpleAdapter(this, list,

R.layout.item_gridview_headpic,new String[] {"picname",

"headpic" },newint[] {

R.id.text_item_gridview_picname,

R.id.imageView_item_gridview_headpic });

gridView_headpic_show.setAdapter(adapter);




gridView_headpic_show.setOnItemClickListener(new OnItemClickListener() {




@Override

publicvoid onItemClick(AdapterView<?> parent, View view,

int position,long id) {

Intent intent = getIntent();

Bundle bundle = new Bundle();

bundle.putInt("imgId",imgIds[position]);

intent.putExtras(bundle);

setResult(1, intent);

finish();

}

});

}




@Override

publicboolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.headpic, menu);

returntrue;

}

}

 

 

三、Activity生命週期

()、課堂引入:人的生命週期是怎麼樣的?

· 胎兒

· 嬰兒

· 兒童、少年、青年、中年

· 突發變故

· 老年

· 死亡

()Activity生命週期:

1Activity一生中有七種不同的狀態。

· onCreate()

· onStart()

· onResume()

· onPause()

· onStop()

· onRestart()

· onDestroy()

【備註:】

A、七個生命週期組合:

· onCreateonStartonResume:啓動應用程序

· onPauseonStop:失去焦點

· onRestartonStartonResume:重新獲得焦點

· onPauseonStoponDestroy :退出應用程序

B、七個生命週期按階段劃分:

· onCreate() --- onDestroy()            完整生命週期    The entire lifetime

· onStart() --- onStop()                    可見生命週期     The visible lifetime

· onResume() --- onPause()            前沿生命週期(焦點生命週期)    The foreground lifetime

 

 

2、定義生命週期的作用:

· ①當用戶接一個電話或切換到另一個程序不會崩潰

· ②當用戶後臺運行程序時不會銷燬有價值的系統資源

· ③當用戶離開再返回你的應用時不會丟失用戶的進程

· ④當手機屏幕進行橫豎屏切換的時候不會崩潰或者丟掉用戶的進程

()實例代碼操作

模仿以下操作,觀察輸出日誌,找尋生命週期方法依次回調的規律:

1. 打開主界面,後退鍵退出

2. 打開主界面,進入第二個頁面

3. 打開主界面,模擬電話進入及掛機,再次顯示主界面

4. 打開主界面,HOME鍵退出

5. 打開主界面,HOME鍵退出,再次啓動app

6. 打開主界面,點擊該界面中捆綁退出事件的按鈕後退出

7. 打開主界面,切換橫屏豎屏。

· 生命週期執行的順序:

04-23 03:51:29.750: I/MainActivity(741): ==MainActivity onCreate執行了

04-23 03:51:29.759: I/MainActivity(741): ==MainActivity onStart執行了

04-23 03:51:29.759: I/MainActivity(741): ==MainActivity onResume執行了

04-23 03:52:04.780: I/MainActivity(741): ==MainActivity onPause執行了

04-23 03:52:04.791: I/MainActivity(741): ==MainActivity onStop執行了

04-23 03:52:04.791: I/MainActivity(741): ==MainActivity onDestroy執行了

04-23 03:52:05.200: I/MainActivity(741): ==MainActivity onCreate執行了

04-23 03:52:05.209: I/MainActivity(741): ==MainActivity onStart執行了

04-23 03:52:05.329: I/MainActivity(741): ==MainActivity onResume執行了

【橫豎屏切換時的生命週期問題的解決】

(一)、android:configChanges屬性

android:configChanges屬性,一般認爲有以下幾點:

1、不設置Activityandroid:configChanges時,切屏會重新調用各個生命週期,切橫屏時會執行一次,切豎屏時會執行兩次

2、設置Activityandroid:configChanges="orientation"時,切屏還是會重新調用各個生命週期,切橫、豎屏時只會執行一次

3、設置Activityandroid:configChanges="orientation|keyboardHidden"時,切屏不會重新調用各個生命週期,只會執行onConfigurationChanged方法

但是,自從Android 3.2API 13),在設置Activityandroid:configChanges="orientation|keyboardHidden"後,還是一樣 會重新調用各個生命週期的。因爲screen size也開始跟着設備的橫豎切換而改變。所以,在AndroidManifest.xml裏設置的MiniSdkVersionTargetSdkVersion屬性大於等於13的情況下,如果你想阻止程序在運行時重新加載Activity,除了設置"orientation", 你還必須設置"ScreenSize"

解決方法:

AndroidManifest.xml中設置android:configChanges="orientation|screenSize

 

 

四、任務回退棧

(一)、任務Task

1、概念:

一個任務(task)就是在執行某項工作時與用戶進行交互的Activity的集合。這些Activity按照被打開的順序依次被安排在一個堆棧中(回退棧)。

 

2、主屏頁面:

設備的主屏是大多數任務的啓動位置,當用戶觸摸一個應用程序啓動器圖標(或者app快捷圖標),應用程序的任務就會在前臺顯示。如果相關應用程序的任務不存在,那麼就會有一個新的任務被創建,並且應用程序打開的”Activity會作爲任務中的根Activity

 

(二)、回退棧:

1、概念:

當前的Activity啓動了另一個Activity時,這個新的Activity被放到了堆棧的頂部,並且帶有焦點前一個Activity並沒有消失,而是保存在回退棧中,此時它處於停止狀態

當用戶按下回退按鈕時,當前的Activity會被從回退棧的頂部彈出(這個Activity被銷燬),而前一個Activity被恢復。堆棧中的Activity不會被重新排列。因此,回退棧的操作跟後進先出的對象結構是一樣的。

在用戶按下回退按鈕時,當前Activity被銷燬,並且前一個Activity被恢復。如果用戶繼續按回退按鈕,那麼回退棧中的每個Activity會被依次彈出,前一個Activity會被顯示,直到用戶返回主屏(或者返回到任務開始時運行的那個Activity)。當所有的Activity從回退棧中被刪除時,這個任務就不再存在了。

 

 

1:用一個時間表顯示了當前回退堆棧中的Activity之間在每個時間點的處理過程

 

 

2、多個任務:

2. 兩個任務:任務B在前臺接受用戶交互,而任務A則在後臺等待被恢復。

 

【注意:】後臺中可以同時擁有多個任務,但是如果用戶同時運行了很多後臺任務,系統爲了回收內存可能銷燬一些後臺的Activity,從而導致Activity的狀態丟失。

 

        因爲回退堆棧中的Activity不曾被重新排列,因此如果允許用戶從多個Activity中啓動一個特殊的Activity,那麼就會創建一個新的Activity實例,並且在堆棧的頂部彈出(而不是把之前的Activity實例帶到堆棧的頂端)。這樣在你的應用程序中一個Activity就可能被實例化多次(甚至來自不同任務)。

 

 

 

(三)、ActivityTask的默認行爲的總結:

 

1、當Activity A啓動Activity B時,ActivityA被終止,但是系統保留了它的狀態(如滾動條的位置和錄入表單的文本)。如果用戶在Activity B中按回退按鈕,Activity A會使用被保存的狀態來進行恢復。

 

2、當用戶通過按主頁(Home)按鈕離開一個任務時,當前的Activity會被終止,並且被放入後臺。系統會保留任務中每個Activity的狀態。如果用戶隨後通過選擇啓動圖標來恢復這個任務,那麼任務會來到前臺,並且恢復了堆棧頂部的Activity

 

3、如果用戶按下回退按鈕,當前的Activity會從堆棧中被彈出並且被銷燬。堆棧中的前一個Activity會被恢復。Activity被銷燬時,系統不會保留Activity的狀態。

 

4Activity能夠被實例化多次,甚至來自其他任務。

 

 

五、Activity啓動模式

        Android中每個界面都是一個Activity,切換界面操作其實是多個不同Activity之間的實例化操作。在AndroidActivity的啓動模式決定了Activity的啓動運行方式。AndroidActivity的啓動模式分爲四種:

(一)、Activity啓動模式設置:

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

(二)、Activity的四種啓動模式:

    1. standard(備註:standard是系統默認的啓動模式。)

        標準啓動模式,每次激活Activity時都會創建Activity,並放入任務棧中。每個窗體的getTaskId()保持不變,但是this.hashCode()發生改變。

    2. singleTop

        如果在任務的棧頂正好存在該Activity的實例, 就重用該實例,而不會創建新的Activity對象,不過它會調用onNewIntent()方法。如果棧頂部不存在就會創建新的實例並放入棧頂(即使棧中已經存在該Activity實例,只要不在棧頂,都會創建實例)。會回調onNewIntent()方法。

    3. singleTask

        如果在棧中已經有該Activity的實例,就重用該實例(會調用實例的onNewIntent())。重用時,會讓該實例回到棧頂,因此在它上面的實例將會被移除棧。如果棧中不存在該實例,將會創建新的實例放入棧中。 

        singleTop在名字上即可看出區別,即singleTop每次只檢測當前棧頂的Activity是否是我們需要請求創建的,而singleTask則會檢測棧中全部的Activity對象,從上向下,如果檢測到是我們所請求的則會消滅此Activity對象上面的對象,直接把檢測到的我們需要的Activity置爲棧頂。

    4. singleInstance

        singleTask模式的區別是存放singleInstance模式窗口對象的回退棧不能有其他任何窗口對象。因此如果該窗口不存在,則要新建任務來存放該singleInstance模式窗口。也就是說getTaskId()會發現任務id發生了變化。

        此啓動模式和我們使用的瀏覽器工作原理類似,在多個程序中訪問瀏覽器時,如果當前瀏覽器沒有打開,則打開瀏覽器,否則會在當前打開的瀏覽器中訪問。此模式會節省大量的系統資源,因爲他能保證要請求的Activity對象在當前的棧中只存在一個。

總之,在開發Android項目時,巧妙設置Activity的啓動模式會節省系統開銷和提高程序運行效率

 

備註:

android:screenOrientation="portrait"

android:configChanges="keyboardHidden|orientation|screenSize"

 

1、不設置Activityandroid:configChanges時,切屏會重新調用各個生命週期,切橫屏時會執行一次,切豎屏時會執行兩次

2、設置Activityandroid:configChanges="orientation"時,切屏還是會重新調用各個生命週期,切橫、豎屏時只會執行一次

3、設置Activityandroid:configChanges="keyboardHidden"時,切屏還是會重新調用各個生命週期,切橫、豎屏時只會執行一次

4、設置Activityandroid:configChanges="orientation|screenSize|keyboardHidden"時,切屏不會重新調用各個生命週期,只會執行onConfigurationChanged方法

 

 

 原創團隊:

【總監】十二春秋之,[email protected]

Master】zelo,[email protected];【運營】狼行天下,[email protected]

【產品設計】流浪貓,[email protected];【體驗設計】兜兜,[email protected]

iOS】淘碼小工,[email protected];iMcG33K,[email protected]

Android】人猿居士,[email protected]思路的頓悟,[email protected]

java】首席工程師MR_W,[email protected];【測試】土鏡問道,[email protected]

【數據】喜樂多,[email protected];【安全】保密,你懂的。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發佈了12 篇原創文章 · 獲贊 1 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章