【Android Surface】从Activity的创建到Surface的创建,源码分析1

文章目录

  • activity的创建
    • performLaunchActivity
    • handleResumeActivity
      • setContentView
      • mInstrumentation.newActivity
      • new出phonewindow
      • WindowManager的创建
  • 回到setContextView
    • findViewById
  • addView
  • ViewRoot
  • Android在哪里“画画”

我们知道Android绘制图形依靠的是surface和surface flinger,但是当我说起这句话的时候,你脑海里面能复现出一幅图画,里面展示了Android绘制图像,然后渲染到屏幕这整个执行流程吗?如果没有,一起来研究研究Android的这个机制。我想,理清楚之后,作为Android开发的我们,必定获益良多。

先看看surface系统的的整体关系:
在这里插入图片描述

不论是使用Skia绘制二维图像,还是使用OpenGL绘制三维图像,APP都会Surface进行交互。surface就行画布一样,app是一个艺术家,在surface上面作画。但是它们关系如何,我们目前不得而知,下面我们通过源码分析,一步步打通它们。

activity的创建

我们知道zygote响应请求之后会fork一个子进程,这个进程就是App进程,入口函数在ActivityThread.java的main函数,它会执行到handleLaunchActivity函数:

    private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {// If we are getting ready to gc after going to the background, well// we are back active so skip it.unscheduleGcIdler();if (localLOGV) Slog.v(TAG, "Handling launch of " + r);//返回一个activityActivity a = performLaunchActivity(r, customIntent);if (a != null) {r.createdConfig = new Configuration(mConfiguration);Bundle oldState = r.state;//重点handleResumeActivity(r.token, false, r.isForward);if (!r.activity.mFinished && r.startsNotResumed) {// The activity manager actually wants this one to start out// paused, because it needs to be visible but isn't in the// foreground.  We accomplish this by going through the// normal startup (because activities expect to go through// onResume() the first time they run, before their window// is displayed), and then pausing it.  However, in this case// we do -not- need to do the full pause cycle (of freezing// and such) because the activity manager assumes it can just// retain the current state it has.try {r.activity.mCalled = false;mInstrumentation.callActivityOnPause(r.activity);// We need to keep around the original state, in case// we need to be created again.r.state = oldState;if (!r.activity.mCalled) {throw new SuperNotCalledException("Activity " + r.intent.getComponent().toShortString() +" did not call through to super.onPause()");}} catch (SuperNotCalledException e) {throw e;} catch (Exception e) {if (!mInstrumentation.onException(r.activity, e)) {throw new RuntimeException("Unable to pause activity "+ r.intent.getComponent().toShortString()+ ": " + e.toString(), e);}}r.paused = true;}} else {// If there was an error, for any reason, tell the activity// manager to stop us.try {ActivityManagerNative.getDefault().finishActivity(r.token, Activity.RESULT_CANCELED, null);} catch (RemoteException ex) {}}}

performLaunchActivity

先看下activity的创建

private final Activity performLaunchActivity(ActivityRecord r, Intent customIntent) {// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");ActivityInfo aInfo = r.activityInfo;if (r.packageInfo == null) {r.packageInfo = getPackageInfo(aInfo.applicationInfo,Context.CONTEXT_INCLUDE_CODE);}ComponentName component = r.intent.getComponent();if (component == null) {component = r.intent.resolveActivity(mInitialApplication.getPackageManager());r.intent.setComponent(component);}if (r.activityInfo.targetActivity != null) {component = new ComponentName(r.activityInfo.packageName,r.activityInfo.targetActivity);}Activity activity = null;try {java.lang.ClassLoader cl = r.packageInfo.getClassLoader();//通过反射创建的Activityactivity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);r.intent.setExtrasClassLoader(cl);if (r.state != null) {r.state.setClassLoader(cl);}} catch (Exception e) {if (!mInstrumentation.onException(activity, e)) {throw new RuntimeException("Unable to instantiate activity " + component+ ": " + e.toString(), e);}}try {Application app = r.packageInfo.makeApplication(false, mInstrumentation);if (localLOGV) Slog.v(TAG, "Performing launch of " + r);if (localLOGV) Slog.v(TAG, r + ": app=" + app+ ", appName=" + app.getPackageName()+ ", pkg=" + r.packageInfo.getPackageName()+ ", comp=" + r.intent.getComponent().toShortString()+ ", dir=" + r.packageInfo.getAppDir());if (activity != null) {//平时我们在Activity中调用的getContext就是它。ContextImpl appContext = new ContextImpl();appContext.init(r.packageInfo, r.token, this);appContext.setOuterContext(activity);CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());Configuration config = new Configuration(mConfiguration);if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "+ r.activityInfo.name + " with config " + config);activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstance,r.lastNonConfigurationChildInstances, config);if (customIntent != null) {activity.mIntent = customIntent;}r.lastNonConfigurationInstance = null;r.lastNonConfigurationChildInstances = null;activity.mStartedActivity = false;int theme = r.activityInfo.getThemeResource();if (theme != 0) {activity.setTheme(theme);}activity.mCalled = false;//调用生命周期的onCreatemInstrumentation.callActivityOnCreate(activity, r.state);if (!activity.mCalled) {throw new SuperNotCalledException("Activity " + r.intent.getComponent().toShortString() +" did not call through to super.onCreate()");}r.activity = activity;r.stopped = true;if (!r.activity.mFinished) {activity.performStart();r.stopped = false;}if (!r.activity.mFinished) {if (r.state != null) {mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);}}if (!r.activity.mFinished) {activity.mCalled = false;mInstrumentation.callActivityOnPostCreate(activity, r.state);if (!activity.mCalled) {throw new SuperNotCalledException("Activity " + r.intent.getComponent().toShortString() +" did not call through to super.onPostCreate()");}}}r.paused = true;mActivities.put(r.token, r);} catch (SuperNotCalledException e) {throw e;} catch (Exception e) {if (!mInstrumentation.onException(activity, e)) {throw new RuntimeException("Unable to start activity " + component+ ": " + e.toString(), e);}}return activity;}

可以看到,这里new了一个activity,并执行了onCreate,开始他的生命周期生涯,让activity活起来了。

handleResumeActivity

 final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {// If we are getting ready to gc after going to the background, well// we are back active so skip it.unscheduleGcIdler();ActivityRecord r = performResumeActivity(token, clearHide);if (r != null) {final Activity a = r.activity;if (localLOGV) Slog.v(TAG, "Resume " + r + " started activity: " +a.mStartedActivity + ", hideForNow: " + r.hideForNow+ ", finished: " + a.mFinished);final int forwardBit = isForward ?WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;// If the window hasn't yet been added to the window manager,// and this guy didn't finish itself or start another activity,// then go ahead and add the window.boolean willBeVisible = !a.mStartedActivity;if (!willBeVisible) {try {willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(a.getActivityToken());} catch (RemoteException e) {}}if (r.window == null && !a.mFinished && willBeVisible) {r.window = r.activity.getWindow();//1:decor view,我们应该很熟悉View decor = r.window.getDecorView();decor.setVisibility(View.INVISIBLE);//2:wm,重要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;//3:把decorview添加wm中wm.addView(decor, l);}// If the window has already been added, but during resume// we started another activity, then don't yet make the// window visible.} else if (!willBeVisible) {if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");r.hideForNow = true;}// The window is now visible if it has been added, we are not// simply finishing, and we are not starting another activity.if (!r.activity.mFinished && willBeVisible&& r.activity.mDecor != null && !r.hideForNow) {if (r.newConfig != null) {if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "+ r.activityInfo.name + " with newConfig " + r.newConfig);performConfigurationChanged(r.activity, r.newConfig);r.newConfig = null;}if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="+ isForward);WindowManager.LayoutParams l = r.window.getAttributes();if ((l.softInputMode& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)!= forwardBit) {l.softInputMode = (l.softInputMode& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))| forwardBit;if (r.activity.mVisibleFromClient) {ViewManager wm = a.getWindowManager();View decor = r.window.getDecorView();wm.updateViewLayout(decor, l);}}r.activity.mVisibleFromServer = true;mNumVisibleActivities++;if (r.activity.mVisibleFromClient) {r.activity.makeVisible();}}r.nextIdle = mNewActivities;mNewActivities = r;if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);Looper.myQueue().addIdleHandler(new Idler());} else {// If an exception was thrown when trying to resume, then// just end this activity.try {ActivityManagerNative.getDefault().finishActivity(token, Activity.RESULT_CANCELED, null);} catch (RemoteException ex) {}}}

代码注释的1 、2、 3,我们看到了UI被丢到wm上了,但是wm是怎么和activity联系起来的?

每次写代码的时候,我们都看到setContentView这函数吧?

setContentView

 /*** Set the activity content to an explicit view.  This view is placed* directly into the activity's view hierarchy.  It can itself be a complex* view hierarhcy.* * @param view The desired content to display.*/public void setContentView(View view) {getWindow().setContentView(view);}

查下Google官方文档,有这样的描述:

Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.
The framework will instantiate an implementation of this class on behalf of the application.
引用:https://developer.android.com/reference/android/view/Window

This class represents the basic building block for user interface components. A View occupies a rectangular area on the screen and is responsible for drawing and event handling. View is the base class for widgets, which are used to create interactive UI components (buttons, text fields, etc.). The ViewGroup subclass is the base class for layouts, which are invisible containers that hold other Views (or other ViewGroups) and define their layout properties.
引用:https://developer.android.com/reference/android/view/View

可以得知,view是在window上面的。按照前面的分析,它们关系目前长这样:
在这里插入图片描述

mInstrumentation.newActivity

前面我们见过这行代码,但是没有进去看看,现在我们再进去看看里面做了什么:

     //通过反射创建的Activityactivity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
//frameworks/base/core/java/android/app/Instrumentation.java/*** Perform instantiation of an {@link Activity} object.  This method is intended for use with* unit tests, such as android.test.ActivityUnitTestCase.  The activity will be useable* locally but will be missing some of the linkages necessary for use within the sytem.* * @param clazz The Class of the desired Activity* @param context The base context for the activity to use* @param token The token for this activity to communicate with* @param application The application object (if any)* @param intent The intent that started this Activity* @param info ActivityInfo from the manifest* @param title The title, typically retrieved from the ActivityInfo record* @param parent The parent Activity (if any)* @param id The embedded Id (if any)* @param lastNonConfigurationInstance Arbitrary object that will be* available via {@link Activity#getLastNonConfigurationInstance()* Activity.getLastNonConfigurationInstance()}.* @return Returns the instantiated activity* @throws InstantiationException* @throws IllegalAccessException*/public Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id,Object lastNonConfigurationInstance) throws InstantiationException, IllegalAccessException {Activity activity = (Activity)clazz.newInstance();ActivityThread aThread = null;activity.attach(context, aThread, this, token, application, intent, info, title,parent, id, lastNonConfigurationInstance, new Configuration());return activity;}

这里又执行了Activity的attach,来看下做了啥:

//frameworks/base/core/java/android/app/Activity.java
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,Object lastNonConfigurationInstance,HashMap<String,Object> lastNonConfigurationChildInstances,Configuration config) {attachBaseContext(context);//利用PolicyManager创建window对象mWindow = PolicyManager.makeNewWindow(this);mWindow.setCallback(this);if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {mWindow.setSoftInputMode(info.softInputMode);}mUiThread = Thread.currentThread();mMainThread = aThread;mInstrumentation = instr;mToken = token;mIdent = ident;mApplication = application;mIntent = intent;mComponent = intent.getComponent();mActivityInfo = info;mTitle = title;mParent = parent;mEmbeddedID = id;mLastNonConfigurationInstance = lastNonConfigurationInstance;mLastNonConfigurationChildInstances = lastNonConfigurationChildInstances;//设置WindowManager对象mWindow.setWindowManager(null, mToken, mComponent.flattenToString());if (mParent != null) {mWindow.setContainer(mParent.getWindow());}mWindowManager = mWindow.getWindowManager();mCurrentConfig = config;}

看下这行代码 mWindow = PolicyManager.makeNewWindow(this);

new出phonewindow

/*** {@hide}*/
public final class PolicyManager {private static final String POLICY_IMPL_CLASS_NAME ="com.android.internal.policy.impl.Policy";private static final IPolicy sPolicy;static {// Pull in the actual implementation of the policy at run-timetry {Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);//sPolicy sPolicy = (IPolicy)policyClass.newInstance();} catch (ClassNotFoundException ex) {throw new RuntimeException(POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);} catch (InstantiationException ex) {throw new RuntimeException(POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);} catch (IllegalAccessException ex) {throw new RuntimeException(POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);}}// Cannot instantiate this classprivate PolicyManager() {}// The static methods to spawn new policy-specific objectspublic static Window makeNewWindow(Context context) {return sPolicy.makeNewWindow(context);}public static LayoutInflater makeNewLayoutInflater(Context context) {return sPolicy.makeNewLayoutInflater(context);}public static WindowManagerPolicy makeNewWindowManager() {return sPolicy.makeNewWindowManager();}public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {return sPolicy.makeNewFallbackEventHandler(context);}
}

看下sPolicy的实现类Policy :

/*** {@hide}*/
// Simple implementation of the policy interface that spawns the right
// set of objects
public class Policy implements IPolicy {private static final String TAG = "PhonePolicy";private static final String[] preload_classes = {"com.android.internal.policy.impl.PhoneLayoutInflater","com.android.internal.policy.impl.PhoneWindow","com.android.internal.policy.impl.PhoneWindow$1","com.android.internal.policy.impl.PhoneWindow$ContextMenuCallback","com.android.internal.policy.impl.PhoneWindow$DecorView","com.android.internal.policy.impl.PhoneWindow$PanelFeatureState","com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",};static {// For performance reasons, preload some policy specific classes when// the policy gets loaded.//加载它们for (String s : preload_classes) {try {Class.forName(s);} catch (ClassNotFoundException ex) {Log.e(TAG, "Could not preload class for phone policy: " + s);}}}public PhoneWindow makeNewWindow(Context context) {//PhoneWindowreturn new PhoneWindow(context);}public PhoneLayoutInflater makeNewLayoutInflater(Context context) {return new PhoneLayoutInflater(context);}public PhoneWindowManager makeNewWindowManager() {return new PhoneWindowManager();}
}

到这里,我们得知 mWindow = PolicyManager.makeNewWindow(this); 执行后new出来的是个phonewindow。

WindowManager的创建

接着我们研究下windowmanager,回头看下 这行代码:mWindow.setWindowManager(null, mToken, mComponent.flattenToString());

frameworks/base/core/java/android/view/Window.java/*** Set the window manager for use by this Window to, for example,* display panels.  This is <em>not</em> used for displaying the* Window itself -- that must be done by the client.** @param wm The ViewManager for adding new windows.*/public void setWindowManager(WindowManager wm,IBinder appToken, String appName) {mAppToken = appToken;mAppName = appName;if (wm == null) {//拿到WindowManagerImplwm = WindowManagerImpl.getDefault();}//LocalWindowManagermWindowManager = new LocalWindowManager(wm);}
frameworks/base/core/java/android/view/Window.java
private class LocalWindowManager implements WindowManager {LocalWindowManager(WindowManager wm) {//传了WindowManager mWindowManager = wm;//拿到mDefaultDisplay mDefaultDisplay = mContext.getResources().getDefaultDisplay(mWindowManager.getDefaultDisplay());}public final void addView(View view, ViewGroup.LayoutParams params) {// Let this throw an exception on a bad params.WindowManager.LayoutParams wp = (WindowManager.LayoutParams)params;CharSequence curTitle = wp.getTitle();if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {if (wp.token == null) {View decor = peekDecorView();if (decor != null) {wp.token = decor.getWindowToken();}}if (curTitle == null || curTitle.length() == 0) {String title;if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA) {title="Media";} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY) {title="MediaOvr";} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {title="Panel";} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL) {title="SubPanel";} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG) {title="AtchDlg";} else {title=Integer.toString(wp.type);}if (mAppName != null) {title += ":" + mAppName;}wp.setTitle(title);}} else {if (wp.token == null) {wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;}if ((curTitle == null || curTitle.length() == 0)&& mAppName != null) {wp.setTitle(mAppName);}}if (wp.packageName == null) {wp.packageName = mContext.getPackageName();}mWindowManager.addView(view, params);}public void updateViewLayout(View view, ViewGroup.LayoutParams params) {mWindowManager.updateViewLayout(view, params);}public final void removeView(View view) {mWindowManager.removeView(view);}public final void removeViewImmediate(View view) {mWindowManager.removeViewImmediate(view);}public Display getDefaultDisplay() {return mDefaultDisplay;}private final WindowManager mWindowManager;private final Display mDefaultDisplay;}

构造函数中 mWindowManager = wm;传递了mWindowManager ,它实际上是WindowManagerImpl。

frameworks/base/core/java/android/view/WindowManagerImpl.javapublic static WindowManagerImpl getDefault(){return mWindowManager;}

到这里,把分析的类的继承和关联关系整理下:
在这里插入图片描述
总结下:activity的mWindow成员变量真实类型是PhoneWindow,mWindowManager成员变量真实类型是LocalWindowManage。然后mWindowManager和LocalWindowManage都实现了WindowManager的接口。

LocalWindowManage把它的工作通过委托交给了WindowManagerImpl完成了。

回到setContextView

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java// This is the view in which the window contents are placed. It is either// mDecor itself, or a child of mDecor where the contents go.ViewGroup mContentParent;@Overridepublic void setContentView(View view, ViewGroup.LayoutParams params) {// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window// decor, when theme attributes and the like are crystalized. Do not check the feature// before this happens.if (mContentParent == null) {//重点installDecor();} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {mContentParent.removeAllViews();}if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {view.setLayoutParams(params);final Scene newScene = new Scene(mContentParent, view);transitionTo(newScene);} else {//把view添加到view groupmContentParent.addView(view, params);}mContentParent.requestApplyInsets();final Callback cb = getCallback();if (cb != null && !isDestroyed()) {cb.onContentChanged();}mContentParentExplicitlySet = true;}

需要留意mContentParent是个viewgroup

 private void installDecor() {mForceDecorInstall = false;if (mDecor == null) {//DecorView,继承于FrameLayoutmDecor = generateDecor(-1);mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);mDecor.setIsRootNamespace(true);if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);}} else {mDecor.setWindow(this);}if (mContentParent == null) {//生成viewgroupmContentParent = generateLayout(mDecor);// Set up decor part of UI to ignore fitsSystemWindows if appropriate.mDecor.makeFrameworkOptionalFitsSystemWindows();final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(R.id.decor_content_parent);if (decorContentParent != null) {mDecorContentParent = decorContentParent;mDecorContentParent.setWindowCallback(getCallback());if (mDecorContentParent.getTitle() == null) {mDecorContentParent.setWindowTitle(mTitle);}final int localFeatures = getLocalFeatures();for (int i = 0; i < FEATURE_MAX; i++) {if ((localFeatures & (1 << i)) != 0) {mDecorContentParent.initFeature(i);}}mDecorContentParent.setUiOptions(mUiOptions);if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||(mIconRes != 0 && !mDecorContentParent.hasIcon())) {mDecorContentParent.setIcon(mIconRes);} else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&mIconRes == 0 && !mDecorContentParent.hasIcon()) {mDecorContentParent.setIcon(getContext().getPackageManager().getDefaultActivityIcon());mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;}if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||(mLogoRes != 0 && !mDecorContentParent.hasLogo())) {mDecorContentParent.setLogo(mLogoRes);}// Invalidate if the panel menu hasn't been created before this.// Panel menu invalidation is deferred avoiding application onCreateOptionsMenu// being called in the middle of onCreate or similar.// A pending invalidation will typically be resolved before the posted message// would run normally in order to satisfy instance state restoration.PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {invalidatePanelMenu(FEATURE_ACTION_BAR);}} else {//创建标题mTitleView = findViewById(R.id.title);if (mTitleView != null) {if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {final View titleContainer = findViewById(R.id.title_container);if (titleContainer != null) {titleContainer.setVisibility(View.GONE);} else {mTitleView.setVisibility(View.GONE);}mContentParent.setForeground(null);} else {mTitleView.setText(mTitle);}}}if (mDecor.getBackground() == null && mBackgroundFallbackDrawable != null) {mDecor.setBackgroundFallback(mBackgroundFallbackDrawable);}// Only inflate or create a new TransitionManager if the caller hasn't// already set a custom one.if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {if (mTransitionManager == null) {final int transitionRes = getWindowStyle().getResourceId(R.styleable.Window_windowContentTransitionManager,0);if (transitionRes != 0) {final TransitionInflater inflater = TransitionInflater.from(getContext());mTransitionManager = inflater.inflateTransitionManager(transitionRes,mContentParent);} else {mTransitionManager = new TransitionManager();}}mEnterTransition = getTransition(mEnterTransition, null,R.styleable.Window_windowEnterTransition);mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION,R.styleable.Window_windowReturnTransition);mExitTransition = getTransition(mExitTransition, null,R.styleable.Window_windowExitTransition);mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION,R.styleable.Window_windowReenterTransition);mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null,R.styleable.Window_windowSharedElementEnterTransition);mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition,USE_DEFAULT_TRANSITION,R.styleable.Window_windowSharedElementReturnTransition);mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null,R.styleable.Window_windowSharedElementExitTransition);mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition,USE_DEFAULT_TRANSITION,R.styleable.Window_windowSharedElementReenterTransition);if (mAllowEnterTransitionOverlap == null) {mAllowEnterTransitionOverlap = getWindowStyle().getBoolean(R.styleable.Window_windowAllowEnterTransitionOverlap, true);}if (mAllowReturnTransitionOverlap == null) {mAllowReturnTransitionOverlap = getWindowStyle().getBoolean(R.styleable.Window_windowAllowReturnTransitionOverlap, true);}if (mBackgroundFadeDurationMillis < 0) {mBackgroundFadeDurationMillis = getWindowStyle().getInteger(R.styleable.Window_windowTransitionBackgroundFadeDuration,DEFAULT_BACKGROUND_FADE_DURATION_MS);}if (mSharedElementsUseOverlay == null) {mSharedElementsUseOverlay = getWindowStyle().getBoolean(R.styleable.Window_windowSharedElementsUseOverlay, true);}}}}

先看下generateLayout:

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.javaprotected ViewGroup generateLayout(DecorView decor) {// Apply data from current theme.TypedArray a = getWindowStyle();if (false) {System.out.println("From style:");String s = "Attrs:";for (int i = 0; i < R.styleable.Window.length; i++) {s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="+ a.getString(i);}System.out.println(s);}mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)& (~getForcedWindowFlags());if (mIsFloating) {setLayout(WRAP_CONTENT, WRAP_CONTENT);setFlags(0, flagsToUpdate);} else {setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);getAttributes().setFitInsetsSides(0);getAttributes().setFitInsetsTypes(0);}if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {requestFeature(FEATURE_NO_TITLE);} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {// Don't allow an action bar if there is no title.requestFeature(FEATURE_ACTION_BAR);}if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {requestFeature(FEATURE_ACTION_BAR_OVERLAY);}if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {requestFeature(FEATURE_ACTION_MODE_OVERLAY);}if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,false)) {setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS& (~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,false)) {setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION& (~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,getContext().getApplicationInfo().targetSdkVersion>= android.os.Build.VERSION_CODES.HONEYCOMB)) {setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));}a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);if (DEBUG) Log.d(TAG, "Min width minor: " + mMinWidthMinor.coerceToString()+ ", major: " + mMinWidthMajor.coerceToString());if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();a.getValue(R.styleable.Window_windowFixedWidthMajor,mFixedWidthMajor);}if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) {if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();a.getValue(R.styleable.Window_windowFixedWidthMinor,mFixedWidthMinor);}if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();a.getValue(R.styleable.Window_windowFixedHeightMajor,mFixedHeightMajor);}if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();a.getValue(R.styleable.Window_windowFixedHeightMinor,mFixedHeightMinor);}if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {requestFeature(FEATURE_CONTENT_TRANSITIONS);}if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {requestFeature(FEATURE_ACTIVITY_TRANSITIONS);}mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false);final Context context = getContext();final int targetSdk = context.getApplicationInfo().targetSdkVersion;final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q;if (!mForcedStatusBarColor) {mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);}if (!mForcedNavigationBarColor) {mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,0x00000000);}if (!targetPreQ) {mEnsureStatusBarContrastWhenTransparent = a.getBoolean(R.styleable.Window_enforceStatusBarContrast, false);mEnsureNavigationBarContrastWhenTransparent = a.getBoolean(R.styleable.Window_enforceNavigationBarContrast, true);}WindowManager.LayoutParams params = getAttributes();// Non-floating windows on high end devices must put up decor beneath the system bars and// therefore must know about visibility changes of those.if (!mIsFloating) {if (!targetPreL && a.getBoolean(R.styleable.Window_windowDrawsSystemBarBackgrounds,false)) {setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());}if (mDecor.mForceWindowDrawsBarBackgrounds) {params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;}params.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;}final int sysUiVis = decor.getSystemUiVisibility();final int statusLightFlag = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;final int statusFlag = a.getBoolean(R.styleable.Window_windowLightStatusBar, false)? statusLightFlag : 0;final int navLightFlag = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;final int navFlag = a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)? navLightFlag : 0;decor.setSystemUiVisibility((sysUiVis & ~(statusLightFlag | navLightFlag)) | (statusFlag | navFlag));if (a.hasValue(R.styleable.Window_windowLayoutInDisplayCutoutMode)) {int mode = a.getInt(R.styleable.Window_windowLayoutInDisplayCutoutMode, -1);if (mode < LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT|| mode > LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {throw new UnsupportedOperationException("Unknown windowLayoutInDisplayCutoutMode: "+ a.getString(R.styleable.Window_windowLayoutInDisplayCutoutMode));}params.layoutInDisplayCutoutMode = mode;}if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion>= android.os.Build.VERSION_CODES.HONEYCOMB) {if (a.getBoolean(R.styleable.Window_windowCloseOnTouchOutside,false)) {setCloseOnTouchOutsideIfNotSet(true);}}if (!hasSoftInputMode()) {params.softInputMode = a.getInt(R.styleable.Window_windowSoftInputMode,params.softInputMode);}if (a.getBoolean(R.styleable.Window_backgroundDimEnabled,mIsFloating)) {/* All dialogs should have the window dimmed */if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;}if (!haveDimAmount()) {params.dimAmount = a.getFloat(android.R.styleable.Window_backgroundDimAmount, 0.5f);}}if (a.getBoolean(R.styleable.Window_windowBlurBehindEnabled, false)) {if ((getForcedWindowFlags() & WindowManager.LayoutParams.FLAG_BLUR_BEHIND) == 0) {params.flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND;}params.setBlurBehindRadius(a.getDimensionPixelSize(android.R.styleable.Window_windowBlurBehindRadius, 0));}setBackgroundBlurRadius(a.getDimensionPixelSize(R.styleable.Window_windowBackgroundBlurRadius, 0));if (params.windowAnimations == 0) {params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);}// The rest are only done if this window is not embedded; otherwise,// the values are inherited from our container.if (getContainer() == null) {if (mBackgroundDrawable == null) {if (mFrameResource == 0) {mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);}if (a.hasValue(R.styleable.Window_windowBackground)) {mBackgroundDrawable = a.getDrawable(R.styleable.Window_windowBackground);}}if (a.hasValue(R.styleable.Window_windowBackgroundFallback)) {mBackgroundFallbackDrawable =a.getDrawable(R.styleable.Window_windowBackgroundFallback);}if (mLoadElevation) {mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);}mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);}// Inflate the window decor.int layoutResource;int features = getLocalFeatures();// System.out.println("Features: 0x" + Integer.toHexString(features));if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {if (mIsFloating) {TypedValue res = new TypedValue();getContext().getTheme().resolveAttribute(R.attr.dialogTitleIconsDecorLayout, res, true);layoutResource = res.resourceId;} else {layoutResource = R.layout.screen_title_icons;}// XXX Remove this once action bar supports these features.removeFeature(FEATURE_ACTION_BAR);// System.out.println("Title Icons!");} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {// Special case for a window with only a progress bar (and title).// XXX Need to have a no-title version of embedded windows.layoutResource = R.layout.screen_progress;// System.out.println("Progress!");} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {// Special case for a window with a custom title.// If the window is floating, we need a dialog layoutif (mIsFloating) {TypedValue res = new TypedValue();getContext().getTheme().resolveAttribute(R.attr.dialogCustomTitleDecorLayout, res, true);layoutResource = res.resourceId;} else {layoutResource = R.layout.screen_custom_title;}// XXX Remove this once action bar supports these features.removeFeature(FEATURE_ACTION_BAR);} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {// If no other features and not embedded, only need a title.// If the window is floating, we need a dialog layoutif (mIsFloating) {TypedValue res = new TypedValue();getContext().getTheme().resolveAttribute(R.attr.dialogTitleDecorLayout, res, true);layoutResource = res.resourceId;} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {layoutResource = a.getResourceId(R.styleable.Window_windowActionBarFullscreenDecorLayout,R.layout.screen_action_bar);} else {layoutResource = R.layout.screen_title;}// System.out.println("Title!");} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {layoutResource = R.layout.screen_simple_overlay_action_mode;} else {// Embedded, so no decoration is needed.layoutResource = R.layout.screen_simple;// System.out.println("Simple!");}mDecor.startChanging();mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);if (contentParent == null) {throw new RuntimeException("Window couldn't find content container view");}if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {ProgressBar progress = getCircularProgressBar(false);if (progress != null) {progress.setIndeterminate(true);}}// Remaining setup -- of background and title -- that only applies// to top-level windows.if (getContainer() == null) {mDecor.setWindowBackground(mBackgroundDrawable);final Drawable frame;if (mFrameResource != 0) {frame = getContext().getDrawable(mFrameResource);} else {frame = null;}mDecor.setWindowFrame(frame);mDecor.setElevation(mElevation);mDecor.setClipToOutline(mClipToOutline);if (mTitle != null) {setTitle(mTitle);}if (mTitleColor == 0) {mTitleColor = mTextColor;}setTitleColor(mTitleColor);}mDecor.finishChanging();return contentParent;}

一系列的操作,创建了viewgroup。

findViewById

frameworks/base/core/java/android/view/Window.java/*** Finds a view that was identified by the {@code android:id} XML attribute* that was processed in {@link android.app.Activity#onCreate}.* <p>* This will implicitly call {@link #getDecorView} with all of the associated side-effects.* <p>* <strong>Note:</strong> In most cases -- depending on compiler support --* the resulting view is automatically cast to the target class type. If* the target class type is unconstrained, an explicit cast may be* necessary.** @param id the ID to search for* @return a view with given ID if found, or {@code null} otherwise* @see View#findViewById(int)* @see Window#requireViewById(int)*/@Nullablepublic <T extends View> T findViewById(@IdRes int id) {return getDecorView().findViewById(id);}

到这里,我们对AndroidUI体系的理解又加深了:
在这里插入图片描述

addView

有了前面的研究“成果”,再来看下addView的实现:

frameworks/base/core/java/android/view/WindowManagerImpl.javaprivate void addView(View view, ViewGroup.LayoutParams params, boolean nest){if (Config.LOGV) Log.v("WindowManager", "addView view=" + view);if (!(params instanceof WindowManager.LayoutParams)) {throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");}final WindowManager.LayoutParams wparams= (WindowManager.LayoutParams)params;//ViewRootViewRoot root;View panelParentView = null;synchronized (this) {// Here's an odd/questionable case: if someone tries to add a// view multiple times, then we simply bump up a nesting count// and they need to remove the view the corresponding number of// times to have it actually removed from the window manager.// This is useful specifically for the notification manager,// which can continually add/remove the same view as a// notification gets updated.int index = findViewLocked(view, false);if (index >= 0) {if (!nest) {throw new IllegalStateException("View " + view+ " has already been added to the window manager.");}root = mRoots[index];root.mAddNesting++;// Update layout parameters.view.setLayoutParams(wparams);root.setLayoutParams(wparams, true);return;}// If this is a panel window, then find the window it is being// attached to for future reference.if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {final int count = mViews != null ? mViews.length : 0;for (int i=0; i<count; i++) {if (mRoots[i].mWindow.asBinder() == wparams.token) {panelParentView = mViews[i];}}}//new 了 ViewRootroot = new ViewRoot(view.getContext());root.mAddNesting = 1;view.setLayoutParams(wparams);if (mViews == null) {index = 1;mViews = new View[1];mRoots = new ViewRoot[1];mParams = new WindowManager.LayoutParams[1];} else {index = mViews.length + 1;Object[] old = mViews;mViews = new View[index];System.arraycopy(old, 0, mViews, 0, index-1);old = mRoots;mRoots = new ViewRoot[index];System.arraycopy(old, 0, mRoots, 0, index-1);old = mParams;mParams = new WindowManager.LayoutParams[index];System.arraycopy(old, 0, mParams, 0, index-1);}index--;mViews[index] = view;mRoots[index] = root;mParams[index] = wparams;}// do this last because it fires off messages to start doing things//把decorView设置进去给viewRootroot.setView(view, wparams, panelParentView);}

ViewRoot

/*** The top of a view hierarchy, implementing the needed protocol between View* and the WindowManager.  This is for the most part an internal implementation* detail of {@link WindowManagerImpl}.** {@hide}*/
@SuppressWarnings({"EmptyCatchBlock"})
public final class ViewRoot extends Handler implements ViewParent,View.AttachInfo.Callbacks {private static final String TAG = "ViewRoot";private static final boolean DBG = false;private static final boolean SHOW_FPS = false;@SuppressWarnings({"ConstantConditionalExpression"})private static final boolean LOCAL_LOGV = false ? Config.LOGD : Config.LOGV;/** @noinspection PointlessBooleanExpression*/private static final boolean DEBUG_DRAW = false || LOCAL_LOGV;private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV;private static final boolean DEBUG_INPUT_RESIZE = false || LOCAL_LOGV;private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV;private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV;private static final boolean DEBUG_IMF = false || LOCAL_LOGV;private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV;private static final boolean WATCH_POINTER = false;private static final boolean MEASURE_LATENCY = false;private static LatencyTimer lt;/*** Maximum time we allow the user to roll the trackball enough to generate* a key event, before resetting the counters.*/static final int MAX_TRACKBALL_DELAY = 250;static long sInstanceCount = 0;static IWindowSession sWindowSession;static final Object mStaticInit = new Object();static boolean mInitialized = false;static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>();static boolean sFirstDrawComplete = false;static final ArrayList<ComponentCallbacks> sConfigCallbacks= new ArrayList<ComponentCallbacks>();private static int sDrawTime;long mLastTrackballTime = 0;final TrackballAxis mTrackballAxisX = new TrackballAxis();final TrackballAxis mTrackballAxisY = new TrackballAxis();final int[] mTmpLocation = new int[2];final InputMethodCallback mInputMethodCallback;final SparseArray<Object> mPendingEvents = new SparseArray<Object>();int mPendingEventSeq = 0;final Thread mThread;final WindowLeaked mLocation;final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//重点final W mWindow;View mView;View mFocusedView;View mRealFocusedView;  // this is not set to null in touch modeint mViewVisibility;boolean mAppVisible = true;final Region mTransparentRegion;final Region mPreviousTransparentRegion;int mWidth;int mHeight;Rect mDirty; // will be a graphics.Region soonboolean mIsAnimating;CompatibilityInfo.Translator mTranslator;final View.AttachInfo mAttachInfo;final Rect mTempRect; // used in the transaction to not thrash the heap.final Rect mVisRect; // used to retrieve visible rect of focused view.boolean mTraversalScheduled;boolean mWillDrawSoon;boolean mLayoutRequested;boolean mFirst;boolean mReportNextDraw;boolean mFullRedrawNeeded;boolean mNewSurfaceNeeded;boolean mHasHadWindowFocus;boolean mLastWasImTarget;boolean mWindowAttributesChanged = false;// These can be accessed by any thread, must be protected with a lock.// Surface can never be reassigned or cleared (use Surface.clear()).//重点private final Surface mSurface = new Surface();boolean mAdded;boolean mAddedTouchMode;/*package*/ int mAddNesting;// These are accessed by multiple threads.final Rect mWinFrame; // frame given by window manager.final Rect mPendingVisibleInsets = new Rect();final Rect mPendingContentInsets = new Rect();final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets= new ViewTreeObserver.InternalInsetsInfo();final Configuration mLastConfiguration = new Configuration();final Configuration mPendingConfiguration = new Configuration();class ResizedInfo {Rect coveredInsets;Rect visibleInsets;Configuration newConfig;}boolean mScrollMayChange;int mSoftInputMode;View mLastScrolledFocus;int mScrollY;int mCurScrollY;Scroller mScroller;EGL10 mEgl;EGLDisplay mEglDisplay;EGLContext mEglContext;EGLSurface mEglSurface;GL11 mGL;Canvas mGlCanvas;boolean mUseGL;boolean mGlWanted;final ViewConfiguration mViewConfiguration;/*** see {@link #playSoundEffect(int)}*/AudioManager mAudioManager;private final int mDensity;}

ViewRoot继承于Handler。有个成员变量,叫做mSurface,有个W类型的mWindow,他其实和Binder通信有关。还有个View类型的mView。

Android在哪里“画画”

我们前面分析到的,是UI控件,但是还没看到作画的画布在哪。篇幅原因,另开一篇。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/811851.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PostgreSQL数据库基础--简易版

数据库 其中runoobdb为数据库名 查看已经存在的数据库 \l进入数据库 \c runoobdb创建数据库 CREATE DATABASE runoobdb;删除数据库 DROP DATABASE runoobdb;表 其中COMPANY为表名 创建表格 CREATE TABLE COMPANY(ID INT PRIMARY KEY NOT NULL,NAME TEXT…

Harmony鸿蒙南向驱动开发-UART接口使用

功能简介 UART指异步收发传输器&#xff08;Universal Asynchronous Receiver/Transmitter&#xff09;&#xff0c;是通用串行数据总线&#xff0c;用于异步通信。该总线双向通信&#xff0c;可以实现全双工传输。 两个UART设备的连接示意图如下&#xff0c;UART与其他模块一…

外观模式:简化复杂系统的统一接口

在面向对象的软件开发中&#xff0c;外观模式是一种常用的结构型设计模式&#xff0c;旨在为复杂的系统提供一个简化的接口。通过创建一个统一的高级接口&#xff0c;这个模式帮助客户端通过一个简单的方式与复杂的子系统交互。本文将详细介绍外观模式的定义、实现、应用场景以…

【Hadoop大数据技术】——Flume日志采集系统(学习笔记)

&#x1f4d6; 前言&#xff1a;在大数据系统的开发中&#xff0c;数据收集工作无疑是开发者首要解决的一个难题&#xff0c;但由于生产数据的源头丰富多样&#xff0c;其中包含网站日志数据、后台监控数据、用户浏览网页数据等&#xff0c;数据工程师要想将它们分门别类的采集…

什么是RMVB视频?如何把视频转成RMVB格式?视频格式转换的方法

一&#xff0c;什么是RMVB视频格式 RMVB是一种视频文件格式&#xff0c;它基于RealNetworks公司开发的RealMedia编解码器&#xff0c;被广泛应用于互联网上的视频流媒体传输和下载。RMVB文件通常具有较小的文件大小&#xff0c;同时保持较高的视频质量&#xff0c;因此在网络传…

python之堆的实现

堆本质是一个完全二叉树&#xff0c;分为大根堆和小根堆&#xff0c;大根堆每个结点的值都大于它的孩子的值&#xff0c;小根堆相反&#xff0c;每个结点的值都小于它的孩子的值 heapq是python的标准库&#xff0c;用于维护堆&#xff0c;非常方便 heapq库常用的几个函数 he…

React添加到现有项目

1.检查现有项目的根目录下是否有package.json文件 如果没有&#xff0c;则在项目的根目录下初始化一个package.json配置文件 2.在根目录下安装react和react-dom依赖 npm install --save react react-dom react-scripts安装成功后&#xff0c;react、react-dom以及react-scr…

上位机图像处理和嵌入式模块部署(qmacvisual缺失的光源控制)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 有些场景下面&#xff0c;是不需要光源和光源控制的&#xff0c;比如说利用摄像头识别对应区域的库位&#xff0c;这部分直接利用红外光采集对应的…

Nuttx系统在 imx6ul 开发板上的移植(一、环境准备和交叉编译)

Nuttx应该是一个不错的系统&#xff0c;有瓜可挖。小米的澎湃os底层内核使用的就是它。 翻出之前别人送我的imax6ul开发板&#xff0c;在那安安静静的吃灰&#xff0c;有了想动一动的冲动。于是想到给自己定一个小目标&#xff0c;逐步实现Nuttx内核系统在imax6ul的开发板上移植…

十五届web模拟题整理

模拟赛一期 1.动态的Tab栏 请在 style.css 文件中补全代码。 当用户向下滚动的高度没有超过标题栏&#xff08;即 .heading 元素&#xff09;的高度时&#xff0c;保持 Tab 栏在其原有的位置。当滚动高度超过标题栏的高度时&#xff0c;固定显示 Tab 栏在网页顶部。 /* TODO…

【题目】【信息安全管理与评估】2022年国赛高职组“信息安全管理与评估”赛项样题2

【题目】【信息安全管理与评估】2022年国赛高职组“信息安全管理与评估”赛项样题2 信息安全管理与评估 网络系统管理 网络搭建与应用 云计算 软件测试 移动应用开发 任务书&#xff0c;赛题&#xff0c;解析等资料&#xff0c;知识点培训服务 添加博主wx&#xff1a;liuliu548…

Python人工智能教学之掌握机器学习深度学习并提升实战能力(共72个视频教学+课程资料)云盘下载

人工智能是未来的发展方向&#xff0c;掌握了人工智能&#xff0c;就掌握了钱图。。。 Python人工智能教学之掌握机器学习深度学习并提升实战能力&#xff08;共72个视频教学课程资料&#xff09; 下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1ryJd5PNx1tLD…

私有化即时通讯软件,WorkPlus提供的私有化、安全通讯解决方案

在当今信息化快速发展的时代&#xff0c;安全问题已经成为各行各业关注的焦点。特别是在金融、政府单位和芯片等关键行业&#xff0c;信息安全的重要性不言而喻。这些行业涉及到大量的敏感数据和关键信息&#xff0c;一旦发生泄露&#xff0c;可能会对国家安全、企业利益甚至个…

DC-2渗透测试复现

DC-2渗透测试复现 目的&#xff1a; 获取最高权限以及5个flag 过程&#xff1a; 信息打点-ssh连接-git提权 环境&#xff1a; 攻击机&#xff1a;kali(192.168.85.136) 靶机&#xff1a;DC_2(192.168.85.132) 复现&#xff1a; 一.信息收集 nmap -sP 192.168.85.0/24 …

如何用个人电脑搭建一台本地服务器,并部署项目到服务器详细教程

服务器是一种高性能计算机&#xff0c;作为网络的节点&#xff0c;它存储、处理网络上80%的数据、信息&#xff0c;因此也被称为网络的灵魂。与普通计算机相比&#xff0c;服务器具有高速CPU运算能力、长时间可靠运行、强大I/O外部数据吞吐能力以及更好的扩展性。 服务器的主要…

c# refc# substring c# 反射c# split c# websocket c# datatable使用

在C#编程中&#xff0c;ref关键字、Substring方法、反射&#xff08;Reflection&#xff09;、Split方法、WebSocket通信以及DataTable的使用都是常见的技术和方法。下面我将逐一为您详解这些内容。 1. C# ref关键字 ref关键字在C#中用于按引用传递参数。这意味着当您将变量作…

原型模式:复制对象的智能解决方案

在软件开发过程中&#xff0c;对象的创建可能是一个昂贵的操作&#xff0c;特别是当对象的初始化包括从数据库加载数据、进行IO操作或进行复杂计算时。原型模式是一种创建型设计模式&#xff0c;它通过复制现有的实例来创建新的对象实例&#xff0c;从而避免了类初始化时的高成…

扭蛋机小程序:线上扭蛋机模式发展空间有多大?

潮玩行业近几年的发展非常快&#xff0c;推动了扭蛋机市场的发展&#xff0c;越来越多的人加入到了扭蛋机赛道中&#xff0c;市场迎来了新的发展期。如今&#xff0c;我国的二次元文化的发展不断成熟&#xff0c;扭蛋机主打的二次元商品迎来了更多的商业机会。 一、互联网扭蛋机…

httpsok-快速申请谷歌SSL免费证书

&#x1f525;httpsok-快速申请谷歌SSL免费证书 使用场景&#xff1a; 部署CDN证书、OSS云存储证书证书类型&#xff1a; 单域名 多域名 通配符域名 混合域名证书厂商&#xff1a; ZeroSSL Lets Encrypt Google证书加密类型&#xff1a; ECC、 RSA 一、证书管理 进入 证书管…

B站基于Apache Ranger的大数据权限服务的技术演进

01 背景 随着云计算、大数据技术的日趋成熟&#xff0c;复杂多元、规模庞大的数据所蕴含的经济价值和社会价值逐步凸显&#xff0c;数据安全也是企业面临的巨大挑战&#xff0c;B站一直致力于对用户隐私数据的保护。 02 Ranger概述 2.1 用户认证 提到安全&#xff0c;就不得不…