基於Fragment的Android前台服務架構

來源:互聯網
上載者:User

從Android 3.0開始,Google引入了全新的Fragment UI體系,重新詮釋了可複用可延展的Android UI設計理念。Android Support Library更是為任何面向低版本Android的應用開發人員提供了完整的Fragment與舊版相容方案(backport)。所以,如果開發一款新的Android應用,使用Fragment已無需有任何顧忌。儘早擁抱這一強大的機制設計,可以幫你省下可觀的開發和維護工作量。

說起Service架構,大家可能已經比較熟悉,但將其與Fragment聯絡在一起,就多少有些讓人覺得詫異了。我們不妨先來看看Android現有的標準Service架構,一般也稱之為後台服務。官方文檔中的定義是:一個可在後台執行長時間操作,不提供UI的應用組件。Service的主要特點是生命週期與應用的UI獨立,不隨應用退出而結束。後台服務的典型用途主要有兩大類:執行不隨應用程式切換而打斷的任務(如下載、播放)或監聽和響應系統事件(如來電、位置)。但在實際開發中,Service的實現複雜度並不低,一方面需要考慮並處理服務的生命週期,另一方面還要痛苦的處理服務與UI間的通訊,倘若需要在服務代碼中與使用者互動,要麼使用相當受限的Toast和Notification機制,要麼實現一個複雜的UI回調……

實際上,在大部分的應用情境中,很多與UI相關的處理(即MVC中的Controller)也有類似後台服務一樣的跨介面複用和共用需求,它們同時也與UI有著密切的聯絡,而且僅在應用開啟時發揮作用,例如賬戶的全域狀態、未讀的通知訊息、購物車等。這種需求我們一般稱之為前台服務(Foreground Service)。過去,一部分的這種需求往往採用SharedPreference的方式在不同的介面間實現共用,這樣做不僅有一些額外的開銷(檔案IO),同時資料類型和邏輯的受限也比較明顯。而且,當狀態較為複雜時,每次在狀態切換(如旋轉螢幕)後重建狀態的效能代價也可能影響到使用者體驗。

其實,Fragment機制完全可以優雅的達成上述前台服務的需求,得益於Fragment本身與介面的緊密聯絡,可以方便的實現服務與UI的雙向互連;受益於Fragment自動的生命週期管理,不必刻意提防記憶體泄露;藉助Fragment的切換保留(retain)機制,可以在狀態切換期間保持服務不中止。另外,由於Fragment的生命週期管理是由架構自動完成的,所以開發人員也完全不必在Activity的生命週期事件代碼中加入各種服務相關的冗贅處理,讓代碼更簡潔清晰。唯一的限制是,Fragment不能跨Activity共用。不過按照基於Fragment的介面設計思想,相關聯的UI組件都應基於Fragment實現,共置於一個共同的Activity之下,只有在生命週期可獨立存在和延續的介面中才需要使用單獨的Activity。因此,在嚴格按照Fragment設計思想開發的App中,這一限制並不是一個真正的問題。

如果你對Fragment的這種『特別用途』仍然持保留意見的話,不妨看看官方文檔中的這一段表述:『Adding a fragment without UI』,它明確的暗示了這種使用方式的合理性與可行性。

接下來,就讓我們一起來探索一個可行的基於Fragment的前台服務架構吧。

(1)前台服務的建立、銷毀和擷取

與後台服務類似,前台服務通常也是『按需建立』的,因此服務的建立和擷取可以封裝在一個操作中。由於無UI的Fragment不能通過介面嵌入點的資源ID來訪問,因此tag通常是唯一可靠的辨識和訪問方式。(以下代碼省略了部分異常處理)

private static final String KServiceTagPrefix = "service:";public static <T> T getService(Class service_class, FragmentManager fm) {  final String service_name = KServiceTagPrefix + service_class.getCanonicalName();  @SuppressWarnings("unchecked") T service = (T) fm.findFragmentByTag(service_name);  if (service == null) {    Log.i(TAG, "Starting service: " + service_class.getSimpleName());    service = service_class.newInstance();    FragmentTransaction transaction = fm.beginTransaction();    transaction.add(service, service_name);    transaction.commit();    fm.executePendingTransactions();  }  return service;}

註:executePendingTransactions()是為了確保service對象在返回給調用者之前完成基本的初始化生命週期。

在Activity或其它Fragment中需要用到前台服務時,調用上述靜態方法即可,它會保證在整個Activity生命週期內只有一份服務執行個體,因此我們直接使用前台服務的Class本身作為其標識。調用中需要傳入的另一個參數『FragmentManager』,在FragmentActivity中可以通過getSupportFragmentManager()得到;在Fragment中可以通過getFragmentManager()獲得。

