1. 程式人生 > >Android基礎筆記(一)

Android基礎筆記(一)

一.android四大元件

(一)android四大元件詳解 Android四大元件分別為activity、service、content provider、broadcast receiver。 1、activity (1)一個Activity通常就是一個單獨的螢幕(視窗)。 (2)Activity之間通過Intent進行通訊。 (3)android應用中每一個Activity都必須要在AndroidManifest.xml配置檔案中宣告,否則系統將不識別也不執行該Activity。 2、service (1)service用於在後臺完成使用者指定的操作。service分為兩種: (a)started(啟動):當應用程式元件(如activity)呼叫startService()方法啟動服務時,服務處於started狀態。 (b)bound(繫結):當應用程式元件呼叫bindService()方法繫結到服務時,服務處於bound狀態。 (2)startService()與bindService()區別: (a)started service(啟動服務)是由其他元件呼叫startService()方法啟動的,這導致服務的onStartCommand()方法被呼叫。當服務是started狀態時,其生命週期與啟動它的元件無關,並且可以在後臺無限期執行,即使啟動服務的元件已經被銷燬。因此,服務需要在完成任務後呼叫stopSelf()方法停止,或者由其他元件呼叫stopService()方法停止。 (b)使用bindService()方法啟用服務,呼叫者與服務繫結在了一起,呼叫者一旦退出,服務也就終止,大有“不求同時生,必須同時死”的特點。 (3)開發人員需要在應用程式配置檔案中宣告全部的service,使用標籤。 (4)Service通常位於後臺執行,它一般不需要與使用者互動,因此Service元件沒有圖形使用者介面。Service元件需要繼承Service基類。Service元件通常用於為其他元件提供後臺服務或監控其他元件的執行狀態。 3、content provider (1)android平臺提供了Content Provider使一個應用程式的指定資料集提供給其他應用程式。其他應用可以通過ContentResolver類從該內容提供者中獲取或存入資料。 (2)只有需要在多個應用程式間共享資料是才需要內容提供者。例如,通訊錄資料被多個應用程式使用,且必須儲存在一個內容提供者中。它的好處是統一資料訪問方式。 (3)ContentProvider實現資料共享。ContentProvider用於儲存和獲取資料,並使其對所有應用程式可見。這是不同應用程式間共享資料的唯一方式,因為android沒有提供所有應用共同訪問的公共儲存區。 (4)開發人員不會直接使用ContentProvider類的物件,大多數是通過ContentResolver物件實現對ContentProvider的操作。 (5)ContentProvider使用URI來唯一標識其資料集,這裡的URI以content://作為字首,表示該資料由ContentProvider來管理。 4、broadcast receiver (1)你的應用可以使用它對外部事件進行過濾,只對感興趣的外部事件(如當電話呼入時,或者資料網路可用時)進行接收並做出響應。廣播接收器沒有使用者介面。然而,它們可以啟動一個activity或serice來響應它們收到的資訊,或者用NotificationManager來通知使用者。通知可以用很多種方式來吸引使用者的注意力,例如閃動背燈、震動、播放聲音等。一般來說是在狀態列上放一個持久的圖示,使用者可以開啟它並獲取訊息。 (2)廣播接收者的註冊有兩種方法,分別是程式動態註冊和AndroidManifest檔案中進行靜態註冊。 (3)動態註冊廣播接收器特點是當用來註冊的Activity關掉後,廣播也就失效了。靜態註冊無需擔憂廣播接收器是否被關閉,只要裝置是開啟狀態,廣播接收器也是開啟著的。也就是說哪怕app本身未啟動,該app訂閱的廣播在觸發時也會對它起作用。

(二)android四大元件總結:

(1)4大元件的註冊 4大基本元件都需要註冊才能使用,每個Activity、service、Content Provider都需要在AndroidManifest檔案中進行配置。AndroidManifest檔案中未進行宣告的activity、服務以及內容提供者將不為系統所見,從而也就不可用。而broadcast receiver廣播接收者的註冊分靜態註冊(在AndroidManifest檔案中進行配置)和通過程式碼動態建立並以呼叫Context.registerReceiver()的方式註冊至系統。需要注意的是在AndroidManifest檔案中進行配置的廣播接收者會隨系統的啟動而一直處於活躍狀態,只要接收到感興趣的廣播就會觸發(即使程式未執行)。 (2)4大元件的啟用 內容提供者的啟用:當接收到ContentResolver發出的請求後,內容提供者被啟用。而其它三種元件activity、服務和廣播接收器被一種叫做intent的非同步訊息所啟用。 (3)4大元件的關閉 內容提供者僅在響應ContentResolver提出請求的時候啟用。而一個廣播接收器僅在響應廣播資訊的時候啟用。所以,沒有必要去顯式的關閉這些元件。Activity關閉:可以通過呼叫它的finish()方法來關閉一個activity。服務關閉:對於通過startService()方法啟動的服務要呼叫Context.stopService()方法關閉服務,使用bindService()方法啟動的服務要呼叫Contex.unbindService()方法關閉服務。 (4)android中的任務(activity棧) (a)任務其實就是activity的棧,它由一個或多個Activity組成,共同完成一個完整的使用者體驗。棧底的是啟動整個任務的Activity,棧頂的是當前執行的使用者可以互動的Activity,當一個activity啟動另外一個的時候,新的activity就被壓入棧,併成為當前執行的activity。而前一個activity仍保持在棧之中。當用戶按下BACK鍵的時候,當前activity出棧,而前一個恢復為當前執行的activity。棧中儲存的其實是物件,棧中的Activity永遠不會重排,只會壓入或彈出。 (b)任務中的所有activity是作為一個整體進行移動的。整個的任務(即activity棧)可以移到前臺,或退至後臺。 (c)Android系統是一個多工(Multi-Task)的作業系統,可以在用手機聽音樂的同時,也執行其他多個程式。每多執行一個應用程式,就會多耗費一些系統記憶體,當同時執行的程式過多,或是關閉的程式沒有正確釋放掉記憶體,系統就會覺得越來越慢,甚至不穩定。為了解決這個問題,Android引入了一個新的機制,即生命週期(Life Cycle)。

