Android應用程式視窗(Activity)視窗對象(Window)建立指南

來源:互聯網
上載者:User

在前文中,我們分析了Android應用程式視窗的運行上下文環境的建立過程。由此可知,每一個Activity組件都有一個關聯的ContextImpl對象,同時,它還關聯有一個Window對象,用來描述一個具體的應用程式視窗。由此又可知,Activity只不過是一個高度抽象的UI組件,它的具體UI實現其實是由其它的一系列對象來實現的。在本文中,我們就將詳細分析Android應用程式視窗對象的建立過程。

從前面Android應用程式視窗(Activity)實現架構簡要介紹和學習計劃一文可以知道,在PHONE平台上,與Activity組件所關聯的視窗對象的實際類型為PhoneWindow,後者是從Window類繼承下來的。Activity、Window和PhonwWindow三個類的關係可以參考Android應用程式視窗(Activity)實現架構簡要介紹和學習計劃一文中的圖3和圖5。為了方便接下來描述類型為PhoneWindow的應用程式視窗的建立過程,我們將這兩個圖拿過來,如以下的圖1和圖2所示:


圖1 Activity和Window的類別關係圖


圖2 Window和PhoneWindow的類別關係圖

上述兩個圖中所涉及到的類的描述可以參考Android應用程式視窗(Activity)實現架構簡要介紹和學習計劃一文,本文主要從Android應用程式視窗的建立過程來理解Activity、Window和PhonwWindow三個類的關係。

從Android應用程式視窗(Activity)的運行上下文環境(Context)的建立過程分析一文又可以知道,與Activity組件所關聯的一個PhoneWindow對象是從Activity類的成員函數attach中建立的,3所示:


圖3 Android應用程式視窗的建立過程

這個過程可以分為9個步驟,接下來我們就詳細分析每一個步驟。

Step 1. Activity.attach

複製代碼 代碼如下:public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks {
......

private Window mWindow;
......

final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
Object lastNonConfigurationInstance,
HashMap<String,Object> lastNonConfigurationChildInstances,
Configuration config) {
......

mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
......

mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
......

}

......
}

這個函數定義在檔案frameworks/base/core/java/android/app/Activity.java中。

在前面Android應用程式視窗(Activity)的運行上下文環境(Context)的建立過程分析一文中,我們已經分析過這個函數的實現了,這裡我們只關注與應用程式視窗建立相關的代碼。

函數首先調用PolicyManager類的靜態成員函數makeNewWindow來建立一個類型為PhoneWindow的應用程式視窗,並且儲存在Activity類的成員變數mWindow中。有了這個類型為PhoneWindow的應用程式視窗,函數接下來還會調用它的成員函數setCallback、setSoftInputMode和setWindowManager來設定視窗回調介面、軟鍵盤輸入地區的顯示模式和本地視窗管理器。

PhoneWindow類的成員函數setCallback、setSoftInputMode和setWindowManager都是從父類Window繼承下來的,因此,接下來我們就繼續分析PolicyManager類的靜態成員函數makeNewWindow,以及Window類的成員函數setCallback、setSoftInputMode和setWindowManager的實現。

Step 2. PolicyManager.makeNewWindow

複製代碼 代碼如下:public final class PolicyManager {
private static final String POLICY_IMPL_CLASS_NAME =
"com.android.internal.policy.impl.Policy";

private static final IPolicy sPolicy;

static {
// Pull in the actual implementation of the policy at run-time
try {
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
sPolicy = (IPolicy)policyClass.newInstance();
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
} catch (InstantiationException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
}
}

......

// The static methods to spawn new policy-specific objects
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}

......
}

這個函數定義在檔案frameworks/base/core/java/com/android/internal/policy/PolicyManager.java中。

PolicyManager是一個視窗管理原則類,它在第一次被使用的時候,就會建立一個Policy類執行個體,並且儲存在靜態成員變數sPolicy中,以後PolicyManager類的視窗管理原則就是通過這個Policy類執行個體來實現的,例如,PolicyManager類的靜態成員函數makeNewWindow就是通過調用這個Policy類執行個體的成員函數makeNewWindow來建立一個具體的應用程式視窗的。

接下來,我們就繼續分析Policy類的成員函數makeNewWindow的實現。

Step 3. Policy.makeNewWindow

