android之setContentView,addContentView(),Window,WindowManager,Dialog源码剖析。

本文深入剖析了Android中的setContentView、addContentView方法以及Window、WindowManager和Dialog的源码。讲解了setContentView如何创建DecorView和关联到PhoneWindow,addContentView如何在已有的DecorView中添加视图。同时,分析了WindowManager的职责和实现,以及Dialog的本质是通过WindowManager添加的视图。内容涉及Activity、Window、WindowManager、Dialog的交互及源码细节。

setContenView:

任何一个Activity在onCreat()方法里要执行一次setContentView,而setContentView作用笔者总结为两大类

ONE:第一次setContentView时创建一个Decorview并关联到当前的PhoneWindows(Windows的具体实现类)

TWO:从DecorView中找到合适的FrameLayout,并将我们传入的自定义布局的id实例化放入其中,如果非第一次调用则清空这个FrameLayout中第一次载入的子View就是第一次我们传入的自定义布局,然后重新载入。

setContentView则是调用PhoneWindows的setContentView, 如下:

 public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

mContentParent就是TWO中我们提到的FrameLayout(方便说明见后续)。这里调用了installDecor(); 而mContentView!=null则调用mContentParent.removeAllViews();清空了mContenParent中所有组件。说明非第一次调用则清空。

if (mContentParent == null) {
            installDecor();
        }
private void installDecor() {
  if (mDecor == null) {
  mDecor = generateDecor(-1);    //截取关键
 } 
  else{
  mDecor.setWindow(this);
 }
if (mContentParent == null) {
    mContentParent = generateLayout(mDecor);
}
}

这里主要执行了两个方法generateDecor(),generateLayout(mDecor);
前者实例化了DecorView,后者实例化了mContentParent 先看下generateDecor()

  protected DecorView generateDecor(int featureId) {
return new DecorView(context, featureId, this, getAttributes());
      //截取关键
}

ok这里说明了确实第一次setContentView时确实创建了DecorView,否则就关联到当前Windwos(),顺带看一下DecorView构造函数:

DecorView(Context context, int featureId, PhoneWindow window,
            WindowManager.LayoutParams params) {
setWindow(window);     //截取部分
}

所以generateDecor(-1)后DecorView存在实例并且关联到Windows上了,然后接着看 generateLayout(mDecor);

protected ViewGroup generateLayout(DecorView decor) {
 layoutResource = R.layout.screen_simple;    
 View in = mLayoutInflater.inflate(layoutResource, null);  
 decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));   
 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
 return contentParent;    
								 //截取部分
}

这里给DecorView添加了一个子合适的子VIew然后通过
findViewById()得到一个VIewGroup然后返回

 public <T extends View> T findViewById(@IdRes int id) {
        return getDecorView().findViewById(id);
    }

所以findViewById()就在DecorView中查找ID为ID_ANDROID_CONTENT的ViewGroup,而这个ViewGroup肯定就在上述DecorView添加的子View中。
看一下ID_ANDROID_CONTENT的值

public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

看一下 R.layout.screen_simple;的XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   android:layout_width="match_parent"  
   android:layout_height="match_parent"  
   android:fitsSystemWindows="true"  
   android:orientation="vertical">  
  <ViewStub android:id="@+id/action_mode_bar_stub"  
          android:inflatedId="@+id/action_mode_bar"  
          android:layout="@layout/action_mode_bar"  
          android:layout_width="match_parent"  
          android:layout_height="wrap_content"  
          android:theme="?attr/actionBarTheme" />  
  <FrameLayout  
     android:id="@android:id/content"  
     android:layout_width="match_parent"  
     android:layout_height="match_parent"  
     android:foregroundInsidePadding="false"  
     android:foregroundGravity="fill_horizontal|top"  
     android:foreground="?android:attr/windowContentOverlay" />  
</LinearLayout>  

这里确实看见了一个id为content的FrameLayout ,这个就是mContentParent。ok到这里就剩下了将我们传入的ID实例化放入其中了。看下setContentView中剩下的代码

public void setContentView(int layoutResID) {
 if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
}

这里就将我们传入布局的ID实例化并放入mContentParent中。

addContentView

 public void addContentView(View view, ViewGroup.LayoutParams params) {
        getWindow().addContentView(view, params);
        initWindowDecorActionBar();
    }

依然调用的是PhoneWIndows中的addContentView();

 public void addContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();
        }
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            // TODO Augment the scenes/transitions API to support this.
            Log.v(TAG, "addContentView does not support content transitions");
        }
        mContentParent.addView(view, params);
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

可以看到addContentVIew同样可以执行 installDecor(),也就说没有执行setContentView并不影响addContentView正常使用;与setContentView不同的是若多次调用setContentView,则下次会清空上一次的mContentParent,而addContentVIew中只是调用了mContentParent.addVIew() 也就说可以多次添加。