Android基礎

  1. 請描述一下Activity 生命週期。 答: 如下圖所示。共有七個周期函式,按順序分別是: onCreate(), onStart(), onRestart(), onResume(), onPause(),onStop(), onDestroy()。 onCreate(): 建立Activity時呼叫,設定在該方法中,還以Bundle的形式提供對以前儲存的任何狀態的訪問。 onStart(): Activity變為在螢幕上對使用者可見時呼叫。 onResume(): Activity開始與使用者互動時呼叫(無論是啟動還是重新啟動一個活動,該方法總是被呼叫。 onPause(): Activity被暫停或收回cpu和其他資源時呼叫,該方法使用者保護活動狀態的,也是保護現場。 onStop(): Activity被停止並轉為不可見階段及後續的生命週期事件時呼叫。 onRestart(): Activity被重新啟動時呼叫。該活動仍然在棧中,而不是啟動新的Activity。 1、完整生命週期: 即從一個Activity從出現到消失,對應的週期方法是從onCreate()到onDestroy()。 2、可見生命週期: 當Activity處於可以使用者看見的狀態,但不一定能與使用者互動時,將多次執行從onStart()到onStop()。 3、前景生命週期: 當Activity處於Activity棧最頂端,能夠與其他使用者進行互動時,將多次執行從onResume()到onPause()。

  2. 兩個Activity之間跳轉時必然會執行的是哪幾個方法。 答: 兩個Activity之間跳轉必然會執行的是下面幾個方法。 onCreate()//在Activity生命週期開始時呼叫。 onRestoreInstanceState()//用來恢復UI狀態。 onRestart()//當Activity重新啟動時呼叫。 onStart()//當Activity對使用者即將可見時呼叫。 onResume()//當Activity與使用者互動時,繪製介面。 onSaveInstanceState()//即將移出棧頂保留UI狀態時呼叫。 onPause()//暫停當前活動Activity,提交持久資料的改變,停止動畫或其他佔用GPU資源的東西,由於下一個Activity在這個方法返回之前不會resume,所以這個方法的程式碼執行要快。 onStop()//Activity不再可見時呼叫。 onDestroy()//Activity銷燬棧時被呼叫的最後一個方法。

  3. 橫豎屏切換時候Activity的生命週期。 答: 1、不設定Activity的android: configChanges時,切屏會重新呼叫各個生命週期,切橫屏時會執行一次,切豎屏時會執行兩次。 2、設定Activity的android: configChanges=“orientation”時,切屏還是會重新呼叫各個生命週期,切橫、豎屏時只會執行一次。 3、設定Activity的android: configChanges=“orientation|keyboardHidden”時,切屏不會重新呼叫各個生命週期,只會執行onConfiguration方法

  4. 如何將一個Activity設定成視窗的樣式。 答: 第一種方法,在styles.xml檔案中,可以新建如下的類似Dialog的style。 name=“Theme.FloatActivity” parent=“android:style/Theme.Dialog” 第二種方法,在AndroidManifest.xml中在需要顯示為視窗的Activity中新增如下屬性: android: theme=“@style/Theme.FloatActivity”即可。 也可以直接新增對應需要展示為Dialog style的Activity的屬性為android: theme=“@ android: style/Theme.Dialog”。

  5. 兩個Activity之間怎麼傳遞資料? 答: 可以在Intent物件中利用Extra來傳遞儲存資料。 在Intent的物件請求中,使用putExtra(“鍵值對的名字”,”鍵值對的值”);在另外一個Activity中將Intent中的請求資料取出來: Intent intent = getIntent(); String value = intent.getStringExtra(“testIntent”);

  6. 怎麼讓在啟動一個Activity是就啟動一個service? 答: 首先定義好一個service,然後在Activity的onCreate裡面進行連線並bindservice或者直接startService。

  7. Activity怎麼和service繫結,怎麼在activity中啟動自己對應的service? 答: 1、activity能進行繫結得益於Serviece的介面。為了支援Service的繫結,實現onBind方法。 2、Service和Activity的連線可以用ServiceConnection來實現。需要實現一個新的ServiceConnection,重現onServiceConnected和OnServiceDisconnected方法,一旦連線建立,就能得到Service例項的引用。 3、執行繫結,呼叫bindService方法,傳入一個選擇了要繫結的Service的Intent(顯示或隱式)和一個你實現了的ServiceConnection的例項

  8. 什麼是Service以及描述下它的生命週期。Service有哪些啟動方法,有什麼區別,怎樣停用Service? 答: Android Service是執行在後臺的程式碼,不能與使用者互動,可以執行在自己的程序,也可以執行在其他應用程式程序的上下文裡。需要通過某一個Activity或者Context物件來呼叫。Service有兩個啟動方法,分別是Context.startService()和Context.bindService()。如果在Service執行耗時的操作需要啟動一個新執行緒來執行。 Android Service只繼承了onCreate(), onStart(),onDestroy()三個方法,當我們第一次啟動Service時,先後呼叫onCreate(), onStart()這兩個方法,當停止Service時,則執行onDestroy()方法時。如果Service已經啟動了,當我們再次啟動Service時,不會再執行onCreate()方法,而是直接執行onStart()方法。

  9. 什麼時候使用Service? 答: 比如播放多媒體的時候,使用者啟動了其他Activity,這個時候程式要在後臺繼續播放,比如檢測SD卡上檔案的變化,再或者在後臺記錄你的地理資訊位置的改變等等。

  10. 請描述一下Intent 和 Intent Filter。 答: Intent在Android中被翻譯為”意圖”,他是三種應用程式基本元件-Activity,Service和broadcast receiver之間相互啟用的手段。在呼叫Intent名稱時使用ComponentName也就是類的全名時為顯示呼叫。這種方式一般用於應用程式的內部呼叫,因為你不一定會知道別人寫的類的全名。而Intent Filter是指意圖過濾,不出現在程式碼中,而是出現在android Manifest檔案中,以的形式。(有一個例外是broadcast receiver的intent filter是使用Context.registerReceiver()來動態設定的,其中intent filter也是在程式碼中建立的) 一個intent有action,data,category等欄位。一個隱式intent為了能夠被某個intent filter接收,必須通過3個測試,一個intent為了被某個元件接收,則必須通過它所有的intent filter中的一個。

  11. Intent傳遞資料時,可以傳遞哪些型別資料? 答: intent間傳送資料一般有兩種常用的方法: 1、extra 2、data。 extra可以用Intent.putExtra放入資料。新啟動的Activity可用Intent.getExtras取出Bundle,然後用Bundles.getLong,getInt,getBoolean,getString等函式來取放進去的值。 Data則是傳輸url。url可以是指我們熟悉的http,ftp等網路地址,也可以指content來指向ContentProvider提供的資源。Intent.setData可以放入資料,Intent.getData可以取出資料。

  12. 說說Activity,Intent,Service是什麼關係 ? 答: 一個Activity通常是一個單獨的螢幕,每一個Activity都被實現為一個單獨的類,這些類都是從Activity基類中繼承而來的。Activity類會顯示由檢視控制元件組成的使用者介面,並對檢視控制元件的事件做出響應。 Intent的呼叫是用來進行螢幕之間的切換。Intent描述應用想要做什麼。Intent資料結構中兩個最重要的部分是動作和動作對應的資料,一個動作對應一個動作資料。 Service是執行在後臺的程式碼,不能與使用者互動,可以執行在自己的程序裡,也可以執行在其他應用程式程序的上下文裡。需要一個Activity或者其他Context物件來呼叫。 Activity跳轉Activity,Activity啟動Service,Service開啟Activity都需要Intent表明意圖,以及傳遞引數,Intent是這些元件間訊號傳遞的承載著。

  13. 請描述一下BroadcastReceiver。 答: Broadcast Receiver用於接收並處理廣播通知(broadcast announcements)。多數的廣播是系統發起的,如地域變換、電量不足、來電簡訊等。程式也可以播放一個廣播。程式可以有任意數量的broadcast receivers來響應它覺得重要的通知。Broadcast receiver可以通過多種方式通知使用者: 啟動activity、使用NotificationManager、開啟背景燈、振動裝置、播放聲音等,最典型的是在狀態列顯示一個圖示,這樣使用者就可以點它開啟看通知內容。通常我們的某個應用或系統本身在某些事件(電池電量不足、來電簡訊)來臨時會廣播一個Intent出去,我們利用註冊一個broadcast receiver來監聽這些Intent並獲取Intent中的資料。

  14. 在manifest和程式碼中如何註冊和使用 broadcast receiver 。 答: 在android的manifest中註冊

    <receiver android: name =“Receiver1”>
             <intent-filter>
                        <!----和Intent中的action對應--->
                        <actionandroid: name=“com.forrest.action.mybroadcast”/>
            </intent-filter>
     </receiver>
    

在程式碼中註冊 1、 IntentFilter filter = new IntentFilter(“com.forrest.action.mybroadcast”);//和廣播中Intent的action對應; 2、 MyBroadcastReceiver br= new MyBroadcastReceiver(); 3、 registerReceiver(br, filter);

二.六大布局:

Android六大介面佈局方式: 宣告Android程式佈局有兩種方式:

  1. 使用XML檔案描述介面佈局;
  2. 在Java程式碼中通過呼叫方法進行控制。 我們既可以使用任何一種宣告介面佈局的方式,也可以同時使用兩種方式。 使用XML檔案宣告有以下3個特點:
  3. 將程式的表現層和控制層分離;
  4. 在後期修改使用者介面時,無須更改程式的源程式;
  5. 可通過WYSIWYG視覺化工具直接看到所設計的使用者介面,有利於加快介面設計的過程。 建議儘量採用XML檔案宣告介面元素佈局。在程式執行時動態新增介面佈局會大大降低應用響應速度,但依然可以在必要時動態改變螢幕內容。 六大介面佈局方式包括: 線性佈局(LinearLayout)、框架佈局(FrameLayout)、表格佈局(TableLayout)、相對佈局(RelativeLayout)、絕對佈局(AbsoluteLayout)和網格佈局(GridLayout) 。 1. LinearLayout線性佈局 LinearLayout容器中的元件一個挨一個排列,通過控制android:orientation屬性,可控制各元件是橫向排列還是縱向排列。 LinearLayout的常用XML屬性及相關方法 在這裡插入圖片描述 其中,gravity屬性支援top, left, right, center_vertical, fill_vertical, center_horizontal, fill_horizontal, center, fill, clip_vertical, clip_horizontal。也可以同時指定多種對齊方式的組合。 LinearLayout子元素支援的常用XML屬性及方法 在這裡插入圖片描述

2. TableLayout表格佈局 TableLayout繼承自Linearout,本質上仍然是線性佈局管理器。表格佈局採用行、列的形式來管理UI元件,並不需要明確地宣告包含多少行、多少列,而是通過新增TableRow、其他元件來控制表格的行數和列數。

-每向TableLayout中新增一個TableRow就代表一行;

  • 每向TableRow中新增一個一個子元件就表示一列;
  • 如果直接向TableLayout新增元件,那麼該元件將直接佔用一行;
  • 在表格佈局中,可以為單元格設定如下三種行為方式: Shrinkable:該列的所有單元格的寬度可以被收縮,以保證該表格能適應父容器的寬度; Strentchable:該列所有單元格的寬度可以被拉伸,以保證元件能完全填滿表格空餘空間; Collapsed:如果該列被設定為Collapsed,那麼該列的所有單元格會被隱藏; TableLayout的常用XML屬性及方法 在這裡插入圖片描述 3. FrameLayout幀佈局 FrameLayout直接繼承自ViewGroup元件。幀佈局為每個加入其中的元件建立一個空白的區域(稱為一幀),每個子元件佔據一幀,這些幀會根據gravity屬性執行自動對齊。 FrameLayout的常用XM了屬性及方法 在這裡插入圖片描述

4. RelativeLayout相對佈局 RelativeLayout的XML屬性及相關方法說明 在這裡插入圖片描述 為了控制該佈局容器的各子元件的佈局分佈,RelativeLayout提供了一個內部類:RelativeLayout.LayoutParams。 RelativeLayout.LayoutParams裡只能設為boolean的XML屬性 在這裡插入圖片描述 RelativeLayout.LayoutParams裡屬性值為其他UI元件ID的XML屬性*

在這裡插入圖片描述

5. Android 4.0新增的網格佈局GridLayout GridLayout是Android4.0增加的網格佈局控制元件,與之前的TableLayout有些相似,它把整個容器劃分為rows × columns個網格,每個網格可以放置一個元件。效能及功能都要比tablelayout好,比如GridLayout佈局中的單元格可以跨越多行,而tablelayout則不行,此外,其渲染速度也比tablelayout要快。GridLayout提供了setRowCount(int)和setColumnCount(int)方法來控制該網格的行和列的數量。

GridLayout常用的XML屬性和方法說明 在這裡插入圖片描述

為了控制GridLayout佈局容器中各子元件的佈局分佈,GridLayout提供了一個內部類:GridLayout.LayoutParams,來控制Gridlayout佈局容器中子元件的佈局分佈。

GridLayout.LayoutParams常用的XML屬性和方法說明 在這裡插入圖片描述

6. AbsoluteLayout絕對佈局 即Android不提供任何佈局控制,而是由開發人員自己通過X座標、Y座標來控制組件的位置。 每個元件都可指定如下兩個XML屬性: layour_x; layout_y; 絕對佈局已經過時,不應使用或少使用。 介面佈局型別的選擇和效能優化 首先得明確,介面佈局型別的巢狀越多越深越複雜,會使佈局例項化變慢,使Activity的展開時間延長。建議儘量減少佈局巢狀,儘量減少建立View物件的數量。 1 . 減少佈局層次,可考慮用RelativeLayout來代替LinearLayout。通過Relative的相對其他元素的位置來佈局,可減少塊狀巢狀; 2 . 另一種減少佈局層次的技巧是使用 標籤來合併佈局;3 . 重用佈局。Android支援在XML中使用 標籤, 通過指定android:layout屬性來指定要包含的另一個XML佈局。

三.五大儲存

在Android中,可供選擇的儲存方式有SharedPreferences、檔案儲存、SQLite資料庫方式、內容提供器(Content provider)和網路。 一.SharedPreferences方式 Android提供用來儲存一些簡單的配置資訊的一種機制,例如,一些預設歡迎語、登入的使用者名稱和密碼等。其以鍵值對的方式儲存, 使得我們可以很方便的讀取和存入. 1)程式要實現的功能: 我們在Name文字框中輸入wangwu,在Password文字框中輸入123456,然後退出這個應用。我們在應用程式列表中找到這個應用,重新啟動,可以看到其使用了前面輸入的Name和Password 2)實現的程式碼 package com.demo;

