Android允许后台活动管理,安卓基础知识(活动)
一、四大組件
Android系統四大組件分別是活動(Activity)、服務(Service)、廣播接收器(Broadcast Receiver)和內容提供器(Content Provider)。其中活動是所有Android應用程序的門面,凡是在應用中你看到的東西,都是放到活動中的。而服務就低調了,你無法看到它,但它會一直在后臺默默的運行,即使用戶退出了應用,服務仍然是可以繼續運行的。廣播接收器允許你的應用接收來自各處的廣播消息,比如電話、短信等,當然你的應用同樣也可以向外發出廣播消息。內容提供器則為應用程序之間共享數據提供了可能,比如你想要讀取系統電話簿中的聯系人,就需要通過內容提供器來實現。
1 活動
1.1 活動是什么
活動(Activity)是最容易吸引用戶的地方,它是一種可以包含用戶界面的組件,主要用于和用戶進行交互 。如果你曾經用 C,C++ 或者 Java 語言編程,你應該知道這些程序從 main() 函數開始。很類似的,Android 系統初始化它的程序是通過活動中的 onCreate() 回調的調用開始的。
1.2 活動的基本用法
1.2.1 在AndroidManifest文件中注冊
所有的活動都需要在AndroidManifest.xml中進行注冊,活動的注冊生命要放在標簽內,此外還需要配置主活動,就是在標簽的內部加入標簽,并在標簽里添加和。另外,如果你的應用程序沒有聲明任何一個活動作為主活動,這個程序仍然是可以正常安裝的,只是你無法在啟動器中看到或者打開這個程序。這種程序一般都是作為第三方服務供其他應用在內部進行調用的,如支付寶快捷服務。
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系統提供的一種非常好的提醒方式,在程序中可以使用它將一些短小的信息通知給用戶。
點擊按鈕button1彈出一個Toast。通過靜態方法makeText()創建出一個Toast對象,然后調用show()將Toast顯示出來。makeText()方法需要傳入3個參數。第一個參數是Context,也就是Toast要求的上下文,由于活動本身就是一個Context對象,因此這里直接傳入FirstActivity.this。第二個參數是Toast顯示的文本內容,第三個參數是顯示的時長。
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
手機和電腦不同,手機的屏幕空間有限,因此充分地利用屏幕空間在手機界面中就顯得非常重要了。Android給我們停供了一種方式,可以讓菜單都能得到展示的同時,還不占用任何屏幕空間。
首先在res目錄下新建menu文件夾,然后再menu文件夾下面新建menu.xml文件,在里面加如下代碼:
android:id="@+id/add_item"
android:title="Add">
android:title="Removie">
這里我們添加了兩個菜單選項,接著回到活動中,重寫onCreateOptionsMenu()方法。通過getMenuInflater()方法能夠得到MenuInflater對象,再調用它的inflate()方法就可以給當前活動創建菜單了。
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
1.3 使用Intent再活動之間穿梭
Intent是Adnroid程序中各組件之間交互的一種重要方式,它不僅可以指明當前組件想要執行的動作,還可以在不同組件之間傳遞數據。Intent一般可被用于 啟動活動、啟動服務以及發送廣播場景等。我們首先構建出一個Intent ,傳入(FirstActivity.this作為上下文,傳入SecendActivity.class作為目標活動,即在FirstActivity這個活動的基礎上打開SecendActivity這個活動。然后通過startActivity()方法來執行這個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等信息,然后交由系統去分析這個Intent,并幫我們找出合適的活動去啟動。
通過在標簽下配置的內容,可以指定當前活動能夠響應的action和categroy,打開AndroidManifest.xml,添加代碼:
在標簽中我們指明了當前活動響應com.example.activityest.ACTION_START這個action,而標簽中包含了一些附加信息,更精確地指明了當前的活動能夠響應得Intent中還可能帶有的categroy。只有和中的內容同時匹配上Intent中指定的action和categroy時,這個活動才能響應應該的Intent。
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View c) {
Intent intent = new Intent("com.example.activityest.ACTION_START");
startActivity(intent);
}
});
直接將action的字符串傳進去,表明想要啟動能夠響應com.example.activityest.ACTION_START這個action活動,categroy是默認值,所以能同樣啟動SecendActivity。
1.3.3 更多隱式Intent的用法
使用隱式Intent,我們不僅可以啟動自己程序內的活動,還可以啟動其他程序的活動。
比如說你的應用程序需要展示一個網頁,只需要調用系統的瀏覽器來打開就行了。我們首先指定了Intent的action是Intent.ACTION_VIEW,這是Android系統的內置動作,其常量值為android.Intent.ACTION_VIEW。然后通過Uri.parse()方法,將一個網址字符串解析成一個Uri對象,再調用Intent的setData()方法將這個Uri對象傳遞進去。
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);
}
});
與此對應,我們還可以在標簽中再配置一個標簽,用于更精確地指定當前活動能夠響應什么類型的數據。標簽中主要可以配置以下內容。
android:scheme。 用于指定數據的協議部分,如上例中的http部分
android:host。 用于指定數據的主機名部分,如上例中的www.baidu.com部分
android:port。 用于指定數據的端口部分,一般在主機名之后
android:path。 用于指定主機名和端口之后的部分,如一段網址中跟在域名之后的內容
android:mimeType。 用于指定可以處理的數據類型,允許使用通配符的方式進行指定
1.3.4 向下一個活動傳遞數據
在啟動活動時傳遞數據的思路很簡單,Intent中提供了一系列putExtra()方法的重載,可以把我們想要傳遞的數據存在Intent中,啟動了另一個活動后,只需要把這些數據再從Intent中取出就可以了。比如說再FirstActivity中有一個字符串,現在想把這個字符串傳遞到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中將數據取出,首先通過getIntent()方法獲取到用于啟動SecondActivity的Intent,然后調用getStringExtra()方法,傳入相應的鍵值,就可以獲得傳遞的數據了。
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 返回數據給上一個活動
Activity中有一個startActivityForResult()方法也是用于啟動活動的,但這個方法期望在活動銷毀時候能夠返回一個結果給上一個活動。
startActivityForResult()方法接收兩個參數,第一個參數是Intent,第二個參數是請求碼,用于在之后回調中判斷數據的來源。修改FirstActivity。
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View c) {
Intent intent = new Intent(FirstActivity.this, SecendActivity.class);
startActivityForResult(intent, 1);
}
});
然后在SecondActivity中添加返回數據的邏輯。先構建了一個Intent用于傳遞數據,然后將要傳遞的數據放在Intent中,然后調用了setResult()方法接收兩個參數,第一個參數用于向上一個活動返回處理結果,一般只會用RESULT_OK或RESULT_CANCELED這兩個值,第二個參數則把帶有數據的Intent傳遞回去,然后調用finish()方法來銷毀當前活動。
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被銷毀之后會回調上一個活動的onActivityResult()方法,因此我們需要在FirstActivity中重寫這個方法來得到返回的數據。
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()方法帶有三個參數,第一個參數requestCode,即請求碼,我們需要通過其來判斷數據來源。第二個參數resultCode,即我們在返回數據時傳入的處理結果。第三個參數data,即攜帶返回數據的Intent。
1.4 活動的生命周期
1.4.1 返回棧
Android中的活動是可以層疊的,我們每啟動一個新的活動,就會覆蓋在原活動之上,然后點擊Back鍵會銷毀最上面的活動,下面的一個活動就會重新顯示出來。
Android是使用任務(Task)來管理活動的,一個任務就是一組存放在棧里的活動的集合,這個棧也被稱為返回棧(Back Stack)。棧是一種后進先出的數據結構,在默認情況下,每當我們啟動了一個新的活動,它會在返回棧中入棧,并處于棧頂的位置。而每當我們按下Back鍵或調用finish()方法銷毀一個活動時,處于棧頂的活動就出出棧。
返回棧工作示意圖
1.4.2 活動狀態
每個活動在其生命周期中最多可能會有4種狀態
運行狀態:當一個活動位于返回棧的棧頂時,這時活動就處于運行狀態。
暫停狀態:當一個活動不再處于棧頂位置,但仍然可見時,這時活動就進入了暫停狀態。
停止狀態:當一個活動不再處于棧頂位置,并且完全不可見的時候,就進入停止狀態。
銷毀狀態:當一個活動從返回棧種移除后就變成了銷毀狀態。
1.4.3 活動的生存周期
Activity類種定義了7各回調方法,覆蓋了活動生命周期的每一個環節。
onCreate()。它會在活動第一次被創建時調用。你應該在這個方法中完成活動的初始化操作,比如說加載布局、綁定事件等。
onStart()。這個方法在活動由不可見變為可見的時候調用。
onResume()。這個方法在活動準備好和用戶進行交互的時候調用。此時活動一定位于返回棧的棧頂,并且處于運行狀態。
onPause()。這個方法在系統準備去啟動或者恢復另一個活動的時候調用。我們通常會在這個方法中將一些消耗CPU的資源釋放掉,以及保存一些關鍵數據,但這個方法的執行速度一定要快,不然會影響到新的棧頂活動的使用。
onStop()。這個方法在活動完全不可見的時候調用。它和onPause()方法的主要區別在于,如果啟動的新活動是一個對話框式的活動,那么onPause()方法會得到執行,而onStop()方法并不會執行。
onDestroy()。這個方法在活動被銷毀之前調用,之后活動的狀態變為銷毀狀態。
onRestart()。這個方法在活動由停止狀態變為運行狀態之前調用,也就是說活動被重啟啟動了。
以上7個方法中除了onRestart()方法,其他都是兩兩相對的,從而又可以將活動分為3種生存期。
完整生存期。活動在onCreate()方法和onDestroy()方法之間所經歷的,就是完整生存期。一般情況下,一個活動會在onCreate()方法中完成各種初始化操作,而在onDestroy()方法中完成釋放內存的操作。
可見生存期。活動在onStart()方法和onStop()方法之間所經歷的,就是可見生存期。在可見生存期內,活動對于用戶總是可見的,即便有可能無法和用戶進行交互。我們可以通過這兩個方法,合理地管理那些對用戶可見的資源。比如在onStart()方法中對資源進行加載,而在onStop()方法中對資源進行釋放,從而保證處于停止狀態的活動不會占用過多內存。
前臺生存期。活動在onResume()方法和onPause()方法之間所經歷的就是前臺生存期。在前臺生存期內,活動總是處于運行狀態,此時的活動是可以和用戶進行交互的,我們平時看到和接觸最多的也就是這個狀態下的活動。
活動的生命周期
1.4.4 活動被回收了怎么辦
應用中有個活動A,用戶在A的基礎上啟動了活動B,活動A就進入了停止狀態,這個時候由于內存不足,將A回收掉了,然后用戶按下Back鍵返回活動A,雖然也會正常顯示活動A,但是這個并不會執行onRestart()方法,而是會執行活動A的onCreate()方法,因為A在這種情況下會被重新創建一次。
例如,在MainActivity中有個文本輸入框,現在你輸入了文字,然后啟動NormalActivity,這時MainActivity由于系統內存不足被回收掉,這時點擊Back鍵返回到MainActivity,你會發現輸入的文字沒得了,因為MainActivity被重新創建了。
這時我們可以使用Activity中提供的onSaveInstanceState()回調方法,這個方法可以保證活動被回收之前一定會被調用,因此可以通過這個方法來解決活動被禍首時臨時數據得不到保存的問題。
onSaveInstanceState()方法會攜帶一個Bundle類型的參數,Bundle提供了一系列的方法用于保存數據。
在MainActivity中添加如下代碼用于保存臨時數據:
@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模式是默認的模式,其他三個想要使用的話,要在AndroidMainFest中進行修改。(例如:)。在實際項目種我們應該根據特定的需求為每個活動指定恰當的啟動模式。
1.5.1 standard
standard是活動默認的啟動模式。對于使用standard模式啟動的活動,系統不會在乎這個活動是否已經在返回棧中存在,每次啟動都會創建該活動的一個新的實例。
standard模式示例圖
1.5.2 singleTop
當活動的模式指定為singleTop,在啟動活動時如果發現返回棧的棧頂已經是該活動,則認為可以直接使用它,不會再創建新的活動實例。
singleTop模式示意圖
1.5.3 singleTask
使用singleTop模式可以很好的解決重復創建棧頂活動的問題,但是如果該活動沒得處于棧頂位置,還是可以創建多個活動實例的。當活動的啟動模式指定為singleTask,每次啟動該活動時系統首先會在返回棧中檢查是否存在該活動對的實例,如果發現已經存在則直接使用該實例,并把在這個活動之上的所有活動統統出棧,如果沒有發現就會創建一個新的活動實例。
singleTask模式示意圖
1.5.4 singleInstance
不同于以上3種啟動模式,指定為singleInstance模式的活動會啟動一個新的返回棧來管理這個活動。例如,假設我們的程序種有一個活動允許其他活動調用,在這種模式下,我們會有一個單獨的返回棧來管理這個活動,不管哪個應用程序來訪問這個活動,都公用的同一個返回棧,也就解決了共享活動實例的問題。
singleInstance模式示例圖
1.6 活動的最佳實踐
1.6.1 知曉當前是在哪個活動
這個技巧將會教會你如何根據程序當前的界面就能判斷出這是哪一個活動。
新建 BaseActivity ,然后讓 BaseActivity 繼承自 AppCompatActivity ,并重寫onCreate()方法,如:
@Override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());
}
我們在onCreate()方法種獲取了當前實例的類名,并通過Log打印出來。接下來讓 BaseActivity 成為項目中所有活動的父類。我們可以通過打印出的類名來判斷是哪個活動。
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() 方法中國調用了 ActivityCollector 的 addActivity() 方法,表明將當前正在創建的活動添加到活動管理器李。然后在 BaseActivity 中重寫 onDestroy() 方法,并調用了 ActivityCollector 的 removeActivity()方法,表明將一個馬上要銷毀的活動從活動管理器中移除。此外,不管你想在什么地方退出程序,只需要調用 ActivityCollector.finishAll()方法即可。
1.6.3 啟動活動的最佳寫法
假設 SecondActivity 中需要用到兩個非常重要的字符串參數,在啟動 SecondActivity 的時候必須傳遞過來,
傳統寫法
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("param1","data1");
intent.putExtra("param2","data2");
startActivity(intent);
但是在真正的項目中經常會有對接問題,比如 SecondActivity 不是你開發的,你不知道需要傳遞哪些數據,這時你可能需要閱讀 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的構建,另外所有 SecondActivity 中需要的數據都是通過 actionStart() 方法的參數傳遞過來的,然后把它存儲到Intent中,最后調用 startActivity() 方法啟動 SecondActivity。
這樣寫的好處是能夠一目了然的知道啟動 SecondActivity 需要傳遞哪些數據,另外簡化了啟動活動的代碼
SecendActivity.actionStart(FirstActivity.this, "data1", "data2");
總結
以上是生活随笔為你收集整理的Android允许后台活动管理,安卓基础知识(活动)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java与java ee_Java EE
- 下一篇: java.util.concurrent