Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android疑問(一)

Android疑問(一)

編輯:關於Android編程

1. View.inflate和LayoutInflater的inflate方法區別

查看這篇博客

2.談談在Android中有消息傳遞機制(Looper、Handler、MessageQueue的關系)

Looper用於線程的消息循環,一個線程只能有一個Looper對象 Handler從MessageQueue中獲取消息並處理 MessageQueue消息隊列

在線程中使用Handler的基本步驟如下:
在被調用線程中完成以下內容:

(1)調用 Looper的prepare()方法為當前線程創建Looper對象,創建Looper對象時,它的構造器會創建與之配套的MessageQueue。
(2)有了Looper之後,創建Handler子類的實例,重寫HandlerMessage()方法,該方法負責處理來自其它線程的消息。
(3)調用Looper的loop()方法啟動Looper。
注:若被調用線程是主線程類,由於系統自動為主線程創建了Looper的實例,因此第一、三步驟可省略,而只需要做第2步即可。
在調用線程中完成:
(1)創建message,並填充內容。
(2)使用被調用類創建的Handler實例,調用sendMessage(Message msg)方法。

3. 通過Acitivty的xml標簽來改變任務棧的默認行為

使用android:launchMode=”standard|singleInstance|singleTask|singleTop”來控制Acivity任務棧。

任務棧是一種後進先出的結構。位於棧頂的Activity處於焦點狀態,當按下back按鈕的時候,棧內的Activity會一個一個的出棧,並且調用其onDestory()方法。如果棧內沒有Activity,那麼系統就會回收這個棧,每個APP默認只有一個棧,以APP的包名來命名.

standard : 標准模式,每次啟動Activity都會創建一個新的Activity實例,並且將其壓入任務棧棧頂,而不管這個Activity是否已經存在。Activity的啟動三回調(onCreate()->onStart()->onResume())都會執行。 singleTop : 棧頂復用模式.這種模式下,如果新Activity已經位於任務棧的棧頂,那麼此Activity不會被重新創建,所以它的啟動三回調就不會執行,同時Activity的onNewIntent()方法會被回調.如果Activity已經存在但是不在棧頂,那麼作用於standard模式一樣。 singleTask: 棧內復用模式.創建這樣的Activity的時候,系統會先確認它所需任務棧已經創建,否則先創建任務棧.然後放入Activity,如果棧中已經有一個Activity實例,那麼這個Activity就會被調到棧頂,onNewIntent(),並且singleTask會清理在當前Activity上面的所有Activity.(clear top)。 singleInstance : 加強版的singleTask模式,這種模式的Activity只能單獨位於一個任務棧內,由於棧內復用的特性,後續請求均不會創建新的Activity,除非這個獨特的任務棧被系統銷毀了

4. Activity緩存方法

有a、b兩個Activity,當從a進入b之後一段時間,可能系統會把a回收,這時候按back,執行的不是a的onRestart而是onCreate方法,a被重新創建一次,這是a中的臨時數據和狀態可能就丟失了。
可以用Activity中的onSaveInstanceState()回調方法保存臨時數據和狀態,這個方法一定會在活動被回收之前調用。方法中有一個Bundle參數,putString()、putInt()等方法需要傳入兩個參數,一個鍵一個值。數據保存之後會在onCreate中恢復,onCreate也有一個Bundle類型的參數。

一、onSaveInstanceState (Bundle outState)

當某個activity變得“容易”被系統銷毀時,該activity的onSaveInstanceState就會被執行,除非該activity是被用戶主動銷毀的,例如當用戶按BACK鍵的時候。
注意上面的雙引號,何為“容易”?言下之意就是該activity還沒有被銷毀,而僅僅是一種可能性。這種可能性有哪些?通過重寫一個activity的所有生命周期的onXXX方法,包括onSaveInstanceState和onRestoreInstanceState方法,我們可以清楚地知道當某個activity(假定為activity A)顯示在當前task的最上層時,其onSaveInstanceState方法會在什麼時候被執行,有這麼幾種情況:
1、當用戶按下HOME鍵時。
這是顯而易見的,系統不知道你按下HOME後要運行多少其他的程序,自然也不知道activity A是否會被銷毀,故系統會調用onSaveInstanceState,讓用戶有機會保存某些非永久性的數據。以下幾種情況的分析都遵循該原則
2、長按HOME鍵,選擇運行其他的程序時。
3、按下電源按鍵(關閉屏幕顯示)時。
4、從activity A中啟動一個新的activity時。
5、屏幕方向切換時,例如從豎屏切換到橫屏時。(如果不指定configchange屬性) 在屏幕切換之前,系統會銷毀activity A,在屏幕切換之後系統又會自動地創建activity A,所以onSaveInstanceState一定會被執行
總而言之,onSaveInstanceState的調用遵循一個重要原則,即當系統“未經你許可”時銷毀了你的activity,則onSaveInstanceState會被系統調用,這是系統的責任,因為它必須要提供一個機會讓你保存你的數據(當然你不保存那就隨便你了)。另外,需要注意的幾點:
1.布局中的每一個View默認實現了onSaveInstanceState()方法,這樣的話,這個UI的任何改變都會自動的存儲和在activity重新創建的時候自動的恢復。但是這種情況只有在你為這個UI提供了唯一的ID之後才起作用,如果沒有提供ID,將不會存儲它的狀態。
2.由於默認的onSaveInstanceState()方法的實現幫助UI存儲它的狀態,所以如果你需要覆蓋這個方法去存儲額外的狀態信息時,你應該在執行任何代碼之前都調用父類的onSaveInstanceState()方法(super.onSaveInstanceState())。 既然有現成的可用,那麼我們到底還要不要自己實現onSaveInstanceState()?這得看情況了,如果你自己的派生類中有變量影響到UI,或你程序的行為,當然就要把這個變量也保存了,那麼就需要自己實現,否則就不需要。
3.由於onSaveInstanceState()方法調用的不確定性,你應該只使用這個方法去記錄activity的瞬間狀態(UI的狀態)。不應該用這個方法去存儲持久化數據。當用戶離開這個activity的時候應該在onPause()方法中存儲持久化數據(例如應該被存儲到數據庫中的數據)。
4.onSaveInstanceState()如果被調用,這個方法會在onStop()前被觸發,但系統並不保證是否在onPause()之前或者之後觸發。

二、onRestoreInstanceState (Bundle outState)
至於onRestoreInstanceState方法,需要注意的是,onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成對的被調用的,
onRestoreInstanceState被調用的前提是,activity A“確實”被系統銷毀了,而如果僅僅是停留在有這種可能性的情況下,則該方法不會被調用,例如,當正在顯示activity A的時候,用戶按下HOME鍵回到主界面,然後用戶緊接著又返回到activity A,這種情況下activity A一般不會因為內存的原因被系統銷毀,故activity A的onRestoreInstanceState方法不會被執行
另外,onRestoreInstanceState的bundle參數也會傳遞到onCreate方法中,你也可以選擇在onCreate方法中做數據還原。 還有onRestoreInstanceState在onstart之後執行。 至於這兩個函數的使用,給出示范代碼(留意自定義代碼在調用super的前或後):

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
        savedInstanceState.putBoolean("MyBoolean", true);
        savedInstanceState.putDouble("myDouble", 1.9);
        savedInstanceState.putInt("MyInt", 1);
        savedInstanceState.putString("MyString", "Welcome back to Android");
        // etc.
        super.onSaveInstanceState(savedInstanceState);
}

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
        double myDouble = savedInstanceState.getDouble("myDouble");
        int myInt = savedInstanceState.getInt("MyInt");
        String myString = savedInstanceState.getString("MyString");
}

5. 為什麼在Service中創建子線程而不是Activity中

這是因為Activity很難對Thread進行控制,當Activity被銷毀之後,就沒有任何其它的辦法可以再重新獲取到之前創建的子線程的實例。而且在一個Activity中創建的子線程,另一個Activity無法對其進行操作。但是Service就不同了,所有的Activity都可以與Service進行關聯,然後可以很方便地操作其中的方法,即使Activity被銷毀了,之後只要重新與Service建立關聯,就又能夠獲取到原有的Service中Binder的實例。因此,使用Service來處理後台任務,Activity就可以放心地finish,完全不需要擔心無法對後台任務進行控制的情況。

6. Context區別

* Activity和Service以及Application的Context是不一樣的,Activity繼承自ContextThemeWraper.其他的繼承自ContextWrapper
* 每一個Activity和Service以及Application的Context都是一個新的ContextImpl對象
* getApplication()用來獲取Application實例的,但是這個方法只有在Activity和Service中才能調用的到。那麼也許在絕大多數情況下我們都是在Activity或者Service中使用Application的,但是如果在一些其它的場景,比如BroadcastReceiver中也想獲得Application的實例,這時就可以借助getApplicationContext()方法,getApplicationContext()比getApplication()方法的作用域會更廣一些,任何一個Context的實例,只要調用getApplicationContext()方法都可以拿到我們的Application對象。
* Activity在創建的時候會new一個ContextImpl對象並在attach方法中關聯它,Application和Service也差不多。ContextWrapper的方法內部都是轉調ContextImpl的方法
* 創建對話框傳入Application的Context是不可以的
* 盡管Application、Activity、Service都有自己的ContextImpl,並且每個ContextImpl都有自己的mResources成員,但是由於它們的mResources成員都來自於唯一的ResourcesManager實例,所以它們看似不同的mResources其實都指向的是同一塊內存
* Context的數量等於Activity的個數 + Service的個數 + 1,這個1為Application

7. IntentService的使用場景與特點

IntentService是Service的子類,是一個異步的,會自動停止的服務,很好解決了傳統的Service中處理完耗時操作忘記停止並銷毀Service的問題

優點:

* 一方面不需要自己去new Thread
* 另一方面不需要考慮在什麼時候關閉該Service

onStartCommand中回調了onStart,onStart中通過mServiceHandler發送消息到該handler的handleMessage中去。最後handleMessage中回調onHandleIntent(intent)。

8.事件分發機制

1).針對MOVE_DOWN

* 對於 dispatchTouchEvent,onTouchEvent,return true是終結事件傳遞。return false 是回溯到父View的onTouchEvent方法。
* ViewGroup 想把自己分發給自己的onTouchEvent,需要攔截器onInterceptTouchEvent方法return true 把事件攔截下來。
* ViewGroup 的攔截器onInterceptTouchEvent 默認是不攔截的,所以return super.onInterceptTouchEvent()=return false;
* View 沒有攔截器,為了讓View可以把事件分發給自己的onTouchEvent,View的dispatchTouchEvent默認實現(super)就是把事件分發給自己的onTouchEvent。

2).關於ACTION_MOVE 和 ACTION_UP
ACTION_DOWN事件在哪個控件消費了(return true), 那麼ACTION_MOVE和ACTION_UP就會從上往下(通過dispatchTouchEvent)做事件分發往下傳,就只會傳到這個控件,不會繼續往下傳,如果ACTION_DOWN事件是在dispatchTouchEvent消費,那麼事件到此為止停止傳遞,如果ACTION_DOWN事件是在onTouchEvent消費的,那麼會把ACTION_MOVE或ACTION_UP事件傳給該控件的onTouchEvent處理並結束傳遞。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved