Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android四大組件之一——Activity知識匯總

Android四大組件之一——Activity知識匯總

編輯:關於Android編程

一. Activity的定義及作用

官方定義:Activity是Android應用程序提供交互界面的一個重要組件。也是Android最重要的組件之一

Activity是業務類 , 是承載應用程序的界面以及業務行為的基礎 。

包括UI , Service ...... 類似於我們的JavaBean

說Activity就不得不說View和Window

 

二. 創建一個 Activity

 

在 android 中創建一個 Activity 是很簡單的事情,編寫一個繼承自android.app.Activity的 Java 類並在AndroidManifest.xml聲明即可。下面是一個為了研究 Activity 生命周期的一個 Activity 實例(工程源碼見下載):

Activity 文件:

 public class EX01 extends Activity { 
    private static final String LOG_TAG = EX01.class.getSimpleName(); 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState);      
        setContentView(R.layout.main); 
        Log.e(LOG_TAG, "onCreate"); 
    } 
   @Override 
    protected void onStart() { 
        Log.e(LOG_TAG, "onStart"); 
        super.onStart(); 
    } 
    @Override 
    protected void onResume() { 
        Log.e(LOG_TAG, "onResume"); 
        super.onResume(); 
    } 
    @Override 
    protected void onPause() { 
        Log.e(LOG_TAG, "onPause"); 
        super.onPause(); 
    } 
    @Override 
    protected void onStop() { 
        Log.e(LOG_TAG, "onStop"); 
        super.onStop(); 
    } 
    @Override 
    protected void onDestroy() { 
        Log.e(LOG_TAG, "onDestroy "); 
        super.onDestroy(); 
    } 
 }

AndroidManifest.xml 中通過 節點說明 Activity,將 apk 文件安裝後,系統根據這裡的說明來查找讀取 Activity,本例中的說明如下:

  
	  
		  
		  
	  
 

啟動另外一個 Activity

Activity.startActivity()方法可以根據傳入的參數啟動另外一個 Activity:

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

當然,OtherActivity同樣需要在 AndroidManifest.xml 中定義。

 

 

結束一個Activity

finish()

finishActivity()

 

創建Activity步驟

 

1.創建Activity及相關視圖文件Layout(View)

2.配置AndroidManifest.xml

後面我們會詳細擴展屬性含義

3.重載onCreate(), 綁定Activity和Layout(View)

什麼是View?

研究setContentView()

Activity , Window , View的關系

如何用inflater來實現界面加載

4.為View(Layout)添加必要組件

如何動態編碼來控制界面

建立界面控件樹的概念

findViewById()

addView()

5.在onCreate()中實現初始業務邏輯

加入事件比如按鈕事件

擴展:Java匿名內部類

後面會詳細講事件機制


 

三. Activity 之間通信

使用 Intent 通信

那麼既然叫做意圖,就類似於一個男孩兒為了追一個女孩兒,傳遞紙條給她 ,向她要電話 ,女孩兒把電話寫在紙條中傳遞給男孩兒。(當然,現代社會都用微信了,最起碼都是短信了)

如此,我們認為Intent就是在不同組件之間傳遞值而設計的一個數據結構

intent :

extras - 加入附加信息

category - IntentFilter

Action - 動作 : Data - 動作相關的值

ComponentName –Context

 

在 Android 中,不同的 Activity 實例可能運行在一個進程中,也可能運行在不同的進程中。因此我們需要一種特別的機制幫助我們在 Activity 之間傳遞消息。Android 中通過 Intent 對象來表示一條消息,一個 Intent 對象不僅包含有這個消息的目的地,還可以包含消息的內容,這好比一封 Email,其中不僅應該包含收件地址,還可以包含具體的內容。對於一個 Intent 對象,消息“目的地”是必須的,而內容則是可選項。

在上面的實例中通過Activity. startActivity(intent)啟動另外一個 Activity 的時候,我們在 Intent 類的構造器中指定了“收件人地址”。