Window.addContentView

阅览源码后Window.addContentView()最后调用的还是PhoneWindow.addContentView(),所以不再累赘了。

Window,WindowManager##

谈这个之前笔者先分享一下个人对Window,WindowManager的理解和看法,

Window:

  • Window是个抽象类,具体的实现类为PhoneWindow,它的的作用就是创建一个DecorView以及保留相关参数

  • Window只能关联一个View。

  • Window与Window之间效果模型是层级关系,即高层会默认抢占焦点,但可通过窗口参数来控制

WindowManager

  • WindowManager由Window来实例化每个Window内部都含有一个WindowManager的引用,

  • 由于所有WIndow关联的View都存放在一个静态类中,而WindowManager实现类都含有这个静态类,所以不同的WindowManager可管理相同的View即Window

Activity在创建的过程会默认实现一个的Window类

public class Activity{
 private Window mWindow;
 final void attach(......){
 mWindow = new PhoneWindow(this, window, activityConfigCallback);
 mWindow.setWindowManager(....)
  }
}

可以看到在attach()方法里面Window就已经创建完成了,而WindowManager是一个接口,具体实现类为WindowManagerImpl,从上述可看到在Activty中也同样存在一个WindowManager的引用,那这个类主要负责什么呢? 看继承关系

public interface WindowManager extends ViewManager{}

继承了ViewManager所以就可以对View增加或移除,当然由于DecorView的绑定所以移除会报错,所以这样针对的就只能是我们调用了WindowManager.addView()添加的View。那么WindowManager的具体实现类在那实例化呢? 注意上述调用了mWindow.setWindowManager(…) 看源码:

 public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;     
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

appToken,appName暂时不提,因为IBinder机制笔者暂时还未研究所以不太清楚后续有机会再写,当然这里我们姑且将它们认为一个标记Window的东西,因为某些窗口只需要在当前Activity内显示,那么为了标记这些Window,则会将token添加到窗口参数
。继续看上述源码,通过传入的wm(其他Window的WindowManagerImpl)有重新创建的一个自己的WindowManagerImpl,

  public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

最后WIndow内部就有了这个WindowManagerImpl的引用,这样实例化就ok了,所以先有了Window实例对象才会有WindowManager实例对象。所以一个Window可以对应多个WindowManager,而这些WindowManager内部都有这个Window的引用,但是这个Window内只能有一个WindowManager。
那么Activity内部的WindowManager是不是就是本身Window内的WindowManager

  mWindowManager = mWindow.getWindowManager();

Activity内的WindowManager引用直接从Window内取WindowManager,ok就是这样。
然后我们研究一下如何获取Window或者WindowManager;

获取Window:在Activity内部调用getWindow()

public Window getWindow() {
        return mWindow;
    }

直接返回Activity内的Window对象。
获取WindowManager:在Activity内部调用getWindowManager()或者调用context.getSystemService((WINDOW_SERVICE)

 public WindowManager getWindowManager() {
        return mWindowManager;
    }

直接返回Activity内的WindowManager引用,

 public Object getSystemService(@ServiceName @NonNull String name) {
   if (WINDOW_SERVICE.equals(name)) {
          return mWindowManager;
        } 
 }

最后还是返回了Activity内的WindowManager,当然这里要注意不同的context最后返回的WindowManager是不同的,这里的不同有两种:

ONE:由Window产生的WindowManager对象不同
TWO:Activity对应的WindowManager里的token!=null,而其他的token==null,继而导致WIndowManager不同

token的是否为null会对添加的窗口产生影响,看源码:

 @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

这里是WindowManager.addView()在添加窗口之前applyDefaultToken(params)会对窗口参数微调,其实就是将自身token赋予params。

上述能反应出什么问题?这里我做个小例子:

 public int onStartCommand(Intent intent, int flags, int startId) {
        WindowManager.LayoutParams la=new WindowManager.LayoutParams();
        la.flags=WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        la.format= PixelFormat.RGBA_8888;
        la.type=WindowManager.LayoutParams.TYPE_APPLICATION;
        la.width=200;
        la.height=200;
        la.x=0;
        la.y=0;
        la.gravity= Gravity.LEFT|Gravity.TOP;
        Button button=new Button(this);
        WindowManager windowManager1= (WindowManager)getSystemService(WINDOW_SERVICE);
        windowManager1.addView(button,la);
	return null
	}

这个是我在Service里写的一个给Window里添加button的实例注意 这里的la.type=WindowManager.LayoutParams.TYPE_APPLICATION;
指的就是在当前的Activity对应的Window里显示如果这个Activity不在栈顶那么则不显示
结果运行报错:这里写图片描述
就是提示了token为null,由于是Service本身并没有WindowManager实例,所以应该来自其他的Context实现类但它们并非Activity所以token==null继而报错。
如果上述代码放在一个Activity里则运行正常。所以token应该就是标识了Activity。
但是WindowManager.LayoutParams.TYPE_APPLICATION;换为其他某些则显示正常,例如WindowManager.LayoutParams.TYPE_PHONE;这个就不用在意是否脱离了Activity只要Application运行则就在屏幕上显示。

WindowManager.addView()

个人理解为WindowManager.addView()添加的才算实质意义添加了,因为包括上述的添加View或者通过VIewGroup添加的都只是依托了Parent,而这些Parent最顶部的就是Decorview,这些VIew直接或间接与DecorVIew相连,形成了树状结构因此可以在Window显示出来,并且执行相关操作。所以实质添加的只是一个DecorView。
而我们若直接调用WindowManager.addVIew()就不同了。看源码:
WindowManager只是接口具体实现类为WindowWindowManagerImpl

 public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

WindowManagerImpl通过调用mGlobal来操作VIew即操作窗口,自身只是一个代理。mGlobal则是一个已经实例化的WindowManagerGlobal对象。注意:WindowManagerGlobal对象整个app只有一个 看源码:


private static WindowManagerGlobal sDefaultWindowManager;

public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            return sDefaultWindowManager;
        }
    }

是吧,看看它的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>(); 


public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            
 ViewRootImpl root;
               
 View panelParentView = null;
		  
 root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);   
          try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
     }
}

笔者的理解mGlobal的才与WindowManagerService打交道,继而生成,销毁一个窗口,上述代码中 mViews,mRoots, mParams 三个集合对象,mRoots笔者猜测记录窗口的层级关系并且与WMS交流。mVIews则保存了所有窗口的View,mParams则保存了所有窗口的参数。最后调用 root.setView(view, wparams, panelParentView);将窗口显示了出来。

Dialog

dialog其实就是通过WindowManager添加的一个View而已,

public class Dialog .......{

    private final WindowManager mWindowManager;
    final Context mContext;
    final Window mWindow;
    View mDecor;

Dialog(Context context ,........){   
mWindowManager = (WindowManager)      context.getSystemService(Context.WINDOW_SERVICE);
	  final Window w = new PhoneWindow(mContext);
        mWindow = w;
		
   } 
}

可以看到在DIalog内部同样存放一个Window,WindowManager,以及一个DecorView。 在初始化过程中mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 则拿到了Activity的WindowManager,所以token!=null,然后new了一个PhoneWindow 正如我们上述提到的Window的目的只是产生一个Decor以及保存窗口参数而已。然后看一下show()方法:

 public void show() {
	 mDecor = mWindow.getDecorView();
	 WindowManager.LayoutParams l = mWindow.getAttributes();
		 .....
	 mWindowManager.addView(mDecor, l);
}

其实过程很简单,从Window中拿到DecorView,Params,然后调整Params,最后添加窗口就ok了。 dismiss()也很简单:

mWindowManager.removeViewImmediate(mDecor);

其实就是调用了removeViewImmediate(),而在WindowManager里:

@Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }

又交给mGloba了然后与WMS交流表示移除窗口。至于WMS的内部运行,还得继续研究啊。。。。

理解如有不正确地方,还希望少侠能提醒一下。 谢谢!!

我的abaqus出现报错 Integration and section point output variables will not be output for deformable elements that are declared as rigid using the *rigid body option Output request peeqvavg is not available for element type c3d8r Output request pevavg is not available for element type c3d8r Output request svavg is not available for element type c3d8r Integration and section point output variables will not be output for deformable elements that are declared as rigid using the *rigid body option Output request peeqvavg is not available for element type c3d8r Output request pevavg is not available for element type c3d8r Output request svavg is not available for element type c3d8r There are 8072 warning messages in the data (.dat) file. Please check the data file for possible errors in the input file. ASSEMBLY_S_SURF-1 is a rigid surface, so it will be made the main surface in the contact pair with surface ASSEMBLY_M_SURF-1. The WEIGHT value reported in the .dat file for this contact pair is not correct. ASSEMBLY_S_SURF-1 is a rigid surface, so it will be made the main surface in the contact pair with surface ASSEMBLY_M_SURF-1. The WEIGHT value reported in the .dat file for this contact pair is not correct. ASSEMBLY_S_SURF-1 is a rigid surface, so it will be made the main surface in the contact pair with surface ASSEMBLY_M_SURF-1. The WEIGHT value reported in the .dat file for this contact pair is not correct. Boundary conditions are defined at the nodes contained in node set WarnNodeBcIntersectKinCon. In addition the nodes are also part of a surface involved in kinematic contact. The kinematic contact constraint will be overridden by the boundary conditions in case of a conflict. Penalty contact may be used instead.
08-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值