import android.app.Activity; import android.content.SharedPreferences; import android.os.Bundle; import android.widget.EditText;

public class SharedPreferencesDemo extends Activity {

public static final String SETTING_INFOS = "SETTING_Infos";   
public static final String NAME = "NAME";   
public static final String PASSWORD = "PASSWORD";   
private EditText field_name;  //接收使用者名稱的元件   
private EditText filed_pass;  //接收密碼的元件   

@Override  
public void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.main);  
      
    //Find VIew    
    field_name = (EditText) findViewById(R.id.name);  //首先獲取用來輸入使用者名稱的元件   
    filed_pass = (EditText) findViewById(R.id.password); //同時也需要獲取輸入密碼的元件   

    // Restore preferences   
    SharedPreferences settings = getSharedPreferences(SETTING_INFOS, 0); //獲取一個SharedPreferences物件   
    String name = settings.getString(NAME, "");  //取出儲存的NAME   
    String password = settings.getString(PASSWORD, ""); //取出儲存的PASSWORD   

    //Set value   
    field_name.setText(name);  //將取出來的使用者名稱賦予field_name   
    filed_pass.setText(password);  //將取出來的密碼賦予filed_pass   
}  
  
@Override   
protected void onStop(){   
    super.onStop();   
      
    SharedPreferences settings = getSharedPreferences(SETTING_INFOS, 0); //首先獲取一個SharedPreferences物件   
    settings.edit()   
            .putString(NAME, field_name.getText().toString())   
            .putString(PASSWORD, filed_pass.getText().toString())   
            .commit();   
} //將使用者名稱和密碼儲存進去   

}

