標籤:
緣起
上一篇博文中講到了幾種實現全螢幕顯示Activity內容的方法。然而實際在實現中發現了一些問題,在本篇博文中進行總結下。首先交代一下開發環境,本人使用的是Android Studio 1.5.1,因此使用Eclipse ADT開發或者低版本的SDK的時候可能不會碰到這個問題。首先看onCreate()方法中的實現代碼:
1 @Override2 protected void onCreate(Bundle savedInstanceState) { 3 super.onCreate(savedInstanceState);4 requestWindowFeature(Window.FEATURE_NO_TITLE);5 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);6 setContentView(R.layout.activity_main);7 }
非常簡單的兩行代碼,然而運行代碼的時候應用卻直接奔潰。在Android Studio中觀察應用的奔潰資訊,只看到一條簡單的訊息:threadid=1: thread exiting with uncaught exception (group=0x419f6c50)。根本無從得知哪裡出的錯誤,因為代碼本來就少,才這麼兩行。於是就在網上搜了一下AS的調試方法,總結了一下Android Studio中捕獲異常的方法。
Android Studio捕獲異常方案一
我們知道Java語言提供了try-catch機制來捕獲運行時異常。因此想到,我們在排查Android運行時異常時是否也可以利用起try-catch這個工具呢?加起來就試試好了:
再次在模擬器中運行應用,可以在logcat中輸出如下資訊:
這時候已經可以看到具體的異常資訊了:requestFeature() must be called before adding content。已經達到了我們想要的結果,但是這個方法有個缺點:就是得估計異常大致出現在什麼地方,這才好用try-catch包裹它。至於這個異常代表著什麼,現在先不說,再來看看第二種異常捕獲方案好了。
Android Studio 捕獲異常方案二
這種方案是從網上看來的,利用了Therad的一個靜態方法,首先定義一個Thread.UncaughtExceptionHandler的執行個體,然後在程式中設定為未捕獲異常的預設處理器:
1 private final Thread.UncaughtExceptionHandler handler = new Thread.UncaughtExceptionHandler() { 2 public void uncaughtException(Thread thread, Throwable ex) { 3 Log.e("TestApplication", "Uncaught exception is: ", ex); 4 // log it 5 } 6 }; 7 8 @Override 9 protected void onCreate(Bundle savedInstanceState) {10 Thread.setDefaultUncaughtExceptionHandler(handler);11 12 super.onCreate(savedInstanceState);13 requestWindowFeature(Window.FEATURE_NO_TITLE);14 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);15 setContentView(R.layout.activity_main);16 }
這種方案只需要一個Thread.UncaughtExceptionHandler的執行個體即可,不需要估計異常發生的一般位置。得到的輸出資訊如下:
異常資訊也是非常的明了,能夠看出異常拋出的堆棧資訊,從而更快的跟蹤定位Bug的所在。那麼這個異常到底說明了什麼呢?看字面意思是,requestWindowFeature()方法必須在添加視圖之前先調用。可是以前也是這麼用的啊,也沒見出現過這種異常。於是又搜尋了一番才在StackOverflow上發現瞭解決方案。簡單的來說就是將requestWindowFeature()放到第一行調用。為什麼呢?關鍵原因在於,我在Android Studio 1.5裡面建立的工程Activity預設是繼承自AppCompatActivity類。在那篇文章裡面提到了一些解決方案:(1)要麼把基類從AppCompatActivity(或者ActionBarActivity)改成Activity。這樣就可以不用將requestWindowFeature放到第一行了。(2)或者是使用supportRequestWindowFeature()方法代替requestWindowFeature()方法,這樣也不用放到第一行去調用。這三種方法隨意一種都可以解決問題。至於原因,文章裡面也是眾說紛紜,不好解釋。不過大致的原因,是由於Google為Android提供的相容包導致的問題。
ActionBarActivity和AppCompatActivity的關係
在StackOverflow的那篇文章中,有提到一個已經被Google廢棄的基類ActionBarActivity。這個類在現在的SDK中已經被廢棄使用了,從原始碼來看,ActionBarActivity現在就是繼承自AppCompatActivity的一個空類,緊緊是為了向下相容考慮。Google已經建議開發人員逐步使用ToolBar來代替以前版本中的ActionBar了,因此廢棄ActionBarActivity,在新版本中使用AppCompatActivity做基類也是情理之中的事情了。
那麼,AppCompatActivity新在哪裡呢?根據文檔說明,AppCompatActivity是一個設計實現的更通用的類,內部使用了AppCompatDelegate作為邏輯實現核心。這個delegate的存在,是為了更好的貫徹Google推行的Material Design的設計理念。有時你可能想在一箇舊版本的Activity(既不是繼承自ActionBarActivity又不是繼承AppCompatActivity的類)中使用Material Design的組件。此時,這個delegate能夠很好的滿足這個要求。只要在這箇舊式Activity中實現AppCompatDelegate的相關方法,然後重寫舊式Activity中的addContentView()、setContentView()等方法,並在這些方法中回調AppCompatDelegate中的對應方法,即可為舊式Activity添加具備Material Design風格的視圖組件。
參考
- http://stackoverflow.com/questions/29797172/whats-the-enhancement-of-appcompatactivity-over-actionbaractivity
- http://stackoverflow.com/questions/16939814/android-util-androidruntimeexception-requestfeature-must-be-called-before-add
Android開發中遇到的requestFeature() must be called before adding content異常