如果我們想要給“收件人”Activity 說點什麼的話,那麼可以通過下面這封“e-mail”來將我們消息傳遞出去:

 Intent intent =new Intent(CurrentActivity.this,OtherActivity.class);
  // 創建一個帶“收件人地址”的 email 
 Bundle bundle =new Bundle();// 創建 email 內容
 bundle.putBoolean("boolean_key", true);// 編寫內容
 bundle.putString("string_key", "string_value"); 
 intent.putExtra("key", bundle);// 封裝 email 
 startActivity(intent);// 啟動新的 Activity

那麼“收件人”該如何收信呢?在OtherActivity類的onCreate()或者其它任何地方使用下面的代碼就可以打開這封“e-mail”閱讀其中的信息:

 Intent intent =getIntent();// 收取 email 
 Bundle bundle =intent.getBundleExtra("key");// 打開 email 
 bundle.getBoolean("boolean_key");// 讀取內容
 bundle.getString("string_key");

上面我們通過bundle對象來傳遞信息,bundle維護了一個HashMap對象,將我們的數據存貯在這個 HashMap 中來進行傳遞。但是像上面這樣的代碼稍顯復雜,因為 Intent 內部為我們准備好了一個bundle,所以我們也可以使用這種更為簡便的方法:

 Intent intent =new Intent(EX06.this,OtherActivity.class); 
 intent.putExtra("boolean_key", true); 
 intent.putExtra("string_key", "string_value"); 
 startActivity(intent);

接收:

 Intent intent=getIntent(); 
 intent.getBooleanExtra("boolean_key",false); 
 intent.getStringExtra("string_key");

使用 SharedPreferences

SharedPreferences 使用 xml 格式為 Android 應用提供一種永久的數據存貯方式。對於一個 Android 應用,它存貯在文件系統的/data/ data/your_app_package_name/shared_prefs/目錄下,可以被處在同一個應用中的所有 Activity 訪問。Android 提供了相關的 API 來處理這些數據而不需要程序員直接操作這些文件或者考慮數據同步問題。

 // 寫入 SharedPreferences 
 SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE); 
 Editor editor = preferences.edit(); 
 editor.putBoolean("boolean_key", true); 
 editor.putString("string_key", "string_value"); 
 editor.commit(); 
        
 // 讀取 SharedPreferences 
 SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE); 
 preferences.getBoolean("boolean_key", false); 
 preferences.getString("string_key", "default_value");

其它方式

Android 提供了包括 SharedPreferences 在內的很多種數據存貯方式,比如 SQLite,文件等,程序員可以通過這些 API 實現 Activity 之間的數據交換。如果必要,我們還可以使用 IPC 方式。

Activity 的 Intent Filter

Intent Filter 描述了一個組件願意接收什麼樣的 Intent 對象,Android 將其抽象為 android.content.IntentFilter 類。在 Android 的 AndroidManifest.xml 配置文件中可以通過節點為一個 Activity 指定其 Intent Filter,以便告訴系統該 Activity 可以響應什麼類型的 Intent。

當程序員使用 startActivity(intent) 來啟動另外一個 Activity 時,如果直接指定 intent 了對象的 Component 屬性,那麼 Activity Manager 將試圖啟動其 Component 屬性指定的 Activity。否則 Android 將通過 Intent 的其它屬性從安裝在系統中的所有 Activity 中查找與之最匹配的一個啟動,如果沒有找到合適的 Activity,應用程序會得到一個系統拋出的異常。這個匹配的過程如下:

圖 4. Activity 種 Intent Filter 的匹配過程
圖 4. Activity 種 Intent Filter 的匹配過程

Action 匹配

Action 是一個用戶定義的字符串,用於描述一個 Android 應用程序組件,一個 Intent Filter 可以包含多個 Action。在 AndroidManifest.xml 的 Activity 定義時可以在其節點指定一個 Action 列表用於標示 Activity 所能接受的“動作”,例如:

  
  
  
……
 

如果我們在啟動一個 Activity 時使用這樣的 Intent 對象:

 Intent intent =new Intent(); 
 intent.setAction("com.zy.myaction");

那麼所有的 Action 列表中包含了“com.zy.myaction”的 Activity 都將會匹配成功。

Android 預定義了一系列的 Action 分別表示特定的系統動作。這些 Action 通過常量的方式定義在android.content. Intent中,以“ACTION_”開頭。我們可以在 Android 提供的文檔中找到它們的詳細說明。

URI 數據匹配

一個 Intent 可以通過 URI 攜帶外部數據給目標組件。在節點中,通過節點匹配外部數據。

mimeType 屬性指定攜帶外部數據的數據類型,scheme 指定協議,host、port、path 指定數據的位置、端口、和路徑。如下:

 

如果在 Intent Filter 中指定了這些屬性,那麼只有所有的屬性都匹配成功時 URI 數據匹配才會成功。

Category 類別匹配

節點中可以為組件定義一個 Category 類別列表,當 Intent 中包含這個列表的所有項目時 Category 類別匹配才會成功。


 

 

 

 

 

 

 

四. Activity生命周期

 

正常情況下的Activity生命周期

所謂正常情況下的生命周期,是指有用戶參與的情況下,Activity所經過的生命周期的改變。正常情況下,Activity會經歷如下過程。
如圖所示:
Activity生命周期的切換過程vce2yMC0xeS21LXEo6xvblJlc3VtZbrNb25QYXVzZcrHtNNBY3Rpdml0ecrHt/HOu9Pax7DMqNXiuPa9x7bIwLTF5LbUtcShozwvcD4NCjxwPiZuYnNwOzwvcD4NCjxwPjIuINLss6PH6b/2z8K1xEFjdGl2aXR5yfrD/Nbcxto8L3A+DQo8cD7L+c690uyzo8fpv/bPwrXEyfrD/NbcxtqjrMrH1rhBY3Rpdml0ebG7z7XNs7vYytW78tXftbHHsMnosbhDb25maWd1cmF0aW9uuMSx5LW81sK1xEFjdGl2aXR5sbvP+rvZ1ti9qKGjPC9wPg0KPHA+0rvW1rXk0M21xLSlt6LM9bz+yse64cr6xsHKsaOsyta7+rvhxMO1vcG91cWyu82stcTNvMaso6y0y8qxQWN0aXZpdHm+zbvhsbvP+rvZsqLH0tbYvaiho9Lss6PP+rvZyrGjrG9uUGF1c2Whom9uU3RvcKGib25EZXN0cm95vvm74bG7tffTw6Os1NpvblN0b3DWrsewo6zPtc2zu+G199PDb25TYXZlSW5zdGFuY2VTdGF0ZcC0saO05rWxx7BBY3Rpdml0ebXE17TMrKOoQWN0aXZpdHm74c6vzdBXaW5kb3ejrFdpbmRvd9TZzq/N0Lj4tqWy48jdxvdEZWNvclZpZXfIpbGjtObK/b7do6zX7rrztqWy48jdxvfU2dK70rvNqNaqxuTX01ZpZXfAtLGjtObK/b7do6mho7Wx1ti9qMqxo6zPtc2zu+HU2m9uU3RhcnTWrrrztffTw29uUmVzdG9yZUluc3RhbmNlU3RhdGWjrM/6u9nKsW9uU2F2ZUluc3RhbmNlU3RhdGXL+bGjtOa1xEJ1bmRsZbbUz/PX986qss7K/bSruPhvblJlc3RvcmVJbnN0YW5jZVN0YXRlus1vbkNyZWF0ZaOs08PT2sihs/bK/b7dsqK71ri0o6hHb29nbGW9qNLpztLDx7LJ08PHsNXfyKW71ri0yv2+3aOpoaM8L3A+DQo8cD61sci7ysfT0Le9t6jIpdfo1rnPtc2zyKXW2L2oQWN0aXZpdHm1xKOsztLDx7/J0tTOqkFjdGl2aXR51ri2qGNvbmZpZ0NoYW5nZXPK9NDUo7o8YnIgLz4NCqOoMaOpscjI587Sw8eyu8/r1NrGwcS70P3Xqsqx1ti9qEFjdGl2aXR5o6zEx8O0vs2/ydLU1ri2qDxhIGNsYXNzPQ=="replace_word" href="http://lib.csdn.net/base/android" target="_blank" title="Android知識庫">Android:configChanges=”orientation”。
(2)其中用的比較多的另兩個屬性為locale、keyboardHidden。前者為設備的本地位置發生了改變,一般指切換了系統語言。後者一般指用戶調出了鍵盤。
(3)screenSize屬性和smallestScreenSize屬性比較特殊,他們是API13時添加的。分別表示的情況為屏幕尺寸發生變化和切換到外部顯示設備時。若android:configChanges=”orientation|screenSize”,那麼在min以及target均低於13時,不會導致重啟,否則導致Activity重啟。在不重建時,系統沒有調用onSaveInstanceState以及onRestoreInstanceState方法,而是調用了onConfigurationChanged方法。
(4)Android4.2增加了一個layoutDirection屬性,當改變語言設置後,該屬性也會成newConfig中的一個mask位。所以ActivityManagerService(實際在ActivityStack)在決定是否重啟Activity的時候總是判斷為重啟。需要在android:configChanges 中同時添加locale和layoutDirection。在不退出應用的情況下切換到Settings裡切換語言,發現該Activity還是重啟了。

