日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android应用Activity、Dialog、PopWindow、Toast窗体加入机制及源代码分析

發(fā)布時(shí)間:2023/12/15 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android应用Activity、Dialog、PopWindow、Toast窗体加入机制及源代码分析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

【工匠若水 http://blog.csdn.net/yanbober 轉(zhuǎn)載煩請(qǐng)注明出處。尊重勞動(dòng)成果】

1 背景

之所以寫這一篇博客的原因是由于之前有寫過一篇《Android應(yīng)用setContentView與LayoutInflater載入解析機(jī)制源代碼分析》。然后有人在文章以下評(píng)論和微博私信中問我關(guān)于Android應(yīng)用Activity、Dialog、PopWindow載入顯示機(jī)制是咋回事,所以我就寫一篇文章來分析分析吧(本文以Android5.1.1 (API 22)源代碼為基礎(chǔ)分析),以便大家在應(yīng)用層開發(fā)時(shí)不再迷糊。

PS一句:不僅有人微博私信我這個(gè)問題,還有人問博客插圖這些是用啥畫的,這里告訴大家。就是我,快來猛戳我

還記得之前《Android應(yīng)用setContentView與LayoutInflater載入解析機(jī)制源代碼分析》這篇文章的最后分析結(jié)果嗎?就是例如以下這幅圖:

在那篇文章里我們當(dāng)時(shí)重點(diǎn)是Activity的View載入解析xml機(jī)制分析,當(dāng)時(shí)說到了Window的東西,但僅僅是皮毛的分析了Activity相關(guān)的一些邏輯。(PS:看到這不清楚上面啥意思的建議先移步到《Android應(yīng)用setContentView與LayoutInflater載入解析機(jī)制源代碼分析》,完事再回頭繼續(xù)看這篇文章。)當(dāng)時(shí)給大家承諾過我們要從應(yīng)用控件一點(diǎn)一點(diǎn)往下慢慢深入分析,所以如今開始深入,可是本篇的深入也僅僅是僅限Window相關(guān)的東東。之后文章還會(huì)繼續(xù)慢慢深入。

【工匠若水 http://blog.csdn.net/yanbober 轉(zhuǎn)載煩請(qǐng)注明出處。尊重勞動(dòng)成果】

2 淺析Window與WindowManager相關(guān)關(guān)系及源代碼

通過上面那幅圖能夠非常直觀的看見,Android屏幕顯示的就是Window和各種View,Activity在當(dāng)中的作用主要是管理生命周期、建立窗體等。也就是說Window相關(guān)的東西對(duì)于Android屏幕來說是至關(guān)重要的(盡管前面分析Activity的setContentView等原理時(shí)說過一點(diǎn)Window。但那僅僅是皮毛。),所以有必要在分析Android應(yīng)用Activity、Dialog、PopWindow載入顯示機(jī)制前再看看Window相關(guān)的一些東西。

2-1 Window與WindowManager基礎(chǔ)關(guān)系

在分析Window與WindowManager之前我們先看一張圖:

接下來看一點(diǎn)代碼,例如以下:

/** Interface to let you add and remove child views to an Activity. To get an instance* of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.*/ public interface ViewManager {public void addView(View view, ViewGroup.LayoutParams params);public void updateViewLayout(View view, ViewGroup.LayoutParams params);public void removeView(View view); }

能夠看見,ViewManager接口定義了一組規(guī)則。也就是add、update、remove的操作View接口。也就是說ViewManager是用來加入和移除activity中View的接口。

繼續(xù)往下看:

public interface WindowManager extends ViewManager {......public Display getDefaultDisplay();public void removeViewImmediate(View view);......public static class LayoutParams extends ViewGroup.LayoutParamsimplements Parcelable {......} }

看見沒有。WindowManager繼承自ViewManager,然后自己還是一個(gè)接口,同一時(shí)候又定義了一個(gè)靜態(tài)內(nèi)部類LayoutParams(這個(gè)類比較重要。后面會(huì)分析。提前透漏下。假設(shè)你在APP做過相似360助手屏幕的那個(gè)懸浮窗或者做過那種相似IOS的小白圓點(diǎn),點(diǎn)擊展開菜單功能,你或多或少就能猜到這個(gè)類的重要性。)。

WindowManager用來在應(yīng)用與Window之間的接口、窗體順序、消息等的管理。繼續(xù)看下ViewManager的還有一個(gè)實(shí)現(xiàn)子類ViewGroup。例如以下:

public abstract class ViewGroup extends View implements ViewParent, ViewManager {//protected ViewParent mParent;//這個(gè)成員是View定義的,ViewGroup繼承自View,所以也能夠擁有。//這個(gè)變量就是前面我們一系列文章分析View向上傳遞的父節(jié)點(diǎn),相似于一個(gè)鏈表Node的next一樣//終于指向了ViewRoot......public void addView(View child, LayoutParams params) {addView(child, -1, params);}......public void addView(View child, int index, LayoutParams params) {......// addViewInner() will call child.requestLayout() when setting the new LayoutParams// therefore, we call requestLayout() on ourselves before, so that the child's request// will be blocked at our levelrequestLayout();invalidate(true);addViewInner(child, index, params, false);}...... }

這下理解上面那幅圖了吧,所以說View通過ViewGroup的addView方法加入到ViewGroup中。而ViewGroup層層嵌套到最頂級(jí)都會(huì)顯示在在一個(gè)窗體Window中(正如上面背景介紹中《Android應(yīng)用setContentView與LayoutInflater載入解析機(jī)制源代碼分析》的示意圖一樣)。當(dāng)中每一個(gè)View都有一個(gè)ViewParent類型的父節(jié)點(diǎn)mParent,最頂上的節(jié)點(diǎn)也是一個(gè)viewGroup,也即前面文章分析的Window的內(nèi)部類DecorView(從《Android應(yīng)用setContentView與LayoutInflater載入解析機(jī)制源代碼分析》的總結(jié)部分或者《Android應(yīng)用層View繪制流程與源代碼分析》的5-1小節(jié)都能夠驗(yàn)證這個(gè)結(jié)論)對(duì)象。同一時(shí)候通過上面背景中那幅圖能夠看出來。對(duì)于一個(gè)Activity僅僅有一個(gè)DecorView(ViewRoot)。也僅僅有一個(gè)Window。

2-2 Activity窗體加入流程拓展

前面文章說過。ActivityThread類的performLaunchActivity方法中調(diào)運(yùn)了activity.attach(…)方法進(jìn)行初始化。例如以下是Activity的attach方法源代碼:

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) {......//創(chuàng)建Window類型的mWindow對(duì)象。實(shí)際為PhoneWindow類實(shí)現(xiàn)了抽象Window類mWindow = PolicyManager.makeNewWindow(this);......//通過抽象Window類的setWindowManager方法給Window類的成員變量WindowManager賦值實(shí)例化mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);......//把抽象Window類相關(guān)的WindowManager對(duì)象拿出來關(guān)聯(lián)到Activity的WindowManager類型成員變量mWindowManagermWindowManager = mWindow.getWindowManager();......}

看見沒有。Activity類中的attach方法又創(chuàng)建了Window類型的新成員變量mWindow(PhoneWindow實(shí)現(xiàn)類)與Activity相關(guān)聯(lián),接著在Activity類的attach方法最后又通過mWindow.setWindowManager(…)方法創(chuàng)建了與Window相關(guān)聯(lián)的WindowManager對(duì)象,最后又通過mWindow.getWindowManager()將Window的WindowManager成員變量賦值給Activity的WindowManager成員變量mWindowManager。

接下來我們看下上面代碼中的mWindow.setWindowManager(…)方法源代碼(PhoneWindow沒有重寫抽象Window的setWindowManager方法,所以直接看Window類的該方法源代碼),例如以下:

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,boolean hardwareAccelerated) {......if (wm == null) {wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);}//實(shí)例化Window類的WindowManager類型成員mWindowManagermWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);}

能夠看見,Window的setWindowManager方法中通過WindowManagerImpl實(shí)例的createLocalWindowManager方法獲取了WindowManager實(shí)例。例如以下:

public final class WindowManagerImpl implements WindowManager {......private WindowManagerImpl(Display display, Window parentWindow) {mDisplay = display;mParentWindow = parentWindow;}......public WindowManagerImpl createLocalWindowManager(Window parentWindow) {return new WindowManagerImpl(mDisplay, parentWindow);}...... }

看見沒有?這樣就把Activity的Window與WindowManager關(guān)聯(lián)起來了。Activity類的Window類型成員變量mWindow及WindowManager類型成員變量mWindowManager就是這么來的。

回過頭繼續(xù)看上面剛剛貼的Activity的attach方法代碼,看見mWindow.setWindowManager方法傳遞的第一個(gè)參數(shù)沒?有人會(huì)想(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)這行代碼是什么意思。如今告訴你。

《Android應(yīng)用Context具體解釋及源代碼解析》一文中第三部分以前說過ActivityThread中創(chuàng)建了Acitivty(運(yùn)行attach等方法)等東東,在創(chuàng)建這個(gè)Activity之前得到了Context的實(shí)例。

記不記得當(dāng)時(shí)說Context的實(shí)現(xiàn)類就是ContextImpl嗎?以下我們看下ContextImpl類的靜態(tài)方法塊,例如以下:

class ContextImpl extends Context {......//靜態(tài)代碼塊。類載入時(shí)運(yùn)行一次static {......//這里有一堆相似的XXX_SERVICE的注冊(cè)......registerService(WINDOW_SERVICE, new ServiceFetcher() {Display mDefaultDisplay;public Object getService(ContextImpl ctx) {//搞一個(gè)Display實(shí)例Display display = ctx.mDisplay;if (display == null) {if (mDefaultDisplay == null) {DisplayManager dm = (DisplayManager)ctx.getOuterContext().getSystemService(Context.DISPLAY_SERVICE);mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);}display = mDefaultDisplay;}//返回一個(gè)WindowManagerImpl實(shí)例return new WindowManagerImpl(display);}});......}//這就是你在外面調(diào)運(yùn)Context的getSystemService獲取到的WindowManagerImpl實(shí)例@Overridepublic Object getSystemService(String name) {ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);return fetcher == null ? null : fetcher.getService(this);}//上面static代碼塊創(chuàng)建WindowManagerImpl實(shí)例用到的方法private static void registerService(String serviceName, ServiceFetcher fetcher) {if (!(fetcher instanceof StaticServiceFetcher)) {fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;}SYSTEM_SERVICE_MAP.put(serviceName, fetcher);} }

看見沒有,我們都知道Java的靜態(tài)代碼塊是類載入是運(yùn)行一次的。也就相當(dāng)于一個(gè)全局的,這樣就相當(dāng)于每一個(gè)Application僅僅有一個(gè)WindowManagerImpl(display)實(shí)例。

還記不記得《Android應(yīng)用setContentView與LayoutInflater載入解析機(jī)制源代碼分析》一文2-6小節(jié)中說的,setContentView的實(shí)質(zhì)顯示是觸發(fā)了Activity的resume狀態(tài)。也就是觸發(fā)了makeVisible方法。那我們?cè)賮砜聪逻@種方法。例如以下:

void makeVisible() {if (!mWindowAdded) {//也就是獲取Activity的mWindowManager//這個(gè)mWindowManager是在Activity的attach中通過mWindow.getWindowManager()獲得ViewManager wm = getWindowManager();//調(diào)運(yùn)的實(shí)質(zhì)就是ViewManager接口的addView方法,傳入的是mDecorViewwm.addView(mDecor, getWindow().getAttributes());mWindowAdded = true;}mDecor.setVisibility(View.VISIBLE);}

特別注意,看見makeVisible方法的wm變量沒。這個(gè)變量就是Window類中通過調(diào)運(yùn)WindowManagerImpl的createLocalWindowManager創(chuàng)建的實(shí)例,也就是說每一個(gè)Activity都會(huì)新創(chuàng)建這么一個(gè)WindowManager實(shí)例來顯示Activity的界面的,有點(diǎn)和上面分析的ContextImpl中static塊創(chuàng)建的WindowManager不太一樣的地方就在于Context的WindowManager對(duì)每一個(gè)APP來說是一個(gè)全局單例的,而Activity的WindowManager是每一個(gè)Activity都會(huì)新創(chuàng)建一個(gè)的(事實(shí)上你從上面分析的兩個(gè)實(shí)例化WindowManagerImpl的構(gòu)造函數(shù)參數(shù)傳遞就能夠看出來,Activity中Window的WindowManager成員在構(gòu)造實(shí)例化時(shí)傳入給WindowManagerImpl中mParentWindow成員的是當(dāng)前Window對(duì)象,而ContextImpl的static塊中單例實(shí)例化WindowManagerImpl時(shí)傳入給WindowManagerImpl中mParentWindow成員的是null值)。

繼續(xù)看makeVisible中調(diào)運(yùn)的WindowManagerImpl的addView方法例如以下:

public final class WindowManagerImpl implements WindowManager {//繼承自O(shè)bject的單例類private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();......public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {applyDefaultToken(params);//mParentWindow是上面分析的在Activity中獲取WindowManagerImpl實(shí)例化時(shí)傳入的當(dāng)前Window//view是Activity中最頂層的mDecormGlobal.addView(view, params, mDisplay, mParentWindow);}...... }

這里當(dāng)前傳入的view是mDecor。LayoutParams呢?能夠看見是getWindow().getAttributes()。那我們進(jìn)去看看Window類的這個(gè)屬性。例如以下:

// The current window attributes.private final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();

原來是WindowManager的靜態(tài)內(nèi)部類LayoutParams的默認(rèn)構(gòu)造函數(shù):

public LayoutParams() {super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);type = TYPE_APPLICATION;format = PixelFormat.OPAQUE; }

看見沒有。Activity窗體的WindowManager.LayoutParams類型是TYPE_APPLICATION的。

繼續(xù)回到WindowManagerImpl的addView方法。分析能夠看見WindowManagerImpl中有一個(gè)單例模式的WindowManagerGlobal成員mGlobal。addView終于調(diào)運(yùn)了WindowManagerGlobal的addView,源代碼例如以下:

public final class WindowManagerGlobal {......private final ArrayList<View> mViews = new ArrayList<View>();private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();private final ArrayList<WindowManager.LayoutParams> mParams =new ArrayList<WindowManager.LayoutParams>();private final ArraySet<View> mDyingViews = new ArraySet<View>();......public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {......//獲取Activity的Window的getWindow().getAttributes()的LayoutParams final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;//假設(shè)是Activity中調(diào)運(yùn)的,parentWindow=Window。假設(shè)不是Activity的。譬如是Context的靜態(tài)代碼塊的實(shí)例化則parentWindow為nullif (parentWindow != null) {//根據(jù)當(dāng)前Activity的Window調(diào)節(jié)sub Window的LayoutParamsparentWindow.adjustLayoutParamsForSubWindow(wparams);} else {......}ViewRootImpl root;......synchronized (mLock) {......//為當(dāng)前Window創(chuàng)建ViewRootroot = new ViewRootImpl(view.getContext(), display);view.setLayoutParams(wparams);//把當(dāng)前Window相關(guān)的東西存入各自的List中,在remove中會(huì)刪掉mViews.add(view);mRoots.add(root);mParams.add(wparams);}// do this last because it fires off messages to start doing thingstry {//把View和ViewRoot關(guān)聯(lián)起來。非常重要!!!

root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { ...... } } ...... }

能夠看見,在addView方法中會(huì)利用LayoutParams獲得Window的屬性,然后為每一個(gè)Window創(chuàng)建ViewRootImpl。最后通過ViewRootImpl的setView方法通過mSession向WindowManagerService發(fā)送加入窗體請(qǐng)求把窗體加入到WindowManager中。并且由WindowManager來管理窗體的view、事件、消息收集處理等(ViewRootImpl的這一加入過程后面會(huì)寫文章分析,這里先記住這個(gè)概念就可以)。

至此我們對(duì)上面背景中那幅圖,也就是《Android應(yīng)用setContentView與LayoutInflater載入解析機(jī)制源代碼分析》這篇文章總結(jié)部分的那幅圖又進(jìn)行了更深入的一點(diǎn)分析。其目的也就是為了以下分析Android應(yīng)用Dialog、PopWindow、Toast載入顯示機(jī)制做鋪墊準(zhǔn)備。

2-3 繼續(xù)順藤摸瓜WindowManager.LayoutParams類的源代碼

上面2-1分析Window與WindowManager基礎(chǔ)關(guān)系時(shí)提到了WindowManager有一個(gè)靜態(tài)內(nèi)部類LayoutParams。它繼承于ViewGroup.LayoutParams。用于向WindowManager描寫敘述Window的管理策略。如今我們來看下這個(gè)類(PS:在AD上也能夠看見,自備梯子點(diǎn)我看AD的),例如以下:

public static class LayoutParams extends ViewGroup.LayoutParamsimplements Parcelable {//窗體的絕對(duì)XY位置,須要考慮gravity屬性public int x;public int y;//在橫縱方向上為相關(guān)的View預(yù)留多少擴(kuò)展像素。假設(shè)是0則此view不能被拉伸。其它情況下擴(kuò)展像素被widget均分public float horizontalWeight;public float verticalWeight;//窗體類型//有3種主要類型例如以下://ApplicationWindows取值在FIRST_APPLICATION_WINDOW與LAST_APPLICATION_WINDOW之間,是經(jīng)常使用的頂層應(yīng)用程序窗體,須將token設(shè)置成Activity的token;//SubWindows取值在FIRST_SUB_WINDOW和LAST_SUB_WINDOW之間,與頂層窗體相關(guān)聯(lián)。需將token設(shè)置成它所附著宿主窗體的token。//SystemWindows取值在FIRST_SYSTEM_WINDOW和LAST_SYSTEM_WINDOW之間,不能用于應(yīng)用程序。使用時(shí)須要有特殊權(quán)限。它是特定的系統(tǒng)功能才干使用;public int type;//WindowType:開始應(yīng)用程序窗體public static final int FIRST_APPLICATION_WINDOW = 1;//WindowType:全部程序窗體的base窗體,其它應(yīng)用程序窗體都顯示在它上面public static final int TYPE_BASE_APPLICATION = 1;//WindowType:普通應(yīng)用程序窗體,token必須設(shè)置為Activity的token來指定窗體屬于誰public static final int TYPE_APPLICATION = 2;//WindowType:應(yīng)用程序啟動(dòng)時(shí)所顯示的窗體,應(yīng)用自己不要使用這樣的類型,它被系統(tǒng)用來顯示一些信息,直到應(yīng)用程序能夠開啟自己的窗體為止public static final int TYPE_APPLICATION_STARTING = 3;//WindowType:結(jié)束應(yīng)用程序窗體public static final int LAST_APPLICATION_WINDOW = 99;//WindowType:SubWindows子窗體。子窗體的Z序和坐標(biāo)空間都依賴于他們的宿主窗體public static final int FIRST_SUB_WINDOW = 1000;//WindowType: 面板窗體。顯示于宿主窗體的上層public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;//WindowType:媒體窗體(比如視頻)。顯示于宿主窗體下層public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1;//WindowType:應(yīng)用程序窗體的子面板。顯示于全部面板窗體的上層public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2;//WindowType:對(duì)話框,相似于面板窗體,繪制相似于頂層窗體,而不是宿主的子窗體public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3;//WindowType:媒體信息。顯示在媒體層和程序窗體之間。須要實(shí)現(xiàn)半透明效果public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4;//WindowType:子窗體結(jié)束public static final int LAST_SUB_WINDOW = 1999;//WindowType:系統(tǒng)窗體,非應(yīng)用程序創(chuàng)建public static final int FIRST_SYSTEM_WINDOW = 2000;//WindowType:狀態(tài)欄,僅僅能有一個(gè)狀態(tài)欄。位于屏幕頂端,其它窗體都位于它下方public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;//WindowType:搜索欄。僅僅能有一個(gè)搜索欄,位于屏幕上方public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;//WindowType:電話窗體,它用于電話交互(特別是呼入),置于全部應(yīng)用程序之上,狀態(tài)欄之下public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;//WindowType:系統(tǒng)提示,出如今應(yīng)用程序窗體之上public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;//WindowType:鎖屏窗體public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;//WindowType:信息窗體。用于顯示Toastpublic static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;//WindowType:系統(tǒng)頂層窗體。顯示在其它一切內(nèi)容之上,此窗體不能獲得輸入焦點(diǎn)。否則影響鎖屏public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6;//WindowType:電話優(yōu)先,當(dāng)鎖屏?xí)r顯示,此窗體不能獲得輸入焦點(diǎn),否則影響鎖屏public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7;//WindowType:系統(tǒng)對(duì)話框public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8;//WindowType:鎖屏?xí)r顯示的對(duì)話框public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9;//WindowType:系統(tǒng)內(nèi)部錯(cuò)誤提示,顯示于全部內(nèi)容之上public static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10;//WindowType:內(nèi)部輸入法窗體,顯示于普通UI之上,應(yīng)用程序可又一次布局以免被此窗體覆蓋public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;//WindowType:內(nèi)部輸入法對(duì)話框。顯示于當(dāng)前輸入法窗體之上public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;//WindowType:墻紙窗體public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;//WindowType:狀態(tài)欄的滑動(dòng)面板public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14;//WindowType:安全系統(tǒng)覆蓋窗體。這些窗戶必須不帶輸入焦點(diǎn)。否則會(huì)干擾鍵盤public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;//WindowType:拖放偽窗體,僅僅有一個(gè)阻力層(最多),它被放置在全部其它窗體上面public static final int TYPE_DRAG = FIRST_SYSTEM_WINDOW+16;//WindowType:狀態(tài)欄下拉面板public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;//WindowType:鼠標(biāo)指針public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;//WindowType:導(dǎo)航欄(有別于狀態(tài)欄時(shí))public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;//WindowType:音量級(jí)別的覆蓋對(duì)話框。顯示當(dāng)用戶更改系統(tǒng)音量大小public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;//WindowType:起機(jī)進(jìn)度框,在一切之上public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;//WindowType:假窗,消費(fèi)導(dǎo)航欄隱藏時(shí)觸摸事件public static final int TYPE_HIDDEN_NAV_CONSUMER = FIRST_SYSTEM_WINDOW+22;//WindowType:夢想(屏保)窗體,略高于鍵盤public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;//WindowType:導(dǎo)航欄面板(不同于狀態(tài)欄的導(dǎo)航欄)public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;//WindowType:universe背后真正的窗戶public static final int TYPE_UNIVERSE_BACKGROUND = FIRST_SYSTEM_WINDOW+25;//WindowType:顯示窗體覆蓋,用于模擬輔助顯示設(shè)備public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;//WindowType:放大窗體覆蓋。用于突出顯示的放大部分可訪問性放大時(shí)啟用public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;//WindowType:......public static final int TYPE_KEYGUARD_SCRIM = FIRST_SYSTEM_WINDOW+29;public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;//WindowType:系統(tǒng)窗體結(jié)束public static final int LAST_SYSTEM_WINDOW = 2999;//MemoryType:窗體緩沖位于主內(nèi)存public static final int MEMORY_TYPE_NORMAL = 0;//MemoryType:窗體緩沖位于能夠被DMA訪問,或者硬件加速的內(nèi)存區(qū)域public static final int MEMORY_TYPE_HARDWARE = 1;//MemoryType:窗體緩沖位于可被圖形加速器訪問的區(qū)域public static final int MEMORY_TYPE_GPU = 2;//MemoryType:窗體緩沖不擁有自己的緩沖區(qū),不能被鎖定,緩沖區(qū)由本地方法提供public static final int MEMORY_TYPE_PUSH_BUFFERS = 3;//指出窗體所使用的內(nèi)存緩沖類型,默覺得NORMAL public int memoryType;//Flag:當(dāng)該window對(duì)用戶可見的時(shí)候。同意鎖屏public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001;//Flag:讓該window后全部的東西都成暗淡public static final int FLAG_DIM_BEHIND = 0x00000002;//Flag:讓該window后全部東西都模糊(4.0以上已經(jīng)放棄這樣的毛玻璃效果)public static final int FLAG_BLUR_BEHIND = 0x00000004;//Flag:讓window不能獲得焦點(diǎn),這樣用戶快就不能向該window發(fā)送按鍵事public static final int FLAG_NOT_FOCUSABLE = 0x00000008;//Flag:讓該window不接受觸摸屏事件public static final int FLAG_NOT_TOUCHABLE = 0x00000010;//Flag:即使在該window在可獲得焦點(diǎn)情況下,依然把該window之外的不論什么event發(fā)送到該window之后的其它windowpublic static final int FLAG_NOT_TOUCH_MODAL = 0x00000020;//Flag:當(dāng)手機(jī)處于睡眠狀態(tài)時(shí),假設(shè)屏幕被按下。那么該window將第一個(gè)收到public static final int FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040;//Flag:當(dāng)該window對(duì)用戶可見時(shí),讓設(shè)備屏幕處于高亮(bright)狀態(tài)public static final int FLAG_KEEP_SCREEN_ON = 0x00000080;//Flag:讓window占滿整個(gè)手機(jī)屏幕。不留不論什么邊界public static final int FLAG_LAYOUT_IN_SCREEN = 0x00000100;//Flag:window大小不再不受手機(jī)屏幕限制大小,即window可能超出屏幕之外public static final int FLAG_LAYOUT_NO_LIMITS = 0x00000200;//Flag:window全屏顯示public static final int FLAG_FULLSCREEN = 0x00000400;//Flag:恢復(fù)window非全屏顯示public static final int FLAG_FORCE_NOT_FULLSCREEN = 0x00000800;//Flag:開啟抖動(dòng)(dithering)public static final int FLAG_DITHER = 0x00001000;//Flag:當(dāng)該window在進(jìn)行顯示的時(shí)候,不同意截屏public static final int FLAG_SECURE = 0x00002000;//Flag:一個(gè)特殊模式的布局參數(shù)用于運(yùn)行擴(kuò)展表面合成時(shí)到屏幕上public static final int FLAG_SCALED = 0x00004000;//Flag:用于windows時(shí),經(jīng)常會(huì)使用屏幕用戶持有反對(duì)他們的臉,它將積極過濾事件流,以防止意外按在這樣的情況下,可能不須要為特定的窗體,在檢測到這樣一個(gè)事件流時(shí),應(yīng)用程序?qū)⒔邮杖∠\(yùn)動(dòng)事件表明,這樣應(yīng)用程序能夠處理這相應(yīng)地採取不論什么行動(dòng)的事件,直到手指釋放public static final int FLAG_IGNORE_CHEEK_PRESSES = 0x00008000;//Flag:一個(gè)特殊的選項(xiàng)僅僅用于結(jié)合FLAG_LAYOUT_IN_SCpublic static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000;//Flag:轉(zhuǎn)化的狀態(tài)FLAG_NOT_FOCUSABLE對(duì)這個(gè)窗體當(dāng)前怎樣進(jìn)行交互的方法public static final int FLAG_ALT_FOCUSABLE_IM = 0x00020000;//Flag:假設(shè)你設(shè)置了該flag,那么在你FLAG_NOT_TOUNCH_MODAL的情況下。即使觸摸屏事件發(fā)送在該window之外。其事件被發(fā)送到了后面的window,那么該window仍然將以MotionEvent.ACTION_OUTSIDE形式收到該觸摸屏事件public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000;//Flag:當(dāng)鎖屏的時(shí)候,顯示該windowpublic static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;//Flag:在該window后顯示系統(tǒng)的墻紙public static final int FLAG_SHOW_WALLPAPER = 0x00100000;//Flag:當(dāng)window被顯示的時(shí)候,系統(tǒng)將把它當(dāng)做一個(gè)用戶活動(dòng)事件,以點(diǎn)亮手機(jī)屏幕public static final int FLAG_TURN_SCREEN_ON = 0x00200000;//Flag:消失鍵盤public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;//Flag:當(dāng)該window在能夠接受觸摸屏情況下。讓因在該window之外,而發(fā)送到后面的window的觸摸屏能夠支持split touchpublic static final int FLAG_SPLIT_TOUCH = 0x00800000;//Flag:對(duì)該window進(jìn)行硬件加速,該flag必須在Activity或Dialog的Content View之前進(jìn)行設(shè)置public static final int FLAG_HARDWARE_ACCELERATED = 0x01000000;//Flag:讓window占滿整個(gè)手機(jī)屏幕。不留不論什么邊界public static final int FLAG_LAYOUT_IN_OVERSCAN = 0x02000000;//Flag:請(qǐng)求一個(gè)半透明的狀態(tài)欄背景以最小的系統(tǒng)提供保護(hù)public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;//Flag:請(qǐng)求一個(gè)半透明的導(dǎo)航欄背景以最小的系統(tǒng)提供保護(hù)public static final int FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;//Flag:......public static final int FLAG_LOCAL_FOCUS_MODE = 0x10000000;public static final int FLAG_SLIPPERY = 0x20000000;public static final int FLAG_LAYOUT_ATTACHED_IN_DECOR = 0x40000000;public static final int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000;//行為選項(xiàng)標(biāo)記public int flags;//PrivateFlags:......public static final int PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED = 0x00000001;public static final int PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED = 0x00000002;public static final int PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS = 0x00000004;public static final int PRIVATE_FLAG_SHOW_FOR_ALL_USERS = 0x00000010;public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 0x00000040;public static final int PRIVATE_FLAG_COMPATIBLE_WINDOW = 0x00000080;public static final int PRIVATE_FLAG_SYSTEM_ERROR = 0x00000100;public static final int PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR = 0x00000200;public static final int PRIVATE_FLAG_KEYGUARD = 0x00000400;public static final int PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS = 0x00000800;//私有的行為選項(xiàng)標(biāo)記public int privateFlags;public static final int NEEDS_MENU_UNSET = 0;public static final int NEEDS_MENU_SET_TRUE = 1;public static final int NEEDS_MENU_SET_FALSE = 2;public int needsMenuKey = NEEDS_MENU_UNSET;public static boolean mayUseInputMethod(int flags) {......}//SOFT_INPUT:用于描寫敘述軟鍵盤顯示規(guī)則的bite的maskpublic static final int SOFT_INPUT_MASK_STATE = 0x0f;//SOFT_INPUT:沒有軟鍵盤顯示的約定規(guī)則public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0;//SOFT_INPUT:可見性狀態(tài)softInputMode,請(qǐng)不要改變軟輸入?yún)^(qū)域的狀態(tài)public static final int SOFT_INPUT_STATE_UNCHANGED = 1;//SOFT_INPUT:用戶導(dǎo)航(navigate)到你的窗體時(shí)隱藏軟鍵盤public static final int SOFT_INPUT_STATE_HIDDEN = 2;//SOFT_INPUT:總是隱藏軟鍵盤public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3;//SOFT_INPUT:用戶導(dǎo)航(navigate)到你的窗體時(shí)顯示軟鍵盤public static final int SOFT_INPUT_STATE_VISIBLE = 4;//SOFT_INPUT:總是顯示軟鍵盤public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;//SOFT_INPUT:顯示軟鍵盤時(shí)用于表示window調(diào)整方式的bite的maskpublic static final int SOFT_INPUT_MASK_ADJUST = 0xf0;//SOFT_INPUT:不指定顯示軟件盤時(shí),window的調(diào)整方式public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00;//SOFT_INPUT:當(dāng)顯示軟鍵盤時(shí),調(diào)整window內(nèi)的控件大小以便顯示軟鍵盤public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;//SOFT_INPUT:當(dāng)顯示軟鍵盤時(shí),調(diào)整window的空白區(qū)域來顯示軟鍵盤,即使調(diào)整空白區(qū)域,軟鍵盤還是有可能遮擋一些有內(nèi)容區(qū)域。這時(shí)用戶就僅僅有退出軟鍵盤才干看到這些被遮擋區(qū)域并進(jìn)行public static final int SOFT_INPUT_ADJUST_PAN = 0x20;//SOFT_INPUT:當(dāng)顯示軟鍵盤時(shí),不調(diào)整window的布局public static final int SOFT_INPUT_ADJUST_NOTHING = 0x30;//SOFT_INPUT:用戶導(dǎo)航(navigate)到了你的windowpublic static final int SOFT_INPUT_IS_FORWARD_NAVIGATION = 0x100;//軟輸入法模式選項(xiàng)public int softInputMode;//窗體怎樣停靠public int gravity;//水平邊距,容器與widget之間的距離,占容器寬度的百分率public float horizontalMargin;//縱向邊距public float verticalMargin;//積極的insets畫圖表面和窗體之間的內(nèi)容public final Rect surfaceInsets = new Rect();//期望的位圖格式,默覺得不透明,參考android.graphics.PixelFormatpublic int format;//窗體所使用的動(dòng)畫設(shè)置,它必須是一個(gè)系統(tǒng)資源而不是應(yīng)用程序資源,由于窗體管理器不能訪問應(yīng)用程序public int windowAnimations;//整個(gè)窗體的半透明值,1.0表示不透明,0.0表示全透明public float alpha = 1.0f;//當(dāng)FLAG_DIM_BEHIND設(shè)置后生效,該變量指示后面的窗體變暗的程度。1.0表示全然不透明,0.0表示沒有變暗public float dimAmount = 1.0f;public static final float BRIGHTNESS_OVERRIDE_NONE = -1.0f;public static final float BRIGHTNESS_OVERRIDE_OFF = 0.0f;public static final float BRIGHTNESS_OVERRIDE_FULL = 1.0f;public float screenBrightness = BRIGHTNESS_OVERRIDE_NONE;//用來覆蓋用戶設(shè)置的屏幕亮度,表示應(yīng)用用戶設(shè)置的屏幕亮度。從0到1調(diào)整亮度從暗到最亮發(fā)生變化public float buttonBrightness = BRIGHTNESS_OVERRIDE_NONE;public static final int ROTATION_ANIMATION_ROTATE = 0;public static final int ROTATION_ANIMATION_CROSSFADE = 1;public static final int ROTATION_ANIMATION_JUMPCUT = 2;//定義出入境動(dòng)畫在這個(gè)窗體旋轉(zhuǎn)設(shè)備時(shí)使用public int rotationAnimation = ROTATION_ANIMATION_ROTATE;//窗體的標(biāo)示符public IBinder token = null;//此窗體所在的包名public String packageName = null;//屏幕方向public int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;//首選的刷新率的窗體public float preferredRefreshRate;//控制status bar是否顯示public int systemUiVisibility;//ui能見度所請(qǐng)求的視圖層次結(jié)構(gòu)public int subtreeSystemUiVisibility;//得到關(guān)于系統(tǒng)ui能見度變化的回調(diào)public boolean hasSystemUiListeners;public static final int INPUT_FEATURE_DISABLE_POINTER_GESTURES = 0x00000001;public static final int INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002;public static final int INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004;public int inputFeatures;public long userActivityTimeout = -1;......public final int copyFrom(LayoutParams o) {......}......public void scale(float scale) {......}......}

看見沒有。從上面類能夠看出,Android窗體類型主要分成了三大類:

  • 應(yīng)用程序窗體。

    一般應(yīng)用程序的窗體,比方我們應(yīng)用程序的Activity的窗體。

  • 子窗體。一般在Activity里面的窗體,比方對(duì)話框等。
  • 系統(tǒng)窗體。系統(tǒng)的窗體,比方輸入法,Toast,墻紙等。

  • 同一時(shí)候還能夠看見,WindowManager.LayoutParams里面窗體的type類型值定義是一個(gè)遞增保留的連續(xù)增大數(shù)值,從凝視能夠看出來事實(shí)上就是窗體的Z-ORDER序列(值越大顯示的位置越在上面,你須要將屏幕想成三維坐標(biāo)模式)。創(chuàng)建不同類型的窗體須要設(shè)置不同的type值,譬如上面拓展Activity窗體載入時(shí)分析的makeVisible方法中的Window默認(rèn)屬性的type=TYPE_APPLICATION。

    既然說這個(gè)類非常重要。那總得感性的體驗(yàn)一下重要性吧,所以我們先來看幾個(gè)實(shí)例。

    2-4 通過上面WindowManager.LayoutParams分析引出的應(yīng)用層開發(fā)經(jīng)常使用經(jīng)典實(shí)例

    有了上面分析相信你一定覺得WindowManager.LayoutParams還是蠻熟悉的,不信我們來看下。

    Part1:開發(fā)APP時(shí)設(shè)置Activity全屏常亮的一種辦法(設(shè)置Activity也就是Activity的Window):

    public class MainActivity extends ActionBarActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//設(shè)置Activity的Window為全屏。當(dāng)然也能夠在xml中設(shè)置Window window = getWindow();WindowManager.LayoutParams windowAttributes = window.getAttributes();windowAttributes.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN | windowAttributes.flags;window.setAttributes(windowAttributes);//設(shè)置Activity的Window為保持屏幕亮window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);setContentView(R.layout.activity_main);} }

    這是運(yùn)行結(jié)果:

    Part2:App開發(fā)中彈出軟鍵盤時(shí)以下的輸入框被軟件盤擋住問題的解決的方法:

    在Activity中的onCreate中setContentView之前寫例如以下代碼:

    //你也能夠在xml文件里設(shè)置。一樣的效果 getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE|WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);

    Part3:創(chuàng)建懸浮窗體(仿IPhone的小圓點(diǎn)或者魅族的小白點(diǎn)或者360手機(jī)衛(wèi)士的小浮標(biāo))。退出當(dāng)前Activity依然可見的一種實(shí)現(xiàn)方法:

    省略了Activity的start與stop Service的按鈕代碼,直接給出了核心代碼例如以下:

    /*** Author : yanbo* Time : 14:47* Description : 手機(jī)屏幕懸浮窗,仿IPhone小圓點(diǎn)* (未全然實(shí)現(xiàn)。僅僅提供思路,如需請(qǐng)自行實(shí)現(xiàn))* Notice : <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />*/ public class WindowService extends Service {private WindowManager mWindowManager;private ImageView mImageView;@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();//創(chuàng)建懸浮窗createFloatWindow();}private void createFloatWindow() {//這里的參數(shù)設(shè)置上面剛剛講過。不再說明WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();mWindowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);//設(shè)置window的typelayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;//設(shè)置效果為背景透明layoutParams.format = PixelFormat.RGBA_8888;//設(shè)置浮動(dòng)窗體不可聚焦layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;layoutParams.gravity = Gravity.BOTTOM | Gravity.RIGHT;layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;layoutParams.x = -50;layoutParams.y = -50;mImageView = new ImageView(this);mImageView.setImageResource(android.R.drawable.ic_menu_add);//加入到WindowmWindowManager.addView(mImageView, layoutParams);//設(shè)置監(jiān)聽mImageView.setOnTouchListener(touchListener);}@Overridepublic void onDestroy() {super.onDestroy();if (mImageView != null) {//講WindowManager時(shí)說過。add,remove成對(duì)出現(xiàn),所以須要removemWindowManager.removeView(mImageView);}}private View.OnTouchListener touchListener = new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {//模擬觸摸觸發(fā)的事件Intent intent = new Intent(Intent.ACTION_VIEW);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(intent);return false;}}; }

    例如以下是運(yùn)行過程模擬,特別留意屏幕右下角的變化:

    怎么樣,通過最后這個(gè)樣例你是不是就能體會(huì)到WindowManager.LayoutParams的Z-ORDER序列類型,值越大顯示的位置越在上面。

    2-5 總結(jié)Activity的窗體加入機(jī)制

    有了上面這么多分析和前幾篇的分析,我們對(duì)Activity的窗體載入再次深入分析總結(jié)例如以下:

    能夠看見Context的WindowManager對(duì)每一個(gè)APP來說是一個(gè)全局單例的。而Activity的WindowManager是每一個(gè)Activity都會(huì)新創(chuàng)建一個(gè)的(事實(shí)上你從上面分析的兩個(gè)實(shí)例化WindowManagerImpl的構(gòu)造函數(shù)參數(shù)傳遞就能夠看出來,Activity中Window的WindowManager成員在構(gòu)造實(shí)例化時(shí)傳入給WindowManagerImpl中mParentWindow成員的是當(dāng)前Window對(duì)象。而ContextImpl的static塊中單例實(shí)例化WindowManagerImpl時(shí)傳入給WindowManagerImpl中mParentWindow成員的是null值)。所以上面模擬蘋果浮動(dòng)小圖標(biāo)使用了Application的WindowManager而不是Activity的,原因就在于這里;使用Activity的WindowManager時(shí)當(dāng)Activity結(jié)束時(shí)WindowManager就無效了。所以使用Activity的getSysytemService(WINDOW_SERVICE)獲取的是Local的WindowManager。同一時(shí)候能夠看出來Activity中的WindowManager.LayoutParams的type為TYPE_APPLICATION。

    好了。上面也說了不少了,有了上面這些知識(shí)點(diǎn)以后我們就來開始分析Android應(yīng)用Activity、Dialog、PopWindow窗體顯示機(jī)制。

    【工匠若水 http://blog.csdn.net/yanbober 轉(zhuǎn)載煩請(qǐng)注明出處,尊重勞動(dòng)成果】

    3 Android應(yīng)用Dialog窗體加入顯示機(jī)制源代碼

    3-1 Dialog窗體源代碼分析

    寫過APP都知道,Dialog是一系列XXXDialog的基類,我們能夠new隨意Dialog或者通過Activity提供的onCreateDialog(……)、onPrepareDialog(……)和showDialog(……)等方法來管理我們的Dialog,可是究事實(shí)上質(zhì)都是來源于Dialog基類,所以我們對(duì)于各種XXXDialog來說僅僅用分析Dialog的窗體載入就能夠了。

    例如以下從Dialog的構(gòu)造函數(shù)開始分析:

    public class Dialog implements DialogInterface, Window.Callback,KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback {......public Dialog(Context context) {this(context, 0, true);}//構(gòu)造函數(shù)終于都調(diào)運(yùn)了這個(gè)默認(rèn)的構(gòu)造函數(shù)Dialog(Context context, int theme, boolean createContextThemeWrapper) {//默認(rèn)構(gòu)造函數(shù)的createContextThemeWrapper為trueif (createContextThemeWrapper) {//默認(rèn)構(gòu)造函數(shù)的theme為0if (theme == 0) {TypedValue outValue = new TypedValue();context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,outValue, true);theme = outValue.resourceId;}mContext = new ContextThemeWrapper(context, theme);} else {mContext = context;}//mContext已經(jīng)從外部傳入的context對(duì)象獲得值(通常是個(gè)Activity)。!。非常重要。先記住!!。//獲取WindowManager對(duì)象mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);//為Dialog創(chuàng)建新的WindowWindow w = PolicyManager.makeNewWindow(mContext);mWindow = w;//Dialog能夠接受到按鍵事件的原因w.setCallback(this);w.setOnWindowDismissedCallback(this);//關(guān)聯(lián)WindowManager與新Window。特別注意第二個(gè)參數(shù)token為null。也就是說Dialog沒有自己的token//一個(gè)Window屬于Dialog的話。那么該Window的mAppToken對(duì)象是nullw.setWindowManager(mWindowManager, null, null);w.setGravity(Gravity.CENTER);mListenersHandler = new ListenersHandler(this);}...... }

    能夠看到。Dialog構(gòu)造函數(shù)首先把外部傳入的參數(shù)context對(duì)象賦值給了當(dāng)前類的成員(我們的Dialog一般都是在Activity中啟動(dòng)的。所以這個(gè)context通常是個(gè)Activity),然后調(diào)用context.getSystemService(Context.WINDOW_SERVICE)獲取WindowManager。這個(gè)WindowManager是哪來的呢?先依照上面說的context通常是個(gè)Activity來看待,能夠發(fā)現(xiàn)這句實(shí)質(zhì)就是Activity的getSystemService方法,我們看下源代碼,例如以下:

    @Overridepublic Object getSystemService(@ServiceName @NonNull String name) {if (getBaseContext() == null) {throw new IllegalStateException("System services not available to Activities before onCreate()");}//我們Dialog中獲得的WindowManager對(duì)象就是這個(gè)分支if (WINDOW_SERVICE.equals(name)) {//Activity的WindowManagerreturn mWindowManager;} else if (SEARCH_SERVICE.equals(name)) {ensureSearchManager();return mSearchManager;}return super.getSystemService(name);}

    看見沒有。Dialog中的WindowManager成員實(shí)質(zhì)和Activity里面是一樣的,也就是共用了一個(gè)WindowManager。

    回到Dialog的構(gòu)造函數(shù)繼續(xù)分析,在得到了WindowManager之后,程序又新建了一個(gè)Window對(duì)象(類型是PhoneWindow類型,和Activity的Window新建過程相似)。接著通過w.setCallback(this)設(shè)置Dialog為當(dāng)前window的回調(diào)接口。這樣Dialog就能夠接收事件處理了;接著把從Activity拿到的WindowManager對(duì)象關(guān)聯(lián)到新創(chuàng)建的Window中。

    至此Dialog的創(chuàng)建過程Window處理已經(jīng)完成,非常easy。所以接下來我們繼續(xù)看看Dialog的show與cancel方法。例如以下:

    public void show() {......if (!mCreated) {//回調(diào)Dialog的onCreate方法dispatchOnCreate(null);}//回調(diào)Dialog的onStart方法onStart();//相似于Activity,獲取當(dāng)前新Window的DecorView對(duì)象,所以有一種自己定義Dialog布局的方式就是重寫Dialog的onCreate方法,使用setContentView傳入布局。就像前面文章分析Activity相似mDecor = mWindow.getDecorView();......//獲取新Window的WindowManager.LayoutParams參數(shù)。和上面分析的Activity一樣type為TYPE_APPLICATIONWindowManager.LayoutParams l = mWindow.getAttributes();......try {//把一個(gè)View加入到Activity共用的windowManager里面去mWindowManager.addView(mDecor, l);......} finally {}}

    能夠看見Dialog的新Window與Activity的Window的type相同都為TYPE_APPLICATION,上面介紹WindowManager.LayoutParams時(shí)TYPE_APPLICATION的凝視明白說過,普通應(yīng)用程序窗體TYPE_APPLICATION的token必須設(shè)置為Activity的token來指定窗體屬于誰。所以能夠看見,既然Dialog和Activity共享同一個(gè)WindowManager(也就是上面分析的WindowManagerImpl),而WindowManagerImpl里面有個(gè)Window類型的mParentWindow變量。這個(gè)變量在Activity的attach中創(chuàng)建WindowManagerImpl時(shí)傳入的為當(dāng)前Activity的Window,而當(dāng)前Activity的Window里面的mAppToken值又為當(dāng)前Activity的token。所以Activity與Dialog共享了同一個(gè)mAppToken值,僅僅是Dialog和Activity的Window對(duì)象不同。

    3-2 Dialog窗體載入總結(jié)

    通過上面分析Dialog的窗體載入原理,我們總結(jié)例如以下圖:

    從圖中能夠看出。Activity和Dialog共用了一個(gè)Token對(duì)象,Dialog必須依賴于Activity而顯示(通過別的context搞完之后token都為null,終于會(huì)在ViewRootImpl的setView方法中載入時(shí)由于token為null拋出異常)。所以Dialog的Context傳入?yún)?shù)通常是一個(gè)存在的Activity,假設(shè)Dialog彈出來之前Activity已經(jīng)被銷毀了,則這個(gè)Dialog在彈出的時(shí)候就會(huì)拋出異常。由于token不可用了。在Dialog的構(gòu)造函數(shù)中我們關(guān)聯(lián)了新Window的callback事件監(jiān)聽處理,所以當(dāng)Dialog顯示時(shí)Activity無法消費(fèi)當(dāng)前的事件。

    到此Dialog的窗體載入機(jī)制就分析完成了,接下來我們說說應(yīng)用開發(fā)中常見的一個(gè)詭異問題。

    3-3 從Dialog窗體載入分析引出的應(yīng)用開發(fā)問題

    有了上面的分析我們接下來看下平時(shí)開發(fā)App剛開始學(xué)習(xí)的人easy犯的幾個(gè)錯(cuò)誤。

    實(shí)如今一個(gè)Activity中顯示一個(gè)Dialog,例如以下代碼:

    public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {setContentView(R.layout.activity_main);//重點(diǎn)關(guān)注構(gòu)造函數(shù)的參數(shù),創(chuàng)建一個(gè)Dialog然后顯示出來Dialog dialog = new ProgressDialog(this);dialog.setTitle("TestDialogContext");dialog.show();} }

    分析:使用了Activity為context,也即和Activity共用token,符合上面的分析。所以不會(huì)報(bào)錯(cuò),正常運(yùn)行。

    實(shí)如今一個(gè)Activity中顯示一個(gè)Dialog,例如以下代碼:

    public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {setContentView(R.layout.activity_main);//重點(diǎn)關(guān)注構(gòu)造函數(shù)的參數(shù),創(chuàng)建一個(gè)Dialog然后顯示出來Dialog dialog = new ProgressDialog(getApplicationContext());dialog.setTitle("TestDialogContext");dialog.show();} }

    分析:傳入的是Application的Context,導(dǎo)致TYPE_APPLICATION類型Dialog的token為null。所以拋出例如以下異常,無法顯示對(duì)話框。

    Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an applicationat android.view.ViewRootImpl.setView(ViewRootImpl.java:566)at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:272)at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)at android.app.Dialog.show(Dialog.java:298)

    實(shí)如今一個(gè)Service中顯示一個(gè)Dialog。例如以下代碼:

    public class WindowService extends Service {@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();//重點(diǎn)關(guān)注構(gòu)造函數(shù)的參數(shù)Dialog dialog = new ProgressDialog(this);dialog.setTitle("TestDialogContext");dialog.show();} }

    分析:傳入的Context是一個(gè)Service,相似上面?zhèn)魅階pplicationContext一樣的后果,一樣的原因。拋出例如以下異常:

    Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an applicationat android.view.ViewRootImpl.setView(ViewRootImpl.java:566)at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:272)at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)at android.app.Dialog.show(Dialog.java:298)

    至此通過我們平時(shí)使用最多的Dialog也驗(yàn)證了Dialog成功顯示的必要條件,同一時(shí)候也讓大家避免了再次使用Dialog不當(dāng)出現(xiàn)異常的情況,或者出現(xiàn)相似異常后知道真實(shí)的背后原因是什么的問題。

    能夠看見。Dialog的實(shí)質(zhì)無非也是使用WindowManager的addView、updateViewLayout、removeView進(jìn)行一些操作展示。

    【工匠若水 http://blog.csdn.net/yanbober 轉(zhuǎn)載煩請(qǐng)注明出處,尊重勞動(dòng)成果】

    4 Android應(yīng)用PopWindow窗體加入顯示機(jī)制源代碼

    PopWindow實(shí)質(zhì)就是彈出式菜單。它與Dialag不同的地方是不會(huì)使依賴的Activity組件失去焦點(diǎn)(PopupWindow彈出后能夠繼續(xù)與依賴的Activity進(jìn)行交互)。Dialog卻不能這樣。同一時(shí)候PopupWindow與Dialog還有一個(gè)不同點(diǎn)是PopupWindow是一個(gè)堵塞的對(duì)話框。假設(shè)你直接在Activity的onCreate等方法中顯示它則會(huì)報(bào)錯(cuò),所以PopupWindow必須在某個(gè)事件中顯示地或者是開啟一個(gè)新線程去調(diào)用。

    說這么多還是直接看代碼吧。

    4-1 PopWindow窗體源代碼分析

    根據(jù)PopWindow的使用,我們選擇最經(jīng)常使用的方式來分析。例如以下先看當(dāng)中經(jīng)常使用的一種構(gòu)造函數(shù):

    public class PopupWindow {......//我們僅僅分析最經(jīng)常使用的一種構(gòu)造函數(shù)public PopupWindow(View contentView, int width, int height, boolean focusable) {if (contentView != null) {//獲取mContext。contentView實(shí)質(zhì)是View,View的mContext都是構(gòu)造函數(shù)傳入的,View又層級(jí)傳遞,所以終于這個(gè)mContext實(shí)質(zhì)是Activity。!!

    非常重要

    mContext = contentView.getContext(); //獲取Activity的getSystemService的WindowManager mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); } //進(jìn)行一些Window類的成員變量初始化賦值操作 setContentView(contentView); setWidth(width); setHeight(height); setFocusable(focusable); } ...... }

    能夠看見,構(gòu)造函數(shù)僅僅是初始化了一些變量,看完構(gòu)造函數(shù)繼續(xù)看下PopWindow的展示函數(shù)。例如以下:

    public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {......//anchor是Activity中PopWindow準(zhǔn)備依附的View,這個(gè)View的token實(shí)質(zhì)也是Activity的Window中的token,也即Activity的token//第一步 初始化WindowManager.LayoutParamsWindowManager.LayoutParams p = createPopupLayout(anchor.getWindowToken());//第二步preparePopup(p);......//第三步invokePopup(p);}

    能夠看見,當(dāng)我們想將PopWindow展示在anchor的下方向(Z軸是在anchor的上面)旁邊時(shí)經(jīng)理了上面三步。我們一步一步來分析。先看第一步。源代碼例如以下:

    private WindowManager.LayoutParams createPopupLayout(IBinder token) {//實(shí)例化一個(gè)默認(rèn)的WindowManager.LayoutParams。當(dāng)中type=TYPE_APPLICATIONWindowManager.LayoutParams p = new WindowManager.LayoutParams();//設(shè)置Gravityp.gravity = Gravity.START | Gravity.TOP;//設(shè)置寬高p.width = mLastWidth = mWidth;p.height = mLastHeight = mHeight;//根據(jù)背景設(shè)置formatif (mBackground != null) {p.format = mBackground.getOpacity();} else {p.format = PixelFormat.TRANSLUCENT;}//設(shè)置flagsp.flags = computeFlags(p.flags);//改動(dòng)type=WindowManager.LayoutParams.TYPE_APPLICATION_PANEL,mWindowLayoutType有初始值,type類型為子窗體p.type = mWindowLayoutType;//設(shè)置token為Activity的tokenp.token = token;......return p;}

    接著回到showAsDropDown方法看看第二步,例如以下源代碼:

    private void preparePopup(WindowManager.LayoutParams p) {......//有無設(shè)置PopWindow的background差別if (mBackground != null) {......//假設(shè)有背景則創(chuàng)建一個(gè)PopupViewContainer對(duì)象的ViewGroupPopupViewContainer popupViewContainer = new PopupViewContainer(mContext);PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height);//把背景設(shè)置給PopupViewContainer的ViewGrouppopupViewContainer.setBackground(mBackground);//把我們構(gòu)造函數(shù)傳入的View加入到這個(gè)ViewGrouppopupViewContainer.addView(mContentView, listParams);//返回這個(gè)ViewGroupmPopupView = popupViewContainer;} else {//假設(shè)沒有通過PopWindow的setBackgroundDrawable設(shè)置背景則直接賦值當(dāng)前傳入的View為PopWindow的ViewmPopupView = mContentView;}......}

    能夠看見preparePopup方法的作用就是推斷設(shè)置View。假設(shè)有背景則會(huì)在傳入的contentView外面包一層PopupViewContainer(實(shí)質(zhì)是一個(gè)重寫了事件處理的FrameLayout)之后作為mPopupView。假設(shè)沒有背景則直接用contentView作為mPopupView。

    我們?cè)賮砜聪逻@里的PopupViewContainer類。例如以下源代碼:

    private class PopupViewContainer extends FrameLayout {......@Overrideprotected int[] onCreateDrawableState(int extraSpace) {......}@Overridepublic boolean dispatchKeyEvent(KeyEvent event) {......}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {return true;}return super.dispatchTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {......if(xxx) {dismiss();}......}@Overridepublic void sendAccessibilityEvent(int eventType) {......}}

    能夠看見,這個(gè)PopupViewContainer是一個(gè)PopWindow的內(nèi)部私有類,它繼承了FrameLayout。在當(dāng)中重寫了Key和Touch事件的分發(fā)處理邏輯。同一時(shí)候查閱PopupView能夠發(fā)現(xiàn),PopupView類自身沒有重寫Key和Touch事件的處理,所以假設(shè)沒有將傳入的View對(duì)象放入封裝的ViewGroup中。則點(diǎn)擊Back鍵或者PopWindow以外的區(qū)域PopWindow是不會(huì)消失的(事實(shí)上PopWindow中沒有向Activity及Dialog一樣new新的Window,所以不會(huì)有新的callback設(shè)置。也就沒法處理事件消費(fèi)了)。

    接著繼續(xù)回到showAsDropDown方法看看第三步。例如以下源代碼:

    private void invokePopup(WindowManager.LayoutParams p) {if (mContext != null) {p.packageName = mContext.getPackageName();}mPopupView.setFitsSystemWindows(mLayoutInsetDecor);setLayoutDirectionFromAnchor();mWindowManager.addView(mPopupView, p);}

    能夠看見。這里使用了Activity的WindowManager將我們的PopWindow進(jìn)行了顯示。

    到此能夠發(fā)現(xiàn)。PopWindow的實(shí)質(zhì)無非也是使用WindowManager的addView、updateViewLayout、removeView進(jìn)行一些操作展示。與Dialog不同的地方是沒有新new Window而已(也就沒法設(shè)置callback。無法消費(fèi)事件,也就是前面說的PopupWindow彈出后能夠繼續(xù)與依賴的Activity進(jìn)行交互的原因)。

    到此PopWindw的窗體載入顯示機(jī)制就分析完成了,接下來進(jìn)行總結(jié)與應(yīng)用開發(fā)技巧提示。

    4-2 PopWindow窗體源代碼分析總結(jié)及應(yīng)用開發(fā)技巧提示

    通過上面分析能夠發(fā)現(xiàn)總結(jié)例如以下圖:

    能夠看見,PopWindow全然使用了Activity的Window與WindowManager,相對(duì)來說比較簡單easy記理解。

    再來看一個(gè)開發(fā)技巧:

    假設(shè)設(shè)置了PopupWindow的background。則點(diǎn)擊Back鍵或者點(diǎn)擊PopupWindow以外的區(qū)域時(shí)PopupWindow就會(huì)dismiss;假設(shè)不設(shè)置PopupWindow的background。則點(diǎn)擊Back鍵或者點(diǎn)擊PopupWindow以外的區(qū)域PopupWindow不會(huì)消失。

    【工匠若水 http://blog.csdn.net/yanbober 轉(zhuǎn)載煩請(qǐng)注明出處,尊重勞動(dòng)成果】

    5 Android應(yīng)用Toast窗體加入顯示機(jī)制源代碼

    5-1 基礎(chǔ)知識(shí)準(zhǔn)備

    在開始分析這幾個(gè)窗體之前須要腦補(bǔ)一點(diǎn)東東,我們從應(yīng)用層開發(fā)來直觀腦補(bǔ),這樣以下分析源代碼時(shí)就不蛋疼了。

    例如以下是一個(gè)我們寫的兩個(gè)應(yīng)用實(shí)現(xiàn)Service跨進(jìn)程調(diào)用服務(wù)ADIL的樣例,client調(diào)運(yùn)遠(yuǎn)程Service的start與stop方法控制遠(yuǎn)程Service的操作。

    Android系統(tǒng)中的應(yīng)用程序都運(yùn)行在各自的進(jìn)程中,進(jìn)程之間是無法直接交換數(shù)據(jù)的。可是Android為開發(fā)人員提供了AIDL跨進(jìn)程調(diào)用Service的功能。事實(shí)上AIDL就相當(dāng)于兩方約定的一個(gè)規(guī)則而已。

    先看下在Android Studio中AIDL開發(fā)的project文件夾結(jié)構(gòu),例如以下:

    由于AIDL文件里不能出現(xiàn)訪問修飾符(如public),同一時(shí)候AIDL文件在兩個(gè)項(xiàng)目中要全然一致并且僅僅支持基本類型。所以我們定義的AIDL文件例如以下:

    ITestService.aidl

    package io.github.yanbober.myapplication;interface ITestService {void start(int id);void stop(int id); }

    再來看下根據(jù)aidl文件自己主動(dòng)生成的ITestService.java文件吧。例如以下:

    /** This file is auto-generated. DO NOT MODIFY.*/ package io.github.yanbober.myapplication; public interface ITestService extends android.os.IInterface {//Stub類是ITestService接口的內(nèi)部靜態(tài)抽象類,該類繼承了Binder類public static abstract class Stub extends android.os.Binder implements io.github.yanbober.myapplication.ITestService{......//這是抽象靜態(tài)Stub類中的asInterface方法,該方法負(fù)責(zé)將service返回至client的對(duì)象轉(zhuǎn)換為ITestService.Stub//把遠(yuǎn)程Service的Binder對(duì)象傳遞進(jìn)去,得到的是遠(yuǎn)程服務(wù)的本地代理public static io.github.yanbober.myapplication.ITestService asInterface(android.os.IBinder obj){......}......//遠(yuǎn)程服務(wù)的本地代理,也會(huì)繼承自ITestServiceprivate static class Proxy implements io.github.yanbober.myapplication.ITestService{......@Overridepublic void start(int id) throws android.os.RemoteException{......}@Overridepublic void stop(int id) throws android.os.RemoteException{......}}......}//兩個(gè)方法是aidl文件里定義的方法public void start(int id) throws android.os.RemoteException;public void stop(int id) throws android.os.RemoteException; }

    這就是自己主動(dòng)生成的java文件,接下來我們看看服務(wù)端的Service源代碼,例如以下:

    //記得在AndroidManifet.xml中注冊(cè)Service的<action android:name="io.github.yanbober.myapplication.aidl" />public class TestService extends Service {private TestBinder mTestBinder;//該類繼承ITestService.Stub類而不是Binder類,由于ITestService.Stub是Binder的子類//進(jìn)程內(nèi)的Service定義TestBinder內(nèi)部類是繼承Binder類public class TestBinder extends ITestService.Stub {@Overridepublic void start(int id) throws RemoteException {Log.i(null, "Server Service is start!");}@Overridepublic void stop(int id) throws RemoteException {Log.i(null, "Server Service is stop!");}}@Overridepublic IBinder onBind(Intent intent) {//返回Binderreturn mTestBinder;}@Overridepublic void onCreate() {super.onCreate();//實(shí)例化BindermTestBinder = new TestBinder();} }

    如今服務(wù)端App的代碼已經(jīng)OK,我們來看下client的代碼。client首先也要像上面的project結(jié)構(gòu)一樣,把AIDL文件放好。接著在client使用遠(yuǎn)程服務(wù)端的Service代碼例如以下:

    public class MainActivity extends Activity {private static final String REMOT_SERVICE_ACTION = "io.github.yanbober.myapplication.aidl";private Button mStart, mStop;private ITestService mBinder;private ServiceConnection connection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {//獲得還有一個(gè)進(jìn)程中的Service傳遞過來的IBinder對(duì)象//用IMyService.Stub.asInterface方法轉(zhuǎn)換該對(duì)象mBinder = ITestService.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mStart = (Button) this.findViewById(R.id.start);mStop = (Button) this.findViewById(R.id.stop);mStart.setOnClickListener(clickListener);mStop.setOnClickListener(clickListener);//綁定遠(yuǎn)程跨進(jìn)程ServicebindService(new Intent(REMOT_SERVICE_ACTION), connection, BIND_AUTO_CREATE);}@Overrideprotected void onDestroy() {super.onDestroy();//取消綁定遠(yuǎn)程跨進(jìn)程ServiceunbindService(connection);}private View.OnClickListener clickListener = new View.OnClickListener() {@Overridepublic void onClick(View v) {調(diào)用遠(yuǎn)程Service中的start與stop方法switch (v.getId()) {case R.id.start:try {mBinder.start(0x110);} catch (RemoteException e) {e.printStackTrace();}break;case R.id.stop:try {mBinder.stop(0x120);} catch (RemoteException e) {e.printStackTrace();}break;}}}; }

    到此你相應(yīng)用層通過AIDL使用遠(yuǎn)程Service的形式已經(jīng)非常熟悉了,至于實(shí)質(zhì)的通信使用Binder的機(jī)制我們后面會(huì)寫文章一步一步往下分析。到此的準(zhǔn)備知識(shí)已經(jīng)足夠用來理解以下我們的源代碼分析了。

    5-2 Toast窗體源代碼分析

    我們經(jīng)常使用的Toast窗體事實(shí)上和前面分析的Activity、Dialog、PopWindow都是不同的。由于它和輸入法、墻紙相似。都是系統(tǒng)窗體。

    我們還是依照最經(jīng)常使用的方式來分析源代碼吧。

    我們先看下Toast的靜態(tài)makeText方法吧,例如以下:

    public static Toast makeText(Context context, CharSequence text, @Duration int duration) {//new一個(gè)Toast對(duì)象Toast result = new Toast(context);//獲取前面有篇文章分析的LayoutInflaterLayoutInflater inflate = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);//載入解析Toast的布局。實(shí)質(zhì)transient_notification.xml是一個(gè)LinearLayout中套了一個(gè)@android:id/message的TextView而已View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);//取出布局中的TextViewTextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);//把我們的文字設(shè)置到TextView上tv.setText(text);//設(shè)置一些屬性result.mNextView = v;result.mDuration = duration;//返回新建的Toastreturn result;}

    能夠看見。這種方法構(gòu)造了一個(gè)Toast,然后把要顯示的文本放到這個(gè)View的TextView中,然后初始化相關(guān)屬性后返回這個(gè)新的Toast對(duì)象。

    當(dāng)我們有了這個(gè)Toast對(duì)象之后。能夠通過show方法來顯示出來。例如以下看下show方法源代碼:

    public void show() {......//通過AIDL(Binder)通信拿到NotificationManagerService的服務(wù)訪問接口。當(dāng)前Toast類相當(dāng)于上面樣例的client!

    !。相當(dāng)重要!!!

    INotificationManager service = getService(); String pkg = mContext.getOpPackageName(); TN tn = mTN; tn.mNextView = mNextView; try { //把TN對(duì)象和一些參數(shù)傳遞到遠(yuǎn)程N(yùn)otificationManagerService中去 service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty } }

    我們看看show方法中調(diào)運(yùn)的getService方法,例如以下:

    //遠(yuǎn)程N(yùn)otificationManagerService的服務(wù)訪問接口private static INotificationManager sService;static private INotificationManager getService() {//單例模式if (sService != null) {return sService;}//通過AIDL(Binder)通信拿到NotificationManagerService的服務(wù)訪問接口sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));return sService;}

    通過上面我們的基礎(chǔ)腦補(bǔ)實(shí)例你也能看懂這個(gè)getService方法了吧。那接著我們來看mTN吧,好像mTN在Toast的構(gòu)造函數(shù)里見過一眼,我們來看看。例如以下:

    public Toast(Context context) {mContext = context;mTN = new TN();mTN.mY = context.getResources().getDimensionPixelSize(com.android.internal.R.dimen.toast_y_offset);mTN.mGravity = context.getResources().getInteger(com.android.internal.R.integer.config_toastDefaultGravity);}

    能夠看見mTN確實(shí)是在構(gòu)造函數(shù)中實(shí)例化的,那我們就來看看這個(gè)TN類,例如以下:

    //相似于上面樣例的服務(wù)端實(shí)例化的Service內(nèi)部類Binderprivate static class TN extends ITransientNotification.Stub {......//實(shí)現(xiàn)了AIDL的show與hide方法@Overridepublic void show() {if (localLOGV) Log.v(TAG, "SHOW: " + this);mHandler.post(mShow);}@Overridepublic void hide() {if (localLOGV) Log.v(TAG, "HIDE: " + this);mHandler.post(mHide);}......}

    看見沒有,TN是Toast內(nèi)部的一個(gè)私有靜態(tài)類,繼承自ITransientNotification.Stub。你這時(shí)指定好奇ITransientNotification.Stub是個(gè)啥玩意,對(duì)吧?事實(shí)上你在上面的腦補(bǔ)實(shí)例中見過它的,他出如今服務(wù)端實(shí)現(xiàn)的Service中,就是一個(gè)Binder對(duì)象。也就是對(duì)一個(gè)aidl文件的實(shí)現(xiàn)而已,我們看下這個(gè)ITransientNotification.aidl文件。例如以下:

    package android.app;/** @hide */ oneway interface ITransientNotification {void show();void hide(); }

    看見沒有,和我們上面的樣例非常相似吧。

    再回到上面分析的show()方法中能夠看到。我們的Toast是傳給遠(yuǎn)程的NotificationManagerService管理的,為了NotificationManagerService回到我們的應(yīng)用程序(回調(diào))。我們須要告訴NotificationManagerService我們當(dāng)前程序的Binder引用是什么(也就是TN)。是不是覺得和上面樣例有些不同,這里感覺Toast又充當(dāng)client。又充當(dāng)服務(wù)端的樣子,實(shí)質(zhì)就是一個(gè)回調(diào)過程而已。

    繼續(xù)來看Toast中的show方法的service.enqueueToast(pkg, tn, mDuration);語句。service實(shí)質(zhì)是遠(yuǎn)程的NotificationManagerService。所以enqueueToast方法就是NotificationManagerService類的。例如以下:

    private final IBinder mService = new INotificationManager.Stub() {// Toasts// ============================================================================@Overridepublic void enqueueToast(String pkg, ITransientNotification callback, int duration){......synchronized (mToastQueue) {int callingPid = Binder.getCallingPid();long callingId = Binder.clearCallingIdentity();try {ToastRecord record;//查看該Toast是否已經(jīng)在隊(duì)列當(dāng)中int index = indexOfToastLocked(pkg, callback);// If it's already in the queue, we update it in place, we don't// move it to the end of the queue.//凝視說了,已經(jīng)存在則直接取出updateif (index >= 0) {record = mToastQueue.get(index);record.update(duration);} else {// Limit the number of toasts that any given package except the android// package can enqueue. Prevents DOS attacks and deals with leaks.......//將Toast封裝成ToastRecord對(duì)象,放入mToastQueue中record = new ToastRecord(callingPid, pkg, callback, duration);//把他加入到ToastQueue隊(duì)列中mToastQueue.add(record);index = mToastQueue.size() - 1;//將當(dāng)前Toast所在的進(jìn)程設(shè)置為前臺(tái)進(jìn)程keepProcessAliveLocked(callingPid);}//假設(shè)index為0,說明當(dāng)前入隊(duì)的Toast在隊(duì)頭,須要調(diào)用showNextToastLocked方法直接顯示if (index == 0) {showNextToastLocked();}} finally {Binder.restoreCallingIdentity(callingId);}}}}

    繼續(xù)看下該方法中調(diào)運(yùn)的showNextToastLocked方法,例如以下:

    void showNextToastLocked() {//取出ToastQueue中隊(duì)列最前面的ToastRecordToastRecord record = mToastQueue.get(0);while (record != null) {try {//Toast類中實(shí)現(xiàn)的ITransientNotification.Stub的Binder接口TN,調(diào)運(yùn)了那個(gè)類的show方法record.callback.show();scheduleTimeoutLocked(record);return;} catch (RemoteException e) {......}}}

    繼續(xù)先看下該方法中調(diào)運(yùn)的scheduleTimeoutLocked方法。例如以下:

    private void scheduleTimeoutLocked(ToastRecord r){//移除上一條消息mHandler.removeCallbacksAndMessages(r);//根據(jù)Toast傳入的duration參數(shù)LENGTH_LONG=1來推斷決定多久發(fā)送消息Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);long delay = r.duration == Toast.LENGTH_LONG ?

    LONG_DELAY : SHORT_DELAY; //根據(jù)設(shè)置的MESSAGE_TIMEOUT后發(fā)送消息 mHandler.sendMessageDelayed(m, delay); }

    能夠看見這里先回調(diào)了Toast的TN的show,以下timeout可能就是hide了。接著還在該類的mHandler處理了這條消息。然后調(diào)運(yùn)了例如以下處理方法:

    private void handleTimeout(ToastRecord record){......synchronized (mToastQueue) {int index = indexOfToastLocked(record.pkg, record.callback);if (index >= 0) {cancelToastLocked(index);}}}

    我們繼續(xù)看cancelToastLocked方法,例如以下:

    void cancelToastLocked(int index) {ToastRecord record = mToastQueue.get(index);try {//回調(diào)Toast的TN中實(shí)現(xiàn)的hide方法record.callback.hide();} catch (RemoteException e) {......}//從隊(duì)列移除當(dāng)前顯示的ToastmToastQueue.remove(index);keepProcessAliveLocked(record.pid);if (mToastQueue.size() > 0) {//假設(shè)當(dāng)前的Toast顯示完成隊(duì)列里還有其它的Toast則顯示其它的ToastshowNextToastLocked();}}

    到此能夠發(fā)現(xiàn),Toast的遠(yuǎn)程管理NotificationManagerService類的處理實(shí)質(zhì)是通過Handler發(fā)送延時(shí)消息顯示取消Toast的,并且在遠(yuǎn)程N(yùn)otificationManagerService類中又遠(yuǎn)程回調(diào)了Toast的TN類實(shí)現(xiàn)的show與hide方法。

    如今我們就回到Toast的TN類再看看這個(gè)show與hide方法,例如以下:

    ```javaprivate static class TN extends ITransientNotification.Stub {......//僅僅是實(shí)例化了一個(gè)Handler。非常重要!!!

    !。!

    !!

    final Handler mHandler = new Handler(); ...... final Runnable mShow = new Runnable() { @Override public void run() { handleShow(); } }; final Runnable mHide = new Runnable() { @Override public void run() { handleHide(); // Don't do this in handleHide() because it is also invoked by handleShow() mNextView = null; } }; ...... //實(shí)現(xiàn)了AIDL的show與hide方法 @Override public void show() { if (localLOGV) Log.v(TAG, "SHOW: " + this); mHandler.post(mShow); } @Override public void hide() { if (localLOGV) Log.v(TAG, "HIDE: " + this); mHandler.post(mHide); } ...... }

    能夠看見。這里實(shí)現(xiàn)aidl接口的方法實(shí)質(zhì)是通過handler的post來運(yùn)行的一個(gè)方法,而這個(gè)Handler僅僅僅僅是new了一下。也就是說。假設(shè)我們寫APP時(shí)使用Toast在子線程中則須要自行準(zhǔn)備Looper對(duì)象。僅僅有主線程Activity創(chuàng)建時(shí)幫忙準(zhǔn)備了Looper(關(guān)于Handler與Looper假設(shè)整不明白請(qǐng)閱讀《Android異步消息處理機(jī)制具體解釋及源代碼分析》)。

    那我們重點(diǎn)關(guān)注一下handleShow與handleHide方法。例如以下:

    public void handleShow() {if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView+ " mNextView=" + mNextView);if (mView != mNextView) {// remove the old view if necessary//假設(shè)有必要就通過WindowManager的remove刪掉舊的handleHide();mView = mNextView;Context context = mView.getContext().getApplicationContext();String packageName = mView.getContext().getOpPackageName();if (context == null) {context = mView.getContext();}//通過得到的context(通常是ContextImpl的context)獲取WindowManager對(duì)象(上一篇文章分析的單例的WindowManager)mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);......//在把Toast的View加入之前發(fā)現(xiàn)Toast的View已經(jīng)被加入過(有partent)則刪掉if (mView.getParent() != null) {......mWM.removeView(mView);}......//把Toast的View加入到窗體,當(dāng)中mParams.type在構(gòu)造函數(shù)中賦值為TYPE_TOAST!!!。!

    特別重要

    mWM.addView(mView, mParams); ...... } } public void handleHide() {if (mView != null) {// note: checking parent() just to make sure the view has// been added... i have seen cases where we get here when// the view isn't yet added, so let's try not to crash.//凝視說得非常清楚了,不解釋,就是removeif (mView.getParent() != null) {mWM.removeView(mView);}mView = null;}}

    到此Toast的窗體加入原理就分析完成了,接下來我們進(jìn)行總結(jié)。

    5-3 Toast窗體源代碼分析總結(jié)及應(yīng)用開發(fā)技巧

    經(jīng)過上面的分析我們總結(jié)例如以下:

    通過上面分析及上圖直觀描寫敘述能夠發(fā)現(xiàn),之所以Toast的顯示交由遠(yuǎn)程的NotificationManagerService管理是由于Toast是每一個(gè)應(yīng)用程序都會(huì)彈出的,并且位置和UI風(fēng)格都差點(diǎn)兒相同。所以假設(shè)我們不統(tǒng)一管理就會(huì)出現(xiàn)覆蓋疊加現(xiàn)象,同一時(shí)候?qū)е虏缓每刂啤K訥oogle把Toast設(shè)計(jì)成為了系統(tǒng)級(jí)的窗體類型,由NotificationManagerService統(tǒng)一隊(duì)列管理。

    在我們開發(fā)應(yīng)用程序時(shí)使用Toast注意事項(xiàng):

  • 通過分析TN類的handler能夠發(fā)現(xiàn),假設(shè)想在非UI線程使用Toast須要自行聲明Looper。否則運(yùn)行會(huì)拋出Looper相關(guān)的異常;UI線程不須要,由于系統(tǒng)已經(jīng)幫忙聲明。

  • 在使用Toast時(shí)context參數(shù)盡量使用getApplicationContext()。能夠有效的防止靜態(tài)引用導(dǎo)致的內(nèi)存泄漏。

  • 有時(shí)候我們會(huì)發(fā)現(xiàn)Toast彈出過多就會(huì)延遲顯示。由于上面源代碼分析能夠看見Toast.makeText是一個(gè)靜態(tài)工廠方法,每次調(diào)用這種方法都會(huì)產(chǎn)生一個(gè)新的Toast對(duì)象,當(dāng)我們?cè)谶@個(gè)新new的對(duì)象上調(diào)用show方法就會(huì)使這個(gè)對(duì)象加入到NotificationManagerService管理的mToastQueue消息顯示隊(duì)列里排隊(duì)等候顯示;所以假設(shè)我們不每次都產(chǎn)生一個(gè)新的Toast對(duì)象(使用單例來處理)就不須要排隊(duì)。也就能及時(shí)更新了。

  • 6 Android應(yīng)用Activity、Dialog、PopWindow、Toast窗體顯示機(jī)制總結(jié)

    能夠看見上面不管Acitivty、Dialog、PopWindow、Toast的實(shí)質(zhì)事實(shí)上都是例如以下接口提供的方法操作:

    public interface ViewManager {public void addView(View view, ViewGroup.LayoutParams params);public void updateViewLayout(View view, ViewGroup.LayoutParams params);public void removeView(View view); }

    整個(gè)應(yīng)用各種窗體的顯示都離不開這三個(gè)方法而已。僅僅是token及type與Window是否共用的問題。

    【工匠若水 http://blog.csdn.net/yanbober 轉(zhuǎn)載煩請(qǐng)注明出處,尊重勞動(dòng)成果】

    總結(jié)

    以上是生活随笔為你收集整理的Android应用Activity、Dialog、PopWindow、Toast窗体加入机制及源代码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

    久久网站免费 | 97国产人人| 精品亚洲免费 | 成人av在线电影 | 操久在线 | 久草在线看片 | 国产又粗又猛又黄视频 | 天天做天天爱天天爽综合网 | 色婷婷午夜 | 久久三级毛片 | 欧美一区二区三区激情视频 | 久久高清视频免费 | 日韩视频在线一区 | 久久高视频 | 天天干天天操天天操 | 亚洲视频精选 | 日日夜夜综合网 | 国产在线精品福利 | 国产精品视频不卡 | 免费视频网 | 中文字幕黄网 | 综合色站导航 | 97国产一区二区 | 区一区二在线 | 久久99免费 | 99视频在线免费看 | av片一区二区 | 在线视频观看91 | 中文日韩在线 | 久久精久久精 | 欧美日韩一区二区在线观看 | 毛片基地黄久久久久久天堂 | 国产精品久久久久久久久久直播 | 99热在线这里只有精品 | av免费电影在线 | 九九热免费视频在线观看 | 西西444www | 91精品国自产在线 | 日韩伦理一区二区三区av在线 | 午夜丁香视频在线观看 | 丁香五月亚洲综合在线 | 国产精品久久久久一区 | 日韩mv欧美mv国产精品 | 91麻豆精品国产91久久久无需广告 | 97精品国产一二三产区 | 国内少妇自拍视频一区 | 91影视成人| 天天干夜夜操视频 | 亚洲综合色视频 | 午夜精品福利一区二区 | 欧美一二三区在线观看 | 久久国际影院 | 91成人在线观看喷潮 | 国产精品久久久久久爽爽爽 | 国产一二三精品 | 蜜臀精品久久久久久蜜臀 | 狠狠色丁香婷婷综合欧美 | 在线观看免费av片 | 91亚洲精品在线观看 | 99在线国产| 久久96国产精品久久99漫画 | 成年人免费在线看 | 久久久久久高潮国产精品视 | 日韩网站免费观看 | 三级在线播放视频 | 视频在线观看入口黄最新永久免费国产 | 久久av中文字幕片 | 欧美动漫一区二区三区 | 天天色天天色天天色 | 久久精品欧美一区 | 三上悠亚一区二区在线观看 | 在线欧美最极品的av | 国产成人精品久 | 亚洲一二三久久 | 欧美精品乱码久久久久 | 国产精品一区二区久久精品爱涩 | 中文av一区二区 | 夜夜躁狠狠躁日日躁视频黑人 | 久久人人插 | 极品嫩模被强到高潮呻吟91 | 久久久久久久国产精品视频 | 国产欧美精品xxxx另类 | avhd高清在线谜片 | 日日夜夜网站 | 国产午夜三级一二三区 | 国产一区二区三区免费视频 | 久久美女免费视频 | 91av在线免费观看 | 免费高清在线视频一区· | 黄色av成人在线观看 | 一本一道久久a久久精品蜜桃 | 国产一级电影在线 | 亚洲国产大片 | 国产手机在线播放 | 国产亚洲观看 | 天天激情站 | av片子在线观看 | 九九久久久久久久久激情 | 人人草网站 | 欧美九九视频 | 日韩欧美视频在线播放 | 色欧美成人精品a∨在线观看 | 欧美国产高清 | 国产精品99久久久久久有的能看 | 成人国产精品免费 | 国色天香永久免费 | 久久99热国产 | 在线观看中文字幕视频 | 久久久久免费精品 | 在线观看日本高清mv视频 | 在线精品视频免费观看 | 91高清完整版在线观看 | 久久av免费观看 | 91福利在线导航 | 在线影视 一区 二区 三区 | 正在播放 久久 | 国产精品99久久久久久人免费 | 182午夜在线观看 | 国产高清在线精品 | 久草青青在线观看 | 日韩精品五月天 | 欧美精品久久久久久久久久久 | 在线视频一区观看 | 国产精品99久久久久久久久 | 精一区二区 | 国产又粗又猛又爽又黄的视频免费 | 国产91精品欧美 | www.操.com | 深爱激情五月综合 | 亚洲激情中文 | 在线观看韩日电影免费 | 天天操夜夜操 | 91九色国产 | 99久久精品免费看国产一区二区三区 | 黄色日批网站 | av导航福利 | 亚洲黄色小说网址 | 久久午夜色播影院免费高清 | 欧美久久久久久久久久久久 | 久久久久久久久久网 | 日韩欧美精品在线观看 | 色综合久久久久久久久五月 | 热99在线视频 | 国产手机在线播放 | 欧美日韩免费网站 | 久久99精品波多结衣一区 | 国产三级精品三级在线观看 | 日韩系列 | 国产精品久久久久久久久久新婚 | 亚洲激情网站免费观看 | 久久免费看av | 免费观看高清 | 日韩欧美视频一区二区三区 | 成年人在线免费视频观看 | 午夜视频免费在线观看 | 日韩av成人在线观看 | 亚洲精品大全 | 亚洲国产中文字幕 | 中文在线| 成人在线观看免费 | 一区二区三区国产欧美 | 亚洲精品国产精品国自产在线 | 欧美一级视频免费 | 欧美日韩一级视频 | 91尤物国产尤物福利在线播放 | 美女搞黄国产视频网站 | 亚洲天堂网在线视频 | 日本h在线播放 | 青青草国产免费 | 成人动漫一区二区 | 97高清免费视频 | 免费观看第二部31集 | 日本护士撒尿xxxx18 | 中文久久精品 | 午夜久草 | 久久精品aaa | 日韩电影中文字幕在线观看 | 国产探花 | 国产精品二区在线 | 久久久久久久久久久精 | 黄色大片入口 | 精品久久精品 | 亚洲国产三级在线观看 | 在线看成人| 日韩欧美国产成人 | 国产一区观看 | 欧美日韩p片 | 国产小视频免费在线网址 | 国产精品久久久久久麻豆一区 | 成人a级黄色片 | 国产精品久久久久久av | 国产精品久久久久婷婷 | 婷婷综合网 | 日本aaaa级毛片在线看 | 亚洲成a人片77777潘金莲 | 色欧美视频 | 亚洲精品欧美成人 | 激情五月婷婷综合 | 亚洲国产剧情av | 国产一区二区视频在线播放 | 国产精品久久久久永久免费 | 亚洲欧美激情精品一区二区 | 91福利社区在线观看 | 国产黄影院色大全免费 | 亚洲激情六月 | 一区二区三区在线免费观看 | 日本女人逼 | 天天色天天操综合 | 欧洲视频一区 | 久久9999久久免费精品国产 | 免费av视屏 | 亚洲色视频 | 国产精品 久久 | 成人av中文字幕 | 国产成人av在线 | 麻豆91小视频| 国产精品久久久久久久久久久久午夜 | 午夜免费福利视频 | 日韩一级片大全 | 麻豆传媒在线免费看 | 久久av免费| 伊人宗合| 欧美性大胆 | 国内精品久久久久 | 在线av资源 | av在线小说| 国产精品久久久久久久久久不蜜月 | 天天夜操 | www.色午夜| 亚洲欧美日韩国产一区二区 | 激情婷婷综合网 | 日韩免费观看一区二区 | 日日夜夜精品免费观看 | 开心激情五月婷婷 | 免费观看9x视频网站在线观看 | 91男人影院| 欧美99热 | 五月天狠狠操 | 丁香花在线视频观看免费 | 成人丝袜| 中文字幕电影高清在线观看 | 久久一区国产 | 天堂久久电影网 | 黄色中文字幕在线 | 日韩国产精品一区 | 亚洲在线资源 | 久久久精品网站 | 免费看日韩 | 三日本三级少妇三级99 | 亚洲精品乱码久久久久久蜜桃动漫 | 日韩肉感妇bbwbbwbbw | 中文字幕乱在线伦视频中文字幕乱码在线 | 超碰在线人人97 | 色天天综合久久久久综合片 | 中文欧美字幕免费 | 激情五月开心 | 国产在线高清视频 | 色丁香久久 | 综合网天天 | 日本资源中文字幕在线 | 亚洲女同ⅹxx女同tv | 五月婷影院 | 九九免费精品视频在线观看 | 国产亚洲精品久久久久久网站 | 最新中文字幕在线资源 | 国产日韩精品一区二区在线观看播放 | 四虎成人精品永久免费av九九 | 91tv国产成人福利 | 欧美日bb | 欧美激情视频一二区 | 在线视频一区观看 | 国产破处在线播放 | 一区精品久久 | 99久久精品国产观看 | 中文字幕人成不卡一区 | 国产va饥渴难耐女保洁员在线观看 | 美女国内精品自产拍在线播放 | 亚洲欧美日韩中文在线 | 中文在线字幕免费观看 | 人人澡人人舔 | 奇米网8888 | 天堂在线一区 | 久久久夜色 | 久久视频免费看 | 国产精品黑丝在线观看 | 手机成人av | 久久久久久久影院 | www.亚洲精品在线 | 精品欧美一区二区三区久久久 | 伊人看片 | 中文字幕一区在线观看视频 | 国产91影视 | 亚洲女人天堂成人av在线 | 亚洲黄色精品 | 国产美女免费 | 国产在线观看高清视频 | 91麻豆文化传媒在线观看 | 一区二区三区四区五区六区 | 日韩一区二区三区视频在线 | 在线观看久 | 国产粉嫩在线 | 91免费在线看片 | 成年人免费在线观看网站 | 三级av网站 | 免费在线中文字幕 | 国产青青青 | 色综合咪咪久久网 | 最新不卡av | 一区二区三区播放 | 久久调教视频 | 欧美做受高潮电影o | 欧美日韩精品国产 | 丁香花五月 | 夜色资源站wwwcom | www.天天干.com | 成人黄色大片 | av 在线观看| 国内久久久久 | 日日夜夜天天操 | 黄色最新网址 | 96av麻豆蜜桃一区二区 | 亚洲精品在线资源 | 97电影网站| 欧美日韩国产精品一区二区三区 | 亚洲一区久久 | 日韩免费观看一区二区三区 | 免费亚洲婷婷 | 91xav | 国产最顶级的黄色片在线免费观看 | 91久久一区二区 | 四川bbb搡bbb爽爽视频 | 天天干天天干天天色 | 国产成人精品久久久 | 久久天天操 | 在线观看视频一区二区三区 | 天天躁日日躁狠狠躁 | 区一区二区三区中文字幕 | 国产精品一区二区三区观看 | 欧美aa在线观看 | 久久r精品 | 精品亚洲国产视频 | 91亚洲精品久久久中文字幕 | 国产第一福利网 | 亚洲视频 一区 | 91精品国产乱码久久 | 一级a毛片高清视频 | 成人av教育 | 99精品久久久久久久久久综合 | 亚洲精品国产免费 | 99在线精品视频在线观看 | 伊人在线视频 | 亚洲国产精品成人综合 | 91传媒在线观看 | 中文字幕久久网 | 久久不卡免费视频 | 精品欧美一区二区三区久久久 | 亚洲成人资源在线观看 | 欧美一级高清片 | 久久草网站 | 国产亚洲成av人片在线观看桃 | 88av网站 | av亚洲产国偷v产偷v自拍小说 | 亚洲精选在线 | 美女精品久久久 | av中文在线播放 | 国产亚洲婷婷免费 | 久久国精品 | 嫩草伊人久久精品少妇av | 日韩69av | 最近日本字幕mv免费观看在线 | 狠狠色丁香婷婷综合基地 | 日韩专区视频 | 国产成人一区二区在线观看 | 亚洲视频电影在线 | 美女在线免费视频 | 日韩av中文 | 成人av.com | 久久久高清一区二区三区 | 国产视频在线一区二区 | 在线观看免费福利 | 久久无码精品一区二区三区 | 亚洲国产影院av久久久久 | av动图| 亚州精品国产 | 亚洲美女视频在线 | 天天操天天草 | 久射网| 国产高清在线免费观看 | 久草在线免费资源站 | 精品999久久久 | 国产a精品 | 丝袜美腿在线视频 | 欧美一性一交一乱 | 亚洲成人av在线 | 日韩理论电影网 | 日韩中文字幕91 | 久久爱992xxoo | 日韩色av色资源 | 成年人国产精品 | 国产免费久久精品 | 99欧美| 国产打女人屁股调教97 | 91久久电影 | 久草精品在线播放 | 在线免费观看羞羞视频 | 激情五月在线观看 | 亚洲精品乱码久久久久久写真 | 国产一区二区影院 | 丝袜网站在线观看 | 精品久久久久久久久久久久久久久久久久 | 一级黄色片毛片 | 免费福利在线视频 | 岛国精品一区二区 | 日韩欧美一区二区三区视频 | 久久精品国产亚洲精品2020 | 日本乱视频 | 国产69精品久久久久9999apgf | 91毛片在线| 亚洲精品高清视频 | 蜜臀久久99静品久久久久久 | 亚洲h色精品 | 日韩在线不卡 | 免费国产在线视频 | www成人精品 | 久精品在线 | 国产99一区视频免费 | 免费观看完整版无人区 | 麻豆91在线看 | 久久成熟| 最近中文字幕大全 | 久久国产精品免费一区二区三区 | 欧美性色黄大片在线观看 | 国产成人久久久久 | www.五月天激情 | 久久久美女| 国产一区在线播放 | 亚洲一区尤物 | 九九爱免费视频 | 超碰在线人人97 | 中文字幕的 | 亚洲最大成人免费网站 | 最近日本字幕mv免费观看在线 | 色88久久| 玖玖国产精品视频 | 天天色天天艹 | 久草线 | 国产精品永久免费视频 | 三级av免费看 | 在线观看一区视频 | 欧美激情第八页 | 亚洲国产偷 | 色资源网免费观看视频 | a在线观看国产 | a成人在线| 久久综合久久综合这里只有精品 | 不卡av电影在线 | 91精品欧美一区二区三区 | 亚洲精品乱码久久久久久蜜桃不爽 | 日本中文字幕影院 | 久久精品精品电影网 | 久久99爱视频 | 丁香综合| h动漫中文字幕 | 天天五月天色 | 中文一区在线 | 一区二区三区日韩精品 | 天天爱天天色 | 国产精品美女久久久久久久久久久 | 亚洲韩国一区二区三区 | 国产中文字幕在线观看 | 在线观看一区二区视频 | 日韩一二三 | 激情婷婷六月 | 久久午夜影视 | 国产成人一区二区精品非洲 | 91福利视频在线 | 国产亚洲成av人片在线观看桃 | 欧美 亚洲 另类 激情 另类 | 久草在线视频国产 | 日日夜夜狠狠干 | 一区二区三区动漫 | av观看免费在线 | www五月| 欧美色噜噜 | 久久久99精品免费观看乱色 | 国产精品久久99综合免费观看尤物 | 久久黄色网页 | 一本色道久久综合亚洲二区三区 | 在线视频一区二区 | 国产无吗一区二区三区在线欢 | av在线播放中文字幕 | 亚洲永久国产精品 | 国产激情免费 | 免费av在线网 | 国产在线精品二区 | jizz欧美性9| 成人午夜电影在线 | 在线免费中文字幕 | 欧美一区二区三区在线视频观看 | 在线免费观看黄 | 91热这里只有精品 | 免费网址在线播放 | 综合色婷婷 | 国产精品久免费的黄网站 | 国产精品久久久999 国产91九色视频 | 在线a人v观看视频 | 国内精品久久久久国产 | 久久与婷婷 | 久久伊人综合 | 国产高清av免费在线观看 | 日韩高清在线一区二区 | 欧美精品久久久久久久久老牛影院 | 91九色porny在线 | 亚洲精品午夜一区人人爽 | 99视频国产精品 | 黄色一及电影 | 久久久久久99精品 | 美女网站色免费 | 96国产精品视频 | 久久五月激情 | 久久久久亚洲a | 国产在线观看中文字幕 | 美女福利视频 | 欧美在线1区 | 国产精品免费看久久久8精臀av | 91麻豆免费视频 | 手机av在线网站 | 亚洲精品在线视频 | 国产视频丨精品|在线观看 国产精品久久久久久久久久久久午夜 | 中文字幕丝袜一区二区 | 亚洲成色777777在线观看影院 | 日韩精品久久久久久 | 成人sm另类专区 | 激情五月开心 | 热九九精品 | 国产精品电影一区 | 精品理论片| ww亚洲ww亚在线观看 | 国产精品1024 | 中文字幕成人网 | 久久电影色 | 国产免费专区 | 深夜免费网站 | 九9热这里真品2 | 国产黄色av| 国产小视频免费观看 | 久久人人爽人人爽人人片av免费 | 97超碰人人网 | 免费福利在线播放 | 欧美三级在线播放 | 国产九色在线播放九色 | 99 国产精品 | 日日天天av | 亚洲精品一区二区三区高潮 | 91精品一区二区三区久久久久久 | 日日夜夜天天干 | 黄色一级网 | 欧洲性视频 | 99在线免费观看视频 | 高清有码中文字幕 | 国内小视频 | 午夜久草 | 友田真希x88av| 美女久久久久 | 日韩中文在线电影 | 久艹在线免费观看 | 日日爱夜夜爱 | 狠狠操操操 | 亚洲国产精品久久久 | 国产精品久久久久久吹潮天美传媒 | 欧美日韩免费在线观看视频 | 久久www免费人成看片高清 | 亚洲国产mv | a色网站| 天天做天天爱天天综合网 | 国产在线永久 | 91中文字幕网 | 在线 国产一区 | 久久手机精品视频 | 一区av在线播放 | 91 在线视频播放 | 成人免费观看完整版电影 | 亚洲乱码国产乱码精品天美传媒 | 免费成人av在线看 | zzijzzij亚洲成熟少妇 | 狠狠干狠狠操 | 99视频偷窥在线精品国自产拍 | 久久精品精品 | 综合在线观看色 | 日韩女同一区二区三区在线观看 | 日本精品一区二区三区在线观看 | 欧美日韩三级 | av在线专区 | 911国产在线观看 | 国产日韩欧美在线观看 | 亚洲黄色在线播放 | 欧美一级看片 | 日韩国产欧美在线视频 | 日韩一级网站 | 国产精品毛片久久久久久久久久99999999 | 婷五月激情 | 日韩视频在线不卡 | 免费国产在线观看 | 久久精品久久国产 | 精品美女视频 | 日韩在线视频国产 | 久久影院中文字幕 | 在线视频黄 | 在线观看视频免费播放 | 亚洲精品国产综合99久久夜夜嗨 | 天堂网av在线 | 97超碰影视 | 欧美激情第八页 | 在线国产日本 | 久久夜av | 中文字幕在线一区观看 | 国产高清精品在线 | 天天躁日日躁狠狠躁av中文 | 日韩视频1 | 午夜电影 电影 | 久免费视频 | 黄色精品免费 | 99九九99九九九视频精品 | 黄色电影网站在线观看 | 亚洲精品国产第一综合99久久 | 天天摸天天干天天操天天射 | 欧美美女激情18p | 中文乱幕日产无线码1区 | 91在线视频观看免费 | 国产看片网站 | 国产精品麻豆三级一区视频 | 黄色特级一级片 | 欧美日韩激情视频8区 | 99欧美精品 | 不卡国产在线 | 亚洲精品黄 | 五月开心六月伊人色婷婷 | 精品久久综合 | 天堂av影院 | 日韩免费久久 | 99免在线观看免费视频高清 | 国产99免费视频 | 91精品视频导航 | 精品久久在线 | www.av免费观看| 视频福利在线观看 | 99精品视频免费观看视频 | 97超碰人人澡人人爱 | 国产亚洲精品久久19p | 91大神在线观看视频 | 麻豆传媒一区二区 | 日韩在线观看一区 | 99re6热在线精品视频 | 国产精品福利无圣光在线一区 | 精品视频亚洲 | 国产精品久久伊人 | 国产精品久久99精品毛片三a | 日本中文字幕网站 | 国产精品毛片久久 | 国产香蕉97碰碰碰视频在线观看 | 国产色拍 | 国产尤物在线 | 国内视频一区二区 | 免费在线精品视频 | 日韩一区二区三区高清在线观看 | 亚洲一区美女视频在线观看免费 | 日本黄色免费电影网站 | 国产日韩中文在线 | 人九九精品 | 国产精品国产三级国产aⅴ无密码 | 香蕉在线视频观看 | 日日干综合| 久久精品久久精品久久精品 | 国产综合视频在线观看 | 国产剧情一区 | 伊人狠狠色| 国产婷婷vvvv激情久 | 依人成人综合网 | 成人在线视频网 | 91麻豆精品一区二区三区 | 久久综合狠狠综合 | 狠狠狠色丁香婷婷综合久久五月 | 成人在线你懂得 | 日韩在线观看小视频 | 美女精品久久久 | 国产精品尤物视频 | 色无五月| 欧美日韩另类在线观看 | 国产123区在线观看 国产精品麻豆91 | 色婷婷88av视频一二三区 | av永久网址| 操天天操 | 久久久国产精品免费 | 免费激情网 | 97理论电影 | 97在线看片 | 亚洲欧美综合 | 91在线网址 | 精品国产亚洲一区二区麻豆 | 中文字幕久久久精品 | 国产精品v欧美精品v日韩 | 久久理论电影网 | 国产精品一区免费观看 | 国产成人精品福利 | 国产精品久久99精品毛片三a | 久久人人爽人人爽人人片av免费 | 最近中文字幕高清字幕在线视频 | 亚洲一区二区视频在线 | 欧美日韩中文视频 | 黄色特级毛片 | 婷婷在线免费 | 91福利在线导航 | 日韩综合在线观看 | 99久久精品无免国产免费 | 综合久久2023 | av中文字幕不卡 | 97在线观看免费高清完整版在线观看 | 国内精品久久久久久久久久久 | 久久99精品波多结衣一区 | 在线播放一区二区三区 | 天海冀一区二区三区 | 一区二区三区不卡在线 | 亚洲电影影音先锋 | 国产又粗又猛又爽又黄的视频先 | 97av视频在线观看 | 久久久久久久久免费 | 青青草国产成人99久久 | 中文 一区二区 | 国语自产偷拍精品视频偷 | 成人免费大片黄在线播放 | www激情网 | 亚洲美女视频在线观看 | 国内精品久久久久久久影视简单 | 国产在线精品观看 | 成人免费xxxxxx视频 | 久久久久久久久久久高潮一区二区 | 天天综合五月天 | 黄色在线看网站 | 色94色欧美 | aaa日本高清在线播放免费观看 | 成人午夜精品福利免费 | 808电影免费观看三年 | 五月丁婷婷 | 麻豆视频免费在线观看 | 国产99久久久久 | 国产精品久久久久久久久久久免费看 | 国产免费xvideos视频入口 | 国产精品美女久久久久久久 | 久久天天拍 | 成人免费xyz网站 | 一级成人在线 | 久久久久久国产一区二区三区 | 超碰公开97 | 国产精品99视频 | 久久久免费毛片 | 日本黄色大片儿 | 91精品国产91p65 | 在线观看免费国产小视频 | 涩涩资源网 | 在线观看黄色免费视频 | 天天干天天天天 | 人人插人人插 | 日本韩国精品一区二区在线观看 | 中文乱码视频在线观看 | 在线观看成人福利 | 手机av网站| 尤物九九久久国产精品的分类 | 国产精品 视频 | 在线观看黄色国产 | 欧美日韩国产精品爽爽 | 国产97视频| 园产精品久久久久久久7电影 | 五月激情综合婷婷 | 欧美成人基地 | 亚洲国产97在线精品一区 | 国产网站av | 日韩欧美99 | 日本中文字幕在线电影 | 国产日韩精品一区二区三区 | 亚洲人在线视频 | 亚洲综合小说 | 中文字幕在线国产精品 | 日韩视频一区二区在线观看 | 91九色国产在线 | 99视频播放| 日本护士三级少妇三级999 | 激情丁香在线 | 中文字幕视频免费观看 | 天天搞天天 | 九9热这里真品2 | 91成人免费在线 | 日韩在线网| 在线免费中文字幕 | 亚洲午夜精品福利 | 欧洲精品在线视频 | 99热在线观看 | 日韩在线视频免费观看 | 91资源在线 | 亚洲永久精品视频 | 99久久er热在这里只有精品15 | 日韩精品91偷拍在线观看 | 国产亚洲精品久久久久久久久久久久 | 91视频 - x99av | 麻豆免费视频 | 免费福利在线 | 91综合视频在线观看 | 一区二区三区视频网站 | 99这里只有精品视频 | 国产一在线精品一区在线观看 | 日韩在线精品视频 | 欧美激情综合色综合啪啪五月 | 日韩经典一区二区三区 | 97品白浆高清久久久久久 | 久久福利精品 | 91精品国产综合久久婷婷香蕉 | 四虎伊人 | 久久婷婷久久 | 国产黄色片免费在线观看 | 亚洲成人精品影院 | 在线观看国产日韩 | 91精品国产电影 | 91视频中文字幕 | 日韩欧美精品免费 | 久久久免费精品国产一区二区 | 久久视频在线观看免费 | 九九热在线视频 | 久久草草热国产精品直播 | 色狠狠操 | 久久久久久久久久免费 | 黄色电影在线免费观看 | 国产在线不卡视频 | 麻豆视频免费版 | 91桃花视频| 涩五月婷婷 | 97电影手机版 | 欧美在线不卡一区 | 国产三级香港三韩国三级 | 日韩中文免费视频 | 国产一区欧美日韩 | 国产精品 国内视频 | 99久久精品国产系列 | 天天干天天看 | 国产特级毛片aaaaaa毛片 | 精品视频区 | 国产成人精品一区二区在线观看 | 中文字幕黄色网 | 欧美日韩色婷婷 | 欧美日韩免费一区二区 | 国产一级一片免费播放放a 一区二区三区国产欧美 | 欧美在线视频精品 | 午夜精品视频一区 | avhd高清在线谜片 | 精品国产一区二区三区av性色 | 日韩二区精品 | 亚洲午夜精品一区二区三区电影院 | 日日射av| www视频在线播放 | 综合网色| 狠狠操导航 | 日韩欧美有码在线 | 中文字幕在线精品 | 久久免费a| 亚洲精品大片www | 亚洲精品中文在线 | 国产区精品在线 | 亚欧洲精品视频在线观看 | 国产另类av| 精品麻豆 | 91成人精品一区在线播放69 | 顶级欧美色妇4khd | 香蕉在线播放 | 国产成人高清av | 色天天天 | 欧美日韩国产一区二区三区 | 日韩伦理片一区二区三区 | 奇米影视8888在线观看大全免费 | 久久久久这里只有精品 | 欧美日韩高清在线 | 国产成人精品不卡 | 99久热在线精品视频成人一区 | 亚洲国产精品人久久电影 | 亚洲视频1区2区 | 日日插日日干 | 亚洲天堂网视频 | 国产色中涩 | 国产色小视频 | 亚洲视频免费在线观看 | aa级黄色大片 | 久久综合狠狠综合久久激情 | 在线 影视 一区 | 在线免费观看欧美日韩 | 日韩在线视频一区 | 欧洲性视频 | 精品视频999 | 免费国产一区二区 | 综合久久婷婷 | 97偷拍在线视频 | 日本久久视频 | 精品视频成人 | 涩涩网站在线播放 | 久草在线视频首页 | 久草在线在线精品观看 | 久久久久 免费视频 | 欧美精品国产综合久久 | 午夜精品剧场 | 国产96av | 欧美日韩国产一区二 | 五月婷婷综合久久 | 国产成人精品一区二区在线 | 成人午夜影院在线观看 | 欧美激情综合五月色丁香 | 8090yy亚洲精品久久 | 国产精品18久久久久vr手机版特色 | 国产一区高清在线 | 亚洲综合视频在线播放 | 天天干天天射天天插 | 五月天激情在线 | 国产亚洲精品综合一区91 | 婷婷亚洲综合五月天小说 | 成片视频在线观看 | 日韩欧美精品在线观看视频 | 成人av免费在线看 | 中文字幕一区二区三区视频 | 亚洲精品久久久久999中文字幕 | 日韩电影在线一区 | 国产日产精品久久久久快鸭 | 免费在线观看亚洲视频 | 亚洲最新在线 | 五月婷婷,六月丁香 | 日韩欧美在线观看一区二区三区 | 麻豆首页 | 在线视频一区二区 | 99精品欧美一区二区三区 | 中文国产字幕 | 激情综合五月婷婷 | 久久久久久久国产精品影院 | 97精品国产91久久久久久 | 一区二区三区播放 | 97干com| 在线亚洲成人 | 久久超级碰 | 在线免费观看视频 | 国产亚洲久久 | 国产一区免费在线 | 国产 欧美 日韩 | 日韩黄色大片在线观看 | 国产精品永久免费观看 | 亚洲色图27p | 日韩三级免费观看 | 美女视频黄在线观看 | 激情欧美一区二区三区免费看 | 免费观看91视频 | 精品色综合 | 美女搞黄国产视频网站 | 久久久综合电影 | 国产成人三级三级三级97 | 国产精品系列在线观看 | 天天玩天天干 | 婷婷播播网 | 久久综合网色—综合色88 | 在线免费观看亚洲视频 | www日韩欧美 | 欧美俄罗斯性视频 | 玖玖玖精品 | 麻豆久久精品 | 久久久91精品国产一区二区三区 | 成人免费视频网站 | 亚洲免费在线观看视频 | 国产中文字幕网 | 在线国产片 | 天天综合91 | 日韩在线播放欧美字幕 | 久久99久久99精品免视看婷婷 | 日韩欧美国产精品 | 91福利国产在线观看 | 成人三级av | 成人av观看 | 人人爽人人爽人人爽 | 欧美精品久久久久久久免费 | 99国产精品久久久久久久久久 | 中文字幕高清免费日韩视频在线 | 国内精品久久久久影院日本资源 | 99久久精品免费看国产麻豆 | 久久精品国产免费 | 色中色资源站 | 日韩欧美精品在线观看 | 国产手机精品视频 | 在线视频在线观看 | 午夜的福利 | 天天色天天爱天天射综合 | 色网站免费在线看 | 国产精品乱码一区二三区 | 国产美女被啪进深处喷白浆视频 | 亚洲国产色一区 | 亚洲精品男人的天堂 | 亚洲国产成人久久综合 | 99视频久| 国内精品毛片 |