天天看點

Android四大基本元件

Android四大基本元件分别是Activity,Service服務,Content Provider内容提供者,BroadcastReceiver廣播接收器。

Activity :

應用程式中,一個Activity通常就是一個單獨的螢幕,它上面可以顯示一些控件也可以監聽并處理使用者的事件做出響應。

Activity之間通過Intent進行通信。在Intent 的描述結構中,有兩個最重要的部分:動作和動作對應的資料。

常用的動作類型:M AIN(activity的門戶)、VIEW、PICK、EDIT 等。而動作對應的資料則以URI 的形式進行表示。

BroadcastReceive廣播接收器:

你的應用可以使用它對外部事件進行過濾隻對感興趣的外部事件(如當電話呼入時,或者資料網絡可用時)進行接收并做出響應。廣播接收器沒有使用者界面。然而,它們可以啟動一個activity或serice 來響應它們收到的資訊,或者用NotificationManager 來通知使用者。通知可以用很多種方式來吸引使用者的注意力──閃動背燈、震動、播放聲音等。一般來說是在狀态欄上放一個持久的圖示,使用者可以打開它并擷取消息。

廣播類型:

普通廣播,通過Context.sendBroadcast(Intent myIntent)發送的

有序廣播,通過Context.sendOrderedBroadcast(intent, receiverPermission)發送的,該方法第2個參數決定該廣播的級别,級别數值是在 -1000 到 1000 之間 , 值越大 , 發送的優先級越高;廣播接收者接收廣播時的級别級别(可通過intentfilter中的priority進行設定設為2147483647時優先級最高),同級别接收的先後是随機的, 再到級别低的收到廣播,進階别的或同級别先接收到廣播的可以通過abortBroadcast()方法截斷廣播使其他的接收者無法收到該廣播,還有其他構造函數

異步廣播,通過Context.sendStickyBroadcast(Intent myIntent)發送的,還有sendStickyOrderedBroadcast(intent, resultReceiver, scheduler,  initialCode, initialData, initialExtras)方法,該方法具有有序廣播的特性也有異步廣播的特性;發送異步廣播要: <uses-permission android:name="android.permission.BROADCAST_STICKY" />權限,接收并處理完Intent後,廣播依然存在,直到你調用removeStickyBroadcast(intent)主動把它去掉

注意:發送廣播時的intent參數與Contex.startActivity()啟動起來的Intent不同,前者可以被多個訂閱它的廣播接收器調用,後者隻能被一個(Activity或service)調用

監聽廣播Intent步驟:

1>             寫一個繼承BroadCastReceiver的類,重寫onReceive()方法,廣播接收器僅在它執行這個方法時處于活躍狀态。當onReceive()傳回後,它即為失活狀态,注意:為了保證使用者互動過程的流暢,一些費時的操作要放到線程裡,如類名SMSBroadcastReceiver

2>            注冊該廣播接收者,注冊有兩種方法程式動态注冊和AndroidManifest檔案中進行靜态注冊(可了解為系統中注冊)如下:

        靜态注冊,注冊的廣播,下面的priority表示接收廣播的級别"2147483647"為最高優先級

<receiver android:name=".SMSBroadcastReceiver" >
  <intent-filter android:priority = "2147483647" >
    <action android:name="android.provider.Telephony.SMS_RECEIVED" />
  </intent-filter>
</receiver >      

動态注冊,一般在Activity可互動時onResume()内注冊BroadcastReceiver

IntentFilter intentFilter=new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(mBatteryInfoReceiver ,intentFilter);

//反注冊
unregisterReceiver(receiver);      

注意:

1.生命周期隻有十秒左右,如果在 onReceive() 内做超過十秒内的事情,就會報ANR(Application No Response) 程式無響應的錯誤資訊,如果需要完成一項比較耗時的工作 , 應該通過發送 Intent 給 Service, 由Service 來完成 . 這裡不能使用子線程來解決 , 因為 BroadcastReceiver 的生命周期很短 , 子線程可能還沒有結束BroadcastReceiver 就先結束了 .BroadcastReceiver 一旦結束 , 此時 BroadcastReceiver 的所在程序很容易在系統需要記憶體時被優先殺死 , 因為它屬于空程序 ( 沒有任何活動元件的程序 ). 如果它的宿主程序被殺死 , 那麼正在工作的子線程也會被殺死 . 是以采用子線程來解決是不可靠的

