關於Android Application類的一些認識

在學習別人的代碼時候,發現有一個Application類,趁機瞭解一下~

下面是看到的幾篇比較好的文章。轉載過來學習學習~


第一篇:Android Application的作用

What is Application

Application和Actovotu,Service一樣是android框架的一個系統組件,當android程序啓動時系統會創建一個 application對象,用來存儲系統的一些信息。通常我們是不需要指定一個Application的,這時系統會自動幫我們創建,如果需要創建自己 的Application,也很簡單創建一個類繼承 Application並在manifest的application標籤中進行註冊(只需要給Application標籤增加個name屬性把自己的 Application的名字定入即可)。

android系統會爲每個程序運行時創建一個Application類的對象且僅創建一個,所以Application可以說是單例 (singleton)模式的一個類.且application對象的生命週期是整個程序中最長的,它的生命週期就等於這個程序的生命週期。因爲它是全局 的單例的,所以在不同的Activity,Service中獲得的對象都是同一個對象。所以通過Application來進行一些,數據傳遞,數據共享 等,數據緩存等操作。

 

Data passing between components using Application

假如有一個Activity A, 跳轉到 Activity B ,並需要推薦一些數據,通常的作法是Intent.putExtra() 讓Intent攜帶,或者有一個Bundle把信息加入Bundle讓Intent推薦Bundle對象,實現傳遞。但這樣作有一個問題在 於,Intent和Bundle所能攜帶的數據類型都是一些基本的數據類型,如果想實現複雜的數據傳遞就比較麻煩了,通常需要實現 Serializable或者Parcellable接口。這其實是Android的一種IPC數據傳遞的方法。如果我們的兩個Activity在同一個 進程當中爲什麼還要這麼麻煩呢,只要把需要傳遞的對象的引用傳遞過去就可以了。

基本思路是這樣的。在Application中創建一個HashMap<String,Object> ,以字符串爲索引,Object爲value這樣我們的HashMap就可以存儲任何類型的對象了。在Activity A中把需要傳遞的對象放入這個HashMap,然後通過Intent或者其它途經再把這人索引的字符串傳遞給Activity B ,Activity B 就可以根據這個字符串在HashMap中取出這個對象了。只要再向下轉個型 ,就實現了對象的傳遞。

Data caching in Application

我一般會習慣在application中建立兩個HashMap<String,Object>一個用於數據的傳遞,一個用於緩 存一些數據。比如有一個Activity需要從網站獲取一些數據,獲取完之後我們就可以把這個數據cache到Application 當中,當頁面設置到其它Activity再回來的時候,就可以直接使用緩存好的數據了。但如果需要cache一些大量的數據,最好是cache一些 (軟引用)SoftReference ,並把這些數據cache到本地rom上或者sd卡上。如果在application中的緩存不存在,從本地緩存查找,如果本地緩存的數據也不存在再從網 絡上獲取。

PitFalls

使用Application如果保存了一些不該保存的對象很容易導致內存泄漏。如果在Application的oncreate中執行比較 耗時的操作,將直接影響的程序的啓動時間。不些清理工作不能依靠onTerminate完成,因爲android會盡量讓你的程序一直運行,所以很有可能 onTerminate不會被調用。

MemoryLeak

在Java中內存泄漏是隻,某個(某些)對象已經不在被使用應該被gc所回收,但有一個對象持有這個對象的引用而阻止這個對象被回收。比如我 們通常會這樣創建一個View TextView tv = new TextView(this);這裏的this通常都是Activity。所以這個TextView就持有着這個Activity的引用。下面看張圖 (Google IO 2011 ppt中抄得)

Android筆記 <wbr>Application對象的使用-數據傳遞以及內存泄漏問題

通常情況下,當用戶轉動手機的時候,android會重新調用OnCreate()方法生成一個新的Activity,原來的 Activity應該被GC所回收。但如果有個對象比如一個View的作用域超過了這個Activity(比如有一個static對象或者我們把這個 View的引用放到了Application當中),這時候原來的Activity將不能被GC所回收,Activity本身又持有很多對象的引用,所以 整個Activity的內存被泄漏了。

經常導致內存泄漏的一些原因:

keeping a long-lived reference to a Context.持有一個context的對象,從而gc不能回收。

1,一個View,的作用域超出了所在的Activity的作用域,比如一個static的View或者 把一個View cache到了application當中 etc

2,某些與View關聯的Drawable的作用域超出了Activity的作用域。

3,Runnable對象:比如在一個Activity中啓用了一個新線程去執行一個任務,在這期間這個Activity被系統回收了, 但Runnalbe的任務還沒有執行完畢並持有Activity的引用而泄漏,但這種泄漏一般來泄漏一段時間,只有Runnalbe的線程執行完閉,這個 Activity又可以被正常回收了。

4,內存類的對象作用域超出Activity的範圍:比如定義了一個內存類來存儲數據,又把這個內存類的對象傳給了其它Activity 或者Service等。因爲內部類的對象會持有當前類的引用,所以也就持有了Context的引用。解決方法是如果不需要當前的引用把內部類寫成 static或者,把內部類抽取出來變成一個單獨的類,或者把避免內部對象作用域超出Activity的作用域。

