4.1.1 Android四大組件之- Activity(初學乍練)

本節開始講解Android的四大組件之一的Activity

官方介紹:

Activity 是一個應用組件,用戶可與其提供的屏幕進行交互,以執行撥打電話、拍攝照片、發送電子郵件或查看地圖等操作。 每個 Activity 都會獲得一個用于繪制其用戶界面的窗口。窗口通常會充滿屏幕,但也可小于屏幕并浮動在其他窗口之上。

從上面這段話,可以得到以下信息:

  1. Activity用于顯示用戶界面,用戶通過Activity交互完成相關操作
  1. 一個App允許有多個Activity

1. Activity的概念與Activity的生命周期:

Paste_Image.png

理解生命周期的回調

在一個activity的生命周期中,系統會像金字塔模型一樣去調用一系列的生命周期回調函數。Activity生命周期的每一個階段就像金字塔中的臺階。當系統創建了一個新的activity實例,每一個回調函數會向上一階移動activity狀態。處在金字塔頂端意味著當前activity處在前臺并處于用戶可與其進行交互的狀態。
當用戶退出這個activity時,為了回收該activity,系統會調用其它方法來向下一階移動activity狀態。在某些情況下,activity會隱藏在金字塔下等待(例如當用戶切換到其他app),此時activity可以重新回到頂端(如果用戶回到這個activity)并恢復用戶離開時的狀態。

理解生命周期的回調

Activity會在上圖所示不同狀態之間過渡的幾種情況。 但是,這些狀態中只有三種可以是靜態。 也就是說,Activity只能在三種狀態之一下存在很長時間。
Resumed: 繼續
在這種狀態下,Activity處于前臺,且用戶可以與其交互。(有時也稱為“運行”狀態。)
Paused: 暫停
在這種狀態下,Activity被在前臺中處于半透明狀態或者未覆蓋整個屏幕的另一個Activity—部分阻擋。 暫停的Activity不會接收用戶輸入并且無法執行任何代碼。
Stopped: 停止
在這種狀態下,Activity被完全隱藏并且對用戶不可見;它被視為處于后臺。 停止時,Activity實例及其諸如成員變量等所有狀態信息將保留,但它無法執行任何代碼。

其他狀態(“創建”和“開始”)是瞬態,系統會通過調用下一個生命周期回調方法從這些狀態快速移到下一個狀態。 也就是說,在系統調用 onCreate()之后,它會快速調用 onStart()
,緊接著快速調用 onResume()

注意事項:

  1. onPause()和onStop()被調用的前提是:
    打開了一個新的Activity!而前者是舊Activity還可見的狀態;后者是舊Activity已經不可見!
  1. 另外,親測:AlertDialog和PopWindow是不會觸發上述兩個回調方法的~

2. Activity/ActionBarActivity/AppCompatActivity的區別:

在開始講解創建Activity之前要說下這三個的一個區別:
Activity就不用說啦,后面這兩個都是為了低版本兼容而提出的提出來的,他們都在v7包下,
ActionBarActivity已被廢棄,從名字就知道,ActionBar~,而在5.0后,被Google棄用了,現在用
ToolBar…而我們現在在Android Studio創建一個Activity默認繼承的會是:AppCompatActivity!
當然你也可以只寫Activity,不過AppCompatActivity給我們提供了一些新的東西而已! 兩個選一個 ~

3. Activity的創建流程

Paste_Image.png

ps:

上面也說過,可以繼承Activity和AppCompatActivity,只不過后者提供了一些新的東西而已!
另外,切記,Android中的四大組件,只要你定義了,無論你用沒用,都要在AndroidManifest.xml對
這個組件進行聲明,不然運行時程序會直接退出,報ClassNotFindException…

4. onCreate()一個參數和兩個參數的區別:

相信用as的朋友在重寫Act的onCreate()方法時會發現

  1. 有兩個參數:
@Override
public void onCreate(Bundle savedInstanceState,PersistableBundle persistentStable) {
    super.onCreate(savedInstanceState,persistentStable);
}
  1. 有一個參數
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

可是正常的才只有一個參數啊, 恩呢,這就是5.0給我們提供的新的方法,要用它,先要在配置文件中為我們的Activity設置一個屬性:

android:persistableMode="persistAcrossReboots"

然后我們的Activity就擁有了持久化的能力了,一般我們會搭配另外兩個方法來使用:

public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState)
public void onRestoreInstanceState(Bundle savedInstanceState, PersistableBundle persistentState)

相信有些朋友對這兩個方法名不陌生吧,前一個方法會在下述情形中被調用:

  • 點擊home鍵回到主頁或長按后選擇運行其他程序
  • 按下電源鍵關閉屏幕
  • 啟動新的Activity
  • 橫豎屏切換時,肯定會執行,因為橫豎屏切換的時候會先銷毀Act,然后再重新創建
  • 重要原則:
    當系統“未經你許可”時銷毀了你的activity,則onSaveInstanceState會被系統調用,
    這是系統的責任,因為它必須要提供一個機會讓你保存你的數據(你可以保存也可以不保存)。

而后一個方法,和onCreate同樣可以從取出前者保存的數據:
一般是在onStart()和onResume()之間執行!
之所以有兩個可以獲取到保存數據的方法,是為了避免Act跳轉而沒有關閉,
然后不走onCreate()方法,而你又想取出保存數據~

說回來:
說回這個Activity擁有了持久化的能力,增加的這個PersistableBundle參數令這些方法
擁有了系統關機后重啟的數據恢復能力!!而且不影響我們其他的序列化操作,臥槽,
具體怎么實現的,暫時還不了解,可能是另外弄了個文件保存吧~!后面知道原理的話會告知下大家!
另外,API版本需要>=21,就是要5.0以上的版本才有效~

5. 啟動一個Activity的幾種方式

在Android中我們可以通過下面兩種方式來啟動一個新的Activity,注意這里是怎么啟動,而非 啟動模式?。》譃轱@示啟動和隱式啟動!

  1. 顯式啟動:通過包名來啟動,寫法如下:
  • ① 最常見的:
    startActivity(new Intent(當前Act.this,要啟動的Act.class));
  • ② 通過Intent的ComponentName:
    ComponentName cn = new ComponentName(“當前Act的全限定類名”,”啟動Act的全限定類名”) ;
    Intent intent = new Intent() ;
    intent.setComponent(cn) ;
    startActivity(intent) ;
  • ③ 初始化Intent時指定包名:
    Intent intent = new Intent(“android.intent.action.MAIN”);
    intent.setClassName(“當前Act的全限定類名”,”啟動Act的全限定類名”);
    startActivity(intent);
  1. 隱式啟動:通過Intent-filter的Action,Category或data來實現

這個是通過Intent的 * intent-filter*來實現的,這個Intent那章會詳細講解!
這里知道個大概就可以了!


Paste_Image.png
  1. 另外還有一個直接通過包名啟動apk的:
Intent intent = getPackageManager().getLaunchIntentForPackage 
(“apk第一個啟動的Activity的全限定類名”) ; 
if(intent != null) startActivity(intent) ;

6. 橫豎屏切換與狀態保存的問題

前面也說到了App橫豎屏切換的時候會銷毀當前的Activity然后重新創建一個,你可以自行在生命周期 的每個方法里都添加打印Log的語句,來進行判斷,又或者設一個按鈕一個TextView點擊按鈕后,修改TextView
文本,然后橫豎屏切換,會神奇的發現TextView文本變回之前的內容了!
橫豎屏切換時Act走下述生命周期:

onPause-> onStop-> onDestory-> onCreate->onStart->onResume

關于橫豎屏切換可能遇到下述問題:

1).先說下如何禁止屏幕橫豎屏自動切換

很簡單,在AndroidManifest.xml中為Act添加一個屬性:
android:screenOrientation