SharedPreferences儲存到哪裡去了? SharedPreferences是以XML的格式以檔案的方式自動儲存的,在DDMS中的File Explorer中展開到/data/data/package name/shared_prefs下,以上面這個為例,可以看到一個叫做SETTING_Infos.xml的檔案 注意:Preferences只能在同一個包內使用,不能在不同的包之間使用。 二.檔案儲存方式 在Android中,其提供了openFileInput 和 openFileOuput 方法讀取裝置上的檔案,下面看個例子程式碼,具體如下所示: String FILE_NAME = “tempfile.tmp”; //確定要操作檔案的檔名 FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE); //初始化 FileInputStream fis = openFileInput(FILE_NAME); //建立寫入流 上述程式碼中兩個方法只支援讀取該應用目錄下的檔案,讀取非其自身目錄下的檔案將會丟擲異常。需要提醒的是,如果呼叫 FileOutputStream 時指定的檔案不存在,Android 會自動建立它。另外,在預設情況下,寫入的時候會覆蓋原檔案內容,如果想把 新寫入的內容附加到原檔案內容後,則可以指定其模式為Context.MODE_APPEND。

三.SQLite資料庫方式 SQLite是Android所帶的一個標準的資料庫,它支援SQL語句,它是一個輕量級的嵌入式資料庫。 1)實現的功能 在這個例子裡邊,我們在程式的主介面有一些按鈕,通過這些按鈕可以對資料庫進行標準的增、刪、改、查。 2)實現程式碼 package com.sqlite;