3. Android為什麼要設計一個生命周期呢

Google官方文檔解釋說,確保提供一個流暢的用戶體驗,在Activity切換時,以及在導致你的Activity停止甚至是銷毀的意外中斷的情況下,保存好Activity狀態。

1.在最前台的Activity處於Resumed狀態,可見,且獲得焦點。你也可能正在編輯信息,這個時候跳出來一個透明提示框,這個時候你當前的Activity就進入了Paused狀態,你想再次回到這個Activity時看到你編輯到一半的信息,這個時候,你就需要在onPause這個回調方法中,來執行這些操作。

2.當你按HOME鍵退出一個應用,或者從一個應用進入了另一個應用,這個時候之前那個Activity就變得完全不可見了,進入了Stopped狀態,那麼它就應該把它大多數的資源都釋放出來了,因為用戶看不見它,它也就不需要維持了。所以這個時候,你就需要在onStop回調方法裡面來執行這些操作。

3.當你接完一個電話,再次回到之前那個Activity,它就從Stopped狀態變成了Resumed狀態,這個時候你肯定希望它記錄住了你離開時的狀態,比如說編輯了一半的信息,正停留在新聞1/3的位置。那麼這個時候,你就需要在onRestart和Start回調方法裡面來執行這些操作,以使它恢復這些狀態。

所以綜上所述,之所以會設計出不同的生命周期狀態,以及各狀態間轉換時的回調方法,就是為了適應用戶使用過程中的不同場景,進而在特定的場景讓Activity完成特定的事情,以此來確保提供一個流暢的用戶體驗。

4. Activity的生命周期是由誰控制的

ActivityManagerService是負責管理Activity的生命周期的。ActivityManagerService是一個非常重要的接口,它不但負責啟動Activity和Service,還負責管理Activity和Service。

 

5. Activity 的狀態及狀態間的轉換

 

在 android 中,Activity 擁有四種基本狀態:

  1. Active/Runing一個新 Activity 啟動入棧後,它在屏幕最前端,處於棧的最頂端,此時它處於可見並可和用戶交互的激活狀態。
  2. Paused當 Activity 被另一個透明或者 Dialog 樣式的 Activity 覆蓋時的狀態。此時它依然與窗口管理器保持連接,系統繼續維護其內部狀態,所以它仍然可見,但它已經失去了焦點故不可與用戶交互。
  3. Stoped當 Activity 被另外一個 Activity 覆蓋、失去焦點並不可見時處於Stoped狀態。
  4. KilledActivity 被系統殺死回收或者沒有被啟動時處於Killed狀態。

當一個 Activity 實例被創建、銷毀或者啟動另外一個 Activity 時,它在這四種狀態之間進行轉換,這種轉換的發生依賴於用戶程序的動作。下圖說明了 Activity 在不同狀態間轉換的時機和條件:

圖 1. Activity 的狀態轉換
圖 1. Activity 的狀態轉換

如上所示,Android 程序員可以決定一個 Activity 的“生”,但不能決定它的“死”,也就時說程序員可以啟動一個 Activity,但是卻不能手動的“結束”一個 Activity。當你調用Activity.finish()方法時,結果和用戶按下 BACK 鍵一樣:告訴 Activity Manager 該 Activity 實例完成了相應的工作,可以被“回收”。隨後 Activity Manager 激活處於棧第二層的 Activity 並重新入棧,同時原 Activity 被壓入到棧的第二層,從 Active 狀態轉到 Paused 狀態。例如:從 Activity1 中啟動了 Activity2,則當前處於棧頂端的是 Activity2,第二層是 Activity1,當我們調用Activity2.finish()方法時,Activity Manager 重新激活 Activity1 並入棧,Activity2 從 Active 狀態轉換 Stoped 狀態,Activity1. onActivityResult(int requestCode, int resultCode, Intent data)方法被執行,Activity2 返回的數據通過data參數返回給 Activity1。

 

6.存儲Activity狀態

onSaveInstanceState()

onRestoreInstanceState(), onCreate()

如何選擇用哪個函數?

 

7.處理設置改變Configuration changes

設置改變的時候恢復一個大對象

自己處理配置改變

 

 

五.Activity的任務棧

Activity 棧

Android 是通過一種 Activity 棧的方式來管理 Activity 的,一個 Activity 的實例的狀態決定它在棧中的位置。處於前台的 Activity 總是在棧的頂端,當前台的 Activity 因為異常或其它原因被銷毀時,處於棧第二層的 Activity 將被激活,上浮到棧頂。當新的 Activity 啟動入棧時,原 Activity 會被壓入到棧的第二層。一個 Activity 在棧中的位置變化反映了它在不同狀態間的轉換。Activity 的狀態與它在棧中的位置關系如下圖所示:

圖 2. Activity 的狀態與它在棧中的位置關系
圖 2. Activity 的狀態與它在棧中的位置關系

如上所示,除了最頂層即處在 Active 狀態的 Activity 外,其它的 Activity 都有可能在系統內存不足時被回收,一個 Activity 的實例越是處在棧的底層,它被系統回收的可能性越大。系統負責管理棧中 Activity 的實例,它根據 Activity 所處的狀態來改變其在棧中的位置。


 

1. Activity的啟動模式

1.1 Standard標准模式

系統默認的啟動模式,即便實例存在,每次啟動都會創建一個新的實例,每個實例可以屬於不同的任務棧。

若ActivityA以此模式啟動了Activity B,那麼B會進入A所在的棧。注意,若是非Activity類型的Context,如ApplicationContext,並沒有任務棧,因此以ApplicationContext啟動Standard模式的Activity會報錯。我們可以在啟動時創建一個新的任務棧,指定FLAG_ACTIVITY_NEW_TASK標記位。(此時實際上是以SingleTask模式啟動)。標記為下文會講述。

 

1.2 SingleTop棧頂復用模式

這種模式下,若新的Activity已位於棧頂,就不會重復創建。不同於Standard模式,此時這個Activity因為沒有發生變化,它的onCreate、onStart不會被調用。

但是它的onNewIntent方法會被調用,通過此方法的參數可以得到請求信息。

 

1.3 SingleTask棧內復用模式

這種模式下,Activity想要的任務棧如果存在,並且此Activity在此棧中存在實例,多次啟動此Activity都不會重新創建實例。同時該模式具有clearTop的效果,已存在的實例上面的Activity全部出棧。onNewIntent方法會被調用。若不存在該實例就新建並壓入該棧。

如果想要的任務棧不存在,就新建一個任務棧,並創建Activity實例放入該棧。

 

1.4 SingleInstance單實例模式

具有SingleTask模式的所有特性,此模式啟動的Activity只能單獨位於一個任務棧(新建的)。後續請求除非這個特殊的任務棧被銷毀,否則不會創建新的Activity實例。

如SingleTask模式一樣,如果按照相同的模式再次某Activity,不重新創建,只是暫停onStop了下,並且調用onNewIntent方法。接著調用onResume就又繼續了。

 

2. TaskAffinity屬性

TaskAffinity屬性標識了一個Activity所需要的任務棧的名字。默認為應用包名。