有下述可選值:

  • unspecified:默認值 由系統來判斷顯示方向.判定的策略是和設備相關的,所以不同的設備會有不同的顯示方向.
  • landscape:橫屏顯示(寬比高要長)
  • portrait:豎屏顯示(高比寬要長)
  • user:用戶當前首選的方向
  • behind:和該Activity下面的那個Activity的方向一致(在Activity堆棧中的)
  • sensor:有物理的感應器來決定。如果用戶旋轉設備這屏幕會橫豎屏切換。
  • nosensor:忽略物理感應器,這樣就不會隨著用戶旋轉設備而更改了(”unspecified”設置除外)。

2). 橫豎屏時想加載不同的布局:

2.1)準備兩套不同的布局,Android會自己根據橫豎屏加載不同布局:
創建兩個布局文件夾:
layout-land橫屏,layout-port豎屏
然后把這兩套布局文件丟這兩文件夾里,文件名一樣,Android就會自行判斷,然后加載相應布局了!

2.2 )自己在代碼中進行判斷,自己想加載什么就加載什么:
我們一般是在onCreate()方法中加載布局文件的,我們可以在這里對橫豎屏的狀態做下判斷,關鍵代碼如下

if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){  
     setContentView(R.layout.橫屏);
}  

else if (this.getResources().getConfiguration().orientation ==Configuration.ORIENTATION_PORTRAIT) {  
    setContentView(R.layout.豎屏);
}
  1. . 如何讓模擬器橫豎屏切換

如果你的模擬器是GM的話。直接按模擬器上的切換按鈕即可,原生模擬器可按ctrl + f11/f12切換!

  1. . 狀態保存問題:

這個上面也說過了,通過一個Bundle savedInstanceState參數即可完成!
三個核心方法:

onCreate(Bundle savedInstanceState);
onSaveInstanceState(Bundle outState);
onRestoreInstanceState(Bundle savedInstanceState);

你只重寫onSaveInstanceState()方法,往這個bundle中寫入數據,比如:

outState.putInt(“num”,1);

這樣,然后你在onCreate或者onRestoreInstanceState中就可以拿出里面存儲的數據,不過拿之前要判斷下是否為null哦!

savedInstanceState.getInt(“num”);

然后想干嘛就干嘛~

7. 系統給我們提供的常見的Activity

好的,最后給大家附上一些系統給我們提供的一些常見的Activtiy吧!

//1.撥打電話
// 給移動客服10086撥打電話
Uri uri = Uri.parse("tel:10086");
Intent intent = new Intent(Intent.ACTION_DIAL, uri);
startActivity(intent);

//2.發送短信
// 給10086發送內容為“Hello”的短信
Uri uri = Uri.parse("smsto:10086");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
intent.putExtra("sms_body", "Hello");
startActivity(intent);

//3.發送彩信(相當于發送帶附件的短信)
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra("sms_body", "Hello");
Uri uri = Uri.parse("content://media/external/images/media/23");
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.setType("image/png");
startActivity(intent);

//4.打開瀏覽器:
// 打開Google主頁
Uri uri = Uri.parse("http://www.baidu.com");
Intent intent  = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);

//5.發送電子郵件:(閹割了Google服務的沒戲!!!!)
// 給someone@domain.com發郵件
Uri uri = Uri.parse("mailto:someone@domain.com");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
startActivity(intent);
// 給someone@domain.com發郵件發送內容為“Hello”的郵件
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, "someone@domain.com");
intent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
intent.putExtra(Intent.EXTRA_TEXT, "Hello");
intent.setType("text/plain");
startActivity(intent);
// 給多人發郵件
Intent intent=new Intent(Intent.ACTION_SEND);
String[] tos = {"1@abc.com", "2@abc.com"}; // 收件人
String[] ccs = {"3@abc.com", "4@abc.com"}; // 抄送
String[] bccs = {"5@abc.com", "6@abc.com"}; // 密送
intent.putExtra(Intent.EXTRA_EMAIL, tos);
intent.putExtra(Intent.EXTRA_CC, ccs);
intent.putExtra(Intent.EXTRA_BCC, bccs);
intent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
intent.putExtra(Intent.EXTRA_TEXT, "Hello");
intent.setType("message/rfc822");
startActivity(intent);

//6.顯示地圖:
// 打開Google地圖中國北京位置(北緯39.9,東經116.3)
Uri uri = Uri.parse("geo:39.9,116.3");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);

//7.路徑規劃
// 路徑規劃:從北京某地(北緯39.9,東經116.3)到上海某地(北緯31.2,東經121.4)
Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=39.9 116.3&daddr=31.2 121.4");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);

//8.多媒體播放:
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("file:///sdcard/foo.mp3");
intent.setDataAndType(uri, "audio/mp3");
startActivity(intent);

//獲取SD卡下所有音頻文件,然后播放第一首=-= 
Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);

//9.打開攝像頭拍照:
// 打開拍照程序
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
startActivityForResult(intent, 0);
// 取出照片數據
Bundle extras = intent.getExtras(); 
Bitmap bitmap = (Bitmap) extras.get("data");

//另一種:
//調用系統相機應用程序,并存儲拍下來的照片
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
time = Calendar.getInstance().getTimeInMillis();
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment
.getExternalStorageDirectory().getAbsolutePath()+"/tucue", time + ".jpg")));
startActivityForResult(intent, ACTIVITY_GET_CAMERA_IMAGE);

//10.獲取并剪切圖片
// 獲取并剪切圖片
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.putExtra("crop", "true"); // 開啟剪切
intent.putExtra("aspectX", 1); // 剪切的寬高比為1:2
intent.putExtra("aspectY", 2);
intent.putExtra("outputX", 20); // 保存圖片的寬和高
intent.putExtra("outputY", 40); 
intent.putExtra("output", Uri.fromFile(new File("/mnt/sdcard/temp"))); // 保存路徑
intent.putExtra("outputFormat", "JPEG");// 返回格式
startActivityForResult(intent, 0);
// 剪切特定圖片
Intent intent = new Intent("com.android.camera.action.CROP"); 
intent.setClassName("com.android.camera", "com.android.camera.CropImage"); 
intent.setData(Uri.fromFile(new File("/mnt/sdcard/temp"))); 
intent.putExtra("outputX", 1); // 剪切的寬高比為1:2
intent.putExtra("outputY", 2);
intent.putExtra("aspectX", 20); // 保存圖片的寬和高
intent.putExtra("aspectY", 40);
intent.putExtra("scale", true);
intent.putExtra("noFaceDetection", true); 
intent.putExtra("output", Uri.parse("file:///mnt/sdcard/temp")); 
startActivityForResult(intent, 0);

//11.打開Google Market 
// 打開Google Market直接進入該程序的詳細頁面
Uri uri = Uri.parse("market://details?id=" + "com.demo.app");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);

//12.進入手機設置界面:
// 進入無線網絡設置界面(其它可以舉一反三)  
Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);  
startActivityForResult(intent, 0);



//13.安裝apk:
Uri installUri = Uri.fromParts("package", "xxx", null);   
returnIt = new Intent(Intent.ACTION_PACKAGE_ADDED, installUri);

//14.卸載apk:
Uri uri = Uri.fromParts("package", strPackageName, null);      
Intent it = new Intent(Intent.ACTION_DELETE, uri);      
startActivity(it); 

//15.發送附件:
Intent it = new Intent(Intent.ACTION_SEND);      
it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");      
it.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/eoe.mp3");      
sendIntent.setType("audio/mp3");      
startActivity(Intent.createChooser(it, "Choose Email Client"));

//16.進入聯系人頁面:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(People.CONTENT_URI);
startActivity(intent);


//17.查看指定聯系人:
Uri personUri = ContentUris.withAppendedId(People.CONTENT_URI, info.id);//info.id聯系人ID
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(personUri);
startActivity(intent);

附: Activity 生命周期回調方法匯總

Activity 生命周期回調方法匯總表
Activity 生命周期回調方法匯總.續
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容