複製代碼 代碼如下:public class Policy implements IPolicy {
......

public PhoneWindow makeNewWindow(Context context) {
return new PhoneWindow(context);
}

......
}

這個函數定義在檔案frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java中。

Policy類的成員函數makeNewWindow的實現很簡單,它只是建立了一個PhoneWindow對象,然後返回給調用者。

接下來,我們就繼續分析PhoneWindow類的建構函式的實現,以便可以瞭解一個類型為PhoneWindow的應用程式視窗的建立過程。

Step 4. new PhoneWindow

複製代碼 代碼如下:public class PhoneWindow extends Window implements MenuBuilder.Callback {
......

// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;

// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
private ViewGroup mContentParent;
......

private LayoutInflater mLayoutInflater;
......

public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}

......
}

這個函數定義在檔案frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。

PhoneWindow類的建構函式很簡單,它首先調用父類Window的建構函式來執行一些初始化操作,接著再調用LayoutInflater的靜態成員函數from建立一個LayoutInflater執行個體,並且儲存在成員變數mLayoutInflater中。這樣,PhoneWindow類以後就可以通過成員變數mLayoutInflater來建立應用程式視窗的視圖,這個視圖使用類型為DecorView的成員變數mDecor來描述。PhoneWindow類還有另外一個類型為ViewGroup的成員變數mContentParent,用來描述一個視圖容器,這個容器存放的就是成員變數mDecor所描述的視圖的內容,不過這個容器也有可能指向的是mDecor本身。在後面的文章中,我們再詳細分析類型為PhoneWindow的應用程式視窗的視圖的建立過程。

Window的建構函式定義在檔案frameworks/base/core/java/android/view/Window.java中,它的實現很簡單,只是初始化了其成員變數mContext,如下所示:

複製代碼 代碼如下:public abstract class Window {
......

private final Context mContext;
......

public Window(Context context) {
mContext = context;
}

......
}

從前面的調用過程可以知道,參數context描述的是正在啟動的Activity組件,將它儲存在Window類的成員變數mContext之後,Window類就可以通過它來訪問與Activity組件相關的資源了。

這一步執行完成之後,回到前面的Step 1中,即Activity類的成員函數attach中,接下來就會繼續調用前面所建立的PhoneWindow對象從父類Window繼承下來的成員函數setCallback來設定視窗回調介面,因此,接下來我們就繼續分析Window類的成員函數setCallback的實現。

Step 5. Window.setCallback

複製代碼 代碼如下:public abstract class Window {
......

private Callback mCallback;
......

/**
* Set the Callback interface for this window, used to intercept key
* events and other dynamic operations in the window.
*
* @param callback The desired Callback interface.
*/
public void setCallback(Callback callback) {
mCallback = callback;
}

......
}

這個函數定義在檔案frameworks/base/core/java/android/view/Window.java中。

正在啟動的Activity組件會將它所實現的一個Callback介面設定到與它所關聯的一個PhoneWindow對象的父類Window的成員變數mCallback中去,這樣當這個PhoneWindow對象接收到系統給它分發的IO輸入事件,例如,鍵盤和觸控螢幕事件,轉寄給與它所關聯的Activity組件處理,這一點可以參考前面Android應用程式鍵盤(Keyboard)訊息處理機制分析一文。

這一步執行完成之後,回到前面的Step 1中,即Activity類的成員函數attach中,接下來就會繼續調用前面所建立的PhoneWindow對象從父類Window繼承下來的成員函數setSoftInputMode來設定應用程式視窗的軟鍵盤輸入地區的顯示模式,因此,接下來我們就繼續分析Window類的成員函數setSoftInputMode的實現。

Step 6. Window.setSoftInputMode

複製代碼 代碼如下:public abstract class Window {
......

private boolean mHasSoftInputMode = false;
......

public void setSoftInputMode(int mode) {
final WindowManager.LayoutParams attrs = getAttributes();
if (mode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
attrs.softInputMode = mode;
mHasSoftInputMode = true;
} else {
mHasSoftInputMode = false;
}
if (mCallback != null) {
mCallback.onWindowAttributesChanged(attrs);
}
}

......
}

這個函數定義在檔案frameworks/base/core/java/android/view/Window.java中。

參數mode有SOFT_INPUT_STATE_UNSPECIFIED、SOFT_INPUT_STATE_UNCHANGED、SOFT_INPUT_STATE_HIDDEN、SOFT_INPUT_STATE_ALWAYS_HIDDEN、SOFT_INPUT_STATE_VISIBLE和SOFT_INPUT_STATE_ALWAYS_VISIBLE一共六個取值,用來描述視窗的軟鍵盤輸入地區的顯示模式,它們的含義如下所示:

1. SOFT_INPUT_STATE_UNSPECIFIED:沒有指定軟鍵盤輸入地區的顯示狀態。

2. SOFT_INPUT_STATE_UNCHANGED:不要改變軟鍵盤輸入地區的顯示狀態。

3. SOFT_INPUT_STATE_HIDDEN:在合適的時候隱藏軟鍵盤輸入地區,例如,當使用者導航到當前視窗時。

4. SOFT_INPUT_STATE_ALWAYS_HIDDEN:當視窗獲得焦點時,總是隱藏軟鍵盤輸入地區。

5. SOFT_INPUT_STATE_VISIBLE:在合適的時候顯示軟鍵盤輸入地區,例如,當使用者導航到當前視窗時。

6. SOFT_INPUT_STATE_ALWAYS_VISIBLE:當視窗獲得焦點時,總是顯示軟鍵盤輸入地區。

當參數mode的值不等於SOFT_INPUT_STATE_UNSPECIFIED時,就表示當前視窗被指定軟鍵盤輸入地區的顯示模式,這時候Window類的成員函數setSoftInputMode就會將成員變數mHasSoftInputMode的值設定為true,並且將這個顯示模式儲存在用來描述視窗布局屬性的一個WindowManager.LayoutParams對象的成員變數softInputMode中,否則的話,就會將成員變數mHasSoftInputMode的值設定為false。

設定完成視窗的軟鍵盤輸入地區的顯示模式之後,如果Window類的成員變數mCallback指向了一個視窗回調介面,那麼Window類的成員函數setSoftInputMode還會調用它的成員函數onWindowAttributesChanged來通知與視窗所關聯的Activity組件,它的視窗布局屬性發生了變化。

這一步執行完成之後,回到前面的Step 1中,即Activity類的成員函數attach中,接下來就會繼續調用前面所建立的PhoneWindow對象從父類Window繼承下來的成員函數setWindowManager來設定應用程式視窗的本地視窗管理器,因此,接下來我們就繼續分析Window類的成員函數setWindowManager的實現。

Step 7. Window.setWindowManager

複製代碼 代碼如下:public abstract class Window {
......

private WindowManager mWindowManager;
private IBinder mAppToken;
private String mAppName;
......

public void setWindowManager(WindowManager wm,
IBinder appToken, String appName) {
mAppToken = appToken;
mAppName = appName;
if (wm == null) {
wm = WindowManagerImpl.getDefault();
}
mWindowManager = new LocalWindowManager(wm);
}

......
}

這個函數定義在檔案frameworks/base/core/java/android/view/Window.java中。

參數appToken用來描述當前正在處理的視窗是與哪一個Activity組件關聯的,它是一個Binder代理對象,引用了在ActivityManagerService這一側所建立的一個類型為ActivityRecord的Binder本機物件。從前面Android應用程式的Activity啟動過程簡要介紹和學習計劃一系列文章可以知道,每一個啟動起來了的Activity組件在ActivityManagerService這一側,都有一個對應的ActivityRecord對象,用來描述該Activity組件的運行狀態。這個Binder代理對象會被儲存在Window類的成員變數mAppToken中,這樣當前正在處理的視窗就可以知道與它所關聯的Activity組件是什麼。

參數appName用來描述當前正在處理的視窗所關聯的Activity組件的名稱,這個名稱會被儲存在Window類的成員變數mAppName中。

參數wm用來描述一個視窗管理器。從前面的調用過程可以知道, 這裡傳進來的參數wm的值等於null,因此,函數首先會調用WindowManagerImpl類的靜態成員函數getDefault來獲得一個預設的視窗管理器。有了這個視窗管理器之後,函數接著再使用它來建立一個本地視窗管理器,即一個LocalWindowManager對象,用來維護當前正在處理的應用程式視窗。

接下來,我們首先分析WindowManagerImpl類的靜態成員函數getDefault的實現,接著再分析本地視窗管理器的建立過程,即LocalWindowManager類的建構函式的實現。

Step 8. WindowManagerImpl.getDefault

複製代碼 代碼如下:public class WindowManagerImpl implements WindowManager {
......

public static WindowManagerImpl getDefault()
{
return mWindowManager;
}

......

private static WindowManagerImpl mWindowManager = new WindowManagerImpl();
}

這個函數定義在檔案frameworks/base/core/java/android/view/WindowManagerImpl.java中。

WindowManagerImpl類的靜態成員函數getDefault的實現很簡單,它只是將靜態成員變數mWindowManager所指向的一個WindowManagerImpl對象返回給調用者,這個WindowManagerImpl對象實現了WindowManager介面,因此,它就可以用來管理應用程式視窗。

這一步執行完成之後,回到前面的Step 7中,即Window類的成員函數setWindowManager中,接下來就會使用前面所獲得一個WindowManagerImpl對象來建立一個本地視窗管理器,即一個LocalWindowManager對象。

Step 9. new LocalWindowManager

複製代碼 代碼如下:public abstract class Window {
......

private final Context mContext;
......

private class LocalWindowManager implements WindowManager {
LocalWindowManager(WindowManager wm) {
mWindowManager = wm;
mDefaultDisplay = mContext.getResources().getDefaultDisplay(
mWindowManager.getDefaultDisplay());
}

......

private final WindowManager mWindowManager;

private final Display mDefaultDisplay;
}

......
}

這個函數定義在檔案frameworks/base/core/java/android/view/Window.java中。

LocalWindowManager類的建構函式首先將參數wm所描述的一個WindowManagerImpl對象儲存它的成員變數mWindowManager中,這樣以後就將視窗管理工作交給它來處理。

LocalWindowManager類的建構函式接著又通過成員變數mWindowManager所描述的一個WindowManagerImpl對象的成員函數getDefaultDisplay來獲得一個Display對象,用來描述系統螢幕屬性。

由於前面所獲得的Display對象描述的是全域的螢幕屬性,而當前正在處理的視窗可能配置了一些可自訂的螢幕屬性,因此,LocalWindowManager類的建構函式需要進一步地調整前面所獲得的Display對象所描述的螢幕屬性,以便可以適合當前正在處理的視窗使用。LocalWindowManager類的建構函式首先通過外部類Window的成員變數mContext的成員函數getResources來獲得一個Resources對象,接著再調用這個Resources對象的成員函數getDefaultDisplay來調整前面所獲得的Display對象所描述的螢幕屬性。最終調整完成的Display對象就儲存在LocalWindowManager類的成員變數mDefaultDisplay中。

從前面的Step 4可以知道,類Window的成員變數mContext描述的是與當前視窗所關聯的一個Activity組件。Activity類的成員函數getResources是從父類ContextWrapper繼續下來的,它實現在檔案frameworks/base/core/java/android/content/ContextWrapper.java中,如下所示:

複製代碼 代碼如下:public class ContextWrapper extends Context {
Context mBase;
......

@Override
public Resources getResources()
{
return mBase.getResources();
}

......
}

從前面Android應用程式視窗(Activity)的運行上下文環境(Context)的建立過程分析一文可以知道,ContextWrapper類的成員變數mBase指向的是一個ContextImpl對象,用來描述一個Activity組件的運行上下文環境。通過調用這個ContextImpl對象的成員函數getResources,就可以獲得與一個Resources對象,而通過這個Resources對象,就可以訪問一個Activity組件的資源資訊,從而可以獲得它所配置的螢幕屬性。

至此,我們就分析完成一個Activity組件所相關 App程式視窗對象的建立過程了。從分析的過程可以知道:

1. 一個Activity組件所相關 App程式視窗對象的類型為PhoneWindow。

2. 這個類型為PhoneWindow的應用程式視窗是通過一個類型為LocalWindowManager的本地視窗管理器來維護的。

3. 這個類型為LocalWindowManager的本地視窗管理器又是通過一個類型為WindowManagerImpl的視窗管理器來維護應用程式視窗的。

4. 這個類型為PhoneWindow的應用程式視窗內部有一個類型為DecorView的視圖對象,這個視圖對象才是真正用來描述一個Activity組件的UI的。

在接下來的一篇文章中,我們將繼續分析應用程式視窗內部的視圖對象的建立過程,敬請關注!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.