import android.app.Activity; import android.content.Context; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button;

/*

  • 一個SQLiteDatabase的例項代表了一個SQLite的資料庫,通過SQLiteDatabase例項的一些方法,我們可以執行SQL語句,
  • 對資料庫進行增、刪、查、改的操作。需要注意的是,資料庫對於一個應用來說是私有的,並且在一個應用當中,資料庫的名字也是惟一的。 */

/*

  • 什麼是SQLiteOpenHelper ?
  • 這個類主要生成一個數據庫,並對資料庫的版本進行管理。
  • 當在程式當中呼叫這個類的方法getWritableDatabase()或者getReadableDatabase()方法的時候,如果當時沒有資料,那麼Android系統就會自動生成一個數據庫。
  • SQLiteOpenHelper 是一個抽象類,我們通常需要繼承它,並且實現裡邊的3個函式,
  • onCreate(SQLiteDatabase):在資料庫第一次生成的時候會呼叫這個方法,一般我們在這個方法裡邊生成資料庫表。 
    
  • onUpgrade(SQLiteDatabase, int, int):當資料庫需要升級的時候,Android系統會主動的呼叫這個方法。一般我們在這個方法裡邊刪除資料表,並建立新的資料表,當然是否還需要做其他的操作,完全取決於應用的需求。 
    
  • onOpen(SQLiteDatabase):這是當開啟資料庫時的回撥函式,一般也不會用到。  
    

*/