考慮到Fragment的被動生命週期隨Activity的銷毀而終止,而App的Activity生命週期通常是短暫的,因此就不必引入『引用計數』之類的複雜機制來維護前台服務的終止時機了。

(2)UI元素與前台服務之前的互動

從UI元素訪問前台服務,可以簡單的直接使用擷取到的服務執行個體,調用其中的方法。服務執行個體的引用可以安全的儲存在同源的Activity或Fragment對象中,但切忌不可儲存在比父Activity生命週期更長的對象中,如靜態成員中。

反過來,從前台服務訪問UI元素,則稍有一些考究。對Activity的訪問是最簡單的,直接使用getActivity()方法即可得到所在的Activity執行個體,因此我們可以方便的將前台服務的處理過程藉助Activity介面的『進度圓圈』(Indeterminate Progress)給方便使用的指示。對其它Fragment的訪問,官方文檔中提到了使用setTargetFragment()及getTargetFragment()實現,但放在前台服務的情境中,尤其是考慮到共用、解耦、並發等問題,這並不是一個好的方案。或許大部分開發人員更容易聯想到『回調模式』,比如在前台服務類中提供回調註冊介面,這當然也不失為一個可行的方案,但個人更傾向使用靈活易用的LocalBroadcastManager實現服務往UI方向的通知。關於這個機制,這裡就不引申介紹了,感興趣的朋友可以直接看看Android
Support Library的Javadoc。

(3)實現跨狀態切換的服務保持

前台服務由於不直接涉及介面布局,因此完全不必在旋轉螢幕等狀態切換中重建,從而有效降低這一過程中的體驗延遲。實現上,其實非常簡單,只需要在Fragment的初始化過程中將自身設定為『可保持』:

@Override public void onCreate(final Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setRetainInstance(true);}
(4)與Loader機制的配合使用

Loader機制也是Android 3.0中增加的一個實用的輔助機制,可以協助開發人員更好的實現非同步IO與UI組件的協同。與(同進程的)Service機制一樣,前台服務的入口代碼也是在主線程(UI線程)中執行的,因此必須儘可能避免在其中執行IO操作。藉助Loader機制,可以很好的將IO,尤其是網路訪問隔離到獨立的背景工作執行緒中,同時兼顧與UI組件的便捷協同,因此前台服務與Loader機制可謂是一對絕佳的搭檔。但在實際搭配使用中,也有一些需要注意的細節,如果使用不當也可能造成一些很難排查的異常。

使用LoaderManager時,需要明確區分是希望使用Activity層級的LoaderManager還是本Fragment(前台服務)層級的LoaderManager,不同於FragmentManager的統一性,它們其實是兩個不同的執行個體,有著不一樣的影響。大多數情況下,Loader僅限這個前台服務使用,因此使用Fragment層級的LoaderManager是最佳的選擇。如果希望在多個前台服務之間複用某些Loader(例如CursorLoader),則須使用Activity層級的LoaderManager,但同時應小心避免Loader
ID的衝突。

 

以上是對前段時間基於Fragment所實現的前台服務架構初步探索的一個總結,這個機制已經在我最近開發的一個App中正常運作了一段時間,期間並未發現顯著的問題或制肘。如果各位在借鑒上述機制的過程中遇到了任何疑惑和苦難,歡迎與我交流探討。後續相關的經驗和技巧,我也會在本文中補充完善。

附:ServiceFragment抽象基類的完整代碼

public abstract class ServiceFragment extends Fragment {  private static final String KServiceTagPrefix = "service:";  @Override public void onCreate(final Bundle state) {    super.onCreate(state);    setRetainInstance(true);  }  /** @see {@link android.support.v4.content.LocalBroadcastManager#sendBroadcast(Intent)} */  protected boolean sendLocalBroadcast(final Intent intent) {    return LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent);  }  public static <T extends ServiceFragment> T getService(final Class<T> service_class, final FragmentManager fm) {    if (fm == null) throw new IllegalArgumentException("FragmentManager is null");    final String service_name = KServiceTagPrefix + service_class.getCanonicalName();    @SuppressWarnings("unchecked") T service = (T) fm.findFragmentByTag(service_name);    if (service == null) {      Log.i(TAG, "Starting service: " + service_class.getSimpleName());      try {        service = service_class.newInstance();      } catch (final java.lang.InstantiationException e) {        throw new IllegalArgumentException(service_class + " cannot be instantiated");      } catch (final IllegalAccessException e) {        throw new IllegalArgumentException(service_class + " is inaccessible");      }      final FragmentTransaction transaction = fm.beginTransaction();      transaction.add(service, service_name);      transaction.commit();      fm.executePendingTransactions();    }    return service;  }  private static final String TAG = ServiceFragment.class.getSimpleName();}
轉自:http://blog.oasisfeng.com/2012/10/02/android-service-framework-based-on-fragment/

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.