平台:
RK3399 + Android8.1
屏幕分辨率: 1920x1080
DPI: 280
问题
Launcher及满屏APP, 显示时, 屏幕右边会显示不全, 不到边.
分调试信息:
Activity:
dumpsys activity mCurrentUser=0 mLastStartReason=startHomeActivity: noMoreActivities resumeHomeStackTask:0:0 mLastStartActivityTimeMs=Mar 28, 2019 7:01:58 AM mLastStartActivityResult=0 mLastStartActivityRecord: packageName=com.android.launcher3 processName=com.android.launcher3 launchedFromUid=0 launchedFromPackage=null userId=0 app=ProcessRecord{59dbaa 1123:com.android.launcher3/u0a10} Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.Launcher } frontOfTask=true task=TaskRecord{221f42d #6 I=com.android.launcher3/.Launcher U=0 StackId=0 sz=1} taskAffinity=null realActivity=com.android.launcher3/.Launcher baseDir=/system/priv-app/Launcher3/Launcher3.apk dataDir=/data/user/0/com.android.launcher3 stateNotNeeded=true componentSpecified=false mActivityType=1 compat={280dpi always-compat} labelRes=0x7f0f000a icon=0x7f020015 theme=0x7f120002 mLastReportedConfigurations: mGlobalConfig={1.0 ?mcc?mnc [en_US] ldltr sw617dp w1097dp h545dp 280dpi lrg long land -touch -keyb/v/h -nav/h appBounds=Rect(0, 0 - 1920, 996) s.4} mOverrideConfig={1.0 ?mcc?mnc [en_US] ldltr sw617dp w1058dp h545dp 280dpi lrg long land -touch -keyb/v/h -nav/h appBounds=Rect(0, 0 - 1853, 996) s.4} CurrentConfiguration={1.0 ?mcc?mnc [en_US] ldltr sw617dp w1058dp h545dp 280dpi lrg long land -touch -keyb/v/h -nav/h appBounds=Rect(0, 0 - 1853, 996) s.4} OverrideConfiguration={0.0 ?mcc?mnc ?localeList ?layoutDir sw617dp w1058dp h545dp ?density lrg long ?ldr ?wideColorGamut land ?uimode ?night ?touch ?keyb/?/? ?nav/? appBounds=Rect(0, 0 - 1853, 996)} mBounds=Rect(0, 0 - 1853, 996) taskDescription: iconFilename=null label="null" primaryColor=fff5f5f5 backgroundColor=fffafafa statusBarColor=0 navigationBarColor=0 launchFailed=false launchCount=1 lastLaunchTime=-6m58s276ms haveState=false icicle=null state=RESUMED stopped=false delayedResume=false finishing=false keysPaused=false inHistory=true visible=true sleeping=false idle=true mStartingWindowState=STARTING_WINDOW_NOT_SHOWN fullscreen=true noDisplay=false immersive=false launchMode=2 frozenBeforeDestroy=false forceNewConfig=false mActivityType=HOME_ACTIVITY_TYPE waitingVisible=false nowVisible=true lastVisibleTime=-6m57s484ms resizeMode=RESIZE_MODE_UNRESIZEABLE mLastReportedMultiWindowMode=false mLastReportedPictureInPictureMode=false maxAspectRatio=1.86
重点:OverrideConfiguration={0.0 ?mcc?mnc ?localeList ?layoutDir sw617dp w1058dp h545dp ?density lrg long ?ldr ?wideColorGamut land ?uimode ?night ?touch ?keyb/?/? ?nav/? appBounds=Rect(0, 0 - 1853, 996)}
Window:
dumpsys window: Window #6 Window{45072ec u0 com.android.launcher3/com.android.launcher3.Launcher}: mDisplayId=0 stackId=0 mSession=Session{c4723f9 1285:u0a10010} mClient=android.os.BinderProxy@e3fa19f mOwnerUid=10010 mShowToOwnerOnly=true package=com.android.launcher3 appop=NONE mAttrs=WM.LayoutParams{(0,0)(fillxfill) sim=#20 ty=1 fl=#81910100 fmt=-2 wanim=0x10302f6 vsysui=0x700 needsMenuKey=2 colorMode=0} Requested w=1853 h=996 mLayoutSeq=185 mHasSurface=true mShownPosition=[0,0] isReadyForDisplay()=true hasSavedSurface()=false mWindowRemovalAllowed=false WindowStateAnimator{d2dce8f com.android.launcher3/com.android.launcher3.Launcher}: Surface: shown=true layer=21000 alpha=1.0 rect=(0.0,0.0) 1853.0 x 996.0 transform=(1.0, 0.0, 1.0, 0.0) mWallpaperX=0.0 mWallpaperY=0.5 isOnScreen=true isVisible=true
rect=(0.0,0.0) 1853.0 x 996.0 transform=(1.0, 0.0, 1.0, 0.0)
源码点滴
|-- frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java
private void updateOverrideConfiguration() { mTmpConfig.unset(); computeBounds(mTmpBounds); if (mTmpBounds.equals(mBounds)) { return; } mBounds.set(mTmpBounds); // Bounds changed...update configuration to match. if (!mBounds.isEmpty()) { task.computeOverrideConfiguration(mTmpConfig, mBounds, null /* insetBounds */, false /* overrideWidth */, false /* overrideHeight */); } //更新了OverrideConfiguration. onOverrideConfigurationChanged(mTmpConfig); } /** * Computes the bounds to fit the Activity within the bounds of the {@link Configuration}. */ // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. private void computeBounds(Rect outBounds) { outBounds.setEmpty(); final float maxAspectRatio = info.maxAspectRatio; final ActivityStack stack = getStack(); if (task == null || stack == null || !task.mFullscreen || maxAspectRatio == 0 || isInVrUiMode(getConfiguration())) { // We don't set override configuration if that activity task isn't fullscreen. I.e. the // activity is in multi-window mode. Or, there isn't a max aspect ratio specified for // the activity. This is indicated by an empty {@link outBounds}. We also don't set it // if we are in VR mode. return; } // We must base this on the parent configuration, because we set our override // configuration's appBounds based on the result of this method. If we used our own // configuration, it would be influenced by past invocations. final Configuration configuration = getParent().getConfiguration(); //1920 final int containingAppWidth = configuration.appBounds.width(); //996 final int containingAppHeight = configuration.appBounds.height(); int maxActivityWidth = containingAppWidth; int maxActivityHeight = containingAppHeight; if (containingAppWidth < containingAppHeight) { // Width is the shorter side, so we use that to figure-out what the max. height // should be given the aspect ratio. maxActivityHeight = (int) ((maxActivityWidth * maxAspectRatio) + 0.5f); } else { //当前是横屏, 所以走了这里. //运算后, maxActivityWidth = 1853, 问题的原因就在这里. // Height is the shorter side, so we use that to figure-out what the max. width // should be given the aspect ratio. maxActivityWidth = (int) ((maxActivityHeight * maxAspectRatio) + 0.5f); } if (containingAppWidth <= maxActivityWidth && containingAppHeight <= maxActivityHeight) { // The display matches or is less than the activity aspect ratio, so nothing else to do. // Return the existing bounds. If this method is running for the first time, // {@link mBounds} will be empty (representing no override). If the method has run // before, then effect of {@link mBounds} will already have been applied to the // value returned from {@link getConfiguration}. Refer to // {@link TaskRecord#computeOverrideConfiguration}. outBounds.set(mBounds); return; } // Compute configuration based on max supported width and height. outBounds.set(0, 0, maxActivityWidth, maxActivityHeight); // Position the activity frame on the opposite side of the nav bar. final int navBarPosition = service.mWindowManager.getNavBarPosition(); final int left = navBarPosition == NAV_BAR_LEFT ? configuration.appBounds.right - outBounds.width() : 0; outBounds.offsetTo(left, 0 /* top */); }
|-- frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { ... // Because we could be starting an Activity in the system process this may not go // across a Binder interface which would create a new Configuration. Consequently // we have to always create a new Configuration here. //OverrideConfiguration 会进一步传递到应用启动: final MergedConfiguration mergedConfiguration = new MergedConfiguration( mService.getGlobalConfiguration(), r.getMergedOverrideConfiguration()); r.setLastReportedConfiguration(mergedConfiguration); logIfTransactionTooLarge(r.intent, r.icicle); app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, System.identityHashCode(r), r.info, // TODO: Have this take the merged configuration instead of separate global // and override configs. mergedConfiguration.getGlobalConfiguration(), mergedConfiguration.getOverrideConfiguration(), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo); }
|-- frameworks/base/core/java/android/app/ActivityThread.java
private class ApplicationThread extends IApplicationThread.Stub { ... // we use token to identify this activity without having to send the // activity itself back to the activity manager. (matters more with ipc) @Override public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) { ... //在前面Configuration:overrideConfig被重写, screenWidthDp 被1058 r.overrideConfig = overrideConfig; updatePendingConfiguration(curConfig); sendMessage(H.LAUNCH_ACTIVITY, r); } public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null, "LAUNCH_ACTIVITY"); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); ... } private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { ... // Make sure we are running with the most recent config. handleConfigurationChanged(null, null); WindowManagerGlobal.initialize(); Activity a = performLaunchActivity(r, customIntent); if (a != null) { r.createdConfig = new Configuration(mConfiguration); reportSizeConfigurations(r); Bundle oldState = r.state; handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason); ... } private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")"); ... //创建了 appContext ContextImpl appContext = createBaseContextForActivity(r); Activity activity = null; try { java.lang.ClassLoader cl = appContext.getClassLoader(); //创建Activity. activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); ... } catch (Exception e) { ... } try { //创建Application? Application app = r.packageInfo.makeApplication(false, mInstrumentation); ... if (activity != null) { CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); //创建Configuration. Configuration config = new Configuration(mCompatConfiguration); if (r.overrideConfig != null) { //覆盖Configuration config.updateFrom(r.overrideConfig); } if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config); Window window = null; if (r.mPendingRemoveWindow != null && r.mPreserveWindow) { window = r.mPendingRemoveWindow; r.mPendingRemoveWindow = null; r.mPendingRemoveWindowManager = null; } appContext.setOuterContext(activity); //调用attach, 传入appContext, 创建PhoneWindow activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback); //...省略Activity启动的生命周期调用代码. mActivities.put(r.token, r); } final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { ... //添加根View到WindowManager if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true; wm.addView(decor, l); } else { // The activity will get a callback for this {@link LayoutParams} change // earlier. However, at that time the decor will not be set (this is set // in this method), so no action will be taken. This call ensures the // callback occurs with the decor set. a.onWindowAttributesChanged(l); } } }
mBase的赋值来自于Activity的调用:
|-- 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, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window, ActivityConfigCallback activityConfigCallback) { attachBaseContext(context); mFragments.attachHost(null /*parent*/); //创建PhoneWindow mWindow = new PhoneWindow(this, window, activityConfigCallback); ... mCurrentConfig = config; }
|-- frameworks/base/core/java/android/app/Instrumentation.java
/** * Perform instantiation of the process's {@link Activity} object. The * default implementation provides the normal system behavior. * * @param cl The ClassLoader with which to instantiate the object. * @param className The name of the class implementing the Activity * object. * @param intent The Intent object that specified the activity class being * instantiated. * * @return The newly instantiated Activity object. */ public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return (Activity)cl.loadClass(className).newInstance(); }
|-- frameworks/base/core/java/android/view/WindowManagerImpl.java
@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); }
|-- frameworks/base/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ... // do this last because it fires off messages to start doing things try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. if (index >= 0) { removeViewLocked(index, true); } throw e; } } }
|-- frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; ... } } private void performTraversals() { // cache mView since it is used so much below... ... if (mFirst) { mFullRedrawNeeded = true; mLayoutRequested = true; final Configuration config = mContext.getResources().getConfiguration(); i