《Android開發藝術探索》
一、Activity 的生命週期和啓動模式
1. 當前 Activity 爲 A,此時打開 Activity B:A.onPause() → B.onCreate() → B.onStart() → B.onResume() → A.onStop(),故不能在 onPause 中做重量級操作,使新 Activity 儘快顯示出來並切換到前臺。
2. 當系統內存不足時,系統會按照 [ 後臺 Activity → 可見但非前臺 Activity → 前臺 Activity ] 的優先級殺死目標 Activity 所在的進程。如果一個進程中沒有四大組件在執行,那麼這個進程將很快被系統殺死 → 故 將後臺工作放入 Service 中從而保證進程有一定的優先級,不易被輕易殺死。
(1) standard 標準模式(系統默認)—— 多實例實現
每次啓動一個 Activity 都會重新創建一個新的實例,不管這個實例是否已經存在,被創建的實例的生命週期符合典型情況下 Activity 的生命週期。
一個任務棧中可以有多個實例,每個實例也可以屬於不同的任務棧。
誰啓動了這個 Activity ,這個 Activity 就運行在啓動它的那個 Activity 所在的棧中。
當用 ApplicationContext 啓動 standard 模式的 Activity 時會報錯,因爲非 Activity 類型的 Context 並沒有所謂的任務棧,解決:爲待啓動的 Activity 指定 FLAG_ACTIVITY_NEW_TASK 標記位,這樣啓動的時候會爲它創建一個新的任務棧,此時待啓動 Activity 實際是以 singleTask 模式啓動的。
(2)singleTop 棧頂複用模式
若新 Activity 已經位於任務棧的棧頂,那麼此 Activity 不會被重新創建,同時它的 onNewIntent 方法會被回調,通過此方法的參數可以取出當前請求的信息。此 Activity 的 onCreate、onStart 不會被調用,因爲它並沒有發生改變。若不在棧頂,則會重新創建。
(3)singleTask 棧內複用模式 —— 一種單實例模式
只要 Activity 在一個棧中存在,那麼多次啓動此 Activity 都不會重新創建實例。也會回調 onNewIntent。
如 Activity A 爲 singleTask 模式啓動後,系統首先尋找是否存在 A 想要的任務棧,若不存在,則重新創建一個任務棧,然後創建 A 的實例放入棧中。若存在 A 所需的任務棧,若棧中有 A 的實例存在,系統就把 A 調到棧頂並調用 onNewIntent 方法,同時由於singleTask 默認具有 clearTop 效果,會導致棧內所有在 A 上面的 Activity 全部出棧;若實例不存在,則創建並壓入棧中。
(4)singleInstance 單實例模式 —— 加強的 singleTask
具有此種模式的 Activity 只能單獨地位於一個任務棧中。
4. Activity 所需的任務棧
參數 TaskAffinity 標識了 Activity 所需的任務棧的名字,其值爲字符串,且中間必須含有包名分隔符“.”。默認所有 Activity 所需的任務棧名字爲應用的包名。也可爲每個 Activity 單獨指定 TaskAffinity 屬性,但其值不能和包名相同。該屬性主要和 singleTask 啓動模式或 allowTaskReparenting 屬性配對使用。
任務棧分爲前臺任務棧和後臺任務棧,後臺任務棧中的 Activity 位於暫停狀態。
(1)TaskAffinity 和 singleTask 結合使用:TaskAffinity 是該 Activity 的目前任務棧的名字,待啓動的 Activity 會運行在名字和 TaskAffinity 相同的任務棧中。
(2)TaskAffinity 和 allowTaskReparenting 結合使用:當應用 A 啓動了應用 B 的某個 Activity 後,若這個 Activity 的 allowTaskReparenting 屬性爲 true,則當 B 被啓動後,此 Activity 會直接從 A 的任務棧轉移到 B 的任務棧中。
5. IntentFilter 匹配規則
(1)action
要求 Intent 中必須有一個 action,且必須能夠和過濾規則中的某個 action 相同
(2)category
Intent 中可以沒有 category,但一旦有 category,則不管有幾個必須每個都要能和過濾規則中任何一個 category 相同。
爲了 Activity 能夠接收隱式調用,必須在 intent-filter 中指定 “android.intent.category.DEFAULT”。
(3)data
語法:
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;"><data android:scheme="string" //URI 的模式,如 http、file、content。若未指定 scheme,則整個 URI 無效。默認爲 content 和 file。
android:host="string" //主機名,如 www.baidu.com。若未指定,則 URI 無效。
android:port="string" //端口號,僅當 URI 中指定了 scheme 和 host 時纔有效。
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string"/></span></span></span>
data 由 2 部分組成,mimeType (媒體類型)和 URI。
URI 結構:
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;"><scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]</span></span></span>
Intent 中必須含有 data,且 data 能完全匹配過濾規則中的某一個 data。
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">android:pathPattern="string"</span></span></span>
注:
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;"><action android:name="android.intent.action.MAIN/>
<category android:name="android.intent.category.LAUNCHER"/></span></span></span>
二、IPC 機制
1. IPC:Inter-Process Communication,進程間通信 / 跨進程通信。
2. 主線程也叫 UI 線程,在 UI 線程裏才能操作界面元素。
3. Android 中的多進程模式
在 Android 中使用多進程只有一種方法:給四大組件在 AndroidMenifest 指定
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">android:process //若未指定,默認進程名是包名</span></span></span>
屬性,即我們無法給一個線程或一個實體類指定其運行時所在的進程。
進程名以 “:” 開頭的進程屬於當前應用的私有進程,其他應用的組件不可以和它跑在同一個進程中;
進程名不以 ":" 開頭的屬於全局進程,其他應用通過 ShareUID 方式可以和它跑在同一個進程中。
Android 系統會爲每一個應用分配一個唯一的 UID,具有相同 UID 的應用才能共享數據。
只有 2 個應用有相同的 ShareUID 且簽名相同纔可以跑在同一個進程中。此時不管它們是否在同一個進程中都可以互相訪問對方的私有數據,如 data 目錄、組件信息等;若在同一個進程中,則還可以共享內存數據。
Android 爲每個進程都分配一個獨立的虛擬機(相當於把應用重啓了一遍),不同的虛擬機在內存分配上有不同的地址空間,導致在不同的虛擬機中訪問同一個類對象會產生多份副本。
所有運行在不同進程中的四大組件,只要它們之間需要通過內存來共享數據,都會共享失敗。
使用多進程有如下問題:
(1)靜態成員和單例模式完全失效。
(2)線程同步機制完全失效。
(3)SharedPreferences 的可靠性下降。(不支持 2 個進程同時寫)
(4)Application 會多次創建。
4. 序列化
(1)Serializable 接口
是 Java 提供的序列化接口,是一個空接口,爲對象提供標準的序列化和反序列化操作。使用簡單,但開銷大,需要大量I/O操作。
使用 Serializable 實現序列化只需這個類實現 Serializable 接口並聲明如下標識即可自動實現默認的序列化過程:
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">private static final long serialVersionUID = 87254421243431L;</span></span></span>
若不聲明會對反序列化產生影響。
注:靜態成員變量屬於類不屬於對象,故不會參與序列化過程;用 transient 關鍵字標記的成員變量不參與序列化過程。
(2)Parcelable 接口
Android 提供的序列化接口。使用稍麻煩,但效率高。
Android 序列化首選 Parcelable,主要用在內存序列化上;將對象序列化到存儲設備或通過網絡傳輸應使用 Serializable。
5. Android 中的 IPC 方式
(1)Bundle
傳輸的數據必須能被序列化,如 基本類型、實現了 Parcelable 或 Serializable 接口的對象以及一些 Android 支持的特殊對象。
(2)文件共享
對文件格式沒有要求,只要讀 / 寫雙方約定數據格式即可。
注:SharedPreferences 是 Android 中提供的輕量級存儲方案,也是文件的一種,但系統對它的讀 / 寫有一定的緩存策略,即在內存中會有一份 SharedPreferences 文件的緩存,在多進程模式下,系統對它的讀 / 寫就變得不靠譜,當面對高併發的讀 / 寫訪問時會有很大機率丟失數據。故不建議在進程間通信中使用 SharedPreferences。
(3)Messenger
一種輕量級的 IPC 方案,底層實現是 AIDL。
一次處理一個請求,故在服務端不用考慮線程同步的問題,因爲服務端中不存在併發執行的問題。
串行方式,不適合大量併發請求。
(4)AIDL
(5)ContentProvider
Android 提供的專門用於不同應用間進行數據共享的方式。底層實現是 Binder。
主要以表格的形式組織數據,還支持文件數據。
需要註冊,
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">android:authorities //ContentProvider 的唯一標識。</span></span></span>
(6)Socket 套接字
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;"><uses-permission android:name="android.permission.INTERNER"/>
<uses-permission android:name="android.permission.ACCESS_NEWWORD_STATE"/></span></span></span>
不能在主線程中訪問網絡名稱 | 優點 | 缺點 | 適用場景 |
Bundle | 簡單易用 | 只能傳輸 Bundle 支持的數據 | 四大組件間的進程間通信 |
文件共享 | 簡單易用 | 不適合高併發場景,無法做到進程間的即時通信 | 無併發訪問情形,交換簡單的數據實時性不高的場景 |
AIDL | 功能強大,支持一對多併發通信,支持實時通信 | 使用稍複雜,需要處理好線程同步 | 一對多通信且有 RPC 需求 |
Messenger | 功能一般,支持一對多串行通信,支持實時通信 | 不能很好處理高併發情形,不支持 RPC,數據通過 Message 進行傳輸,只能傳輸 Bundle 支持的數據類型 | 低併發的一對多即時通信,無 RPC 需求,或者無須返回結果的 RPC 需求 |
ContentProvider | 在數據源訪問方面功能強大,支持一對多併發數據共享,可通過 Call 方法擴展其他操作 | 可理解爲受約束的 AIDL,主要提供數據源的 CRUD 操作 | 一對多的進程間的數據共享 |
Socket | 功能強大,可以通過網絡傳輸字節流,支持一對多併發實時通信 | 實現細節稍有繁瑣,不支持直接的 RPC | 網絡數據交換 |
三、View 的事件體系
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">x = left + tanslationX;
y = top + translationY;</span></span></span>
- ACTION_DOWN
- ACTON_MOVE
- ACTION_UP
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">getX / getY:相對於當前 View 左上角的 x 和 y 座標;
getRawX / getRawY:相對於手機屏幕左上角的 x 和 y;</span></span></span>
② TouchSlop
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">ViewConfiguration.get(getContext()).getScaledTouchSlop();</span></span></span>
(3)
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);</span></span></span>
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">velocityTracker.computeCurrentVelocity(1000);//計算速度:在一段時間內手指所劃過的像素數,如 1000ms
int xVelocity = (int) velocityTracker.getXVelocity();
int yVelocity = (int) velocityTracker.getYVelocity();</span></span></span>
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">velocityTracker.clear();
velocityTracker.recycle();</span></span></span>
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">GestureDetector mGestureDetector = new GestureDetector(this);
//解決長按屏幕後無法拖動的現象
mGestureDetector.setIsLongpressEnabled(false);</span></span></span>
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">boolean consume = mGestureDetector.onTouchEvent(event);
return consume;</span></span></span>
完成上述 2 步後就可以有選擇地實現 OnGestureListener 和 OnDoubleTapListener 中的方法了。
2. View 的滑動
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">mScrollX = View 左邊緣 - View 內容左邊緣;
mScrollY = View 上邊緣 - View 內容上邊緣;</span></span></span>
即從右向左滑動 mScrollX 爲正,從下往上滑動 mScrollY 爲正。View 動畫(xml)是對 View 的影像做操作,它並不能真正改變 View 的位置參數,包括寬 / 高,故會影響 View 的單擊等交互事件。若希望動畫後的位置狀態得以保留還必須將 fillAfter 屬性設置爲 true。
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">MarginLayoutParams params = (MarginLayoutParams) btn.getLayoutParams();
params.width += 100;
params.leftMargin += 100;
btn.requestLayout();
//或 btn.setLayoutParams(params);</span></span></span>
②
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}</span></span></span>
點擊事件優先級:
(10)onClick 會發生的前提是當前 View 是可點擊的,且收到了 down 和 up 事件。
<span style="font-size:14px;"><span style="font-size:14px;">public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercepted = false;
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
intercepted = false; //父容器必須返回 false,即不攔截 ACTION_DOWN 事件,否則後續的 MOVE 和 UP 都會直接由父容器處理,而不會傳遞給子元素了。
break;
case MotionEvent.ACTION_MOVE:
if (父容器需要當前點擊事件) {
intercepted = true;
} else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP:
intercepted = false; //UP 事件沒多大意義,必須返回 false。
break;
default:
break;
}
mLastXIntercept = x;
mLastYIntercept = y;
return intercepted;
}</span></span>
(2)內部攔截法
<span style="font-size:14px;"><span style="font-size:14px;">public boolean dispatchTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
parent.requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastX;
int deltaY = y - mLastY;
if (父容器需要當前點擊事件) {
parent.requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
mLastX = x;
mLastY = y;
return super.dispatchTouchEvent(event);
}</span></span>
<span style="font-size:14px;"><span style="font-size:14px;">public boolean onInterceptTouchEvent(MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
return false;
} else {
return true;
}
}</span></span>
四、View 的工作原理
- measure:測量 View 的寬和高,Measure 完成以後,可以通過 getMeasuredWidth 和 getMeasuredHeight 獲取 View 測量後的寬 / 高,在幾乎所有的情況下它都等同於 View 的最終的寬 / 高。特殊情況除外。
- layout:確定 View 在父容器中的放置位置,即 View 的四個頂點的座標和實際的 View 寬 / 高,完成後可以通過 getTop、getBottom、getLeft、getRight獲取四個頂點的位置,getWidth 和 getHeight 獲取最終寬 / 高。
- draw:負責將 View 繪製在屏幕上,只有 draw 完成後 View 的內容才能顯示在屏幕上。
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">ViewGroup content = (ViewGroup) findViewById(android.id.content); //獲取 content
content.getChildAt(0); //獲取我們設置的 View</span></span></span>
2. MeasureSpec
- UNSPECIFIED
- EXACTLY
- AT_MOST
- LayoutParams.MATCH_PARENT:精確模式,大小就是窗口的大小。
- LayoutParams.WRAP_CONTENT:最大模式,大小不定,但不能超過窗口的大小。
- 固定大小:精確模式,大小爲 LayoutParams 中指定的大小。
關於 getSuggestedMinimumWidth() 方法:若 View 沒有設置背景,那麼返回 android:minWidth 的值(可以爲 0);若 View 設置了背景,則返回 android:minWidth 和 背景的最小寬度 兩者中的最大值。getSuggestedMinimumWidth 的返回值就是 View 在 UNSPECIFIED 情況下的測量寬。
<span style="font-size:14px;"><span style="font-size:14px;">/**
* 只需給 View 指定一個默認的內部 寬/高(mWidth 和 mHeight),並在 wrap_content 時設置此寬/高即可。
* 對於非 wrap_content 情形,沿用系統的測量值即可。
*/
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mWidth, mHeight);
} else if (widthSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mWidth, heightSpecSize);
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSpecSize, mHeight);
}
}</span></span>
<span style="font-size:14px;">public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
}
}</span>
②view.post(runnable)。
<span style="font-size:14px;">protected void onStart() {
super.onStart();
view.post(new Runnable() {
@override
public void run() {
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
}
});
}</span>
<span style="font-size:14px;">protected void onStart() {
super.onStart();
ViewTreeObserver observer = view.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
}
});
}</span>
(3)draw 過程
- 繼承 View 重寫 onDraw 方法
- 繼承 ViewGroup 派生特殊的 Layout
- 繼承特定的 View (如 TextView)
- 繼承特定的 ViewGroup(如 LinearLayout)
②若有必要,讓 View 支持 wrap_content
《Android 應用程序開發權威指南》
一、基礎
-
管理器 功能 LocationManager 和設備上的基於位置的服務進行交互 ViewManager,WindowManager 負責顯示界面以及設備相關的用戶界面的基礎 AccessibilityManager 負責輔助事件,提供對物理損傷的用戶的設備支持 ClipboardManager 提供了訪問設備全局剪貼板的能力,可以剪切和複製內容 DownloadManager 作爲系統服務,負責 HTTP 的後臺下載 FragmentManager 管理一個 Activity 的 Fragment AudioManager 提供了音頻和振鈴控制的訪問 -
文件 功能 AndroidManifest.xml 應用的核心配置文件。定義了應用程序的功能和權限,以及如何運行。 ic_launcher-web.png 一張 32 位的 512*512 大小的高分辨率圖標,用來在 Google Play 商店中顯示。該圖大小不能超過 1024KB。 proguard-project.txt Android IDE 和 ProGuard 使用的編譯文件。可以通過編輯該文件來配置代碼優化選項,以及發佈版本的混淆設置。 project.properties Android IDE 中使用的編譯文件。定義了應用程序的構建目標,以及其他編譯系統選項。不要編輯這個文件 /src 必須的文件夾,包含所有的源代碼 /gen 必須的文件夾,包含所有的自動生成的文件。 /gen/.../BuildConfig.java 調試應用程序時,該源文件自動生成。不要編輯 /gen/.../R.java 自動生成的資源管理的源文件。不要編輯 /assets 必須的文件夾。包含了項目中未編譯的資源文件,一些你不想作爲應用程序資源管理的應用程序數據(文件、目錄) -
術語 描述 Context(上下文) 是 Android 應用的中央指揮中心。大部分應用特定的功能可以通過上下文訪問或引用。Context 類是任何 Android 應用的基本構建模塊,提供了訪問應用程序範圍的功能,譬如應用程序的私有文件、設備資源,以及整個系統的服務。應用程序的 Context 對象會被實例化爲一個 Application 對象。 Activity(活動) 一個 Activity 類是 Context 類的子類,因此它也擁有 Context 類的所有功能。 Fragment(碎片) 一個活動有一個獨特的任務或目的,但它可以進一步組件化,每一個組件被稱爲碎片。Fragment 類往往被用來組織活動的功能,從而允許在不同的屏幕大小、方向和縱橫比上提供更靈活的用戶體驗。碎片常常用來在由多個 Activity 類組成的不同的屏幕上,使用相同的代碼和屏幕邏輯放置相同的用戶界面。 Intent(意圖) Android 操作系統使用異步的消息傳遞機制,將任務匹配到合適的 Activity。每一個請求被打包成一個意圖。使用 Intent 類是應用程序組件如活動和服務之間通信的主要方法。 Service(服務) 不需要用戶交互的任務可以封裝成一個服務。當需要處理耗時任務或需要定時處理時使用服務,用來處理後臺操作。繼承自 Context 類。 -
應用程序 Context
因爲 Activity 類是由 Context 類派生的,故有時可以使用它而不是顯示地獲取應用程序 Context。但不要在任何情況下都使用 Activity Context,因爲可能會導致內存泄漏。- 獲取應用程序資源,如字符串、圖形、xml 文件。
getResources()
- 訪問應用程序首選項
getSharedPreferences()
- 管理私有的應用程序文件和目錄
- 獲取未編譯的應用程序資產
getAssets()
- 訪問系統服務
- 管理自由的應用程序數據庫(SQLite)
- 以應用程序權限工作
- 獲取應用程序資源,如字符串、圖形、xml 文件。
-
Activity 生命週期
onCreate()
初始化靜態 Activity 數據onResume()
初始化及取回 Activity 數據-
onPause()
停止、保存和釋放 Activity 數據,保存重要數據到用久存儲,使用 onSaveInstanceState() 保存一些可以從當前屏幕快速恢復的數據或某些不重要的信息(如 未提交的表單數據或任何其他減少用戶麻煩的狀態信息)。停止任何聲音、視頻、動畫,也必須停用資源如數據庫遊標對象 或 其他 Activity 終止時應該清理的對象。onPause() 可能是 Activity 進入後臺時最後用來清理或釋放不需要的資源的機會,需要在這裏保存任何未提交的數據。
調用 onPause() 後,系統保留殺死任何一個 Activity 而沒有進一步通知的權利。
Activity 需要在 onPause() 方法中執行快速的代碼,因爲只有 onPause() 方法返回後,新的前臺 Activity 纔會啓動。
一般來說,任何在 onResume() 方法中獲取的資源和數據應該在 onPause() 方法中釋放,否則當進程終止後,這些資源可能無法乾淨地釋放。避免 Activity 被殺死
內存不足時 Android 操作系統可以殺死任何暫停、停止或銷燬的 Activity。這基本意味着任何沒有在前臺的 Activity 都會面臨被關閉的可能。
若 Activity 在 onPause() 後被殺掉,那麼 onStop() 和 onDestroy() 方法將不會被調用。在 onPause() 方法內更多的釋放 Activity 的資源,那麼 Activity 就越不太可能在後臺被直接殺掉(沒有其他的狀態切換方法被調用)。
殺死一個 Activity 並不會導致它從 Activity 堆棧中的移除。若 Activity 實現並使用了 onSaveInstanceState() 用於自定義數據,Activity 狀態將被保存到 Bundle 對象中(雖然一些 View 數據將會被自動保存)。當用戶返回到 Activity 後,onCreate() 方法會被再次調用,這次會有一個有效的 Bundle 對象作爲方法參數。 -
onDestroy()
銷燬靜態 Activity 數據
當 Activity 通過正常的操作被銷燬,onDestroy() 方法將會被調用。
onDestroy() 會在 2 種情況下被調用:Activity 自己完成了它的生命週期,或因爲資源問題,Activity 被 Android 操作系統殺掉,但仍有足夠的時間從容銷燬 Activity(與不調用 onDestroy() 方法直接終止 Activity 不同)。若 Activity 是被 Android 操作系統殺掉的,isFinishing() 方法會返回 false。該方法在 onPause() 中十分有用,可以知道 Activity 是否能夠恢復。
-
AndroidManifest.xml 清單文件
作用:- Android 操作系統使用清單文件來安裝、更新和運行應用程序包
- 顯示應用程序的詳細信息,如名稱、描述、圖標
- 指定應用的系統需求,包括對 Android SDK 的支持、設備配置的需求(如方向鍵),以及應用依賴的平臺功能(如多點觸摸)
- 以市場過濾爲目的,指定應用的哪些功能是必須的
- 註冊應用的 Activity,並指定如何啓動
- 管理應用程序的權限
- 配置其他高級的應用組件配置詳細信息,包括定義 Service、Broadcast Receiver 及 Content Provider
- 爲你的 Activity、Service 及 Broadcast Receiver 指定 Intent 過濾器
- 爲應用測試開啓應用設置,如調試和配置儀器
設置應用程序的系統需求:
-
<uses-feature>
標籤用於指定應用需要哪些 Android 功能才能正常運行。這些設置只供參考不會強制使用。當使用<uses-feature>
標籤時,可以指定android:required
的可選屬性,並設置爲 true 或 false,這個可以用於配置 Google Play 商店中的過濾。若該值爲 true,Google Play 將只會在具有特定硬件或軟件功能的設備上顯示你的應用程序(如攝像頭)。若應用程序需要多個功能,則必須爲每個功能創建一個<uses-feature>
標籤。若應用正常運行時並不需要一個特定的功能,與其在應用商店內過濾並限制特定的設備,還可以使用
getPackageManager().hasSystemFeature()
在運行時檢查特定的設備功能,並在用戶的設備上支持該功能時才允許特定的功能,這樣可以最大化安裝和使用你應用的人羣。 <uses-sdk>
標籤用於指定應用程序支持的 Android 平臺版本。應用商店會根據應用的清單文件的<uses-sdk>
標籤等的設置來爲給定的用戶過濾應用。忽略使用該標籤將會在編譯環境中產生一個警告信息。<uses-configuration>
標籤用於指定應用程序支持的硬件或軟件的輸入方法。有 5 個方向的配置屬性:硬件鍵盤和鍵盤類型;方向設備如方向鍵、軌跡球和滾輪;觸摸屏的設置。若應用程序支持多種輸入配置,則必須有多個<uses-configuration>
標籤。<supports-screens>
標籤用於指定應用支持的 Android 屏幕類型。- <application> 內的
<uses-library>
標籤用於註冊應用中鏈接到的外部庫。 <supports-gl-texture>
用於指定應用支持的 GL 材質的壓縮格式。使用圖形庫的應用使用該標籤,並用於兼容可以支持指定壓縮格式的設備。<application>
標籤屬性中設置應用程序範圍的主題。<instrumentation>
設置單元測試功能。<activity-alias>
爲 Activity 起別名。<receiver>
註冊 Broadcast Receivers。<provider>
註冊 Content Provider,使用<grant-uri-permission>
和<path-permission>
管理 Content Provider 的權限。<meta-data>
包含應用的 Activity、Service、Receiver 組件註冊的其他數據。
-
管理資源
-
所有 Android 應用程序由 2 部分組成:
功能部分——代碼指令(程序運行的任何算法)
數據部分——資源(文本字符串、樣式主題、尺寸、圖片圖標、音視頻文件等) -
默認 Android 資源目錄,所有資源必須存放在項目的 /res 目錄下的指定子目錄,且目錄名必須小寫。
資源子目錄 內容 /res/drawable-*/ 圖形資源 /res/layout/ 用戶界面資源 /res/menu/ 菜單資源,用於顯示 Activity 中的選項或操作 /res/values/ 簡單的數據,如字符串、樣式主題、尺寸資源 /res/values-sw*/ 覆蓋默認的尺寸資源 /res/values-v*/ 較新 API 自定義的樣式和主題資源 -
常見的資源類型及存儲結構
資源類型 所需目錄 建議文件名 XML 標籤 字符串 /values/ strings.xml <string>
字符串複數形式 /values/ strings.xml <plurals>, <item>
字符串數組 /values/ strings.xml 或 arrays.xml <string-array>, <item>
布爾類型 /values/ bools.xml <bool>
顏色 /values/ colors.xml <color>
顏色狀態列表 /color/ 包括 buttonstates.xml, indicators.xml <selector>, <item>
尺寸 /values/ dimens.xml <dimen>
ID /values/ ids.xml <item>
整型 /values/ integers.xml <integer>
整型數組 /values/ integers.xml <integer-array>
混合類型數組 /values/ arrays.xml <array>, <item>
簡單可繪製圖形(可打印) /values/ drawables.xml <drawable>
XML 文件定義的圖形(如形狀) /drawable/ 包括 icon.png, logo.jpg 支持的圖形文件或可繪製圖形 補間動畫 /anim/ 包括 fadesequence.xml, spinsequence.xml <set>, <alpha>, <scale>,<translate>, <rotate>
屬性動畫 /animator/ mypropanims.xml <set>, <objectAnimator>, <valueAnimator>
幀動畫 /drawable/ 包括 sequence1.xml, sequence2.xml <animation-list>, <item>
菜單 /menu/ 包括 mainmenu.xml, helpmenu.xml <menu>
XML 文件 /xml/ 包括 data.xml, data2.xml 開發者定義 原始文件 /raw/ 包括 jingle.mp3, video.mp4, text.txt 開發者定義 佈局 /layout/ 包括 main.xml, help.xml 多樣,但必須是佈局類型 樣式和主題 /values/ styles.xml, themes.xml <style>
-
使用字符串資源
- 所有包含撇號及單引號的字符串需要被轉義 "\" 或被雙引號包裹
- 可使用 HTML 樣式的屬性(粗體<b>、斜體<i>、下劃線<u>標籤)
- 忽略格式:
String s = getResources().getString(R.string.hello);
- 保留字符串格式: ①
CharSequence cs = getResourece().getText(R.string.hello);
②使用 TextUtils 的 htmlEncode()
-
-
TexdView 文本中創建上下文鏈接:autoLink 屬性
值 描述 note 禁用所有鏈接 web 允許 web 網頁的 URL 鏈接 email 允許電子郵件地址鏈接,並在郵件客戶端填寫收件人 phone 允許電話號碼鏈接,可以在撥號器應用中填寫電話號碼來撥打 map 允許街道地址的鏈接,可以在地圖應用中顯示位置 all 允許所有類型的鏈接 開啓 autoLink 功能依賴於 Android SDK 中的各種類型的檢測。有時候也可能鏈接不正確或產生誤導。
-
EditText
-
輸入過濾器限制輸入
使用setFilters()
來設置 InputFilter 限制用戶輸入,InputFilter 接口包含一個filter()
方法。可以實現 InputFilter 接口來創建自定義的過濾器。setFilters()
的參數是 InputFilter 對象的數組,這對於組合多個過濾器是非常有用的。 -
自動完成
AutoCompleteTextView
:基於用戶輸入的內容來填寫整個文本。- 允許用戶輸入字符串列表,每一個都具有自動完成功能。這些字符串都要以某種方式分隔,提供給
MultiAutoCompleteTextView
對象的Tokenizer
來處理。這對於指定通用標籤等的列表有所幫助。也可以自己實現MultiAutoCompleteTextView.Tokenizer
接口,內置逗號分隔符CommaTokenizer()
。
-
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;"> <span> </span> android:pathPattern="string"</span></span></span>