public class SQLiteDemo extends Activity {

OnClickListener listener1 = null;  
OnClickListener listener2 = null;  
OnClickListener listener3 = null;  
OnClickListener listener4 = null;  
OnClickListener listener5 = null;  

Button button1;  
Button button2;  
Button button3;  
Button button4;  
Button button5;  

DatabaseHelper mOpenHelper;  

private static final String DATABASE_NAME = "dbForTest.db";  
private static final int DATABASE_VERSION = 1;  
private static final String TABLE_NAME = "diary";  
private static final String TITLE = "title";  
private static final String BODY = "body";  

//建立一個內部類,主要生成一個數據庫   
private static class DatabaseHelper extends SQLiteOpenHelper {  
      
    DatabaseHelper(Context context) {  
        super(context, DATABASE_NAME, null, DATABASE_VERSION);  
    }  

    //在資料庫第一次生成的時候會呼叫這個方法,一般我們在這個方法裡邊生成資料庫表。   
    @Override  
    public void onCreate(SQLiteDatabase db) {  

        String sql = "CREATE TABLE " + TABLE_NAME + " (" + TITLE  
                + " text not null, " + BODY + " text not null " + ");";  
        Log.i("haiyang:createDB=", sql);  
        db.execSQL(sql);  
    }  

    @Override  
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
    }  
}  

@Override  
public void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.main);  
    prepareListener();  
    initLayout();  
    mOpenHelper = new DatabaseHelper(this);  

}  

private void initLayout() {  
    button1 = (Button) findViewById(R.id.button1);  
    button1.setOnClickListener(listener1);  

    button2 = (Button) findViewById(R.id.button2);  
    button2.setOnClickListener(listener2);  

    button3 = (Button) findViewById(R.id.button3);  
    button3.setOnClickListener(listener3);  
      
    button4 = (Button) findViewById(R.id.button4);  
    button4.setOnClickListener(listener4);  

    button5 = (Button) findViewById(R.id.button5);  
    button5.setOnClickListener(listener5);  

}  

private void prepareListener() {  
    listener1 = new OnClickListener() {  
        public void onClick(View v) {  
            CreateTable();  
        }  
    };  
    listener2 = new OnClickListener() {  
        public void onClick(View v) {  
            dropTable();  
        }  
    };  
    listener3 = new OnClickListener() {  
        public void onClick(View v) {  
            insertItem();  
        }  
    };  
    listener4 = new OnClickListener() {  
        public void onClick(View v) {  
            deleteItem();  
        }  
    };  
    listener5 = new OnClickListener() {  
        public void onClick(View v) {  
            showItems();  
        }  
    };  
}  

/* 
 * 重新建立資料表 
 */  
private void CreateTable() {  
    //mOpenHelper.getWritableDatabase()語句負責得到一個可寫的SQLite資料庫,如果這個資料庫還沒有建立,   
    //那麼mOpenHelper輔助類負責建立這個資料庫。如果資料庫已經建立,那麼直接返回一個可寫的資料庫。   
    SQLiteDatabase db = mOpenHelper.getWritableDatabase();  
    String sql = "CREATE TABLE " + TABLE_NAME + " (" + TITLE  
            + " text not null, " + BODY + " text not null " + ");";  
    Log.i("haiyang:createDB=", sql);  

    try {  
        db.execSQL("DROP TABLE IF EXISTS diary");  
        db.execSQL(sql);  
        setTitle("資料表成功重建");  
    } catch (SQLException e) {  
        setTitle("資料表重建錯誤");  
    }  
}  

/* 
 * 刪除資料表 
 */  
private void dropTable() {  
    //mOpenHelper.getWritableDatabase()語句負責得到一個可寫的SQLite資料庫,如果這個資料庫還沒有建立,   
    //那麼mOpenHelper輔助類負責建立這個資料庫。如果資料庫已經建立,那麼直接返回一個可寫的資料庫。   
    SQLiteDatabase db = mOpenHelper.getWritableDatabase();  
    String sql = "drop table " + TABLE_NAME;  
    try {  
        db.execSQL(sql);  
        setTitle("資料表成功刪除:" + sql);  
    } catch (SQLException e) {  
        setTitle("資料表刪除錯誤");  
    }  
}  

/* 
 * 插入兩條資料 
 */  
private void insertItem() {  
    //mOpenHelper.getWritableDatabase()語句負責得到一個可寫的SQLite資料庫,如果這個資料庫還沒有建立,   
    //那麼mOpenHelper輔助類負責建立這個資料庫。如果資料庫已經建立,那麼直接返回一個可寫的資料庫。   
    SQLiteDatabase db = mOpenHelper.getWritableDatabase();  
    String sql1 = "insert into " + TABLE_NAME + " (" + TITLE + ", " + BODY  
            + ") values('haiyang', 'android的發展真是迅速啊');";  
    String sql2 = "insert into " + TABLE_NAME + " (" + TITLE + ", " + BODY  
            + ") values('icesky', 'android的發展真是迅速啊');";  
    try {  
        // Log.i()會將引數內容列印到日誌當中,並且列印級別是Info級別   
        // Android支援5種列印級別,分別是Verbose、Debug、Info、Warning、Error,當然我們在程式當中一般用到的是Info級別   
        Log.i("haiyang:sql1=", sql1);  
        Log.i("haiyang:sql2=", sql2);  
        db.execSQL(sql1);  
        db.execSQL(sql2);  
        setTitle("插入兩條資料成功");  
    } catch (SQLException e) {  
        setTitle("插入兩條資料失敗");  
    }  
}  

