1、什么是Activity?
Activity是安卓四大組件之一,主要用來與用戶進行交互。除了一些用來后臺啟動一些服務的程序,基本上每個程序都會包含Activity的,想一些簡單的程序,比如在學習安卓的時候,可能就只有簡單的一個Activity,但是,在實際的開發(fā)中,一個程序至少也會由幾十個Activity組成。
2、加載布局
在Activity中加載布局的方法也很簡單,只要在onCreate()方法中調(diào)用setContentView()方法,然后將要調(diào)用的布局文件傳進setContentView()方法即可,當然,也可以用java代碼進行布局的構(gòu)建,但是并不推薦這樣子,因為這樣代碼量過大,而且也不方便,在這里更加推薦使用xml文件進行布局的構(gòu)建。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//加載布局文件
}
3、在AndroidManifest.xml中注冊創(chuàng)建的Activity
每創(chuàng)建一個Activity,都需要在AndroidManifest.xml中注冊,沒有經(jīng)過注冊的Activity,不僅沒法運行,更會導致程序奔潰閃退。雖然我們在使用Android Studio創(chuàng)建Acitivity的時候,會自動為我們注冊,但是我們也是要知道這件事的。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.helloworld">
<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/Theme.HelloWorld">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Activity的注冊,需要放在<application>標簽之內(nèi),然后通過<activity>標簽來對Activity進行注冊。
細心的人可以發(fā)現(xiàn),在注冊信息中,存在著以下語句:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
這兩句語句通過<intent-filter>這個標簽來申明,具體信息代表著這個Activity注冊成為程序的主活動,也就是說,程序啟動的時候,最先啟動此Activity。
此外,在注冊信息中,另外的幾個屬性代表的含義如下:
android:allowBackup這個屬性代表改程序是否允許自動進行備份;
android:icon 這個屬性代表該程序要以哪個圖片作為程序的圖標;
android:label 這個代表程序的要顯示的名字;
android:roundIcon 表示程序指定了哪張圖片作為應用程序的圓形桌面圖標;android:supportsRtl 表示該程序是否支持從右往左顯示。
android:theme表示該程序要調(diào)用那種主題風格
4、Activity的跳轉(zhuǎn)
在學習Activity的跳轉(zhuǎn)之前,我們得先認識Intent,那么Intent是什么呢?
Intent:作為安卓程序中各組件進行交互的重要方式,可以用于啟動Activity,廣播,以及服務。不僅可以指明當前組件想要執(zhí)行的動作,而且可以攜帶數(shù)據(jù)進行交互。Intent一般可以分為顯示Intent和隱式Intent。
(1)顯示Intent
mTurnSecondButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
}
});
在上面的例子中,當我們點擊mTurnSecondButton這個按鈕的時候,程序會跳轉(zhuǎn)到SecondActivity這個活動中,當中我們使用了Intent(Context packageContext, Class<?> cls)這個構(gòu)造函數(shù)來創(chuàng)建Intent實例,在這個構(gòu)造方法中,需要傳入兩個參數(shù),一個是上下文對象,另一個則是指定想要跳轉(zhuǎn)的Activity,最后,調(diào)用startActivity()這個方法,傳入Intent參數(shù)便可以通過顯示Intent的方式啟動另一個Activity了。
(2)隱式Inent
隱式Intent是通過添加action,category等信息,然后交由系統(tǒng)自行分析啟動哪個Activity的方法。如下所示,在注冊Activity的時候添加所需要的action以及category。
<activity
android:name=".SecondActivity"
android:exported="false" >
<intent-filter>
<action android:name="android.intent.test.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.MY_INTENT"/>
</intent-filter>
</activity>
然后在啟動的邏輯代碼中進行如下操作:
mTurnSecondButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("android.intent.test.ACTION_START");
intent.addCategory("android.intent.category.MY_INTENT");
startActivity(intent);
}
});
當action信息和category信息都對應上的時候,系統(tǒng)便會去啟動對應的Activity。有一種特殊的情況,當我們在注冊Activity的時候,可以直接這么寫:
<activity
android:name=".SecondActivity"
android:exported="false" >
<intent-filter>
<action android:name="android.intent.test.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
android.intent.category.DEFAULT 這個是一個默認的category,在我們調(diào)用startActivity()這個方法的時候,會自動加載進去,因此,如果按照上面這樣寫得話,我們在使用隱式Intent的時候只需要這么寫:
Intent intent = new Intent("android.intent.test.ACTION_START");
startActivity(intent);
另外,在使用隱式Intent去啟動Activity的時候,有一個踩坑點,就是在上面使用隱式Intent的第一種方法,也就是使用我們自己定義的category的時候,一定要將android.intent.category.DEFAULT這個在注冊的時候也加進去,否則會出現(xiàn)找不到要啟動的Acitivity,從而導致程序運行奔潰。
5、攜帶數(shù)據(jù)到下一個Activity
在前面介紹過,Intent不僅可以指明控件的意圖,還可以攜帶數(shù)據(jù),在這里,我們變使用Intent的另一個功能:攜帶數(shù)據(jù)進行交互。
其實通過Intent攜帶數(shù)據(jù)進行交互也很簡單,只需要調(diào)用Intent當中的putExtra()方法就可以了,這個方法通過鍵值對的方式進行數(shù)據(jù)的攜帶保存。例如:
String data = "I Love Android!";
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
intent.putExtra("intent_string",data);
startActivity(intent);
putExtra()方法需要傳入兩個參數(shù),一個是“鍵”,也就是上面代碼中的intent_string,另一個是“值”,也就是上面中的data這個字符串。最后通過 startActivity(intent)將數(shù)據(jù)傳送到下一個Activity當中。
既然有傳遞數(shù)據(jù),那么必然就有獲取數(shù)據(jù),否則,數(shù)據(jù)的傳遞就沒有意義了。通過Intent發(fā)送的數(shù)據(jù),獲取方法如下:
(1)通過getIntent()的方法獲取啟動當前Activity(SecondActivity)的Intent。
(2)通過getStringExtra()的方法獲取傳遞的數(shù)據(jù),例子中傳遞的為String類型數(shù)據(jù),因此這里就使用getStringExtra()的方法,根據(jù)傳遞數(shù)據(jù)類型的不同,也可以使用getBooleanExtra()或者getIntExtra()等等方法。
Intent intent = getIntent();
String data = intent.getStringExtra("intent_string");
tv_getIntent.setText(data);
例子中,我們通過一個TextView來顯示獲取的數(shù)據(jù),效果如下:

6、返回數(shù)據(jù)到上一個Activity
(1)使用startActivityForResult()來啟動下一個Activity
mTurnSecondButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivityForResult(intent,1);
}
});
在這里我們使用startActivityForResult()來啟動下一個Activity,傳入了唯一的Request Code,值為1。這個數(shù)值后面在獲取返回的數(shù)值的時候,還要作為判斷的依據(jù)。
(2)使用setResult()方法返回數(shù)據(jù)
btn_forRusult.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent1 = new Intent();
intent1.putExtra("data_return","Turn to the FirstActivity!!");
setResult(RESULT_OK,intent1);
finish();
}
});
在上面的例子中,先構(gòu)建了一個Intent來傳遞數(shù)據(jù),然后調(diào)用setResult()的方法,改方法接收兩個參數(shù),第一個參數(shù)用于向上一個Activity返回處理的結(jié)果,一般傳遞RESULT_OK或者RESULT_CANCELED這兩個參數(shù);第二個參數(shù)則是帶著數(shù)據(jù)的Intent。
(3)在返回上一個Activity后調(diào)用onActivityResult()方法。
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
switch (requestCode){
case 1:
if (resultCode == RESULT_OK){
String dataReturn = data.getStringExtra("data_return");
Log.d("Activity",dataReturn);
}
}
super.onActivityResult(requestCode, resultCode, data);
}
onActivityResult()方法需要傳入3個參數(shù),第一個是requestCode,是我們啟動下一個Activity是傳入的請求碼,也就是上面例子中的1;第二個是resultCode,是我們在返回數(shù)據(jù)時傳入的處理結(jié)果,也就是上面例子中的RESULT_OK;最后一個是攜帶著是數(shù)據(jù)的Intent。
上面的例子先去判斷requestCode,因為一個Activity中,可能會存在不止一處用startActivityForResult()來啟動下一個Activity,因此,通過唯一的requestCode來判斷是哪個Activity返回的數(shù)據(jù),再通過resultCode來判斷處理結(jié)果,最后取出返回的數(shù)據(jù)并打印出來。
7、Activity的啟動模式
Activity的啟動模式有4種,分別為standard、singleTop、singleTask和singleInstance。Activity的啟動模式可以在AndroidManifest.xml中給<activity>標簽指定android:launchMode屬性來選擇需要的啟動模式。
(1)standard模式
standard模式是默認的啟動模式,如果沒有對Activity進行特殊的聲明的話,都是屬于standard模式。在standard模式下,系統(tǒng)每次都會創(chuàng)建該活動的一個新的實例。
首先我們在AndroidManifest.xml中將對象Activity設置成standard的啟動模式。
<activity
android:name=".setupFirstActivity"
android:launchMode="standard"
android:exported="false" />
接著將按鈕的事件設置為啟動本Activity。然后點擊3次按鈕,打開信息過濾。