2.他們兩者之間的作用域不同,Activity動态注冊隻有目前activity有用,而靜态注冊隻要系統啟動就會起作用。

Service 服務:

一個Service 是一段長生命周期的,沒有使用者界面的程式,可以用來開發如監控類程式。

比較好的一個例子就是一個正在從播放清單中播放歌曲的媒體播放器。在一個媒體播放器的應用中,應該會有多個activity,讓使用者可以選擇歌曲并播放歌曲。然而,音樂重放這個功能并沒有對應的activity,因為使用者當然會認為在導航到其它螢幕時音樂應該還在播放的。在這個例子中,媒體播放器這個activity 會使用Context.startService()來啟動一個service,進而可以在背景保持音樂的播放。同時,系統也将保持這個service 一直執行,直到這個service 運作結束。另外,我們還可以通過使用Context.bindService()方法,連接配接到一個service 上(如果這個service 還沒有運作将啟動它)。當連接配接到一個service 之後,我們還可以service 提供的接口與它進行通訊。拿媒體播放器這個例子來說,我們還可以進行暫停、重播等操作。

Service使用步驟如下

       1>繼承service類

       2>AndroidManifast.xml配置清單檔案中<application>節點裡對服務進行配置

              <service name=".SMSService"/>

服務不能自己運作,需要通過Contex.startService()或Contex.bindService()啟動服務

通過startService()方法啟動的服務于調用者沒有關系,即使調用者關閉了,服務仍然運作想停止服務要調用Context.stopService(),此時系統會調用onDestory(),使用此方法啟動時,服務首次啟動系統先調用服務的onCreate()-->onStart(),如果服務已經啟動再次調用隻會觸發onStart()方法

使用bindService()啟動的服務與調用者綁定,隻要調用者關閉服務就終止,使用此方法啟動時,服務首次啟動系統先調用服務的onCreate()-->onBind(),如果服務已經啟動再次調用不會再觸發這2個方法,調用者退出時系統會調用服務的onUnbind()-->onDestory(),想主動解除綁定可使用Contex.unbindService(),系統依次調用onUnbind()-->onDestory();

如果做長連接配接服務,可以通過service來實作。

Content Provider内容提供者 :

android平台提供了Content Provider使一個應用程式的指定資料集提供給其他應用程式。這些資料可以存儲在檔案系統中、在一個SQLite資料庫、或以任何其他合理的方式,

其他應用可以通過ContentResolver類(見ContentProviderAccessApp例子)從該内容提供者中擷取或存入資料.(相當于在應用外包了一層殼),

隻有需要在多個應用程式間共享資料是才需要内容提供者。例如,通訊錄資料被多個應用程式使用,且必須存儲在一個内容提供者中

它的好處:統一資料通路方式。

android系統自帶的内容提供者(頂級的表示資料庫名,非頂級的都是表名)這些内容提供者在SDK文檔的android.provider Java包中都有介紹。見:http://developer.android.com/reference/android/provider/package-summary.html

├────Browser

├────CallLog

├────Contacts

│                ├────Groups

│                ├────People

│                ├────Phones

│                └────Photos

├────Images

│                └────Thumbnails

├────MediaStore

│                ├────Albums

│                ├────Artists

│                ├────Audio

│                ├────Genres

│                └────Playlists

├────Settings

└────Video

 CallLog:位址和接收到的電話資訊

 Contact.People.Phones:存儲電話号碼

 Setting.System:系統設定和偏好設定

使用Content Provider對外共享資料的步驟

1>繼承ContentProvider類并根據需求重寫以下方法:

Android四大基本元件
    public boolean onCreate();//處理初始化操作

       /**
        * 插入資料到内容提供者(允許其他應用向你的應用中插入資料時重寫)
        * @param uri
        * @param initialValues 插入的資料
        * @return
        */
       public Uri insert(Uri uri, ContentValues initialValues);

       /**
        * 從内容提供者中删除資料(允許其他應用删除你應用的資料時重寫)
        * @param uri
        * @param selection 條件語句
        * @param selectionArgs 參數
        * @return
        */
       public int delete(Uri uri, String selection, String[] selectionArgs);

       /**
        * 更新内容提供者已存在的資料(允許其他應用更新你應用的資料時重寫)
        * @param uri
        * @param values 更新的資料
        * @param selection 條件語句
        * @param selectionArgs 參數
        * @return
        */
       public int update(Uri uri, ContentValues values, String selection,
                     String[] selectionArgs);

       /**
        * 傳回資料給調用者(允許其他應用從你的應用中擷取資料時重寫)
        * @param uri
        * @param projection 列名
        * @param selection 條件語句
        * @param selectionArgs 參數
        * @param sortOrder 排序
        * @return
        */
       public Cursor query(Uri uri, String[] projection, String selection,
                     String[] selectionArgs, String sortOrder) ;         

       /**
        * 用于傳回目前Uri所代表資料的MIME類型
        * 如果操作的資料為集合類型(多條資料),那麼傳回的類型字元串應該為vnd.android.cursor.dir/開頭
        * 例如要得到所有person記錄的Uri為content://com.bravestarr.provider.personprovider/person,
     *   那麼傳回的MIME類型字元串應該為"vnd.android.cursor.dir/person"
        * 如果操作的資料為單一資料,那麼傳回的類型字元串應該為vnd.android.cursor.item/開頭
        * 例如要得到id為10的person記錄的Uri為content://com.bravestarr.provider.personprovider/person/10,
     *   那麼傳回的MIME類型字元串應該為"vnd.android.cursor.item/person"
        * @param uri
        */
       public String getType(Uri uri)      
Android四大基本元件

這些方法中的Uri參數,得到後需要進行解析然後做對應處理,Uri表示要操作的資料,包含兩部分資訊:

       1.需要操作的contentprovider

       2.對contentprovider中的什麼資料進行操作,一個Uri格式:結構頭://authorities(域名)/路徑(要操作的資料,根據業務而定)

              content://com.bravestarr.provider.personprovider/person/10

說明:contentprovider的結構頭已經由android規定為content://

authorities用于唯一辨別這個contentprovider程式,外部調用者可以根據這個找到他

路徑表示我們要操作的資料,路徑的建構根據業務而定.路徑格式如下:                                                        

       要操作person表行号為10的記錄,可以這樣建構/person/10

       要操作person表的所有記錄,可以這樣建構/person

2>在AndroidManifest.xml中使用<provider>對ContentProvider進行配置注冊(内容提供者注冊它自己就像網站注冊域名),ContentProvider采用authoritie(原意授權,可了解為域名)作為唯一辨別,友善其他應用能找到

Android四大基本元件
<application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <!-- authorities屬性命名建議:公司名.provider.SomeProvider-->
        <provider android:name=".PersonProvider" android:authorities="com.bravestarr.provider.personprovider"/>
         ...
</application>      
Android四大基本元件

Activity生命周期:

Android四大基本元件

圖3.1activity生命周期圖

Activity整個生命周期的4種狀态、7個重要方法和3個嵌套循環

1>   四種狀态

  1.       活動(Active/Running)狀态

當Activity運作在螢幕前台(處于目前任務活動棧的最上面),此時它擷取了焦點能響應使用者的操作,屬于運作狀态,同一個時刻隻會有一個Activity 處于活動(Active)或運作

(Running)狀态

  1.     暫停(Paused)狀态

當Activity失去焦點但仍對使用者可見(如在它之上有另一個透明的Activity或Toast、AlertDialog等彈出視窗時)它處于暫停狀态。暫停的Activity仍然是存活狀态(它保留着所有的狀态和成員資訊并保持和視窗管理器的連接配接),但是當系統記憶體極小時可以被系統殺掉

3.      停止(Stopped)狀态

完全被另一個Activity遮擋時處于停止狀态,它仍然保留着所有的狀态和成員資訊。隻是對使用者不可見,當其他地方需要記憶體時它往往被系統殺掉