當TaskAffinity屬性和SingleTask啟動模式結合使用時,待啟動的Activity會運行在名字和TaskAffinity相同的任務棧中。

當TaskAffinity屬性和allowTaskReparenting結合使用時,若應用A啟動應用B中的Activity C(C運行在A的任務棧中),並且此Activity的allowTaskReparenting = true,當應用B被啟動後,B的主Activity不會顯示,因為Activity C會直接從應用A的任務棧轉移到應用B的任務棧中(因為C想要的任務棧被創建了),所以會顯示Activity C。

 

3.指定啟動模式

3.1通過AndroidMenifest.xml為Activity指定

android:launchMode="singleTask"

3.2通過Intent標識為Activity指定

  1. Intentintent=newIntent();
  2. intent.setClass(MainActivity.class,SecondActivity.class);
  3. intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  4. startActivity(intent);

兩種設置方式,第一種優先級較低,並且無法直接給Activity設置FLAG_ACTIVITY_CLEAR_TOP標識。

第二種無法為Activity指定singleInstance模式。

 

 

4. ActivityFlags

 

大部分情況我們不需要為Activity設置標記位,下面介紹幾種比較常用的標記位。

 

4. 1 FLAG_ACTIVITY_NEW_TASK

這種Activity標記位,作用是為Activity指定SingleTask的啟動模式。和在清單文件裡設置效果相同。

 

4. 2 FLAG_ACTIVITY_SINGLE_TOP

這種Activity標記位,作用是為Activity指定SingleTop的啟動模式。和在清單文件裡設置效果相同。

 

4. 3 FLAG_ACTIVITY_CLEAR_TOP

SingleTask模式默認具有此標記效果,即被啟動Activity的實例已經存在,onNewIntent方法會被調用,已存在的實例上面的Activity全部出棧。

如果是Standard啟動模式,那麼它連同它之上的Activity都要出棧,並創建新的Activity實例入棧。

 

4. 4 FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

效果和在清單文件裡指定Android:excludeFromRecents = “true”相同。即不會出現在歷史Activity列表中。

 

 

六. Activity Manifest配置詳解

android:allowTaskReparenting

android:alwaysRetainTaskState

android:clearTaskOnLaunch

android:configChanges

android:finishOnTaskLaunch

android:hardwareAccelerated

android:launchMode

Standard

SingleTop

SingleTask

SingleInstance

android:multiprocess

android:noHistory

android:process

android:stateNotNeeded

android:screenOrientation

android:excludeFromRecents

 


 

七.Android提供的專有Activity

MapActivity

ListActivity

ExpandableListActivity

TabActivity

 

 

八.一些關於 Activity 的技巧

鎖定 Activity 運行時的屏幕方向

 

Android 內置了方向感應器的支持。在 G1 中,Android 會根據 G1 所處的方向自動在豎屏和橫屏間切換。但是有時我們的應用程序僅能在橫屏 / 豎屏時運行,比如某些游戲,此時我們需要鎖定該 Activity 運行時的屏幕方向,節點的android:screenOrientation屬性可以完成該項任務,示例代碼如下:

 // 豎屏 , 值為 landscape 時為橫屏
…………
 

全屏的 Activity

要使一個 Activity 全屏運行,可以在其onCreate()方法中添加如下代碼實現:

 // 設置全屏模式
 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
    WindowManager.LayoutParams.FLAG_FULLSCREEN); 
 // 去除標題欄
 requestWindowFeature(Window.FEATURE_NO_TITLE);

在 Activity 的 Title 中加入進度條

為了更友好的用戶體驗,在處理一些需要花費較長時間的任務時可以使用一個進度條來提示用戶“不要著急,我們正在努力的完成你交給的任務”。如下圖:

在 Activity 的標題欄中顯示進度條不失為一個好辦法,下面是實現代碼:

 // 不明確進度條
 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 
 setContentView(R.layout.main); 
 setProgressBarIndeterminateVisibility(true); 

 // 明確進度條
 requestWindowFeature(Window.FEATURE_PROGRESS); 
 setContentView(R.layout.main); 
 setProgress(5000);
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved