Android疑难点解答
来源:互联网 发布:金蝶软件数据备份 编辑:程序博客网 时间:2024/05/18 04:50
一.概述
这篇文章主要解答一些在平时学习工作过程中遇到的一些比较值得研究的问题。
二.问题
1.DecorView是何时被创建的
是在PhoneWindow的generateDecor中被创建的
protected DecorView generateDecor() { return new DecorView(getContext(), -1); }
2.ViewRootImpl是何时被创建的
是在WindowManagerGlobal的addView方法中创建的
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { //省去大量代码 ViewRootImpl root; View panelParentView = null;//省去部分代码 root = new ViewRootImpl(view.getContext(), display); ...... }
3.Activity加载布局的流程
在Activity中调用setContentView加载布局,setContentView最终调用PhoneWindow的setContentView方法,在此方法中将根布局xml文件解析成View添加到DecorView中,然后从根布局文件中找到id为contente的父容器,将我们在setContentView中指定的布局文件通过inflate的方式添加进去
//将我们的布局文件添加到mContentParent中,这个mContentParent是根布局中id为content的一个FrameLayout mLayoutInflater.inflate(layoutResID, mContentParent);
4.Window对象何时被创建
在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) { attachBaseContext(context); mFragments.attachActivity(this, mContainer, null); //创建window对象 mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(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); } mUiThread = Thread.currentThread(); ..... }
5.Application对象何时被创建
在Instrumentation的newApplication方法中
public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return newApplication(cl.loadClass(className), context); }
6.View绘制的起点在何处
当启动Activity的时候,其中有一步是执行ActivityThread的handleResumeActivity.
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {....... ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; wm.addView(decor, l); } .......
上面首先调用a的getWindowManager方法,其中a是Activity对象,getWindowManager获得是WindowManager对象,此处时期返回的是它的实现类WindowManagerImpl,.
然后调用了WindowManagerImpl的addView方法
@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mDisplay, mParentWindow); }
可以看出,WMI只是一个代理类,真正的实现实在WindowManagerGlobal中
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ...... ViewRootImpl root; View panelParentView = null; ..... //创建ViewRootImpl对象 root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); } } // do this last because it fires off messages to start doing things try { //这句话很重要,将DecorView添加到ViewRootImpl中 root.setView(view, wparams, panelParentView);
然后我们看砍setView方法,
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {..... requestLayout(); ......
这里调用了requestLayout方法,这就是View开始绘制的起点
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }
mTraversalRunnable是一个Runnable对象,此时会执行它的run方法
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }
ViewRootImpl的performTraversals方法
private void performTraversals() { //省去海量代码 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//省去部分代码 performLayout(lp, desiredWindowWidth, desiredWindowHeight); //省去大量代码 performDraw(); ..... }
7.DecorView何时被添加到Window中
在Activity的makeVisible方法中
void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }
8.影响图片占用内存大小的因素有哪些?
(1).图片尺寸
(2)色彩细节
在Bitmap.Config中有如下定义
//8位ALPHA图public static final Bitmap.Config ALPHA_8//16位ARGB图,占2KBpublic static final Bitmap.Config ARGB_4444//32位ARGB图,占4KB大小public static final Bitmap.Config ARGB_8888//16位RGB图public static final Bitmap.Config RGB_565
9.measure方法的两个参数代表什么意思?
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
widthMeasureSpec封装了视图的宽度大小和规格
heightMeasureSpec封装了视图的高度大小和规格
这两个参数是父视图经过计算传递下来的
我们可以通过下面的方式取出视图的大小和规格
int mode = MeasureSpec.getMode(widthMeasureSpec); int size = MeasureSpec.getSize(widthMeasureSpec);
10.父视图的widthMeasureSpec和heightMeasureSpec从哪里来?
View的绘制是从ViewRootImpl的performTraversals开始的,下面的代码定义了widthMeasureSpec和heightMeasureSpec
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
我们看看getRootMeasureSpec这个方法
/** * Figures out the measure spec for the root view in a window based on it's * layout params. * * @param windowSize * The available width or height of the window * * @param rootDimension * The layout params for one dimension (width or height) of the * window. * * @return The measure spec to use to measure the root view. */ private static int getRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: // Window can resize. Set max size for root view. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: // Window wants to be an exact size. Force root view to be that size. measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec; }
这个方法接收两个参数,第一个是Window的尺寸,第二个是代表WIndow尺寸的布局参数。那么看门看看这两个参数的定义
if (mWidth != frame.width() || mHeight != frame.height()) { mWidth = frame.width(); mHeight = frame.height(); }
WindowManager.LayoutParams lp = mWindowAttributes; final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
从源码我们可以看出,根布局的widthMeasureSpec中的宽度为Window的宽度,heightMeasureSpec中的高度为Window的高度,规格都是MeasureSpec.EXACTLY,也就是精确的。
- Android疑难点解答
- Android消息机制的疑难点说明
- 解答java相关疑难:264122622
- 编程疑难点
- 多态疑难点
- c疑难点
- Linux 下目录权限解答疑难
- JavaScript 中的疑难杂点
- java 线程之疑难点
- 疑难点积累(二)
- Java多线程基础知识疑难点
- Python知识疑难点汇集
- xmlhttprequest open()函数疑难点使用
- java 正则表达式 易忘点 疑难点
- ICE第三篇------一些疑难点
- FREERTOS读写TF卡驱动疑难点
- 说说常见的几个js疑难点
- 安卓基础知识疑难点总结
- html基础
- mongo报错:Error: couldn't connect to server 127.0.0.1:27017, connection attempt failed
- SPI,UART,I2C都有什么区别,及其各自的特点
- java.lang.UnsupportedClassVersionError错误的原因及解决方案
- MySql常用语句
- Android疑难点解答
- Ubuntu 安装 .bundle 文件
- mysql 生成日期维表
- SDRAM电路设计详解
- 顏色設置
- ORA-01123:无法启动联机备份;未启用介质恢复(错误分析)
- 开奖计算---五星直选复式
- Xcode 插件失效解决方法
- 环信即时通讯单聊集成,添加好友,实现单聊