/* 
 * 刪除其中的一條資料 
 */  
private void deleteItem() {  
    try {  
        //mOpenHelper.getWritableDatabase()語句負責得到一個可寫的SQLite資料庫,如果這個資料庫還沒有建立,   
        //那麼mOpenHelper輔助類負責建立這個資料庫。如果資料庫已經建立,那麼直接返回一個可寫的資料庫。   
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();  
        //第一個引數是資料庫表名,在這裡是TABLE_NAME,也就是diary。    
        //第二個引數,相當於SQL語句當中的where部分,也就是描述了刪除的條件。   
        //如果在第二個引數當中有“?”符號,那麼第三個引數中的字串會依次替換在第二個引數當中出現的“?”符號。    
        db.delete(TABLE_NAME, " title = 'haiyang'", null);  
        setTitle("刪除title為haiyang的一條記錄");  
    } catch (SQLException e) {  

    }  

}  

/* 
 * 在螢幕的title區域顯示當前資料表當中的資料的條數。 
 */  
/* 
 * Cursor cur = db.query(TABLE_NAME, col, null, null, null, null, null)語句將查詢到的資料放到一個Cursor 當中。 
    這個Cursor裡邊封裝了這個資料表TABLE_NAME當中的所有條列。  
    query()方法相當的有用,在這裡我們簡單地講一下。 
        第一個引數是資料庫裡邊表的名字,比如在我們這個例子,表的名字就是TABLE_NAME,也就是"diary"。 
        第二個欄位是我們想要返回資料包含的列的資訊。在這個例子當中我們想要得到的列有title、body。我們把這兩個列的名字放到字串數組裡邊來。 
        第三個引數為selection,相當於SQL語句的where部分,如果想返回所有的資料,那麼就直接置為null。 
        第四個引數為selectionArgs。在selection部分,你有可能用到“?”,那麼在selectionArgs定義的字串會代替selection中的“?”。 
        第五個引數為groupBy。定義查詢出來的資料是否分組,如果為null則說明不用分組。 
        第六個引數為having ,相當於SQL語句當中的having部分。 
        第七個引數為orderBy,來描述我們期望的返回值是否需要排序,如果設定為null則說明不需要排序。 
 */  
  
private void showItems() {  

    SQLiteDatabase db = mOpenHelper.getReadableDatabase();  
    String col[] = { TITLE, BODY };  
    //查詢資料   
    Cursor cur = db.query(TABLE_NAME, col, null, null, null, null, null);  
    Integer num = cur.getCount();  
    setTitle(Integer.toString(num) + " 條記錄");  
}

四.內容提供器(Content provider)方式 在Android的設計“哲學”裡是鼓勵開發者使用內部類的,這樣不但使用方便,而且執行效率也高。 1.什麼是ContentProvider 資料在Android當中是私有的,當然這些資料包括檔案資料和資料庫資料以及一些其他型別的資料。難道兩個程式之間就沒有辦法對於資料進行交換?解決這個問題主要靠ContentProvider。 一個Content Provider類實現了一組標準的方法介面,從而能夠讓其他的應用儲存或讀取此Content Provider的各種資料型別。也就是說,一個程式可以通過實現一個Content Provider的抽象介面將自己的資料暴露出去。外界根本看不到,也不用看到這個應用暴露的資料在應用當中是如何儲存的,或者是用資料庫儲存還是用檔案儲存,還是通過網上獲得,這些一切都不重要,重要的是外界可以通過這一套標準及統一的介面和程式裡的資料打交道,可以讀取程式的資料,也可以刪除程式的資料,當然,中間也會涉及一些許可權的問題。 下邊列舉一些較常見的介面,這些介面如下所示。 query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通過Uri進行查詢,返回一個Cursor。 insert(Uri url, ContentValues values):將一組資料插入到Uri 指定的地方。 update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的資料。 delete(Uri url, String where, String[] selectionArgs):刪除指定Uri並且符合一定條件的資料。 2.什麼是ContentResolver 外界的程式通過ContentResolver介面可以訪問ContentProvider提供的資料,在Activity當中通過getContentResolver()可以得到當前應用的ContentResolver例項。 ContentResolver提供的介面和ContentProvider中需要實現的介面對應,主要有以下幾個。 query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通過Uri進行查詢,返回一個Cursor。 insert(Uri url, ContentValues values):將一組資料插入到Uri 指定的地方。 update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的資料。 delete(Uri url, String where, String[] selectionArgs):刪除指定Uri並且符合一定條件的資料。 3.ContentProvider和ContentResolver中用到的Uri 在ContentProvider和ContentResolver當中用到了Uri的形式通常有兩種,一種是指定全部資料,另一種是指定某個ID的資料。 我們看下面的例子。 content://contacts/people/ 這個Uri指定的就是全部的聯絡人資料。 content://contacts/people/1 這個Uri指定的是ID為1的聯絡人的資料。 在上邊兩個類中用到的Uri一般由3部分組成。 第一部分是:“content://” 。 第二部分是要獲得資料的一個字串片段。 最後就是ID(如果沒有指定ID,那麼表示返回全部)。 由於URI通常比較長,而且有時候容易出錯,且難以理解。所以,在Android當中定義了一些輔助類,並且定義了一些常量來代替這些長字串的使用,例如下邊的程式碼: Contacts.People.CONTENT_URI (聯絡人的URI)。 1)實現的功能 在這個例子裡邊,首先在系統的聯絡人應用當中插入一些聯絡人資訊,然後把這些聯絡人的名字和電話再顯示出來 2)實現方法 package com.contentProvider;

