Activity、Window、View三者關係

目錄介紹

  • 01.Window,View,子Window
  • 02.什麼是Activity
  • 03.什麼是Window
  • 04.什麼是DecorView
  • 05.什麼是View
  • 06.關係結構圖
  • 07.Window建立過程
  • 08.建立機制分析
    • 8.1 Activity實例的建立
    • 8.2 Activity中Window的建立
    • 8.3 DecorView的建立

給本身相個親

彈窗系列博客

  • 01.Activity、Window、View三者關係
    • 深刻分析Activity、Window、View三者之間的關係
  • 02.Toast源碼深度分析
    • 最簡單的建立,簡單改造避免重複建立,show()方法源碼分析,scheduleTimeoutLocked吐司如何自動銷燬的,TN類中的消息機制是如何執行的,普通應用的Toast顯示數量是有限制的,用代碼解釋爲什麼Activity銷燬後Toast仍會顯示,Toast偶爾報錯Unable to add window是如何產生的,Toast運行在子線程問題,Toast如何添加系統窗口的權限等等
  • 03.DialogFragment源碼分析
    • 最簡單的使用方法,onCreate(@Nullable Bundle savedInstanceState)源碼分析,重點分析彈窗展現和銷燬源碼,使用中show()方法遇到的IllegalStateException分析
  • 04.Dialog源碼分析
    • AlertDialog源碼分析,經過AlertDialog.Builder對象設置屬性,Dialog生命週期,Dialog中show方法展現彈窗分析,Dialog的dismiss銷燬彈窗,Dialog彈窗問題分析等等
  • 05.PopupWindow源碼分析
    • 顯示PopupWindow,注意問題寬和高屬性,showAsDropDown()源碼,dismiss()源碼分析,PopupWindow和Dialog有什麼區別?爲什麼彈窗點擊一下就dismiss呢?
  • 06.Snackbar源碼分析
    • 最簡單的建立,Snackbar的make方法源碼分析,Snackbar的show顯示與點擊消失源碼分析,顯示和隱藏中動畫源碼分析,Snackbar的設計思路,爲何Snackbar老是顯示在最下面
  • 07.彈窗常見問題
    • DialogFragment使用中show()方法遇到的IllegalStateException,什麼常見產生的?Toast偶爾報錯Unable to add window,Toast運行在子線程致使崩潰如何解決?
  • 09.onAttachedToWindow和onDetachedFromWindow
    • onAttachedToWindow的調用過程,onDetachedFromWindow能夠作什麼?
  • 10.DecorView介紹
    • 什麼是DecorView,DecorView的建立,DecorView的顯示,深度解析

01.Window,View,子Window

  • 彈窗有哪些類型
    • 使用子窗口:在 Android 進程內,咱們能夠直接使用類型爲子窗口類型的窗口。在 Android 代碼中的直接應用是 PopupWindow 或者是 Dialog 。這固然能夠,不過這種窗口依賴於它的宿主窗口,它可用的條件是你的宿主窗口可用
    • 採用View系統:使用 View 系統去模擬一個窗口行爲,且能更加快速的實現動畫效果,好比SnackBar 就是採用這套方案
    • 使用系統窗口:好比吐司Toast

02.什麼是Activity

  • Activity並不負責視圖控制,它只是控制生命週期和處理事件。真正控制視圖的是Window。一個Activity包含了一個Window,Window纔是真正表明一個窗口。
  • Activity就像一個控制器,統籌視圖的添加與顯示,以及經過其餘回調方法,來與Window、以及View進行交互。

03.什麼是Window

  • Window是什麼?
    • 表示一個窗口的概念,是全部View的直接管理者,任何視圖都經過Window呈現(點擊事件由Window->DecorView->View; Activity的setContentView底層經過Window完成)
    • Window是一個抽象類,具體實現是PhoneWindow。PhoneWindow中有個內部類DecorView,經過建立DecorView來加載Activity中設置的佈局R.layout.activity_main
    • 建立Window須要經過WindowManager建立,經過WindowManager將DecorView加載其中,並將DecorView交給ViewRoot,進行視圖繪製以及其餘交互。
    • WindowManager是外界訪問Window的入口
    • Window具體實現位於WindowManagerService中
    • WindowManager和WindowManagerService的交互是經過IPC完成
  • 如何經過WindowManager添加Window(代碼實現)?
    • 以下所示
      //1. 控件 
      Button button = new Button(this); 
      button.setText("Window Button"); 
      //2. 佈局參數 
      WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, 0, 0, PixelFormat.TRANSPARENT); 
      layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 
      layoutParams.gravity = Gravity.LEFT | Gravity.TOP; 
      layoutParams.x = 100; 
      layoutParams.y = 300; 
      // 必需要有type否則會異常: the specified window type 0 is not valid 
      layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; 
      //3. 獲取WindowManager並添加控件到Window中 
      WindowManager windowManager = getWindowManager(); 
      windowManager.addView(button, layoutParams);
      複製代碼
  • WindowManager的主要功能是什麼?
    • 添加、更新、刪除View
      public interface ViewManager{ 
          public void addView(View view, ViewGroup.LayoutParams params); 
          //添加View 
          public void updateViewLayout(View view, ViewGroup.LayoutParams params); 
          //更新View 
          public void removeView(View view); 
          //刪除View 
      }
      複製代碼

04.什麼是DecorView

  • DecorView是FrameLayout的子類,它能夠被認爲是Android視圖樹的根節點視圖。
    • DecorView做爲頂級View,通常狀況下它內部包含一個豎直方向的LinearLayout,在這個LinearLayout裏面有上下三個部分,上面是個ViewStub,延遲加載的視圖(應該是設置ActionBar,根據Theme設置),中間的是標題欄(根據Theme設置,有的佈局沒有),下面的是內容欄。
    • 具體狀況和Android版本及主體有關,以其中一個佈局爲例,以下所示:
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:fitsSystemWindows="true"
        android:orientation="vertical">
        <!-- Popout bar for action modes -->
        <ViewStub
            android:id="@+id/action_mode_bar_stub"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inflatedId="@+id/action_mode_bar"
            android:layout="@layout/action_mode_bar"
            android:theme="?attr/actionBarTheme" />
    
        <FrameLayout
            style="?android:attr/windowTitleBackgroundStyle"
            android:layout_width="match_parent"
            android:layout_height="?android:attr/windowTitleSize">
    
            <TextView
                android:id="@android:id/title"
                style="?android:attr/windowTitleStyle"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@null"
                android:fadingEdge="horizontal"
                android:gravity="center_vertical" />
        </FrameLayout>
    
        <FrameLayout
            android:id="@android:id/content"
            android:layout_width="match_parent"
            android:layout_height="0dip"
            android:layout_weight="1"
            android:foreground="?android:attr/windowContentOverlay"
            android:foregroundGravity="fill_horizontal|top" />
    </LinearLayout>
    複製代碼
  • 在Activity中經過setContentView所設置的佈局文件其實就是被加到內容欄之中的,成爲其惟一子View,就是上面的id爲content的FrameLayout中,在代碼中能夠經過content來獲得對應加載的佈局。
    ViewGroup content = (ViewGroup)findViewById(android.R.id.content);
    ViewGroup rootView = (ViewGroup) content.getChildAt(0);
    複製代碼

06.關係結構圖

  • Activity 與 PhoneWindow 與 DecorView 關係圖
    • image

07.Window建立過程

  • App點擊桌面圖片啓動過程
    • image
  • window啓動流程
    • image
  • Activity 與 PhoneWindow 與 DecorView 之間什麼關係?
    • 一個 Activity 對應一個 Window 也就是 PhoneWindow,一個 PhoneWindow 持有一個 DecorView 的實例,DecorView 自己是一個 FrameLayout。

08.建立機制分析

8.1 Activity實例的建立

  • ActivityThread中執行performLaunchActivity,從而生成了Activity的實例。源碼以下所示,ActivityThread類中源碼
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            ...
        } catch (Exception e) {
            ...
        }
    
        try {
            ...
            if (activity != null) {
                ...
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor);
                ...
            }
            ...
        } catch (SuperNotCalledException e) {
            throw e;
        } catch (Exception e) {
            ...
        }
    
        return activity;
    }
    複製代碼

8.2 Activity中Window的建立

  • 從上面的performLaunchActivity能夠看出,在建立Activity實例的同時,會調用Activity的內部方法attach
  • 在attach該方法中完成window的初始化。源碼以下所示,Activity類中源碼
    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,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
    
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
    }
    複製代碼

8.3 DecorView的建立

  • 用戶執行Activity的setContentView方法,內部是調用PhoneWindow的setContentView方法,在PhoneWindow中完成DecorView的建立。流程
    • 1.Activity中的setContentView
    • 2.PhoneWindow中的setContentView
    • 3.PhoneWindow中的installDecor
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
    
    @Override
    public void setContentView(int layoutResID) {
        ...
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        ...
    }
    
    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        ...
    }
    複製代碼

關於其餘內容介紹

01.關於博客彙總連接

02.關於個人博客

GitHub連接:github.com/yangchong21…

相關文章
相關標籤/搜索