從上圖中可以看出,F(xiàn)irstSetupActivity創(chuàng)建了3次。然后我們一直按返回鍵,知道退出桌面。

我們會發(fā)現(xiàn),我們需要按4次返回鍵,才能退出到桌面,這是因為standard模式,會不管返回棧里面是什么情況,都會新創(chuàng)建一個Activity實例,我們啟動了3次setupFitstActivity,再加上本身存在一個,我們就需要返回4次,才能將返回棧中的Activity全部退出,然后到達桌面。
(2)singleTop模式
從上面的例子中,或許我們可以看到standard模式確實存在一些不太合理的地方,我們不妨來看一下第二種啟動模式--singleTop模式,在這種模式下,如果要啟動的Activity已經(jīng)是返回棧的棧頂了,那么這時候就不會再去創(chuàng)建一個Activity實例了,相當于是直接復用了棧頂?shù)腁ctivity。
復用上面例子中的程序,將啟動模式改成singleTop模式,同樣是點擊3次按鈕。我們會發(fā)現(xiàn)從程序運行到點擊完3次按鈕,Activity就只創(chuàng)建了一次。

(3)singleTask模式
在singleTop模式下,雖然在棧頂?shù)腁ctivity不會被重新創(chuàng)建,但是如果要啟動的Activity是存在于返回棧的,只是不是再棧頂,那么它還是會被重新創(chuàng)建的。為了讓整個上下文當中一個Activity不被重復創(chuàng)建,只有一個實例,我們可以使用第三種啟動模式--singleTask模式。在這個模式之下,系統(tǒng)每次啟動該Activity的時候,都會檢查返回棧中是否存在該Activity的實例,如果存在,則不再創(chuàng)建,把這個Activity實例上面的所有Activity全部彈出棧,讓這個Activity成為棧頂。
我們將setupFirstActivity的啟動模式改成singleTask模式,然后將Button的事件改成跳轉(zhuǎn)到setupSecondActivity,然后將setupSecondActivity的按鈕點擊事件設置成跳轉(zhuǎn)到setupFirstActivity,完成代碼編寫之后,完成如下操作:在setupFirstActivity啟動setupSecondActivity,然后再從setupScondActivity啟動setupFirstActivity,我們可以從過濾信息中看到如下信息:

可以看到這時候我們啟動setupFirstActivity的時候,并沒有執(zhí)行onCreate()的方法,而是直接執(zhí)行了onRestart(),并且可以看到,setupSecondActivity直接執(zhí)行了onDestroy()的方法進行了銷毀。
(4)singleInstance模式
不同于以上3中啟動模式,singleInstance模式是直接開辟一個新的返回棧出來的。
創(chuàng)建一個新的setupThirdActivity,然后將setupSecondActivity的啟動模式改成singleInstance模式,然后在每個Activity的onCreate()方法里面打印當前Activity的返回棧id,通過過濾信息,可以看到如下信息:

從圖片中可以看到,setupFirstActivity和setupThirdActivity的返回棧的id是一樣的,都是26,但是setupSecondActivity的返回棧的id是27,這是由于我們將setupSecondActivity的啟動模式改成singleInstance了,在啟動這個Activity的時候,重新創(chuàng)建了一個返回棧,并且,這個返回棧只有這一個Activity。
接著我們按返回鍵,我們會發(fā)現(xiàn),從setupThirdActivity會調(diào)到setupFirstActivity,然后再按一次返回鍵,才會到setupSecondActivity。