Android允许后台活动管理,安卓基础知识(活动)
一、四大組件
Android系統(tǒng)四大組件分別是活動(Activity)、服務(wù)(Service)、廣播接收器(Broadcast Receiver)和內(nèi)容提供器(Content Provider)。其中活動是所有Android應(yīng)用程序的門面,凡是在應(yīng)用中你看到的東西,都是放到活動中的。而服務(wù)就低調(diào)了,你無法看到它,但它會一直在后臺默默的運(yùn)行,即使用戶退出了應(yīng)用,服務(wù)仍然是可以繼續(xù)運(yùn)行的。廣播接收器允許你的應(yīng)用接收來自各處的廣播消息,比如電話、短信等,當(dāng)然你的應(yīng)用同樣也可以向外發(fā)出廣播消息。內(nèi)容提供器則為應(yīng)用程序之間共享數(shù)據(jù)提供了可能,比如你想要讀取系統(tǒng)電話簿中的聯(lián)系人,就需要通過內(nèi)容提供器來實(shí)現(xiàn)。
1 活動
1.1 活動是什么
活動(Activity)是最容易吸引用戶的地方,它是一種可以包含用戶界面的組件,主要用于和用戶進(jìn)行交互 。如果你曾經(jīng)用 C,C++ 或者 Java 語言編程,你應(yīng)該知道這些程序從 main() 函數(shù)開始。很類似的,Android 系統(tǒng)初始化它的程序是通過活動中的 onCreate() 回調(diào)的調(diào)用開始的。
1.2 活動的基本用法
1.2.1 在AndroidManifest文件中注冊
所有的活動都需要在AndroidManifest.xml中進(jìn)行注冊,活動的注冊生命要放在標(biāo)簽內(nèi),此外還需要配置主活動,就是在標(biāo)簽的內(nèi)部加入標(biāo)簽,并在標(biāo)簽里添加和。另外,如果你的應(yīng)用程序沒有聲明任何一個活動作為主活動,這個程序仍然是可以正常安裝的,只是你無法在啟動器中看到或者打開這個程序。這種程序一般都是作為第三方服務(wù)供其他應(yīng)用在內(nèi)部進(jìn)行調(diào)用的,如支付寶快捷服務(wù)。
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
android:name=".FirstActivity"
android:launchMode="singleInstance"
android:label="This is FirstActivity">
1.2.1 在活動中使用Toast
Toast是Android系統(tǒng)提供的一種非常好的提醒方式,在程序中可以使用它將一些短小的信息通知給用戶。
點(diǎn)擊按鈕button1彈出一個Toast。通過靜態(tài)方法makeText()創(chuàng)建出一個Toast對象,然后調(diào)用show()將Toast顯示出來。makeText()方法需要傳入3個參數(shù)。第一個參數(shù)是Context,也就是Toast要求的上下文,由于活動本身就是一個Context對象,因此這里直接傳入FirstActivity.this。第二個參數(shù)是Toast顯示的文本內(nèi)容,第三個參數(shù)是顯示的時長。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout);
Button button1 = (Button) findViewById(R.id.button_1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View c) {
Toast.makeText(FirstActivity.this, "You clicked Button 1", Toast.LENGTH_SHORT).show();
}
});
}
1.2.1 在活動中使用Menu
手機(jī)和電腦不同,手機(jī)的屏幕空間有限,因此充分地利用屏幕空間在手機(jī)界面中就顯得非常重要了。Android給我們停供了一種方式,可以讓菜單都能得到展示的同時,還不占用任何屏幕空間。
首先在res目錄下新建menu文件夾,然后再menu文件夾下面新建menu.xml文件,在里面加如下代碼:
android:id="@+id/add_item"
android:title="Add">
android:title="Removie">
這里我們添加了兩個菜單選項(xiàng),接著回到活動中,重寫onCreateOptionsMenu()方法。通過getMenuInflater()方法能夠得到MenuInflater對象,再調(diào)用它的inflate()方法就可以給當(dāng)前活動創(chuàng)建菜單了。
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
1.3 使用Intent再活動之間穿梭
Intent是Adnroid程序中各組件之間交互的一種重要方式,它不僅可以指明當(dāng)前組件想要執(zhí)行的動作,還可以在不同組件之間傳遞數(shù)據(jù)。Intent一般可被用于 啟動活動、啟動服務(wù)以及發(fā)送廣播場景等。我們首先構(gòu)建出一個Intent ,傳入(FirstActivity.this作為上下文,傳入SecendActivity.class作為目標(biāo)活動,即在FirstActivity這個活動的基礎(chǔ)上打開SecendActivity這個活動。然后通過startActivity()方法來執(zhí)行這個Intent 。
1.3.1 使用顯示Intent
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View c) {
Intent intent = new Intent(FirstActivity.this,SecendActivity.class);
startActivity(intent);
}
});
1.3.2 使用隱式Intent
相比顯示Intent,它并不明確指出我們想要啟動哪個活動,而是指定了一系列更為抽象的action和category等信息,然后交由系統(tǒng)去分析這個Intent,并幫我們找出合適的活動去啟動。
通過在標(biāo)簽下配置的內(nèi)容,可以指定當(dāng)前活動能夠響應(yīng)的action和categroy,打開AndroidManifest.xml,添加代碼:
在標(biāo)簽中我們指明了當(dāng)前活動響應(yīng)com.example.activityest.ACTION_START這個action,而標(biāo)簽中包含了一些附加信息,更精確地指明了當(dāng)前的活動能夠響應(yīng)得Intent中還可能帶有的categroy。只有和中的內(nèi)容同時匹配上Intent中指定的action和categroy時,這個活動才能響應(yīng)應(yīng)該的Intent。
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View c) {
Intent intent = new Intent("com.example.activityest.ACTION_START");
startActivity(intent);
}
});
直接將action的字符串傳進(jìn)去,表明想要啟動能夠響應(yīng)com.example.activityest.ACTION_START這個action活動,categroy是默認(rèn)值,所以能同樣啟動SecendActivity。
1.3.3 更多隱式Intent的用法
使用隱式Intent,我們不僅可以啟動自己程序內(nèi)的活動,還可以啟動其他程序的活動。
比如說你的應(yīng)用程序需要展示一個網(wǎng)頁,只需要調(diào)用系統(tǒng)的瀏覽器來打開就行了。我們首先指定了Intent的action是Intent.ACTION_VIEW,這是Android系統(tǒng)的內(nèi)置動作,其常量值為android.Intent.ACTION_VIEW。然后通過Uri.parse()方法,將一個網(wǎng)址字符串解析成一個Uri對象,再調(diào)用Intent的setData()方法將這個Uri對象傳遞進(jìn)去。
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View c) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com/"));
startActivity(intent);
}
});
與此對應(yīng),我們還可以在標(biāo)簽中再配置一個標(biāo)簽,用于更精確地指定當(dāng)前活動能夠響應(yīng)什么類型的數(shù)據(jù)。標(biāo)簽中主要可以配置以下內(nèi)容。
android:scheme。 用于指定數(shù)據(jù)的協(xié)議部分,如上例中的http部分
android:host。 用于指定數(shù)據(jù)的主機(jī)名部分,如上例中的www.baidu.com部分
android:port。 用于指定數(shù)據(jù)的端口部分,一般在主機(jī)名之后
android:path。 用于指定主機(jī)名和端口之后的部分,如一段網(wǎng)址中跟在域名之后的內(nèi)容
android:mimeType。 用于指定可以處理的數(shù)據(jù)類型,允許使用通配符的方式進(jìn)行指定
1.3.4 向下一個活動傳遞數(shù)據(jù)
在啟動活動時傳遞數(shù)據(jù)的思路很簡單,Intent中提供了一系列putExtra()方法的重載,可以把我們想要傳遞的數(shù)據(jù)存在Intent中,啟動了另一個活動后,只需要把這些數(shù)據(jù)再從Intent中取出就可以了。比如說再FirstActivity中有一個字符串,現(xiàn)在想把這個字符串傳遞到SecondActivity中,
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View c) {
String data = "hello SecendActivity";
Intent intent = new Intent(FirstActivity.this, SecendActivity.class)
intent.putExtra("extra_data", data);
startActivity(intent);
}
});
然后再SecondActivity中將數(shù)據(jù)取出,首先通過getIntent()方法獲取到用于啟動SecondActivity的Intent,然后調(diào)用getStringExtra()方法,傳入相應(yīng)的鍵值,就可以獲得傳遞的數(shù)據(jù)了。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.secend_layout);
Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");
}
1.3.5 返回?cái)?shù)據(jù)給上一個活動
Activity中有一個startActivityForResult()方法也是用于啟動活動的,但這個方法期望在活動銷毀時候能夠返回一個結(jié)果給上一個活動。
startActivityForResult()方法接收兩個參數(shù),第一個參數(shù)是Intent,第二個參數(shù)是請求碼,用于在之后回調(diào)中判斷數(shù)據(jù)的來源。修改FirstActivity。
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View c) {
Intent intent = new Intent(FirstActivity.this, SecendActivity.class);
startActivityForResult(intent, 1);
}
});
然后在SecondActivity中添加返回?cái)?shù)據(jù)的邏輯。先構(gòu)建了一個Intent用于傳遞數(shù)據(jù),然后將要傳遞的數(shù)據(jù)放在Intent中,然后調(diào)用了setResult()方法接收兩個參數(shù),第一個參數(shù)用于向上一個活動返回處理結(jié)果,一般只會用RESULT_OK或RESULT_CANCELED這兩個值,第二個參數(shù)則把帶有數(shù)據(jù)的Intent傳遞回去,然后調(diào)用finish()方法來銷毀當(dāng)前活動。
Button button2 = (Button) findViewById(R.id.button_2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("data_return", "hello FirstActivity");
setResult(RESULT_OK, intent);
finish();
}
});
由于使用startActivityForResult()方法來啟動SecondActivity,在SecondActivity被銷毀之后會回調(diào)上一個活動的onActivityResult()方法,因此我們需要在FirstActivity中重寫這個方法來得到返回的數(shù)據(jù)。
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 1:
if (resultCode == RESULT_OK) {
String returnedData = data.getStringExtra("data_return");
}
break;
default:
}
}
onActivityResult()方法帶有三個參數(shù),第一個參數(shù)requestCode,即請求碼,我們需要通過其來判斷數(shù)據(jù)來源。第二個參數(shù)resultCode,即我們在返回?cái)?shù)據(jù)時傳入的處理結(jié)果。第三個參數(shù)data,即攜帶返回?cái)?shù)據(jù)的Intent。
1.4 活動的生命周期
1.4.1 返回棧
Android中的活動是可以層疊的,我們每啟動一個新的活動,就會覆蓋在原活動之上,然后點(diǎn)擊Back鍵會銷毀最上面的活動,下面的一個活動就會重新顯示出來。
Android是使用任務(wù)(Task)來管理活動的,一個任務(wù)就是一組存放在棧里的活動的集合,這個棧也被稱為返回棧(Back Stack)。棧是一種后進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),在默認(rèn)情況下,每當(dāng)我們啟動了一個新的活動,它會在返回棧中入棧,并處于棧頂?shù)奈恢谩6慨?dāng)我們按下Back鍵或調(diào)用finish()方法銷毀一個活動時,處于棧頂?shù)幕顒泳统龀鰲!?/p>
返回棧工作示意圖
1.4.2 活動狀態(tài)
每個活動在其生命周期中最多可能會有4種狀態(tài)
運(yùn)行狀態(tài):當(dāng)一個活動位于返回棧的棧頂時,這時活動就處于運(yùn)行狀態(tài)。
暫停狀態(tài):當(dāng)一個活動不再處于棧頂位置,但仍然可見時,這時活動就進(jìn)入了暫停狀態(tài)。
停止?fàn)顟B(tài):當(dāng)一個活動不再處于棧頂位置,并且完全不可見的時候,就進(jìn)入停止?fàn)顟B(tài)。
銷毀狀態(tài):當(dāng)一個活動從返回棧種移除后就變成了銷毀狀態(tài)。
1.4.3 活動的生存周期
Activity類種定義了7各回調(diào)方法,覆蓋了活動生命周期的每一個環(huán)節(jié)。
onCreate()。它會在活動第一次被創(chuàng)建時調(diào)用。你應(yīng)該在這個方法中完成活動的初始化操作,比如說加載布局、綁定事件等。
onStart()。這個方法在活動由不可見變?yōu)榭梢姷臅r候調(diào)用。
onResume()。這個方法在活動準(zhǔn)備好和用戶進(jìn)行交互的時候調(diào)用。此時活動一定位于返回棧的棧頂,并且處于運(yùn)行狀態(tài)。
onPause()。這個方法在系統(tǒng)準(zhǔn)備去啟動或者恢復(fù)另一個活動的時候調(diào)用。我們通常會在這個方法中將一些消耗CPU的資源釋放掉,以及保存一些關(guān)鍵數(shù)據(jù),但這個方法的執(zhí)行速度一定要快,不然會影響到新的棧頂活動的使用。
onStop()。這個方法在活動完全不可見的時候調(diào)用。它和onPause()方法的主要區(qū)別在于,如果啟動的新活動是一個對話框式的活動,那么onPause()方法會得到執(zhí)行,而onStop()方法并不會執(zhí)行。
onDestroy()。這個方法在活動被銷毀之前調(diào)用,之后活動的狀態(tài)變?yōu)殇N毀狀態(tài)。
onRestart()。這個方法在活動由停止?fàn)顟B(tài)變?yōu)檫\(yùn)行狀態(tài)之前調(diào)用,也就是說活動被重啟啟動了。
以上7個方法中除了onRestart()方法,其他都是兩兩相對的,從而又可以將活動分為3種生存期。
完整生存期?;顒釉趏nCreate()方法和onDestroy()方法之間所經(jīng)歷的,就是完整生存期。一般情況下,一個活動會在onCreate()方法中完成各種初始化操作,而在onDestroy()方法中完成釋放內(nèi)存的操作。
可見生存期?;顒釉趏nStart()方法和onStop()方法之間所經(jīng)歷的,就是可見生存期。在可見生存期內(nèi),活動對于用戶總是可見的,即便有可能無法和用戶進(jìn)行交互。我們可以通過這兩個方法,合理地管理那些對用戶可見的資源。比如在onStart()方法中對資源進(jìn)行加載,而在onStop()方法中對資源進(jìn)行釋放,從而保證處于停止?fàn)顟B(tài)的活動不會占用過多內(nèi)存。
前臺生存期?;顒釉趏nResume()方法和onPause()方法之間所經(jīng)歷的就是前臺生存期。在前臺生存期內(nèi),活動總是處于運(yùn)行狀態(tài),此時的活動是可以和用戶進(jìn)行交互的,我們平時看到和接觸最多的也就是這個狀態(tài)下的活動。
活動的生命周期
1.4.4 活動被回收了怎么辦
應(yīng)用中有個活動A,用戶在A的基礎(chǔ)上啟動了活動B,活動A就進(jìn)入了停止?fàn)顟B(tài),這個時候由于內(nèi)存不足,將A回收掉了,然后用戶按下Back鍵返回活動A,雖然也會正常顯示活動A,但是這個并不會執(zhí)行onRestart()方法,而是會執(zhí)行活動A的onCreate()方法,因?yàn)锳在這種情況下會被重新創(chuàng)建一次。
例如,在MainActivity中有個文本輸入框,現(xiàn)在你輸入了文字,然后啟動NormalActivity,這時MainActivity由于系統(tǒng)內(nèi)存不足被回收掉,這時點(diǎn)擊Back鍵返回到MainActivity,你會發(fā)現(xiàn)輸入的文字沒得了,因?yàn)镸ainActivity被重新創(chuàng)建了。
這時我們可以使用Activity中提供的onSaveInstanceState()回調(diào)方法,這個方法可以保證活動被回收之前一定會被調(diào)用,因此可以通過這個方法來解決活動被禍?zhǔn)讜r臨時數(shù)據(jù)得不到保存的問題。
onSaveInstanceState()方法會攜帶一個Bundle類型的參數(shù),Bundle提供了一系列的方法用于保存數(shù)據(jù)。
在MainActivity中添加如下代碼用于保存臨時數(shù)據(jù):
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String tempData = "aaaaa";
outState.putString("data_key",tempData);
}
然后修改MainActivity中的onCreate()方法
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState!=null) {
String tempData = savedInstanceState.getString("data_key");
}
}
1.5 活動的啟動模式
啟動模式一共有4種,分別是 standard,SingleTop,SingleTask,SingleInstance。這四種模式中,standard模式是默認(rèn)的模式,其他三個想要使用的話,要在AndroidMainFest中進(jìn)行修改。(例如:)。在實(shí)際項(xiàng)目種我們應(yīng)該根據(jù)特定的需求為每個活動指定恰當(dāng)?shù)膯幽J健?/p>
1.5.1 standard
standard是活動默認(rèn)的啟動模式。對于使用standard模式啟動的活動,系統(tǒng)不會在乎這個活動是否已經(jīng)在返回棧中存在,每次啟動都會創(chuàng)建該活動的一個新的實(shí)例。
standard模式示例圖
1.5.2 singleTop
當(dāng)活動的模式指定為singleTop,在啟動活動時如果發(fā)現(xiàn)返回棧的棧頂已經(jīng)是該活動,則認(rèn)為可以直接使用它,不會再創(chuàng)建新的活動實(shí)例。
singleTop模式示意圖
1.5.3 singleTask
使用singleTop模式可以很好的解決重復(fù)創(chuàng)建棧頂活動的問題,但是如果該活動沒得處于棧頂位置,還是可以創(chuàng)建多個活動實(shí)例的。當(dāng)活動的啟動模式指定為singleTask,每次啟動該活動時系統(tǒng)首先會在返回棧中檢查是否存在該活動對的實(shí)例,如果發(fā)現(xiàn)已經(jīng)存在則直接使用該實(shí)例,并把在這個活動之上的所有活動統(tǒng)統(tǒng)出棧,如果沒有發(fā)現(xiàn)就會創(chuàng)建一個新的活動實(shí)例。
singleTask模式示意圖
1.5.4 singleInstance
不同于以上3種啟動模式,指定為singleInstance模式的活動會啟動一個新的返回棧來管理這個活動。例如,假設(shè)我們的程序種有一個活動允許其他活動調(diào)用,在這種模式下,我們會有一個單獨(dú)的返回棧來管理這個活動,不管哪個應(yīng)用程序來訪問這個活動,都公用的同一個返回棧,也就解決了共享活動實(shí)例的問題。
singleInstance模式示例圖
1.6 活動的最佳實(shí)踐
1.6.1 知曉當(dāng)前是在哪個活動
這個技巧將會教會你如何根據(jù)程序當(dāng)前的界面就能判斷出這是哪一個活動。
新建 BaseActivity ,然后讓 BaseActivity 繼承自 AppCompatActivity ,并重寫onCreate()方法,如:
@Override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());
}
我們在onCreate()方法種獲取了當(dāng)前實(shí)例的類名,并通過Log打印出來。接下來讓 BaseActivity 成為項(xiàng)目中所有活動的父類。我們可以通過打印出的類名來判斷是哪個活動。
1.6.2 隨時隨地退出程序
新建一個ActivityCollector類作為活動管理器,
public class ActivityCollector {
public static List activities = new ArrayList<>();
public static void addActivity(Activity activity){
activities.add(activity);
}
public static void removeActivity(Activity activity){
activities.remove(activity);
}
public static void finishAll(){
for(Activity activity:activities){
if (!activity.isFinishing()){
activity.finish();
}
}
activities.clear();
}
}
在活動管理器中,我們通過一個List來暫存活動,然后提供了一個addActivity()方法用于向List中添加一個活動,提供了一個removeActivity()方法用于從List中移除活動,最后提供了一個finishAll()方法用于將List中存儲的活動娶不銷毀。
接下來修改 BaseActivity 中的代碼,
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
在 BaseActivity 的 onCreate() 方法中國調(diào)用了 ActivityCollector 的 addActivity() 方法,表明將當(dāng)前正在創(chuàng)建的活動添加到活動管理器李。然后在 BaseActivity 中重寫 onDestroy() 方法,并調(diào)用了 ActivityCollector 的 removeActivity()方法,表明將一個馬上要銷毀的活動從活動管理器中移除。此外,不管你想在什么地方退出程序,只需要調(diào)用 ActivityCollector.finishAll()方法即可。
1.6.3 啟動活動的最佳寫法
假設(shè) SecondActivity 中需要用到兩個非常重要的字符串參數(shù),在啟動 SecondActivity 的時候必須傳遞過來,
傳統(tǒng)寫法
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("param1","data1");
intent.putExtra("param2","data2");
startActivity(intent);
但是在真正的項(xiàng)目中經(jīng)常會有對接問題,比如 SecondActivity 不是你開發(fā)的,你不知道需要傳遞哪些數(shù)據(jù),這時你可能需要閱讀 SecondActivity 代碼或者詢問編寫它的同事,但是你可以換種寫法。
修改 SecondActivity 種的代碼:
public class SecondActivity extends BaseActivity{
public static void actionStart(Context context,String data1,String data2){
Intent intent = new Intent(context,SecondActivity.class);
intent.putExtra("param1",data1);
intent.putExtra("param2",data2);
context.startActivity(intent);
}
...
}
我們在 SecondActivity 種添加了一個 actionStart() 方法,這個方法中完成了Intent的構(gòu)建,另外所有 SecondActivity 中需要的數(shù)據(jù)都是通過 actionStart() 方法的參數(shù)傳遞過來的,然后把它存儲到Intent中,最后調(diào)用 startActivity() 方法啟動 SecondActivity。
這樣寫的好處是能夠一目了然的知道啟動 SecondActivity 需要傳遞哪些數(shù)據(jù),另外簡化了啟動活動的代碼
SecendActivity.actionStart(FirstActivity.this, "data1", "data2");
總結(jié)
以上是生活随笔為你收集整理的Android允许后台活动管理,安卓基础知识(活动)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java与java ee_Java EE
- 下一篇: 外币兑换人民币公式?