生活随笔
收集整理的這篇文章主要介紹了
Android 4.0 Launcher源码分析系列(二)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
原文:http://mobile.51cto.com/hot-314700.htm
上一節我們研究了Launcher的整體結構,這一節我們看看整個Laucher的入口點,同時Laucher在加載了它的布局文件Laucher.xml時都干了些什么。
我們在源代碼中可以找到LauncherApplication, 它繼承了Application類,當整個Launcher啟動時,它就是整個程序的入口。我們先來看它們在AndroidManifest.xml中是怎么配置的。
<application?????android:name="com.android.launcher2.LauncherApplication"?????android:label="@string/application_name"?????android:icon="@drawable/ic_launcher_home"?????android:hardwareAccelerated="@bool/config_hardwareAccelerated"?????android:largeHeap="@bool/config_largeHeap">????? 首先通過android:name指定了整個Launcher的Application也就是入口是在 com.android.launcher2.LauncherApplication這個路徑下,android:lable指定了桌面的名字是叫 Launcher,如果要改名字就改values文件夾的string.xml中的相應屬性就可以了。android:icon指定了Laucher的圖標,這個圖標可以在應用程序管理器中看見,如下圖所示,是個可愛機器人住在一個小房子里面,如果需要更改Laucher的圖片,重新設置這個屬性就可以了。
android:hardwareAccelerated="@bool/config_hardwareAccelerated" 指定了整個應用程序是啟用硬件加速的,這樣整個應用程序的運行速度會更快。
android:largeHeap="@bool/config_largeHeap" 指定了應用程序使用了大的堆內存,能在一定程度上避免,對內存out of memory錯誤的出現。我們可以在values文件夾的config.xml中看到對是否啟用硬件加速和大內存的配置。如下所示:
<bool?name="config_hardwareAccelerated">true</bool>?<bool?name="config_largeHeap">false</bool>? 在Application中onCreate()方法通過:sIsScreenLarge = screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE || screenSize == Configuration.SCREENLAYOUT_SIZE_XLARGE; 和sScreenDensity = getResources().getDisplayMetrics().density;來判斷是否是大屏幕,同時得到它的屏幕密度。同時通過mIconCache = new IconCache(this); 來設置了應用程序的圖標的cache,然后申明了LauncherModel,mModel = new LauncherModel(this, mIconCache); LauncherModel主要用于加載桌面的圖標、插件和文件夾,同時LaucherModel是一個廣播接收器,在程序包發生改變、區域、或者配置文件發生改變時,都會發送廣播給LaucherModel,LaucherModel會根據不同的廣播來做相應加載操作,此部分會在后面做詳細介紹。
在LauncherApplication完成初始化工作之后,我們就來到了Launcher.java的onCreate()方法,同樣是啟動桌面時的一系列初始化工作。
首先需要注意的是在加載launcher布局文件時的一個TraceView的調試方法,它能夠對在他們之間的方法進行圖形化的性能分析,并且能夠具體到method 代碼如下:
if?(PROFILE_STARTUP)?{???????android.os.Debug.startMethodTracing(???????????????Environment.getDataDirectory()?+?"/data/com.android.launcher/launcher");???}???if?(PROFILE_STARTUP)?{???????android.os.Debug.stopMethodTracing();???}?? 我指定的生成性能分析的路徑是:/data/data/com.android.launcher/launcher,啟動launcher后我們會發現在指定的目錄下生成了launcher.trace文件,如下圖所示:
把launcher.trace文件通過DDMS pull到電腦上,在SDK的tools目錄里,執行traceview工具來打開launcher.trace .如下圖所示:
點擊查看大圖
可以看到setContentView使用了448.623ms,占整個跟蹤代碼時間的62%,所以說在加載布局文件時,肯定經過了一系列的加載運算,我們接著分析。
當加載launcher布局文件的過程時,最為關鍵的時對整個workspace的加載,workspace是一個自定義組件,它的繼承關系如下所示,可以看到Workspace實際上也是一個ViewGroup,可以加入其他控件。
當ViewGroup組件進行加載的時候首先會讀取本控件對應的XML文件,然后Framework層會執行它的onMeasure()方法,根據它所包含的子控件大小來計算出整個控件要在屏幕上占的大小。Workspace重寫了ViewGroup的onMeasure方法(在PagedView中),在workspace中是對5個子CellLayout進行測量,的方法如下, 具體含義請看注釋:
@Override?????protected?void?onMeasure(int?widthMeasureSpec,?int?heightMeasureSpec)?{?????????if?(!mIsDataReady)?{?????????????super.onMeasure(widthMeasureSpec,?heightMeasureSpec);?????????????return;?????????}?????????//得到寬度的模式(在配置文件中對應的是match_parent?或者?wrap_content)和其大小?????????final?int?widthMode?=?MeasureSpec.getMode(widthMeasureSpec);?????????final?int?widthSize?=?MeasureSpec.getSize(widthMeasureSpec);?????????//寬度必須是match_parent,否則會拋出異常。?????????if?(widthMode?!=?MeasureSpec.EXACTLY)?{?????????????throw?new?IllegalStateException("Workspace?can?only?be?used?in?EXACTLY?mode.");?????????}??????????/*?Allow?the?height?to?be?set?as?WRAP_CONTENT.?This?allows?the?particular?case??????????*?of?the?All?apps?view?on?XLarge?displays?to?not?take?up?more?space?then?it?needs.?Width??????????*?is?still?not?allowed?to?be?set?as?WRAP_CONTENT?since?many?parts?of?the?code?expect??????????*?each?page?to?have?the?same?width.??????????*/?????????//高度允許是wrap_content,因為在大屏幕的情況下,會占了多余的位置?????????final?int?heightMode?=?MeasureSpec.getMode(heightMeasureSpec);?????????int?heightSize?=?MeasureSpec.getSize(heightMeasureSpec);?????????int?maxChildHeight?=?0;?????????//得到在豎值方向上和水平方向上的Padding??????????final?int?verticalPadding?=?mPaddingTop?+?mPaddingBottom;?????????final?int?horizontalPadding?=?mPaddingLeft?+?mPaddingRight;???????????//?The?children?are?given?the?same?width?and?height?as?the?workspace?????????//?unless?they?were?set?to?WRAP_CONTENT?????????if?(DEBUG)?Log.d(TAG,?"PagedView.onMeasure():?"?+?widthSize?+?",?"?+?heightSize??+?"?mPaddingTop="+mPaddingTop?+?"?mPaddingBottom="+mPaddingBottom);?????????final?int?childCount?=?getChildCount();?????????//對workspace的子View進行遍歷,從而對它的幾個子view進行測量。?????????for?(int?i?=?0;?i?<?childCount;?i++)?{?????????????//?disallowing?padding?in?paged?view?(just?pass?0)?????????????final?View?child?=?getPageAt(i);?????????????final?LayoutParams?lp?=?(LayoutParams)?child.getLayoutParams();??????????????int?childWidthMode;?????????????if?(lp.width?==?LayoutParams.WRAP_CONTENT)?{?????????????????childWidthMode?=?MeasureSpec.AT_MOST;?????????????}?else?{?????????????????childWidthMode?=?MeasureSpec.EXACTLY;?????????????}??????????????int?childHeightMode;?????????????if?(lp.height?==?LayoutParams.WRAP_CONTENT)?{?????????????????childHeightMode?=?MeasureSpec.AT_MOST;?????????????}?else?{?????????????????childHeightMode?=?MeasureSpec.EXACTLY;?????????????}??????????????final?int?childWidthMeasureSpec?=?????????????????MeasureSpec.makeMeasureSpec(widthSize?-?horizontalPadding,?childWidthMode);?????????????final?int?childHeightMeasureSpec?=?????????????????MeasureSpec.makeMeasureSpec(heightSize?-?verticalPadding,?childHeightMode);?????????????//對子View的大小進行設置,傳入width和height參數?????????????child.measure(childWidthMeasureSpec,?childHeightMeasureSpec);?????????????maxChildHeight?=?Math.max(maxChildHeight,?child.getMeasuredHeight());?????????????if?(DEBUG)?Log.d(TAG,?"\tmeasure-child"?+?i?+?":?"?+?child.getMeasuredWidth()?+?",?"?????????????????????+?child.getMeasuredHeight());?????????}??????????if?(heightMode?==?MeasureSpec.AT_MOST)?{?????????????heightSize?=?maxChildHeight?+?verticalPadding;?????????}?????????//存儲測量后的寬度和高度?????????setMeasuredDimension(widthSize,?heightSize);??????????//?We?can't?call?getChildOffset/getRelativeChildOffset?until?we?set?the?measured?dimensions.?????????//?We?also?wait?until?we?set?the?measured?dimensions?before?flushing?the?cache?as?well,?to?????????//?ensure?that?the?cache?is?filled?with?good?values.?????????invalidateCachedOffsets();?????????updateScrollingIndicatorPosition();??????????if?(childCount?>?0)?{?????????????mMaxScrollX?=?getChildOffset(childCount?-?1)?-?getRelativeChildOffset(childCount?-?1);?????????}?else?{?????????????mMaxScrollX?=?0;?????????}?????}?
測量完畢之后就可以對子控件進行布局了,這時候Framework層會調用PagedView中重寫的onLayout方法。
@Override????protected?void?onLayout(boolean?changed,?int?left,?int?top,?int?right,?int?bottom)?{????????if?(!mIsDataReady)?{????????????return;????????}?????????if?(DEBUG)?Log.d(TAG,?"PagedView.onLayout()");????????//豎值方向的Padding????????final?int?verticalPadding?=?mPaddingTop?+?mPaddingBottom;????????final?int?childCount?=?getChildCount();????????int?childLeft?=?0;????????if?(childCount?>?0)?{????????????if?(DEBUG)?Log.d(TAG,?"getRelativeChildOffset():?"?+?getMeasuredWidth()?+?",?"????????????????????+?getChildWidth(0));????????????childLeft?=?getRelativeChildOffset(0);????????????//偏移量為0????????????if?(DEBUG)?Log.d(TAG,?"childLeft:"+childLeft);???????????????//?Calculate?the?variable?page?spacing?if?necessary????????????//?如果mPageSpacing小于0的話,就重新計算mPageSpacing,并且給它賦值。????????????if?(mPageSpacing?<?0)?{????????????????setPageSpacing(((right?-?left)?-?getChildAt(0).getMeasuredWidth())?/?2);????????????}????????}?????????for?(int?i?=?0;?i?<?childCount;?i++)?{????????????final?View?child?=?getPageAt(i);????????????if?(child.getVisibility()?!=?View.GONE)?{????????????????final?int?childWidth?=?getScaledMeasuredWidth(child);????????????????final?int?childchildHeight?=?child.getMeasuredHeight();????????????????int?childTop?=?mPaddingTop;????????????????if?(mCenterPagesVertically)?{????????????????????childTop?+=?((getMeasuredHeight()?-?verticalPadding)?-?childHeight)?/?2;????????????????}????????????????????????????????if?(DEBUG)?Log.d(TAG,?"\tlayout-child"?+?i?+?":?"?+?childLeft?+?",?"?+?childTop);????????????????//把5個CellLayout布局到相應的位置,layout的4個參數分別是?左、上、右、下。????????????????child.layout(childLeft,?childTop,????????????????????????childLeft?+?child.getMeasuredWidth(),?childTop?+?childHeight);????????????????childLeft?+=?childWidth?+?mPageSpacing;????????????}????????}????????//第一次布局完畢之后,就根據當前頁偏移量(當前頁距離Workspace最左邊的距離)滾動到默認的頁面去,第一次布局時????????//默認的當前頁是3,則它的便宜量就是兩個CellLayout的寬度。????????if?(mFirstLayout?&&?mCurrentPage?>=?0?&&?mCurrentPage?<?getChildCount())?{????????????setHorizontalScrollBarEnabled(false);????????????int?newX?=?getChildOffset(mCurrentPage)?-?getRelativeChildOffset(mCurrentPage);????????????//滾動到指定的位置????????????scrollTo(newX,?0);????????????mScroller.setFinalX(newX);????????????if?(DEBUG)?Log.d(TAG,?"newX?is?"+newX);????????????setHorizontalScrollBarEnabled(true);????????????mFirstLayout?=?false;????????}?????????if?(mFirstLayout?&&?mCurrentPage?>=?0?&&?mCurrentPage?<?getChildCount())?{????????????mFirstLayout?=?false;????????}????}?
轉載于:https://www.cnblogs.com/deve/archive/2012/06/28/2568977.html
總結
以上是生活随笔為你收集整理的Android 4.0 Launcher源码分析系列(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。