import android.app.ListActivity; import android.database.Cursor; import android.os.Bundle; import android.provider.Contacts.Phones; import android.widget.ListAdapter; import android.widget.SimpleCursorAdapter;

public class ContentProviderDemo extends ListActivity {

protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    //getContentResolver()方法得到應用的ContentResolver例項。   
    // query(Phones.CONTENT_URI, null, null, null, null)。它是ContentResolver裡的方法,負責查詢所有聯絡人,並返回一個Cursor。這個方法引數比較多,每個引數的具體含義如下。   
    //·  第一個引數為Uri,在這個例子裡邊這個Uri是聯絡人的Uri。   
    //·  第二個引數是一個字串的陣列,數組裡邊的每一個字串都是資料表中某一列的名字,它指定返回資料表中那些列的值。   
    //·  第三個引數相當於SQL語句的where部分,描述哪些值是我們需要的。   
    //·  第四個引數是一個字串陣列,它裡邊的值依次代替在第三個引數中出現的“?”符號。   
    //·  第五個引數指定了排序的方式。   
    Cursor c = getContentResolver().query(Phones.CONTENT_URI, null, null, null, null);  
    startManagingCursor(c); //讓系統來管理生成的Cursor。   
    ListAdapter adapter = new SimpleCursorAdapter(  
            this,  
            android.R.layout.simple_list_item_2,   
            c,   
            new String[] { Phones.NAME, Phones.NUMBER },   
            new int[] { android.R.id.text1, android.R.id.text2 });  
    setListAdapter(adapter); //將ListView和SimpleCursorAdapter進行繫結。   
}      

}

五. 網路儲存方式

1.例子介紹 通過郵政編碼查詢該地區的天氣預報,以POST傳送的方式傳送請求到webservicex.net站點,訪問WebService.webservicex.net站點上提供查詢天氣預報的服務,具體資訊請參考其WSDL文件,網址為: http://www.webservicex.net/WeatherForecast.asmx?WSDL。 輸入:美國某個城市的郵政編碼。 輸出:該郵政編碼對應城市的天氣預報。 2.實現步驟如下 (1)如果需要訪問外部網路,則需要在AndroidManifest.xml檔案中加入如下程式碼申請許可權許可: <! – Permissions --> <uses-permission Android:name=“Android.permission.INTERNET” / (2)以HTTP POST的方式傳送(注意:SERVER_URL並不是指WSDL的URL,而是服務本身的URL)。實現的程式碼如下所示:

private static final String SERVER_URL=“http://www.webservicex.net/WeatherForecast.asmx/GetWeatherByZipCode”; //定義需要獲取的內容來源地址 HttpPost request = new HttpPost(SERVER_URL); //根據內容來源地址建立一個Http請求 // 新增一個變數 List params = new ArrayList (); // 設定一個華盛頓區號 params.add(new BasicNameValuePair(“ZipCode”, “200120”)); //新增必須的引數 request.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); //設定引數的編碼 try { HttpResponse httpResponse = new DefaultHttpClient().execute(request); //傳送請求並獲取反饋 // 解析返回的內容 if(httpResponse.getStatusLine().getStatusCode() != 404) { String result = EntityUtils.toString(httpResponse.getEntity()); Log.d(LOG_TAG, result); } } catch (Exception e) { Log.e(LOG_TAG, e.getMessage()); } 程式碼使用Http從webservicex獲取ZipCode為“200120”(美國WASHINGTON D.C)的內容

Android小知識

  1. UI中, Padding和Margin有什麼區別? 答: Padding是控制元件的內容相對控制元件的邊緣的邊距,而Margin是控制元件邊緣相對於其他控制元件的邊距。如下圖所示:
  2. android本身的一些限制,比如apk包大小限制,讀取大檔案時的時間限。 答:apk包大小限制不好說,有的apk為100M,還是能裝到手機上。一般的apk大小為5~10M左右。讀取大檔案的時間應該是在main執行緒裡面,時間限制為5秒左右。
  3. ListView如何提高其效率? 答:1、使用分頁載入,不要一次性載入所有資料。 2、複用convertView。在getItemView中,判斷converView是否為空,如果不為空,可複用。 3、非同步載入圖片。Item中如果包含有webimage,那麼最好非同步載入。 4、快速滑動時,不顯示圖片。當快速滑動列表(SCROLL_STATE_FLING),item中的圖片或獲取需要消耗資源的view,可以不顯示出來;而處於其他兩種狀態(SCROLL_STATE_IDLE和SCROLL_STATE_TOUCH_SCROLL),則將那些view顯示出來