1.1 Activity的基本用法
創(chuàng)建布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button 1" />
</LinearLayout>
創(chuàng)建Activity并加載布局
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
}
}
在AndroidManifest.xml中注冊
Activity的注冊聲明要放在<application>標(biāo)簽內(nèi)僵缺,這里是通過<activity>標(biāo)簽來對Activity進(jìn)行注冊的胡桃。
在<activity>標(biāo)簽中,我們使用了android::name來指定具體注冊哪一個Activity磕潮,這里填入的.FirstActivity是com.example.myapplication.FirstActivity的縮寫翠胰。因為在最外層的<manifest>標(biāo)簽中已經(jīng)通過package屬性制定了程序的包名是com.example.myapplication容贝。
我們程序需要配置主Activity,即程序運行起來的時候首先啟動的Activity之景。配置主Activity的方法是在<activity>標(biāo)簽的內(nèi)部加入<intent-filter>標(biāo)簽斤富,并在這個標(biāo)簽里添加<action android:name="android.intent.action.MAIN" />、<category android:name="android.intent.category.LAUNCHER" />這兩句聲明即可锻狗。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplication">
<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.MyApplication">
<activity
android:name=".FirstActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
如果你的程序沒有聲明任何一個Activity作為主Activity满力,這個程序仍然是可以正常安裝的,只是你無法在啟動器中看到或者打開這個程序轻纪。這種程序一般是作為第三方服務(wù)供其他應(yīng)用在內(nèi)部進(jìn)行調(diào)用的油额。
在Activity中使用Toast
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
Button button1 = findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(FirstActivity.this, "You clicked Button 1", Toast.LENGTH_SHORT).show();
}
});
}
}
銷毀Activity
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
1.2 使用Intent在Activity之間穿梭
使用顯式Intent
顯式Intent明確指出了想要啟動哪一個Activity。
創(chuàng)建SecondActivity桐磁,并修改FirstActivity中按鈕的點擊事件悔耘,點擊button1即在FirstActivity的基礎(chǔ)上打開SecondActivity:
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);
}
});
使用隱式Intent
隱式Intent制定了一系列更為抽象的action和category等信息,系統(tǒng)幫我們找出可以響應(yīng)這個隱式Intent的Activity去啟動我擂。
通過在<activity>標(biāo)簽下配置<intent-filter>的內(nèi)容衬以,可以指定當(dāng)前Activity能夠響應(yīng)的action和category。只有action和category中的內(nèi)容同時匹配Intent中指定的action和category時校摩,這個Activity才能響應(yīng)該Intent看峻。
修改AndroidManifest.xml中SecondActivity的<activity>標(biāo)簽:
<activity
android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.myapplication.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
修改FirstActivity中按鈕的點擊事件:
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.example.myapplication.ACTION_START");
startActivity(intent);
}
});
"android.intent.category.DEFAULT"是一種默認(rèn)的category,在調(diào)用startActivity()方法的時候會自動將這個category添加到Intent中衙吩。所以點擊button1在FirstActivity的基礎(chǔ)上成功打開了SecondActivity互妓。
每個Intent中只能指定一個action,但能通過addCategory()方法指定多個category坤塞。
修改AndroidManifest.xml中SecondActivity的<activity>標(biāo)簽:
<activity
android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.myapplication.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.myapplication.MY_CATEGORY" />
</intent-filter>
</activity>
修改FirstActivity中按鈕的點擊事件:
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.example.myapplication.ACTION_START");
intent.addCategory("com.example.myapplication.MY_CATEGORY");
startActivity(intent);
}
});
再次運行冯勉,點擊button1在FirstActivity的基礎(chǔ)上同樣成功打開了SecondActivity。
使用隱式Intent啟動其他程序的Activity
使用隱式Intent不僅可以啟動自己程序內(nèi)的Activity摹芙,還可以啟動其他程序的Activity灼狰。
修改FirstActivity中按鈕的點擊事件,點擊button1即調(diào)用系統(tǒng)的瀏覽器來打開這個網(wǎng)頁:
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://www.baidu.com"));
startActivity(intent);
}
});
這里我們首先指定了Intent的action是Intent.ACTION_VIEW浮禾,其常量值為"android.intent.action.VIEW"交胚。然后通過Uri.parse()方法將一個網(wǎng)址字符串解析成一個Uri對象,再調(diào)用Intent的setData()方法將這個Uri對象傳遞進(jìn)去盈电。
我們可以在<intent-filter>標(biāo)簽中再配置一個<data>標(biāo)簽蝴簇,用于更精確地指定當(dāng)前Activity能夠響應(yīng)的數(shù)據(jù)。只有<data>標(biāo)簽中指定的內(nèi)容和Intent中攜帶的Data完全一致時匆帚,當(dāng)前Activity才能夠響應(yīng)該Intent熬词。
創(chuàng)建ThirdActivity,修改AndroidManifest.xml中ThirdActivity的<activity>標(biāo)簽:
<activity
android:name=".ThirdActivity">
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https" />
</intent-filter>
</activity>
在<data>標(biāo)簽中吸重,通過android:scheme指定了數(shù)據(jù)的協(xié)議必須是https協(xié)議荡澎。這樣子ThirdActivity就和瀏覽器一樣均践,能夠響應(yīng)一個打開網(wǎng)頁的Intent了。
由于Android Studio認(rèn)為所有能夠響應(yīng)ACTION_VIEW的Activity都應(yīng)該加上BROWSABLE的category摩幔,否則會警告彤委,這里直接在<intent-filter>標(biāo)簽上使用tools:ignore忽略警告。
可以看到或衡,系統(tǒng)自動彈出了一個列表焦影,顯示了目前能夠響應(yīng)這個Intent的所有程序。
我們還可以指定很多其他協(xié)議封断,比如geo表示顯示地理位置斯辰、tel表示撥打電話。修改FirstActivity中按鈕的點擊事件坡疼,點擊button1即調(diào)用系統(tǒng)撥號界面:
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10000"));
startActivity(intent);
}
});
向下一個Activity傳遞數(shù)據(jù)
Intent中提供了一系列的putExtra()方法的重載彬呻,可以把我們想要傳遞的數(shù)據(jù)暫存在Intent中,在啟動另一個Activity后柄瑰,只需要把這些數(shù)據(jù)從Intent中取出就可以了闸氮。
修改FirstActivity中按鈕的點擊事件,把字符串傳遞到SecondActivity中:
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String data = "Hello SecondActivity";
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("extra_data", data);
startActivity(intent);
}
});
修改SecondActivity教沾,將傳遞的數(shù)據(jù)取出:
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
String data = getIntent().getStringExtra("extra_data");
Log.d("SecondActivity", "extra data is " + data);
}
}
返回數(shù)據(jù)給上一個Activity
Activity類中還有一個用于啟動Activity的startActivityForResult()方法蒲跨,它期望在Activity銷毀的時候能夠返回一個結(jié)果給上一個Activity。
修改FirstActivity中按鈕的點擊事件:
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivityForResult(intent, 1);
}
});
修改SecondActivity授翻,添加按鈕button2或悲,給按鈕注冊點擊事件,并在點擊事件中添加返回數(shù)據(jù)的邏輯:
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Button button2 = findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.putExtra("data_return", "Hello FirstActivity");
setResult(RESULT_OK, intent);
finish();
}
});
}
}
在SecondActivity被銷毀之后會回調(diào)上一個Activity的onActivityResult()方法堪唐,因此我們需要再FirstActivity中重寫這個方法得到返回的數(shù)據(jù):
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 1:
if (resultCode == RESULT_OK) {
String dataReturn = "null";
if (data != null) {
dataReturn = data.getStringExtra("data_return");
}
Log.d("FirstActivity", "return data is " + dataReturn);
}
}
}
onActivityResult()方法帶有3個參數(shù):第一個參數(shù)requestCode巡语,即在我們啟動Activity是傳入的請求碼;第二個參數(shù)resultCode淮菠,即我們在返回數(shù)據(jù)時傳入的處理結(jié)果男公;第三個參數(shù)data,即攜帶著返回數(shù)據(jù)的Intent兜材。
1.3 Activity的生命周期
返回棧
Android是使用任務(wù)(Task)來管理Activity的理澎,一個任務(wù)就是一組存放在棧里的Activity的集合逞力,這個棧也被稱作返回棧(Back Stack)曙寡。每當(dāng)啟動一個新的Activity,它會入棧寇荧,并處于棧頂?shù)奈恢镁偈6慨?dāng)按下Back鍵或調(diào)用finish()方法銷毀一個Activity時,處于棧頂?shù)腁ctivity就會出棧揩抡,這時前一個入棧的Activity就會重新處于棧頂?shù)奈恢没Ы摹O到y(tǒng)總是會顯示處于棧頂?shù)腁ctivity镀琉。
Activity的生命周期
- onCreate() 這個方法在Activity第一次被創(chuàng)建的時候調(diào)用。
- onStart() 這個方法在Activity由不可見變?yōu)榭梢姷臅r候調(diào)用蕊唐。
- onResume() 這個方法在Activity準(zhǔn)備好和用戶進(jìn)行交互的時候調(diào)用屋摔。
- onPause() 這個方法在系統(tǒng)準(zhǔn)備去啟動或者恢復(fù)另一個Activity的時候調(diào)用。
- onStop() 這個方法在Activity完全不可見的時候調(diào)用替梨。
- onDestroy() 這個方法在Activity被銷毀之前調(diào)用钓试。
- onRestart() 這個方法在Activity由停止?fàn)顟B(tài)變?yōu)檫\行狀態(tài)之前調(diào)用,也就是Activity被重新啟動了副瀑。
系統(tǒng)回收Activity
當(dāng)一個Activity進(jìn)入停止?fàn)顟B(tài)弓熏,是有可能被系統(tǒng)回收的。假設(shè)應(yīng)用中有一個Activity A糠睡,用戶在Activity A的基礎(chǔ)上啟動了Activity B挽鞠,Activity A就進(jìn)入停止?fàn)顟B(tài),這個時候由于系統(tǒng)內(nèi)存不足狈孔,將Activity A回收信认,然后用戶按下 Back 鍵返回Activity A,這時還是會顯示Activity A 除抛,只是不會執(zhí)行onRestart()方法狮杨,而是會執(zhí)行Activity A的onCreate()方法,因為Activity A 被重新創(chuàng)建了一次到忽。
但是般婆,Activity A是可能存在臨時數(shù)據(jù)和狀態(tài)的。比如文本輸入框中輸入的文字露戒。
Activity中提供了onSaveInstanceState()回調(diào)方法占哟,它會在Activity被回收之前被調(diào)用,因此我們可以通過這個方法來解決活動被回收時需要保存臨時數(shù)據(jù)的場景翩迈。
onSaveInstanceState()方法會攜帶一個Bundle類型參數(shù)持灰,Bundle提供了一系列的方法用于保存數(shù)據(jù),比如可以使用putString()保存字符串负饲。
在Activity中添加如下代碼堤魁,保存臨時數(shù)據(jù):
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
String data = "something you just typed";
outState.putString("key", data);
}
修改Activity的onCreate()方法,將之前保存的數(shù)據(jù)取出:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
String data = savedInstanceState.getString("key");
Log.d("MainActivity", data);
}
}
1.4 Activity的啟動模式
Activity的啟動模式一共有4種返十,分別是standard妥泉、singleTop、singleTask洞坑、singleInstance盲链,可以在AndroidManifest.xml中通過給<activity>標(biāo)簽指定android:launchMode屬性來選擇啟動模式。
standard
standard是Activity默認(rèn)的啟動模式,在不進(jìn)行顯式指定的情況下刽沾,所有Activity都會自動使用這種啟動模式本慕。
在standard模式下,每當(dāng)啟動一個新的Activity侧漓,它就會在返回棧中入棧锅尘,并處于棧頂?shù)奈恢谩?br>
對于使用standard模式的Activity,系統(tǒng)不會在乎這個Activity是否已經(jīng)在返回棧中存在布蔗,每次啟動都會創(chuàng)建一個該Activity的新實例鉴象。
修改FirstActivity中按鈕的點擊事件,每次點擊都會打開FirstActivity:
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(FirstActivity.this, FirstActivity.class);
startActivity(intent);
}
});
FirstActivity的啟動模式為默認(rèn)的standard何鸡,返回棧如圖:
singleTop
當(dāng)Activity的啟動模式指定為singleTop纺弊,在啟動Activity時如果發(fā)現(xiàn)返回棧的棧頂已經(jīng)是該Activity,則認(rèn)為可以直接使用它骡男,不會再創(chuàng)建新的Activity實例淆游。
修改AndroidManifest.xm中FirstActivity的啟動模式為singleTop,再次運行:每當(dāng)想要再啟動FirstActivity時都會直接使用棧頂?shù)腇irstActivity隔盛,因此這時的FirstActivity也只會有一個實例犹菱,也僅需按一次Back鍵就可以退出程序。
修改FirstActivity中按鈕的點擊事件吮炕,點擊會打開SecondActivity腊脱,返回棧如圖:
singleTask
當(dāng)Activity的啟動模式指定為singleTask,每次啟動該Activity時龙亲,系統(tǒng)首先會在返回棧中檢查是否存在該Activity的實例陕凹,如果發(fā)現(xiàn)已經(jīng)存在則直接使用該實例,并把在這個Activity之上的所有其他Activity統(tǒng)統(tǒng)出棧鳄炉,如果沒有發(fā)現(xiàn)就會創(chuàng)建一個新的Activity實例杜耙。
修改AndroidManifest.xm中FirstActivity的啟動模式為singleTask,再次運行拂盯,返回棧如圖:
singleInstance
當(dāng)Activity的啟動模式指定為singleInstance佑女,會啟用一個新的返回棧來管理這個Activity。
假設(shè)我們的程序中有一個Activity是允許其他程序調(diào)用的谈竿,如果想實現(xiàn)其他程序和我們的程序可以共享這個Activity的實例团驱,就可以使用singleInstance模式。
在這種模式下空凸,會有一個單獨的返回棧來管理這個Activity嚎花,不管是哪個應(yīng)用程序來訪問這個Activity,都共用的同一個返回棧劫恒,也就解決了共享Activity實例的問題贩幻。
舉個例子,F(xiàn)irstActivity(默認(rèn)standard模式)點擊按鈕打開SecondActivity(singleInstance模式)两嘴,SecondActivity點擊按鈕打開ThirdActivity(默認(rèn)standard模式)丛楚,返回棧如圖:
因為FirstActivity和ThirdActivity是存放在同一個棧里的,所以在ThirdActivity的界面按下Back鍵憔辫,ThirdActivity才會出棧趣些,這時FirstActivity就到了棧頂,因此也就出現(xiàn)了從ThirdActivity直接返回到FirstActivity的現(xiàn)象贰您。在FirstActivity界面再次按下Back鍵坏平,這時當(dāng)前的棧已經(jīng)空了,于是就顯示了另一個棧的棧頂活動锦亦,即SecondActivity舶替。最后再次按下Back鍵,這時所有的棧都已經(jīng)空了杠园,也就很自然退出了程序顾瞪。