Android四大組件之Activity
Android四大組件之Service
Android四大組件之BroadcastReceiver
Android四大組件之ContentProvider
Android最重要的是四大組件,分別為Activity、Service、ContentProvider、Broadcast。Activity負責UI元素的加載與頁面之間的跳轉,代表了一個頁面單元;Service負責與UI無關的工作,如在后臺執行耗時操作等;ContentProvider負責存儲、共享數據,使得數據可以再多個應用之間共享;Broadcast則是在各個組件、應用之間進行通信,簡化了Android開發中的通信問題。
本文學習Android的四大組件中的Activity。
Activity
Activity是一個應用組件,用戶可與其提供的屏幕進行交互,我們在應用程序中能看到的內容,絕大多數都是Activity組件提供的。同時,Activity還可以在不同的Activity之間跳轉,將不同的頁面串連在一起,共同完成特定的操作流程。每個應用都是由一個或者多個Activity組成,它是Android應用程序中不可缺少的部分。
一、創建Activity
我們剛創建一個應用程序的時候,會給我們創建一個默認的MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//加載布局文件
setContentView(R.layout.activity_main);
}
}
當我們的程序創建好,會默認有一個onCreate()方法,其中"R.layout.activity_main"是繪制UI控件的,如TextView、Button等控件。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"/>
</RelativeLayout>
這個activity在AndroidManifest.xml中會被設置為如下intent-filter:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
其中<action>元素指定這是應用的“主”入口點。
<category>元素指定此 Activity 應列入系統的應用啟動器內(以便用戶啟動該 Activity)。
二、Activity的生命周期
程序創建好可以直接運行了,接下來我們就需要了解其生命周期,在不同的階段會回調不同的生命周期函數,Activity的生命周期函數如下:
-
onCreate()
每個Activity中我們都會重寫這個方法。首次創建 Activity 時調用。 我們應該在此方法中執行所有正常的靜態設置 — 創建視圖、將數據綁定到列表等等。 -
onRestart()
這個方法在Activity由停止狀態變為運行狀態之前調用,也就是Activity被重新啟動了。 -
onStart()
這個方法在Activity不可見變為可見的時候調用。 -
onResume()
這個方法在Activity準備好和用戶進行交互的時候調用,此時的Activity一定位于返回棧的棧頂、并且處于運行狀態。 -
onPause()
這個方法在系統準備去啟動或者恢復另一個Activity的時候調用。我們通常會在這個方法中將一些消耗CPU的資源釋放掉,以及保存一些關鍵數據,但這個方法的執行速度一定要快,不然會影響到新的棧頂Activity的使用。 -
onStop()
這個方法在Activity完全不可見的時候調用。它和onPause()方法主要區別在于,如果啟動的新活動是一個對話框式的活動,那么onPause()方法會得到執行,而onStop()方法不會執行。 -
onDestroy()
這個方法在Activity被銷毀之前調用,之后Activity的狀態將變為銷毀狀態。
為了幫助讀者能夠更好的理解,Android 官方提供了Activity生命周期示意圖,如下:
讓我們用代碼來體驗下Activity的生命周期
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e("TAG","---MainActivity--onCreate-----");
findViewById(R.id.btn_test).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//啟動第二個activity
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onRestart() {
super.onRestart();
Log.e("TAG","---MainActivity--onRestart-----");
}
@Override
protected void onStart() {
super.onStart();
Log.e("TAG","---MainActivity--onStart-----");
}
@Override
protected void onResume() {
super.onResume();
Log.e("TAG","---MainActivity--onResume-----");
}
@Override
protected void onPause() {
super.onPause();
Log.e("TAG","---MainActivity--onPause-----");
}
@Override
protected void onStop() {
super.onStop();
Log.e("TAG","---MainActivity--onStop-----");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("TAG","---MainActivity--onDestroy-----");
}
}
運行結果如下:
程序剛啟動時執行
TAG: --MainActivity--onCreate--
TAG: --MainActivity--onStart--
TAG: --MainActivity--onResume--
當按下Home鍵時執行
TAG: --MainActivity--onPause--
TAG: --MainActivity--onStop--
再次啟動時程序時執行
TAG: --MainActivity--onRestart--
TAG: --MainActivity--onStart--
TAG: --MainActivity--onResume--
當啟動第二個activity時執行
TAG: --MainActivity--onPause--
TAG: --SecondActivity--onCreate--
TAG: --SecondActivity--onStart--
TAG: --SecondActivity--onResume--
TAG: --MainActivity--onStop--
在SecondActivity中按下返回鍵執行
TAG: --SecondActivity--onPause--
TAG: --MainActivity--onRestart--
TAG: --MainActivity--onStart--
TAG: --MainActivity--onResume--
TAG: --SecondActivity--onStop--
TAG: --SecondActivity--onDestroy--
當設備旋轉的時候執行
TAG: --MainActivity--onPause--
TAG: --MainActivity--onStop--
TAG: --MainActivity--onDestroy--
TAG: --MainActivity--onCreate--
TAG: --MainActivity--onStart--
TAG: --MainActivity--onResume--
由此可以看出Activity可以分為三種生存期:
- Activity 的整個生命周期發生在onCreate()與 onDestroy()方法之間,Activity 應在onCreate()中執行“全局”狀態設置(例如初始化布局),并在onDestroy()中釋放所有已創建的資源。
- Activity 的可見生命周期發生在onStart()與onStop()方法之間,在這段時間,用戶可以在屏幕上看到Activity 并與其交互。
- Activity 的前臺生命周期發生在onResume()與onPause()方法之間,在這段時間,Activity 位于屏幕上的所有其他 Activity 之前,并具有用戶輸入焦點(用戶可以對其進行操作)。
三、Activity的啟動模式
每個Activity都有一個相應的啟動模式,啟動模式一共有四種,分別是standard、singleTop、singleTask、singleInstance,可以在AndroidManifest.xml中通過給<activity>標簽指定android:launchMode屬性來選擇啟動模式。
為了打印方便,定義一個基礎Activity,在其onCreate方法和onNewIntent方法中打印出當前Activity的日志信息,主要包括所屬的task,當前類的hashcode值
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e("TAG", "*******onCreate()*********");
Log.e("TAG", "onCreate:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.e("TAG", "====onNewIntent()===");
Log.e("TAG", "===onNewIntent:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode()+"====");
}
}
1. standard
standard 是Activity的默認啟動模式,每啟動一個Activity就會在棧頂創建一個新的實例,當Activity已經位于棧頂時,而再次啟動Activity時還需要在創建一個新的實例,不能直接復用。
其配置如下
<activity android:name=".MainActivity " android:launchMode="standard"/>
具體代碼如下:
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_test).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.e("TAG","---MainActivity--onCreate-----");
Intent intent = new Intent(MainActivity.this, MainActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onRestart() {
super.onRestart();
Log.e("TAG","---MainActivity--onRestart-----");
}
@Override
protected void onStart() {
super.onStart();
Log.e("TAG","---MainActivity--onStart-----");
}
@Override
protected void onResume() {
super.onResume();
Log.e("TAG","---MainActivity--onResume-----");
}
@Override
protected void onPause() {
super.onPause();
Log.e("TAG","---MainActivity--onPause-----");
}
@Override
protected void onStop() {
super.onStop();
Log.e("TAG","---MainActivity--onStop-----");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("TAG","---MainActivity--onDestroy-----");
}
}
在這里我們直接使用MainActivity再次啟動一個MainActivity,多點幾次按鈕看看效果是什么樣的
TAG: *******onCreate()*********
TAG: onCreate:MainActivity TaskId: 2214 hasCode:79599417
TAG: ---MainActivity--onStart-----
TAG: ---MainActivity--onResume-----
TAG: ---MainActivity--onCreate-----
TAG: ---MainActivity--onPause-----
TAG: *******onCreate()*********
TAG: onCreate:MainActivity TaskId: 2214 hasCode:123556960
TAG: ---MainActivity--onStart-----
TAG: ---MainActivity--onResume-----
TAG: ---MainActivity--onStop-----
TAG: ---MainActivity--onCreate-----
TAG: ---MainActivity--onPause-----
TAG: *******onCreate()*********
TAG: onCreate:MainActivity TaskId: 2214 hasCode:164071754
TAG: ---MainActivity--onStart-----
TAG: ---MainActivity--onResume-----
TAG: ---MainActivity--onStop-----
TAG: ---MainActivity--onCreate-----
TAG: ---MainActivity--onPause-----
TAG: *******onCreate()*********
TAG: onCreate:MainActivity TaskId: 2214 hasCode:170963973
TAG: ---MainActivity--onStart-----
TAG: ---MainActivity--onResume-----
TAG: ---MainActivity--onStop-----
可以看到MainActivity 的日志,從剛啟動MainActivity,到后來我們又按了三次按鈕,總共四次MainActivity 的日志,并且所屬的任務棧的id都是2214 ,這也驗證了誰啟動了該模式的Activity,該Activity就屬于啟動它的Activity的任務棧中,我么可以發現每一個Activity的hashcode都是不一樣的,說明他們是不同的實例,即每次啟動一個Activity都會重寫創建一個新的實例。
2. singleTop
singleTop棧頂復用模式,如果新的activity已經位于棧頂,那么這個Activity不會被重寫創建,同時它的onNewIntent方法會被調用,通過此方法的參數我們可以去除當前請求的信息。如果棧頂不存在該Activity的實例,則情況與standard模式相同。需要注意的是這個Activity它的onCreate(),onStart()方法不會被調用,因為它并沒有發生改變。
配置如下:
<activity android:name=".MainActivity" android:launchMode="singleTop"/>
在這里我們繼續使用MainActivity的方法執行下程序
日志信息如下:
TAG: *******onCreate()********* 剛啟動程序
TAG: onCreate:MainActivity TaskId: 2214 hasCode:79599417
TAG: ---MainActivity--onStart-----
TAG: ---MainActivity--onResume-----
TAG: ---MainActivity--onCreate-----
TAG: ---MainActivity--onPause-----
====onNewIntent()=== 第二次創建MainActivity
TAG: ===onNewIntent:MainActivity TaskId: 2214 hasCode:79599417====
TAG: ---MainActivity--onResume-----
TAG: ---MainActivity--onCreate-----
TAG: ---MainActivity--onPause-----
====onNewIntent()=== 第三次創建MainActivity
TAG: ===onNewIntent:MainActivity TaskId: 2214 hasCode:79599417====
TAG: ---MainActivity--onResume-----
TAG: ---MainActivity--onCreate-----
TAG: ---MainActivity--onPause-----
====onNewIntent()=== 第四次創建MainActivity
TAG: ===onNewIntent:MainActivity TaskId: 2214 hasCode:79599417====
TAG: ---MainActivity--onResume-----
由此我們可以看出,除了第一次進入MainActivity這個Activity時,輸出的是onCreate方法中的日志,后續的都是調用了onNewIntent方法,并沒有調用onCreate方法,并且四個日志的hashcode都是一樣的,說明棧中只有一個實例。這是因為第一次進入的時候,棧中沒有該實例,則創建,后續的三次發現棧頂有這個實例,則直接復用,并且調用onNewIntent方法。那么假設棧中有該實例,但是該實例不在棧頂情況又如何呢?
其配置如下:
<activity android:name=".MainActivity" android:launchMode="singleTop">
<activity android:name=".SecondActivity" android:launchMode="singleTop" />
<activity android:name=".OtherActivity" android:launchMode="singleTop"/>
TAG: *******onCreate()*********
TAG: onCreate:MainActivity TaskId: 2215 hasCode:79599417
TAG: ---MainActivity--onStart-----
TAG: ---MainActivity--onResume-----
TAG: ---MainActivity--onCreate-----
TAG: ---MainActivity--onPause-----
TAG: *******onCreate()*********
TAG: onCreate:SecondActivity TaskId: 2215 hasCode:179525260
TAG: ---SecondActivity--onCreate-----
TAG: ---SecondActivity--onStart-----
TAG: ---SecondActivity--onResume-----
TAG: ---MainActivity--onStop-----
TAG: ---SecondActivity--onPause-----
TAG: *******onCreate()*********
TAG: onCreate:OtherActivity TaskId: 2215 hasCode:242469764
TAG: ---OtherActivity--onCreate-----
TAG: ---OtherActivity--onStart-----
TAG: ---OtherActivity--onResume-----
TAG: ---SecondActivity--onStop-----
TAG: ---OtherActivity--onPause-----
TAG: *******onCreate()*********
onCreate:MainActivity TaskId: 2215 hasCode:185437057
TAG: ---MainActivity--onStart-----
TAG: ---MainActivity--onResume-----
TAG: ---OtherActivity--onStop-----
我們看到從MainActivity進入到SecondActivity 時,新建了一個SecondActivity 對象,并且task id與MainActivity是一樣的,然后從SecondActivity 跳到OtherActivity時,新建了一個OtherActivity,此時task中存在三個Activity,從棧底到棧頂依次是MainActivity,SingleTopActivity,OtherActivity,此時如果再跳到MainActivity,即使棧中已經有MainActivity實例了,但是依然會創建一個新的MainActivity實例,這一點從上面的日志的hashCode可以看出,此時棧頂是MainActivity,如果再跳到MainActivity,就會復用棧頂的MainActivity,即會調用MainActivity的onNewIntent方法。這就是上述日志的全過程。
singleTop模式總結
1). 當前棧中已有該Activity的實例并且該實例位于棧頂時,不會新建實例,而是復用棧頂的實例,并且會將Intent對象傳入,回調onNewIntent方法。
2). 當前棧中已有該Activity的實例但是該實例不在棧頂時(或者該棧內不存在activity的實例),其行為和standard啟動模式一樣,依然會創建一個新的實例。
3. singleTask
singleTask-棧內復用模式,在這個模式下,如果棧中存在這個Activity的實例就會復用這個Activity,不管它是否位于棧頂,復用時,會將它上面的Activity全部出棧,并且會回調該實例的onNewIntent方法。其實這個過程還存在一個任務棧的匹配,因為這個模式啟動時,會在自己需要的任務棧中尋找實例,這個任務棧就是通過taskAffinity屬性指定。如果這個任務棧不存在,則會創建這個任務棧。
在這里我們繼續使用執行流程,只需修改下配置文件:
<activity android:name=".MainActivity" android:launchMode="singleTask">
<activity android:name=".SecondActivity" android:launchMode="singleTask" />
<activity android:name=".OtherActivity" android:launchMode="singleTask"/>
TAG: *******onCreate()*********
TAG: onCreate:MainActivity TaskId: 2218 hasCode:79599417
TAG: ---MainActivity--onStart-----
TAG: ---MainActivity--onResume-----
TAG: ---MainActivity--onCreate-----
TAG: ---MainActivity--onPause-----
TAG: *******onCreate()*********
TAG: onCreate:SecondActivity TaskId: 2218 hasCode:44397022
TAG: ---SecondActivity--onCreate-----
TAG: ---SecondActivity--onStart-----
TAG: ---SecondActivity--onResume-----
TAG: ---MainActivity--onStop-----
TAG: ---SecondActivity--onPause-----
TAG: *******onCreate()*********
onCreate:OtherActivity TaskId: 2218 hasCode:106765719
TAG: ---OtherActivity--onCreate-----
TAG: ---OtherActivity--onStart-----
TAG: ---OtherActivity--onResume-----
TAG: ---SecondActivity--onStop-----
TAG: ---SecondActivity--onDestroy-----
TAG: ---OtherActivity--onPause-----
TAG: ====onNewIntent()===
TAG: ===onNewIntent:MainActivity TaskId: 2218 hasCode:79599417====
TAG: ---MainActivity--onRestart-----
TAG: ---MainActivity--onStart-----
TAG: ---MainActivity--onResume-----
TAG: ---OtherActivity--onStop-----
TAG: ---OtherActivity--onDestroy-----
由此我們可以看出,都在同一個棧內,當我們執行一圈后,再次回到MainActivity 中時,會調用onNewIntent()方法,不會在執行OnCreate()方法,而其中的SecondActivity和OtherActivity已經銷毀了,這點是和singleTop不一樣的。
我們在開發中會碰到如下需求:
我們從ActivityA---> B --->C --->A,回到A時,需要銷毀掉BC;這時候我們就需要使用singleTask模式了,如果使用默認模式,回到A時,當我們點擊返回鍵的時候,還是會回到C中的。
4. singleInstance
singleInstance-全局唯一模式,這是一種加強的singleTask模式,它除了具有singleTask模式的所有特性外,還加強了一點,那就是具有此種模式的activity只能單獨的位于一個任務棧中。也就是說整個系統中就這么一個實例,由于棧內復用的特性,后續的請求均不會創建新的Activity實例,除非這個特殊的任務棧被銷毀了。
我們繼續使用上面的案例:
配置如下
<activity android:name=".MainActivity" android:launchMode="singleInstance">
<activity android:name=".SecondActivity" android:launchMode="singleInstance" />
<activity android:name=".OtherActivity" android:launchMode="singleInstance"/>
運行結果如下
TAG: *******onCreate()*********
TAG: onCreate:MainActivity TaskId: 2222 hasCode:79599417==
TAG: ---MainActivity--onStart-----
TAG: ---MainActivity--onResume-----
TAG: ---MainActivity--onCreate-----
TAG: ---MainActivity--onPause-----
TAG: *******onCreate()*********
TAG: onCreate:SecondActivity TaskId: 2223 hasCode:44397022
TAG: ---SecondActivity--onCreate-----
TAG: ---SecondActivity--onStart-----
TAG: ---SecondActivity--onResume-----
TAG: ---MainActivity--onStop-----
TAG: ---SecondActivity--onPause-----
TAG: *******onCreate()*********
TAG: onCreate:OtherActivity TaskId: 2224 hasCode:106765719
TAG: ---OtherActivity--onCreate-----
TAG: ---OtherActivity--onStart-----
TAG: ---OtherActivity--onResume-----
TAG: ---SecondActivity--onStop-----
TAG: ---OtherActivity--onPause-----
TAG: ====onNewIntent()===
TAG: ===onNewIntent:MainActivity TaskId: 2222 hasCode:79599417==
TAG: ---MainActivity--onRestart-----
TAG: ---MainActivity--onStart-----
TAG: ---MainActivity--onResume-----
TAG: ---OtherActivity--onStop---
由此我們可以看出,前三次taskId 是不一樣的,也就是說它們不在同一個棧內,而當我們再次回到MainActivity中時他會調用onNewIntent(),不會再執行onCreate()方法,因為已經存在了一個實例,不會再創建新的Task,直接復用該實例,并且回調onNewIntent方法。可以從他們的hashcode中可以看出這是同一個實例。
四、Activity的Flags
Activity的Flags有很多,在這里只看幾個常用的標記位。其中有的標記位可以設定Activity的啟動模式,
1. FLAG_ACTIVITY_NEW_TASK
這個標記位的作用是為Activity指定singleTask啟動模式,其效果和在清單文件中指定相同。
2. FLAG_ACTIVITY_SINGLE_TOP
這個標記位的作用是為Activity指定singleTop啟動模式,其效果和在清單文件中指定相同。
3. FLAG_ACTIVITY_CLEAR_TOP
具有此標記位的Activity,當它啟動時,在同一個任務棧中,所有位于它上面的Activity都要出棧。
使用方式:
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
關于Activity就學習到這里,如果理解的有問題,還望指正。
參考資料: