<application>節點是AndroidManifest.xml文件中必須持有的一個節點,它包含在<manifest>節點下。通過<application>節點的相關屬性,我們可以聲明Android應用程序的相關特性。這個節點包含所有應用程序組件的節點,包括Activity,服務,廣播接收器和內容提供者,並且包含了一些可能影響所有組件的屬性。這些屬性中的其中一些又會作為默認值而被設置到應用程序組件的相同屬性上,比如icon,label,permission,process,taskAffinity和allowTaskReparenting等,而其他的一些值則作為應用程序的整體被設置,並且不能被應用程序組件的屬性覆蓋,比如debuggable,enabled,description和allowClearUserData等。
1.<application>節點配置
一般來說,在生成Android應用程序的時候,默認的AndroidManifest.xml文件中就已經包含了一些默認的<application>節點,其中包含應用程序的基本屬性。現在我們就來看看<application>節點信息的全集,代碼如下:
<application android:allowTaskReparenting=["true"|"false"]
android:backupAgent="string"
android:debuggable=["true"|"false"];
android:description="string resource"
android:enabled=["true"|"false"]
android:hasCode=["true"|"false"]
android:hardwareAccelerated=["true"|"false"]
android:icon="drawable reource"
android:killAfterRestore=["true"|"false"]
android:label="string resource"
android:logo="drawable resource"
android:manageSpaceActivity="string"
android:name="string"
android:permission="string"
android:persistent=["true"|"false"]
android:process="string"
android:restoreAnyVersion=["true"|"false"]
android:taskAffinity="string"
android:theme="resource or theme">
</application>
2.如何實現Application類
首先要介紹的是android:name屬性,它指的是Application類的子類,當應用程序進程被啟動的時候,由android:name屬性指定的類將會在所有應用程序組件(activity,服務,廣播接收器,內容提供者)被實例化之前實例化。
一般情況下,應用程序無需指定這個屬性,Android會實例化Android框架下的applicaiton類。
然而,在一些特殊的情況下,比如希望在應用程序組件啟動之前就完成一個初始化工作,或者在系統低內存的時候做一些特別的處理,就要考慮實現自己的Application類的一個子類。
在Android系統提供的系統應用中,就有一個實現了自己的Application實例,這個應用程序就是Launcher。我們可以仿照它來實現一個自己的Application類,具體步驟如下。
①創建一個叫做ApplicationTest的項目,並且在默認生成的MainActivity里的onCreate()方法中添加一行代碼來輸出一條日志。這樣就可以看到Application創建時間,具體代碼如下:
public class MainActivity extends Activity { private static final String TAG="MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.e(TAG,"MainActivity is created"); } }
②實現自己的MyApplication類,代碼如下:
public class MyApplication extends Application { private static final String TAG="MyApplication"; @Override public void onCreate() { super.onCreate(); Log.e(TAG,"MyApplication is created"); } }
③將MyApplication添加到清單文件AndroidManifest.xml中<application>內android:name中
從圖中可以看出來,Android先創建的MyApplication,最后才創建的MainActivity。
3.Application提供的函數及其用法
android.app.Application類提供了許多類似onCreate()的方法,它們會在不同的場景下被Android框架回調。與此同時,Application類還提供了一些監控的函數,用於監視本應用中組件的生命周期。如下表所示:
方法名稱 | 返回值 | 注解 |
onConfigurationChanged(Configuration newConfig) | void | 如果組件正在運行時設備配置(包括語種,方向,網絡等)發生改變,則由系統調用此方法通知應用程序 |
onCreate() | void | 當應用程序正在啟動時,並且在創建任何其他應用程序對象之前,調用此方法。由於花費在此功能上的時間直接影響了啟動一個進程中首個Activity服務或者接收器的速度,所以盡可能快地執行(例如使用緩慢的初始化狀態)。如果你重寫了這個方法,需要確保調用super.onCreated() 需要注意的是,在實際應用程序中,如果你的應用程序中的某些組件指定了一個process屬性(進程),並且此進程並不存在,那么Application的onCreate()回調就會被調用,換句話說,此方法可能會被多次調用 |
onLowMemory() | void | 當整個系統正在低內存運行時,並且希望應用程序縮減使用內存的時候,系統調用此方法通知應用程序。但調用此方法的准確點沒有定義時,通常它將在所有后台進程已經終止的時間附近發生。 應用程序可執行此方法來釋放任何緩沖或其擁有的不必要的資源。系統在從此方法中返回后運行垃圾回收操作。 |
onTerminate() | void | 此方法在仿真進程環境中使用,不在生產Android設備上調用,在生產Android設備上,可以通過簡單地終止進程來移除進程。進行移除工作時,則不執行任何用戶代碼(包括此回調) |
onTrimMemory() | void | 回收內存的時候調用。例如,當它進入后台並且沒有足夠內存保持許多后台進程運行時。 |
監控回調接口 registerComponentCallbacks unregisterComponentCallbacks |
void void |
在應用程序中注冊一個ComponentCallbacks接口。在Activity生命周期發生改變之前,通過此接口的各個方法通知應用程序。使用這個接口,我們可以在Activity生命周期發生改變之前做一些必要的處理 需要大家注意的是,必須確保在未來恰當的時候使用unregisterComponentCallbacks(ComponentCallbacks)移除ComponentCallbacks對象,它是我們之前用registerComponentCallbacks(ComponentCallbacks)注冊的。 |
接下來,我們通過一些實例來說明如何使用這些方法和接口
①使用onConfigurationChanged()方法監聽系統配置更新
onConfigurationChanged()方法的函數原型如下:
public void onConfigurationChanged(Configuration newConfig){}其中newConfig參數表示新的設備配置
onConfigurationChanged()方法是一個回調接口,在設備配置發生變化時由Android系統調用。與此同時,Android系統會通過參數(newConfig)傳給應用程序,由應用程序處理這個變化。注意,不同於Activity,其他組件在一個配置改變時從不重新啟動,它們孫弱自己處理改變的結果。這里所述的“配置”如下表所示:
配置項 | 注解 |
fontScale | 表示當前的系統的字體縮放比例,它是基於像素密度縮放的。 注意,在使用用戶模式編譯出來的系統固件中,不包含修改此項配置的界面,只能通過編程的方法去改變。 數據類型:浮點型 |
hardKeyBoardHidden | 指示硬鍵盤是否被隱藏起來,此配置項有3個取值,具體如下所示。 0.HARDKEYBOARDHIDDEN_UNDEFINED(Android無法識別的鍵盤狀態) 1.HARDKEYBOARDHIDDEN_NO(硬鍵盤可用) 2.HARDKEYBOARDHIDDEN_YES(硬鍵盤被隱藏) 數據類型:整型 |
keyboard | 指示添加到設備上的是哪個種類的鍵盤,此配置項有以下4個取值 0.KEYBOARD_UNDEFINED(Android無法識別的鍵盤) 1.KEYBOARD_NOKEYS(無按鍵鍵盤) 2.KEYBOARD_QWERTY(打字機鍵盤) 3.KEYBOARD_12KEY(12鍵鍵盤) 數據類型:整型 |
keyboardHidden | 指示當前是否有鍵盤可用。如果在有硬鍵盤的Android設備中,硬鍵盤被收起,而仍有軟鍵盤,則認為鍵盤是可用的。這個字段有如下3個取值。 0.KEYBOARDHIDDEN_UNDEFINED(Android無法識別的鍵盤狀態) 1.KEYBOARDHIDDEN_NO(仍有軟鍵盤可見) 2.KEYBOARDHIDDEN_YES(所有的軟鍵盤都被隱藏)。 數據類型:整型 |
locale | 定義了設備的語言環境。它包含了國家以及語言信息,這些信息被包含在一個java.util.Locale類型的對象中 |
mcc | IMSI的移動國家碼,如果是0,表示未定義。 注意:IMSI是指國際移動用戶識別碼,它存儲在我們的SIM卡中,其總長度不超過15位。 數據類型:整型 |
mnc | IMSI的移動網絡號,如果是0表示未定義 數據類型:整型 |
navigation | 指示當前設備可用的導航方式,它有如下5個取值。 0.NAVIGATION_UNDEFINED(未定義的導航方式) 1.NAVIGATION_NONAV(無導航) 2.NAVIGATION_DPAD(面板導航方式) 3.NAVIGATION_TRACKBALL(軌跡球導航) 4.NAVIGATION_WHEEL(滾輪方式導航) 數據類型:整型 |
navigationHidden | 用於指示導航是否可用,有如下取值。 0.NAVIGATIONHIDDEN_UNDEFINED 1.NAVIGATIONHIDDEN_NO 2.NAVIGATIONHIDDEN_YES 數據類型:整型 |
orientation | 指示屏幕方向的標志,有如下4個取值。 0.ORIENTATION_UNDEFINED(未定義的方法) 1.ORIENTATION_PORTRAIT(豎屏方向,屏幕寬度小於高度) 2.ORIENTATION_LANDSCAPE(橫屏方向,屏幕寬度大於高度) 3.ORIENTATION_SQUARE(正方形屏幕,認為屏幕寬度等於高度) 注意:在窗口管理服務(WindowManagerService)中計算新配置時,orientation的默認配置是ORIENTATION_SQUARE 數據類型:整型 |
screenHeightDp | 屏幕可用部分的高度 |
screenLayout | 指示屏幕的整體屬性,它包括兩個部分。 ⒈SCREENAYOUT_SIZE_MASK:標志屏幕大小的屬性(比如大屏幕,小屏幕等),它有以下5個取值。 SCREENAYOUT_SIZE_UNDEFINED:未定義(值:0) SCREENAYOUT_SIZE_SMALL:小屏幕(值:1,屏幕分辨率至少為320*426)。 SCREENAYOUT_SIZE_NORMAL:普通屏幕(值:2,屏幕分辨率至少為320*470) SCREENAYOUT_SIZE_LARGE:大屏幕(值:3,屏幕分辨率至少為480*640) SCREENAYOUT_SIZE_XLARGE:加大屏幕(值:4,屏幕分辨率至少為720*960) ⒉SCREENAYOUT_LONG_MASK:指示屏幕是否比通常情況上更高或者更寬,它有如下3個取值。 SCREENAYOUT_LONG_UNDEFINED:未定義(十六進制值為0) SCREENAYOUT_LONG_YES:是(十六進制值為20) SCREENAYOUT_LONG_NO:否(十六進制值為10) |
screenWidthDp | 屏幕可用部分的寬度 |
smallestScreenWidthDp | 在正常操作中,應用程序將會看到最小的屏幕尺寸。這是在豎屏和橫屏中screenWidthDp和ScreenHeightDp的最小值。 |
touchscreen | 設備上觸摸屏的種類,它支持如下取值。 0.TOUCHSCREEN_UNDEFINED(未定義模式) 1.TOUCHSCREEN_NOTOUCH(無觸屏模式) 2.TOUCHSCREEN_STYLUS(手寫筆模式) 3.TOUCHSCREEN_FINGER(手指觸屏模式) |
uiMode | UI模式的位掩碼,目前有兩個字段。 ⒈UI_MODE_TYPE_MASK:定義了設備的整個UI模式,它支持如下取值。 UI_MODE_TYPE_UNDEFINED:未知模式 UI_MODE_TYPE_NORMAL:通常模式 UI_MODE_TYPE_DESK:帶底座模式‘ UI_MODE_TYPE_CAR:車載模式 ⒉UI_MODE_NIGHT_MASK:定義了屏幕是否在一個特殊模式中。它支持如下取值。 UI_MODE_NIGHT_UNDEFINED:未定義模式 UI_MODE_NIGHT_NO:非夜晚模式 UI_MODE_NIGHT_YES:夜晚模式 |
下面我們通過一個實例來說明當設備配置發生變化的時候,系統如何通過onConfigurationChanged回調接口來通知應用程序的。
㈠為前面的應用程序添加一個名叫ConfigApplication的Application的子類,並實現onCreate()方法及onConfigurationChanged()方法。在onCreate()方法中,我們會獲取應用程序在創建之初所擁有的配置信息。而在onConfigurationChanged()方法中,則可以添加一些代碼以便用日志的方式來實時體現配置更新。相關代碼如下:
public class ConfigApplication extends Application { private static final String TAG="ConfigApplication"; private Configuration mConfiguration; @Override public void onCreate() { super.onCreate(); this.mConfiguration=getResources().getConfiguration();//獲取配置信息 Log.e(TAG,"onCreate::infomation:orientation="+this.mConfiguration.orientation); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); //打印更新后的配置信息 Log.e(TAG,"onConfigurationChanged:infomation:orientation="+newConfig.orientation); } }
㈡按前文所述,將ConfigApplication配置到AndroidManifest.xml文件中。
㈢設備運行應用程序,就可以看到如下的日志信息了。
對於日志,說明如下:
日志信息的第一行是初始狀態下的方向配置,通過上圖我們知道最初的方向值是1。而根據前面的表,可知當前是豎屏方向。
日志信息的第五行是切換橫屏后,Android系統回調了我們實現的onConfigurationChanged()方法,這時系統配置已經發生了改變,因此這里的日志打印了當前的屏幕方向是2,也是就橫屏。
建議:由於基類onConfigurationChanged()方法中實現了對一些回調接口的調用,所以如果我們重寫了這個方法,那么為了維持原Application類的行為,建議在重寫的方法入口調用super.onConfigurationChanged(newConfig)。
②使用onCreate()完成應用程序初始化
onCreate()方法的原型為:
public void onCreate(){}
如前面的表所示,onCreate()方法是一個回調接口。Android系統會在應用程序啟動的時候,在任何應用程序組件(Activity,服務,廣播接收器,內容提供者)被創建之前調用這個接口。
需要注意的是,這個方法的執行效率會直接影響到啟動Activity,服務,廣播接收器,或者內容提供者的性能,因此該方法應盡可能快地完成。
最后,如果實現了這個回調接口,請前晚不要忘記調用super.onCreate(),否則應用程序會報錯。
前面我們實現了Appplication類的子類------Configuration,並且也已經實現了自身的onCreate()方法。這里來做個小實驗,讓大家更清楚這些知識。
現在,在源代碼的onCreate()方法中加入一個大約20秒的等待,以此來模擬在onCreate()方法中做了過於繁重的工作而導致該方法長時間無法完成的情況,修改后的代碼如下:
public class ConfigApplication extends Application { private static final String TAG="ConfigApplication"; private Configuration mConfiguration; @Override public void onCreate() { super.onCreate(); this.mConfiguration=getResources().getConfiguration();//獲取配置信息 Log.e(TAG,"onCreate::infomation:orientation="+this.mConfiguration.orientation); SystemClock.sleep(20000);//沉睡20秒 } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); //打印更新后的配置信息 Log.e(TAG,"onConfigurationChanged:infomation:orientation="+newConfig.orientation); } }
此時運行程序,程序就會崩潰,當然,在真實的設備上,是可以等待的,有的並不會造成崩潰,比如經在小米上測試50秒,程序並沒有崩潰,而是等待下去,直到程序正常。當這樣會造成不好的用戶體驗。所以在以后開發過程中,要充分考慮到這些容易出錯的情況。
③使用onLowMemory()回調方法監視低內存
該方法的原型為:
public void onLowMemory(){}
當整個系統在低內存運行時,將調用該方法。
好的應用程序會實現該方法來釋放任何緩存或者其他不需要的資源。系統從該方法返回之后,將執行一個垃圾回收操作。
④使用registerActivityLifecycleCallbacks()注冊可以監視Activity生命周期的接口
registerActivityLifecycleCallbacks()方法的原型為:
public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback){}
在該方法中,參數callbacks表示Activity生命周期的接口。
從Android4.0以后,Android SDK為應用程序提供了一套完整的接口以便監視與本Application相關的Activity的生命周期(創建,啟動以及暫停等),它的名字叫做ActivityLifecycleCallbacks。只要在Application中通過registerActivityLifecycleCallbacks()方法將接口注冊上,它就會通過ActivityLifecycleCallbacks提供應用程序中相關的Activity生命周期信息。下表列出了這些接口以及用途。
方法原型 | 參數說明 | 用途 |
abstract void onActivityCreated(Activity activity,Bundle savedInstanceState) | activity:創建的Activity實例 savedInstanceState:創建該Activity時所帶的信息(一個Bundle實例) |
在應用程序創建Activity之前調用,用於通知接口實現者Activity將要被創建。 |
abstract void onActivityDestroyed(Activity activity) | activity:銷毀的Activity實例 | 在應用程序銷毀Activity之前調用,用於通知接口實現者Activity將要被銷毀。 |
abstract void onActivityPaused(Activity activity) | activity:暫停的Activity實例 | 在應用程序暫停Activity之前調用,用於通知接口實現者Activity將要被暫停。 |
abstract void onActivityResumed(Activity activity) | activity:恢復的Activity實例 | 在應用程序正在恢復Activity之前調用,用於通知接口實現者Activity將要被恢復。 |
abstract void onActivitySaveInstanceState(Activity activity,Bundle outState) | activity:正在執行狀態保存的的Activity實例 outState:需要保存的Activity狀態 |
指示當前Activity正在保存自己的狀態,這些狀態包含在outState中。 |
abstract void onActivityStarted(Activity activity) | activity:啟動的Activity實例 | 在應用程序正在啟動Activity之前調用,用於通知接口實現者Activity將要被啟動。 |
abstract void onActivityStopped(Activity activity) | activity:停止的Activity實例 | 在應用程序正在停止Activity之前調用,用於通知接口實現者Activity將要被停止。 |
特別提醒:從接口定義中,我們可以知道如下信息。
Ⅰ這些接口都是抽象的,因此當我們實現ActivityLifecycleCallbacks接口時,就必須實現這些方法,哪怕只是空實現。
Ⅱ這些接口的返回值都是void,這說明它們只用於通知,別無它用。
另外我們在必要時要調用unregisterActivityLifecycleCallbacks()方法來注銷掉原先注冊的接口以免造成不必要的資源浪費。
下面我們通過一個實例來說明配置發生變化的時候,系統如何通過onConfigurationChanged()回調接口來通知應用程序,具體的步驟如下所示。
㈠實現自己的Application子類(名叫ALCApplication)。我們將在應用程序創建(onCreate()方法中)時注冊自己的Activity生命周期接口,在程序終止(onTerMinate()方法中)時注銷這個接口。當完成這些工作以后,將得到如下所示的代碼:
public class ALCApplication extends Application { private final static String TAG="ALCApplication"; private ActivityLifecycleCallbacks mActivityLifecycleCallbacks=new ActivityLifecycleCallbacks() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { Log.e(TAG,"onActivityCreated"); } @Override public void onActivityStarted(Activity activity) { Log.e(TAG,"onActivityStarted"); } @Override public void onActivityResumed(Activity activity) { Log.e(TAG,"onActivityResumed"); } @Override public void onActivityPaused(Activity activity) { Log.e(TAG,"onActivityPaused"); } @Override public void onActivityStopped(Activity activity) { Log.e(TAG,"onActivityStopped"); } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { Log.e(TAG,"onActivitySaveInstanceState"); } @Override public void onActivityDestroyed(Activity activity) { Log.e(TAG,"onActivityDestroyed"); } }; @Override public void onCreate() { super.onCreate(); registerActivityLifecycleCallbacks(this.mActivityLifecycleCallbacks); } @Override public void onTerminate() { super.onTerminate(); unregisterActivityLifecycleCallbacks(this.mActivityLifecycleCallbacks); } }
㈡將ALCApplication配置到AndroidManifest.xml中,當配置完成時,最后的結果看起來與下圖類似:
這里我們通過接口監視Activity從啟動到推出的生命周期。
在這個實例中,我們在onTerminate()方法中做了注銷接口的工作。但值得注意的是,onTerminate()方法只會在虛擬機進程中被調用,永遠不會在真實的Android設備中被調用。
⑤使用registerComponentCallbacks()注冊一個可以用來艦艇Activity生命周期的接口
該方法原型為:
public void registerComponentCallbacks(ComponentCallbacks callback){}
其中參數callback是ComponentCallbacks 接口的一個實現。當Activity的生命周期發生變化時,會通過這個接口通知應用程序。對於所有應用程序來,它是通用的回調API集合的接口。ComponentCallbacks中只包括兩個方法,它們分別是public abstract void onConfigurationChanged(Configuration newConfig)和public abstract void onLowMemory()。 這兩個方法的調用與Application中的同名回調方法的調用條件一樣的。
ComponentCallbacks()和registerComponentCallbacks()方法的用法與ActivityLifecycleCallbacks()和registerActivityLifecycleCallbacks()的用法是一樣的,這里就不單舉例說明了。