out Of Memery Error 在android中每一個程序所分到的內存大小是有限的,如果超過了這個數就會報Out Of Memory Error。android給程序分配的內存大小與手機硬件有關,以下是一些手機的數據:

G1:16M Droid:24 Nexus One:32M Xoom:48Ms

所以儘量把程序中的一些大的數據cache到本地文件。以免內存使用量超標。

Snippets

1,通過Application在兩個Activity間傳遞數據

Android筆記 <wbr>Application對象的使用-數據傳遞以及內存泄漏問題  Android筆記 <wbr>Application對象的使用-數據傳遞以及內存泄漏問題

記得數據傳遞完成之後,把存放在application的HashMap中的數據remove掉,以免發生內存的泄漏。

 

 

其實我們開發的每個android應用程序就是一個Appliction,定義這個類往往是在AndroidManifes.xml中用到。例如:

 

<application android:icon="@drawable/icon"
      android:label="@string/app_name"
      android:name=".MyApplication">
 
這裏定義了我們整個應用程序的屬性,例如名稱和圖標。
我們可以自定義Appliction,例如:
public class MyApplication extends Application {
  }
就是這兒,將我們以前一直用的默認Application給他設置成我們自己做的MyApplication
MyApplication類的作用是爲了放一些全局的和一些上下文都要用到變量和方法之類的。


談談你對Application類的理解

其實說對什麼的理解,就是考察你對這個東西會不會用,重點是有沒有什麼坑!


首先,Application在一個Dalvik虛擬機裏面只會存在一個實例,所以你不要傻傻的去弄什麼單例模式,來靜態獲取Application了,你把Application構造函數設置成privete都不可能實現(我年輕的時候就這麼傻傻的試過,想着如果可以通過Singleton.getInstance()就能獲取到Application對象,多爽呀~)。

那麼爲什麼強調說是一個Dalvik虛擬機,而不是說一個App呢?

因爲一個App有可能有多個Dalvik虛擬機,也就是傳說中的多進程模式。在這種模式下,每一個Dalvik都會存在一個Application實例,他們之間沒有關係,在A進程Application裏面保存的數據不能在B進程的Application獲取,因爲他們根本不是一個對象,而且被隔離在了兩個進程裏面,所以這裏強調是一個Dalvik虛擬機,而不是一個App。


其次,Application的實質是一個Context,它繼承自ContextWrapper。

    android.content.Context
       ↳  android.content.ContextWrapper
        ↳ android.app.Application     

ContextWrapper是什麼玩意?就是對Context的一個包裝而已。

Application有兩個子類,一個是MultiDexApplication,如果你遇到了"65535"問題,可以選擇繼承自他,完成多Dex打包配置的相關工作。

另外一個是在TDD(測試用例驅動)開發模式中使用Moke進行測試的時候用到的,可以來代替Application的Moke類MockApplication。


在應用啓動的時候,會首先調用Application.attach(),當然,這個方法是隱藏的,開發者能接觸到的第一個被調用的方法其實是Application.onCreate(),我們通常會在這個方法裏面完成各種初始化,比如圖片加載庫、Http請求庫的默認配置初始化操作等等。但是最好別在這個方法裏面進行太多耗時操作,因爲這會影響App的啓動速度,所以對於不必要的操作可以使用異步操作、懶加載、延時加載等策略來減少對UI線程的影響。


除此之外,由於在Context中可以通過getApplicationContext()獲取到Application對象,或者是通過Activity.getApplication()Service.getApplication()獲取到Application,所以可以在Application保存全局的數據,供所有的Activity或者是Service使用。

PS:使用上面的三種方法獲取到的都是同一個Application對象,getApplicationContext()是在Context的實現類ContextImpl中具體實現的,而getApplication()則是在Activity和Service中單獨實現的,所以他們的作用域不同,但是獲取到的都是同一個Application對象,因爲一個Dalvik虛擬機只有一個Application對象。

但是這裏存在着一個坑,那就是在低內存情況下,Application有可能被銷燬,從而導致保存在Application裏面的數據信息丟失,最後程序錯亂,甚至是Crash。

所以當你想在Application保存數據的時候,請做好爲空判斷,或者是選擇其他方式保存你的數據信息。


另外,在Application中存在着幾個有用的方法,比如onLowMemory()和onTrimMemory(),在這兩個方法裏面,我們可以實現自己的內存回收邏輯,比如關閉數據庫連接、移除圖片內存緩存等操作來降低內存消耗,從而降低被系統回收的風險。


最後,就是要注意Application的生命週期,他和Dalvik虛擬機生命週期一樣長,所以在進行單例或者是靜態變量的初始化操作時,一定要用Application作爲Context進行初始化,否則會造成內存泄露的發生。使用Dialog的時候一般使用Activity作爲Context,但是也可以使用Application作爲上下文,前提是你必須設置Window類型爲TYPE_SYSTEM_DIALOG,並且申請相關權限。這個時候彈出的Dialog是屬於整個Application的,彈出這個Dialog的Activity銷燬時也不會回收Dialog,只有在Application銷燬時,這個Dialog纔會自動消失。

更多參考資料



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章