4.      非活動(Dead)狀态

Activity 尚未被啟動、已經被手動終止,或已經被系統回收時處于非活動的狀态,要手動終止Activity,可以在程式中調用"finish"方法。

如果是(按根據記憶體不足時的回收規則)被系統回收,可能是因為記憶體不足了

記憶體不足時,Dalvak 虛拟機會根據其記憶體回收規則來回收記憶體:

      1. 先回收與其他Activity 或Service/Intent Receiver 無關的程序(即優先回收獨

立的Activity)是以建議,我們的一些(耗時)背景操作,最好是作成Service的形式

      2.不可見(處于Stopped狀态的)Activity

      3.Service程序(除非真的沒有記憶體可用時會被銷毀)

      4.非活動的可見的(Paused狀态的)Activity

      5.目前正在運作(Active/Running狀态的)Activity

2>  7個重要方法,當Activity從一種狀态進入另一狀态時系統會自動調用下面相應的方

法來通知使用者這種變化

當Activity第一次被執行個體化的時候系統會調用,

整個生命周期隻調用1次這個方法

通常用于初始化設定: 1、為Activity設定所要使用的布局檔案2、為按鈕綁定監聽器等靜态的設定操作

      onCreate(Bundle savedInstanceState);

當Activity可見未獲得使用者焦點不能互動時系統會調用

      onStart();

當Activity已經停止然後重新被啟動時系統會調用

      onRestart();

當Activity可見且獲得使用者焦點能互動時系統會調用

      onResume();

當系統啟動另外一個新的Activity時,在新Activity啟動之前被系統調用儲存現有的Activity中的持久資料、停止動畫等,這個實作方法必須非常快。當系統而不是使用者自己出于回收記憶體時,關閉了activity 之後。使用者會期望當他再次回到這個activity 的時候,它仍保持着上次離開時的樣子。此時用到了onSaveInstanceState(),方法onSaveInstanceState()用來儲存Activity被殺之前的狀态,在onPause()之前被觸發,當系統為了節省記憶體銷毀了Activity(使用者本不想銷毀)時就需要重寫這個方法了,當此Activity再次被執行個體化時會通過onCreate(Bundle savedInstanceState)将已經儲存的臨時狀态資料傳入因為onSaveInstanceState()方法不總是被調用,觸發條件為(按下HOME鍵,按下電源按鍵關閉螢幕,橫豎屏切換情況下),你應該僅重寫onSaveInstanceState()來記錄activity的臨時狀态,而不是持久的資料。應該使用onPause()來存儲持久資料。

      onPause();

當Activity被新的Activity完全覆寫不可見時被系統調用

      onStop();

當Activity(使用者調用finish()或系統由于記憶體不足)被系統銷毀殺掉時系統調用,(整個生命周期隻調用1次)用來釋放onCreate ()方法中建立的資源,如結束線程等

      onDestroy();

3>  3個嵌套循環

             1.Activity完整的生命周期:從第一次調用onCreate()開始直到調用onDestroy()結束

             2.Activity的可視生命周期:從調用onStart()到相應的調用onStop()

                    在這兩個方法之間,可以保持顯示Activity所需要的資源。如在onStart()中注冊一個廣播接收者監聽影響你的UI的改變,在onStop() 中登出。

             3.Activity的前台生命周期:從調用onResume()到相應的調用onPause()。

橫豎屏切換時候Activity的生命周期

他切換時具體的生命周期是怎麼樣的:

1、建立一個Activity,并把各個生命周期列印出來

2、運作Activity,得到如下資訊

onCreate-->

onStart-->

onResume-->

3、按crtl+f12切換成橫屏時

onSaveInstanceState-->

onPause-->

onStop-->

onDestroy-->

onCreate-->

onStart-->

onRestoreInstanceState-->

onResume-->

4、再按crtl+f12切換成豎屏時,發現列印了兩次相同的log

onSaveInstanceState-->

onPause-->

onStop-->

onDestroy-->

onCreate-->

onStart-->

onRestoreInstanceState-->

onResume-->

onSaveInstanceState-->

onPause-->

onStop-->

onDestroy-->

onCreate-->

onStart-->

onRestoreInstanceState-->

onResume-->

5、修改AndroidManifest.xml,把該Activity添加android:configChanges="orientation",執行步驟3

onSaveInstanceState-->

onPause-->

onStop-->

onDestroy-->

onCreate-->

onStart-->

onRestoreInstanceState-->

onResume-->

6、再執行步驟4,發現不會再列印相同資訊,但多列印了一行onConfigChanged

onSaveInstanceState-->

onPause-->

onStop-->

onDestroy-->

onCreate-->

onStart-->

onRestoreInstanceState-->

onResume-->

onConfigurationChanged-->

總結:

1、不設定Activity的android:configChanges時,切屏會重新調用各個生命周期,切橫屏時會執行一次,切豎屏時會執行兩次

2、設定Activity的android:configChanges="orientation"時,切屏還是會重新調用各個生命周期,切橫、豎屏時隻會執行一次

3、設定Activity的android:configChanges="orientation|keyboardHidden"時,切屏不會重新調用各個生命周期,隻會執行onConfigurationChanged方法

總結一下整個Activity的生命周期

補充一點,目前Activity産生事件彈出Toast和AlertDialog的時候Activity的生命周期不會有改變

Activity運作時按下HOME鍵(跟被完全覆寫是一樣的):onSaveInstanceState --> onPause --> onStop,再次進入激活狀态時: onRestart -->onStart--->onResume

Service服務生命周期:

Android四大基本元件

Service完整的生命周期:從調用onCreate()開始直到調用onDestroy()結束

Service有兩種使用方法:

1>以調用Context.startService()啟動,而以調用Context.stopService()結束

2>以調用Context.bindService()方法建立,以調用Context.unbindService()關閉

service重要的生命周期方法

當使用者調用startService ()或bindService()時,Service第一次被執行個體化的時候系統會調用,整個生命周期隻調用1次這個方法,通常用于初始化設定。注意:多次調用startService()或bindService()方法不會多次觸發onCreate()方法

void onCreate()      

當使用者調用stopService()或unbindService()來停止服務時被系統調用,(整個生命周期隻調用1次)用來釋放onCreate()方法中建立的資源

void onDestroy()      

通過startService()方法啟動的服務

      初始化結束後系統會調用該方法,用于處理傳遞給startService()的Intent對象。如音樂服務會打開Intent 來探明将要播放哪首音樂,并開始播放。注意:多次調用startService()方法會多次觸發onStart()方法

void onStart(Intent intent)      

通過bindService ()方法啟動的服務

      初始化結束後系統會調用該方法,用來綁定傳遞給bindService 的Intent 的對象。注意:多次調用bindService()時,如果該服務已啟動則不會再觸發此方法

IBinder onBind(Intent intent)      

使用者調用unbindService()時系統調用此方法,Intent 對象同樣傳遞給該方法

boolean onUnbind(Intent intent)      

如果有新的用戶端連接配接至該服務,隻有當舊的調用onUnbind()後,新的才會調用該方法

void onRebind(Intent intent)      

補充:onCreate(Bundle savedInstanceState)與onSaveInstanceState(Bundle savedInstanceState)配合使用,見如下代碼,達到顯示activity被系統殺死前的狀态

Android四大基本元件
@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (null != savedInstanceState) {
            String _userid = savedInstanceState.getString("StrUserId");
            String _uid = savedInstanceState.getString("StrUid");
            String _serverid = savedInstanceState.getString("StrServerId");
            String _servername = savedInstanceState.getString("StrServerName");
            int _rate = savedInstanceState.getInt("StrRate");
            //updateUserId(_userid);
            //updateUId(_uid);
            //updateServerId(_serverid);
            //updateUserServer(_servername);
            //updateRate(_rate);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        savedInstanceState.putString("StrUserId", getUserId());
        savedInstanceState.putString("StrUid", getUId());
        savedInstanceState.putString("StrServerId", getServerId());
        savedInstanceState.putString("StrServerName", getServerName());
        savedInstanceState.putInt("StrRate", getRate());
    }      
Android四大基本元件

繼續閱讀