Android Application詳解與用法
1:Application是什麼?
Application和Activity,Service一樣,是android框架的一個系統元件,當android程式啟動時系統會建立一個 application物件,用來儲存系統的一些資訊。通常我們是不需要指定一個Application的,這時系統會自動幫我們建立,如果需要建立自己 的Application,也很簡單建立一個類繼承 Application並在manifest的application標籤中進行註冊(只需要給Application標籤增加個name屬性把自己的 Application的名字定入即可)。
android系統會為每個程式執行時建立一個Application類的物件且僅建立一個,所以Application可以說是單例 (singleton)模式的一個類.且application物件的 生命週期是整個程式中最長的,它的生命週期就等於這個程式的生命週期。因為它是全域性 的單例的,所以在不同的Activity,Service中獲得的物件都是同一個物件。所以通過Application來進行一些,資料傳遞,資料共享 等,資料快取等操作。
2:通過Application傳遞資料
假如有一個Activity A, 跳轉到 Activity B ,並需要推薦一些資料,通常的作法是Intent.putExtra() 讓Intent攜帶,或者有一個Bundle把資訊加入Bundle讓Intent推薦Bundle物件,實現傳遞。但這樣作有一個問題在 於,Intent和Bundle所能攜帶的資料型別都是一些基本的資料型別,如果想實現複雜的資料傳遞就比較麻煩了,通常需要實現 Serializable或者Parcellable介面。這其實是Android的一種IPC資料傳遞的方法。如果我們的兩個Activity在同一個 程序當中為什麼還要這麼麻煩呢,只要把需要傳遞的物件的引用傳遞過去就可以了。 基本思路是這樣的。在Application中建立一個HashMap ,以字串為索引,Object為value這樣我們的HashMap就可以儲存任何型別的物件了。在Activity A中把需要傳遞的物件放入這個HashMap,然後通過Intent或者其它途經再把這索引的字串傳遞給Activity B ,Activity B 就可以根據這個字串在HashMap中取出這個物件了。只要再向下轉個型 ,就實現了物件的傳遞。
3:Application資料快取
我一般會習慣在application中建立兩個HashMap一個用於資料的傳遞,一個用於緩 存一些資料。比如有一個Activity需要從網站獲取一些資料,獲取完之後我們就可以把這個資料cache到Application 當中,當頁面設定到其它Activity再回來的時候,就可以直接使用快取好的資料了。但如果需要cache一些大量的資料,最好是cache一些 (軟引用)SoftReference ,並把這些資料cache到本地rom上或者sd卡上。如果在application中的快取不存在,從本地快取查詢,如果本地快取的資料也不存在再從網 絡上獲取。
4:PitFalls(漢語:易犯的錯誤)
使用Application如果儲存了一些不該儲存的物件很容易導致記憶體洩漏。如果在Application的oncreate中執行比較 耗時的操作,將直接影響的程式的啟動時間。不些清理工作不能依靠onTerminate完成,因為android會盡量讓你的程式一直執行,所以很有可能 onTerminate不會被呼叫。
5:MemoryLeak
在Java中記憶體洩漏是隻,某個(某些)物件已經不在被使用應該被gc所回收,但有一個物件持有這個物件的引用而阻止這個物件被回收。比如我 們通常會這樣建立一個View TextView tv = new TextView(this);這裡的this通常都是Activity。所以這個TextView就持有著這個Activity的引用。下面看張圖 (Google IO 2011 ppt中抄得)
通常情況下,當用戶轉動手機的時候,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到本地檔案。以免記憶體使用量超標。
記得資料傳遞完成之後,把存放在application的HashMap中的資料remove掉,以免發生記憶體的洩漏
6:生命週期:
onCreate 在建立應用程式時建立
onTerminate 當終止應用程式物件時呼叫,不保證一定被呼叫,當程式是被核心終止以便為其他應用程式釋放資源,那
麼將不會提醒,並且不呼叫應用程式的物件的onTerminate方法而直接終止進 程
onLowMemory 當後臺程式已經終止資源還匱乏時會呼叫這個方法。好的應用程式一般會在這個方法裡面釋放一些不必
要的資源來應付當後臺程式已經終止,前臺應用程式記憶體還不夠時的情況。
onConfigurationChanged 配置改變時觸發這個方法
備註:application 被殺死的情況分析:
為了決定在記憶體較低的時候殺掉哪個程序, Android會根據執行在這些程序內的元件及他們的狀態把程序劃分成一個”重要程度層次”. 其重要的程度按以下規則排序:
1:前端程序可以是一個持有執行在螢幕最前端並與使用者互動的Activity的程序(onResume方法被呼叫時),也可以是持有一個正在執行的IntentReceiver(也就是說他正在執行自己的onReceiveIntent方法)的程序. 在系統中, 只會有少數這樣的程序, 並且除非記憶體已經低到不夠這些程序執行, 否則系統不會主動殺掉這些程序. 這時, 裝置通常已經達到了需要記憶體整理的狀態, 所以殺掉這些程序是為了不讓使用者介面停止響應.
2:可視程序是持有一個被使用者可見, 但沒有顯示在最前端 (onPause方法被呼叫時) 的Activity的程序. 舉例來說, 這種程序通常出現在一個前端Activity以一個對話框出現並保持前一個Activity可見時. 這種程序被系統認為是極其重要的, 並且通常不會被殺掉, 除非為了保持所有前端程序正常執行不得不殺掉這些可見程序.
3:服務程序是持有一個Service的程序, 該Service是由startService()方法啟動的, 儘管這些程序使用者不能直接看到, 但是通常他們做的工作使用者是十分關注的(例如, 在後臺播放mp3或是在後臺下載 上傳檔案), 所以, 除非為了保持所有的前端程序和可視程序正常執行外, 系統是不會殺掉服務程序的.
4:後臺程序是持有一個不再被使用者可見的Activity(onStop()方法被呼叫時)的程序. 這些程序不會直接影響使用者體驗. 加入這些程序已經完整的,正確的完成了自己的生命週期(訪問Activity檢視更多細節), 系統會在為前三種程序釋放記憶體時隨時殺掉這些後臺程序. 通常會有很多的後臺程序在執行, 所以這些程序被存放在一個LRU列表中, 以保證在低記憶體的時候, 最近一個被使用者看到的程序會被最後殺掉.
5:空程序是沒有持有任何活動應用元件的程序. 保留這種程序的唯一理由是為了提供一種快取機制, 縮短他的應用下次執行時的啟動時間. 就其本身而言, 系統殺掉這些程序的目的是為了在這些空程序和底層的核心快取之間平衡整個系統的資源.
當需要給一個程序分類的時候, 系統會在該程序中處於活動狀態的所有元件裡掉選一個重要等級最高作為分類依據. 檢視Activity, Service,和IntentReceiver的文件, 瞭解每個元件在程序整個生命週期中的貢獻. 每一個classes的文件詳細描述他們在各自應用的生命週期中所起得作用.
7:application 的context
1、它描述的是一個應用程式環境的資訊,即上下文。
2、該類是一個抽象(abstract class)類,Android提供了該抽象類的具體實現類(後面我們會講到是ContextIml類)。
3、通過它我們可以獲取應用程式的資源和類,也包括一些應用級別操作,例如:啟動一個Activity,傳送廣播,接受Intent
資訊 等。。
用法詳解:
MyApp.java
-
package com.android.test;
-
import android.app.Application;
-
public class MyApp extends Application{
-
private String mylabel ;
-
public String getLabel(){
-
return mylabel;
-
}
-
public void setLabel(String s){
-
this.mylabel = s;
-
}
-
@Override
-
public void onCreate() {
-
// TODO Auto-generated method stub
-
super.onCreate();
-
setLabel("Welcome!"); //初始化全域性變數
-
}
- }
MainActivity.java
-
import android.app.Activity;
-
import android.content.Intent;
-
import android.os.Bundle;
-
import android.util.Log;
-
public class MainActivity extends Activity {
-
private MyApp myApp;
-
@Override
-
public void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.main);
-
myApp = (MyApp) getApplication(); //獲得自定義的應用程式MyApp
-
Log.i("guoll", "InitLabel:"+myApp.getLabel()); //將我們放到程序中的全域性變數拿出來,看是不是我們曾經設定的值
-
myApp.setLabel("Changing!"); //修改一下
-
Log.i("guoll", "ChangeLabel:"+myApp.getLabel()); //看下,這個值改變了沒有
-
Intent intent = new Intent(); //再看一下在另一個Activity中是取到初始化的值,還是取到修改後的值
-
intent.setClass(this, otherActivity.class);
-
startActivity(intent);
-
}
- }
OtherActivity.java:
-
import android.app.Activity;
-
import android.os.Bundle;
-
import android.util.Log;
-
public class OtherActivity extends Activity{
-
private MyApp myApp;
-
@Override
-
protected void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.main);
-
myApp = (MyApp) getApplication(); //獲得自定義的應用程式MyApp
-
Log.i("guoll", "OhterActivity receive the Label:"+myApp.getLabel()); //檢視變數值是否修改了
-
}
- }
修改配置檔案ApplicationManifest.xml,將要執行的應用程式MyApp加進去:
-
2<?xml version="1.0" encoding="utf-8"?>
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-
package="com.android.test"
-
android:versionCode="1"
-
android:versionName="1.0">
-
<!-- 在這裡,將預設的Application設定成自己做的MyApp-->
-
<application android:name="MyApp"
-
android:icon="@drawable/icon"
-
android:label="@string/app_name"
-
>
-
<activity android:name=".mainActivity"
-
android:label="@string/app_name">
-
<intent-filter>
-
<action android:name="android.intent.action.MAIN" />
-
<category android:name="android.intent.category.LAUNCHER" />
-
</intent-filter>
-
</activity>
-
<activity android:name=".otherActivity">
-
</activity>
-
</application>
- </manifest>