2013年4月27日 星期六

Android - Fragment

        在Android 3.0之後,設計上發生了一些變革,最重要的莫過於Fragment與Action Bar,除了可以讓應用程式設計上更有彈性,使用更優雅之外,更可以重複性的使用這些Fragment,並且在Support Library的支援下,使用Fragment來設計跨版本的應用程式是個不錯的選擇。
Fragment是擁有部份功能的Activity,Fragment必須使用在Activity裡,也直接受到Activity的影響,例如:如果Activity Pause或Destroy,則在該Activity裡的Fragment也會一併Pause或Destroy。

        Fragment的設計應該視為一個可重用的Activity模組,因為Fragment會有自己的Layout及Life-cycle,並且Fragment是可以使用到不同的Activity裡進行重用,所以在設計Fragment時,不應該在Fragment裡再呼叫其他Fragment,例如:在Tablet時,因為有比較大的螢幕,所以使用單獨的兩個Fragment包在同一個Activity裡,而在Handset時,則不必再重寫Fragment,直接將不同的Fragment運用在不同的Activity裡;並且當使用FragmentTransaction添加或取代一個Fragment到一個Activity的Layout時,其Fragment的Layout是添加到Activity的ViewGroup內。



Fragment生命週期

       如果要建立一個Fragment,則必須使用繼承自Fragment的子類別,例如:ListFragment、DialogFragment、PreferenceFragment等。下圖為Fragment的生命週期,紅色框代表必須實作的事件,而onCreateView會在要呈現該Fragment畫面時就會執行,所以onCreateView所傳回的是View,並且帶入參數就有LayoutInflater,可以幫助Layout轉化成View加入到Activity的ViewGroup中,如果使用的是ListFragment,則預設該View就為ListView;如果一個Activity裡配置一個Fragment,則container參數會是null,也就是Fragment的Layout並沒有跟Activity的Layout綁在一起,如果是使用FragmentTransaction加入或取代Fragment,則container參數會是Activity的ViewGroup。
例:

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,  Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.example_fragment, container, false);
    }


        至於其他的事件,例如:onCreate()、onStart()、onResume()、onPause()、onStop()、onDestroy(),基本上都與Activity所對應的事件相似。除了直接在Layout裡指定Fragment之外,如果要在程式端動態增加、取代、刪除Fragment,可以使用FragmentTransaction來達成,例如:

FragmentTransaction ft = getFragmentManager().beginTransaction();

//只要是ViewGroup即可,不一定要FrameLayout
ft.replace(R.id.details, details);//進行取代   
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);

//如果有加上這一行會將Fragment加到Back Stack,
//這樣就可以直接使用上一頁鈕回到上一個Fragment的狀態,
//否則按下上一頁就會直接關掉Activity
ft.addToBackStack(null);

//完成Fragment的異動
ft.commit();

commit必須在Activity Save State
(也就是 Another activity comes into the foreground)之前執行,否則會拋錯,
要不就要執行commitAllowingStateLoss()。


轉貼請註明出處,最好直接使用聯結轉貼!Thanks~
作者: Samuel - 林靖傑
日期:2013/04/27

2013年1月18日 星期五

Android - App Widget - 基本概念

1. 需繼承AppWidgetProvider,該類別繼承自BroadcastReceiver,所以在AndroidManifest.xml註冊時需要使用receiver。
2. 對於App Widget一定需要加上接收android.appwidget.action.APPWIDGET_UPDATE這個ACTION的Intent-filter,因為當AppWidgetManager要更新時,會對App Widget進行該動作的Broadcast,至於其它的ACTION會隱含接收,所以不明確指定也會接收以下ACTION:
  • AppWidgetManager.ACTION_APPWIDGET_DELETED
  • AppWidgetManager.ACTION_APPWIDGET_ENABLED
  • AppWidgetManager.ACTION_APPWIDGET_DISABLED
  • AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED
3. 必需在AndroidManifest.xml裡指定AppWidgetProviderInfo的xml檔,主要是用來描述App Widget的相關描述,例如:
<meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget_info"/>。
  • updatePeriodMillis:為了電池的使用長度,通常更新並不會一定在所設定的時間內執行,Google建議每一個小時不要更新超過一次,如果設備正在睡眠,也會在指定的時間進行更新,如果想要更新的更加頻繁,或者不想要在設備睡眠時進行更新,則可以在Configuration Activity使用AlarmManager來發起更新。
  • resizeMode:App Widget可以Resize的模式有"horizontal"、"vertical"、"none",如果要水平及垂直都允許的話,可以設置為"horizontal|vertical"。
  • previewImage:指定App Widget在選擇區時所呈現的預覽圖片,如果沒有指定就依程式預設的圖示呈現,Android 3.0開始支援。
  • android:previewImage="@drawable/preview"
  • widgetCategory:Android 4.2(API 17)後支援讓App Widget可以呈現在鎖定畫面,該設定值有"home_screen"及"keyguard",一般沒有特別設定時,App Widget預設支援"home_screen",如果要出現在鎖定頁面,則可以設定為"keyguard|home_screen"。
  • initialLayout:指定App Widget的Layout。
  • initialKeyguardLayout:Android 4.2後,支援讓App Widget可以呈現在鎖定畫面,該設定為指定鎖定畫面所呈現的App Widget Layout。
  • minWidth & minHeight:App Widget Layout最小應該呈現的長寬,以dp為單位,為了跨平台最好不要超過4*4 Cells。
  • minResizeWidth & minResizeHeight:設定使用者在resize App Widget時,最小能夠調整到的長寬,避免調整太過頭而無法操作。
 
4. AppWidgetProvider - onEnabled:不管加入多少個同樣的App Widget,這個方法只會在第一次將App Widget加入App Widget Host中時才會執行。
 
5. AppWidgetProvider - onUpdate():該方法會在User每加入一次App Widget時便會呼叫,以及updatePeriodMillis所設定的間隔呼叫,如果有使用Configuration Activity,則使用者加入App Widget時不會被呼叫,也就是如果沒有Configuration Activity,則View的Event Handler在onUpdate()設定,如果有Configuration Activity,則在Configuration Activity中設定。
 
6. AppWidgetProvider - onDeleted:每次刪除App Widget時都會執行。
 
7. AppWidgetProvider - onDisabled:當最後一個App Widget從App Widget Host中移除時便會呼叫,在這邊對應到onEnabled中所做的工作,例如在onEnabled建立暫時資料庫,則在onDisabled就需刪除暫時資料庫。
 
8. AppWidgetProvider - onReceive:因為AppWidgetProvider繼承自BroadcastReceiver,所以會有onRecived事件,但是通常不建議自行處理該處的事件,除非有需要自行處理所接收的App Widget Broadcast,否則對應的事件應該使用之前所述的onEnabled、onUpdate、onDeleted、onDisabled即可。onRecived所處理的ACTION:
  • AppWidgetManager.ACTION_APPWIDGET_UPDATE
  • AppWidgetManager.ACTION_APPWIDGET_DELETED
  • AppWidgetManager.ACTION_APPWIDGET_ENABLED
  • AppWidgetManager.ACTION_APPWIDGET_DISABLED
  • AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED
 
9. AppWidgetProvider - onAppWidgetOptionsChanged():在API Level 16 (Android 4.1)後才支援,當App Widget進行Resize的動作或第一次置放時就會執行,可以使用getAppWidgetOptions()傳回的Bundle來取得以下的資訊:
  • OPTION_APPWIDGET_MIN_WIDTH—Contains the lower bound on the current width, in dp units, of a widget instance.
  • OPTION_APPWIDGET_MIN_HEIGHT—Contains the lower bound on the current height, in dp units, of a widget instance.
  • OPTION_APPWIDGET_MAX_WIDTH—Contains the upper bound on the current width, in dp units, of a widget instance.
  • OPTION_APPWIDGET_MAX_HEIGHT—Contains the upper bound on the current width, in dp units, of a widget instance.

轉貼請註明出處,最好直接使用聯結轉貼!Thanks~
作者: Samuel - 林靖傑
日期:2013/01/18

2012年11月21日 星期三

Android - Process, Thread, MessageQueue and Looper 2013/09/24 Updated.

        當一個應用程式Process執行後,會啟動一個Thread來執行應用程式,該Thread稱之為Main Thread,Main Thread非常的重要,例如:調度事件到對應的Widget執行,像OnClick、OnKeyDown或drawing events 等,由於Main Thread也是跟Android UI toolkit(繼承android.widget和android.view)的Components(以下簡稱UI Toolkits)互動的主要Thread,所以我們也將Main Thread稱之為UI Thread。

        所有在同一個Process的Components,都會在UI Thread初次執行,並且Android會使用UI Thread來調度這些Components,例如;當使用者點擊螢幕上的按鈕,UI Thread就會調派Touch Event給對應的Button Widget (也就是改變Button壓下的狀態,並且將該事件佇列到event queue (Message Queue)),然後UI Thread再從event queue (Message Queue) 取出(透過Looper一個一個取出),通知widget進行畫面重畫並執行相對應的事件Callback。

        基於UI Thread的重要性,如果將UI Thread執行long-running的行為,例如:下載或者透過網路查詢資料等動作,勢必會使UI Thread無法分心其他事,甚至連drawing events也無法執行,更不用說Event調度等工作,如果UI Thread超過五秒沒反應時,系統會拋出application not responding (ANR) dialog,告知系統沒有回應,所以我們必須使用其它Thread來完成long-running的行為,這樣的Thread也稱為Worker Thread。

    因為UI Toolkits本身並沒有進行Thread-safe的設計,所以當Worker Thread要存取修改UI Toolkits時,要注意以下兩點規則:
  1. Do not block the UI thread
  2. 別透過UI Thread以外的Thread(Worker Thread)來直接存取UI Toolkits

         對於上述的第二點規則,必須要先瞭解Android的Thread架構,在Android系統中,一個Thread就有一個Message Queue,將所需的工作進行佇列,以便逐一取出執行,執行完再取出下一個執行,使其不會導致資源競用,而達成Thread-safe的狀態,但是要如何逐一取出使用,則必須要靠Looper來查看Message Queue中是否有尚未執行的訊息,而一個Thread中會有一個Looper來查看Message Queue的佇列狀態,如本人整理之投影片:


 



        而如果要更改UI Toolkits的狀態,則Worker Thread必須透過Handler將Message傳送到Message Queue,以等待Looper取出後交由Main Thread執行,如下圖:



轉貼請註明出處,最好直接使用聯結轉貼!Thanks~
作者: Samuel - 林靖傑
日期:2013/09/24