美文网首页
非UI线程不能更新View源码探索

非UI线程不能更新View源码探索

作者: zxcvto | 来源:发表于2016-08-25 16:15 被阅读85次
    非UI线程不能更新View,相信对每个Android开发者来说都不陌生,那大家有没有想过为什么非UI线程就不能更新View呢?

    <p>

    写在前面:此文默认你对Activity、Window、ViewRootImpl、WindowManager、AMS有一定了解

    <p>

    1. 首先 ActivityManagerService通过ApplicationThread 回调ActivityThread里的 handleLunchActivity(),而handleLunchActivity()里的关键方法是performLaunchActivity()方法,在ActivityThread类里:

    <p>

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
            // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
    
            ActivityInfo aInfo = r.activityInfo;
            if (r.packageInfo == null) {
                r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                        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();
    -----------------▶  // 通过反射创建activity对象
                activity = mInstrumentation.newActivity(
                        cl, component.getClassName(), r.intent);
                StrictMode.incrementExpectedActivityCount(activity.getClass());
                r.intent.setExtrasClassLoader(cl);
                r.intent.prepareToEnterProcess();
                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对象
                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的context对象,并且绑定context和activity。
                    Context appContext = createBaseContextForActivity(r, activity);
                    CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                    Configuration config = new Configuration(mCompatConfiguration);
                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                            + r.activityInfo.name + " with config " + config);
    -----------------▶// 创建根布局,window对象(PhoneWindow),关联WindowManager,设置初始化activity的成员变量
                    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);
    
                    if (customIntent != null) {
                        activity.mIntent = customIntent;
                    }
                    r.lastNonConfigurationInstances = null;
                    activity.mStartedActivity = false;
                    int theme = r.activityInfo.getThemeResource();
                    if (theme != 0) {
                        activity.setTheme(theme);
                    }
    
                    activity.mCalled = false;
                    if (r.isPersistable()) {
    -----------------▶// 回调activity的oncreate()方法
                        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                    } else {
                        mInstrumentation.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.isPersistable()) {
                            if (r.state != null || r.persistentState != null) {
                                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                        r.persistentState);
                            }
                        } else if (r.state != null) {
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                        }
                    }
                    if (!r.activity.mFinished) {
                        activity.mCalled = false;
                        if (r.isPersistable()) {
                            mInstrumentation.callActivityOnPostCreate(activity, r.state,
                                    r.persistentState);
                        } else {
                            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;
    -----------------▶ // ActivityThread的一个成员变量,保存相关的activity
                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;
        }
    
    2. 经过这个过程我们的Activity就创建成功了,下一步ActivityManagerService通过ApplicationThread 回调ActivityThread里的 handleResumeActivity(),这个方法有点长,只标注我们最关心的代码,在ActivityThread类里:

    <p>

     final void handleResumeActivity(IBinder token,
                boolean clearHide, boolean isForward, boolean reallyResume) {
            // If we are getting ready to gc after going to the background, well
            // we are back active so skip it.
            unscheduleGcIdler();
            mSomeActivitiesChanged = true;
    
            // TODO Push resumeArgs into the activity for consideration
    -----------------▶// performResumeActivity()方法里会回调Activity的onResume(),所以其实可以看到
                                   // 当Activity的onResume()方法被执行之后,view任然是不可见的
            ActivityClientRecord 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) {
                    }
                }
    -----------------▶// 默认ActivityClientRecord的window是为null
                if (r.window == null && !a.mFinished && willBeVisible) {
    -----------------▶// 这里activity的window对象和windowManager对象是在 performLaunchActivity()
                      //里的activity.attach()方法里初始化的  (不清楚回看1)                                             
                    r.window = r.activity.getWindow();
                    View decor = r.window.getDecorView();
                    decor.setVisibility(View.INVISIBLE);
                    ViewManager wm = a.getWindowManager();
                    WindowManager.LayoutParams l = r.window.getAttributes();
                    a.mDecor = decor;
                    l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                    l.softInputMode |= forwardBit;
    -----------------▶// 这里的wm其现类是WindowManagerGlobal 
                     // WindowManager 继承 ViewManager,其实现类有WindowManagerGlobal  和
                  //WindowManagerImpl,(WindowManagerImpl里所有方法都是通过调用WindowManagerGlobal  对应的方法,为桥接模式)
                    if (a.mVisibleFromClient) {
                        a.mWindowAdded = true;
    -----------------▶// 该方法下一步重点分析该方法
                        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;
                }
    
                // Get rid of anything left hanging around.
                cleanUpPendingRemoveWindows(r);
    
                // 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) {
                        r.tmpConfig.setTo(r.newConfig);
                        if (r.overrideConfig != null) {
                            r.tmpConfig.updateFrom(r.overrideConfig);
                        }
                        if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
                                + r.activityInfo.name + " with newConfig " + r.tmpConfig);
                        performConfigurationChanged(r.activity, r.tmpConfig);
                        freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
                        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) {
    -----------------▶// 把Activity置为可见状态
                        r.activity.makeVisible();
                    }
                }
    
                if (!r.onlyLocalRequest) {
                    r.nextIdle = mNewActivities;
                    mNewActivities = r;
                    if (localLOGV) Slog.v(
                        TAG, "Scheduling idle handler for " + r);
                    Looper.myQueue().addIdleHandler(new Idler());
                }
                r.onlyLocalRequest = false;
    
                // Tell the activity manager we have resumed.
                if (reallyResume) {
                    try {
                        ActivityManagerNative.getDefault().activityResumed(token);
                    } catch (RemoteException ex) {
                    }
                }
    
            } else {
                // If an exception was thrown when trying to resume, then
                // just end this activity.
                try {
                    ActivityManagerNative.getDefault()
                        .finishActivity(token, Activity.RESULT_CANCELED, null, false);
                } catch (RemoteException ex) {
                }
            }
        }
    
    3. 接下来,我们要分析WindowManagerGlobal 里的addView()方法,在WindowManagerGlobal类里:

    <p>

    public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {
            if (view == null) {
                throw new IllegalArgumentException("view must not be null");
            }
            if (display == null) {
                throw new IllegalArgumentException("display must not be null");
            }
            if (!(params instanceof WindowManager.LayoutParams)) {
                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
            }
    
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
            if (parentWindow != null) {
                parentWindow.adjustLayoutParamsForSubWindow(wparams);
            } else {
                // If there's no parent, then hardware acceleration for this view is
                // set from the application's hardware acceleration setting.
                final Context context = view.getContext();
                if (context != null
                        && (context.getApplicationInfo().flags
                                & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                    wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
                }
            }
    
            ViewRootImpl root;
            View panelParentView = null;
    
            synchronized (mLock) {
                // Start watching for system property changes.
                if (mSystemPropertyUpdater == null) {
                    mSystemPropertyUpdater = new Runnable() {
                        @Override public void run() {
                            synchronized (mLock) {
                                for (int i = mRoots.size() - 1; i >= 0; --i) {
                                    mRoots.get(i).loadSystemProperties();
                                }
                            }
                        }
                    };
                    SystemProperties.addChangeCallback(mSystemPropertyUpdater);
                }
    
                int index = findViewLocked(view, false);
                if (index >= 0) {
                    if (mDyingViews.contains(view)) {
                        // Don't wait for MSG_DIE to make it's way through root's queue.
                        mRoots.get(index).doDie();
                    } else {
                        throw new IllegalStateException("View " + view
                                + " has already been added to the window manager.");
                    }
                    // The previous removeView() had not completed executing. Now it has.
                }
    
                // 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.size();
                    for (int i = 0; i < count; i++) {
                        if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                            panelParentView = mViews.get(i);
                        }
                    }
                }
    -----------------▶// 创建ViewRootImpl
                root = new ViewRootImpl(view.getContext(), display);
    
                view.setLayoutParams(wparams);
    -----------------▶//mViews和mRoots、mParams是WindowManagerGlobal 的两个成员变量,
                     // mViews 储存所有Window对应的View;mRoots储存所有Window所对应的ViewRootImpl
                    // mParams 储存所有Window所对应的布局参数
                mViews.add(view);
                mRoots.add(root);
                mParams.add(wparams);
            }
    
            // do this last because it fires off messages to start doing things
            try {
    -----------------▶// 调用ViewRootImpl的setView 方法,在setView ()方法内部,
                               //通过requestLayout()来完成异步刷新请求
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                synchronized (mLock) {
                    final int index = findViewLocked(view, false);
                    if (index >= 0) {
                        removeViewLocked(index, true);
                    }
                }
                throw e;
            }
        }
    
    4. 接下来再分析ViewRootImpl里的setView()方法,这个方法也是巨长,只关注我们关心的地方,可以看到里面调用了requestLayout()

    <p>

     public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            synchronized (this) {
                if (mView == null) {
                    mView = view;
    
                    mAttachInfo.mDisplayState = mDisplay.getState();
                    mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
    
                    mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
                    mFallbackEventHandler.setView(view);
                    mWindowAttributes.copyFrom(attrs);
                    if (mWindowAttributes.packageName == null) {
                        mWindowAttributes.packageName = mBasePackageName;
                    }
                    attrs = mWindowAttributes;
                    // Keep track of the actual window flags supplied by the client.
                    mClientWindowLayoutFlags = attrs.flags;
    
                    setAccessibilityFocus(null, null);
    
                    if (view instanceof RootViewSurfaceTaker) {
                        mSurfaceHolderCallback =
                                ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
                        if (mSurfaceHolderCallback != null) {
                            mSurfaceHolder = new TakenSurfaceHolder();
                            mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
                        }
                    }
    
                    // Compute surface insets required to draw at specified Z value.
                    // TODO: Use real shadow insets for a constant max Z.
                    if (!attrs.hasManualSurfaceInsets) {
                        final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
                        attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
                    }
    
                    CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
                    mTranslator = compatibilityInfo.getTranslator();
    
                    // If the application owns the surface, don't enable hardware acceleration
                    if (mSurfaceHolder == null) {
                        enableHardwareAcceleration(attrs);
                    }
    
                    boolean restore = false;
                    if (mTranslator != null) {
                        mSurface.setCompatibilityTranslator(mTranslator);
                        restore = true;
                        attrs.backup();
                        mTranslator.translateWindowLayout(attrs);
                    }
                    if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);
    
                    if (!compatibilityInfo.supportsScreen()) {
                        attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
                        mLastInCompatMode = true;
                    }
    
                    mSoftInputMode = attrs.softInputMode;
                    mWindowAttributesChanged = true;
                    mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
                    mAttachInfo.mRootView = view;
                    mAttachInfo.mScalingRequired = mTranslator != null;
                    mAttachInfo.mApplicationScale =
                            mTranslator == null ? 1.0f : mTranslator.applicationScale;
                    if (panelParentView != null) {
                        mAttachInfo.mPanelParentWindowToken
                                = panelParentView.getApplicationWindowToken();
                    }
                    mAdded = true;
                    int res; /* = WindowManagerImpl.ADD_OKAY; */
    
                    // Schedule the first layout -before- adding to the window
                    // manager, to make sure we do the relayout before receiving
                    // any other events from the system.
    -----------------▶// !!!!!!!激动,找的就是他(其他代码不用看了)
                    requestLayout();
                    if ((mWindowAttributes.inputFeatures
                            & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                        mInputChannel = new InputChannel();
                    }
                    try {
                        mOrigWindowType = mWindowAttributes.type;
                        mAttachInfo.mRecomputeGlobalAttributes = true;
                        collectViewAttributes();
                        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
                    } catch (RemoteException e) {
                        mAdded = false;
                        mView = null;
                        mAttachInfo.mRootView = null;
                        mInputChannel = null;
                        mFallbackEventHandler.setView(null);
                        unscheduleTraversals();
                        setAccessibilityFocus(null, null);
                        throw new RuntimeException("Adding window failed", e);
                    } finally {
                        if (restore) {
                            attrs.restore();
                        }
                    }
    
                    if (mTranslator != null) {
                        mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
                    }
                    mPendingOverscanInsets.set(0, 0, 0, 0);
                    mPendingContentInsets.set(mAttachInfo.mContentInsets);
                    mPendingStableInsets.set(mAttachInfo.mStableInsets);
                    mPendingVisibleInsets.set(0, 0, 0, 0);
                    if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow);
                    if (res < WindowManagerGlobal.ADD_OKAY) {
                        mAttachInfo.mRootView = null;
                        mAdded = false;
                        mFallbackEventHandler.setView(null);
                        unscheduleTraversals();
                        setAccessibilityFocus(null, null);
                        switch (res) {
                            case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
                            case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
                                throw new WindowManager.BadTokenException(
                                        "Unable to add window -- token " + attrs.token
                                        + " is not valid; is your activity running?");
                            case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
                                throw new WindowManager.BadTokenException(
                                        "Unable to add window -- token " + attrs.token
                                        + " is not for an application");
                            case WindowManagerGlobal.ADD_APP_EXITING:
                                throw new WindowManager.BadTokenException(
                                        "Unable to add window -- app for token " + attrs.token
                                        + " is exiting");
                            case WindowManagerGlobal.ADD_DUPLICATE_ADD:
                                throw new WindowManager.BadTokenException(
                                        "Unable to add window -- window " + mWindow
                                        + " has already been added");
                            case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
                                // Silently ignore -- we would have just removed it
                                // right away, anyway.
                                return;
                            case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
                                throw new WindowManager.BadTokenException(
                                        "Unable to add window " + mWindow +
                                        " -- another window of this type already exists");
                            case WindowManagerGlobal.ADD_PERMISSION_DENIED:
                                throw new WindowManager.BadTokenException(
                                        "Unable to add window " + mWindow +
                                        " -- permission denied for this window type");
                            case WindowManagerGlobal.ADD_INVALID_DISPLAY:
                                throw new WindowManager.InvalidDisplayException(
                                        "Unable to add window " + mWindow +
                                        " -- the specified display can not be found");
                            case WindowManagerGlobal.ADD_INVALID_TYPE:
                                throw new WindowManager.InvalidDisplayException(
                                        "Unable to add window " + mWindow
                                        + " -- the specified window type is not valid");
                        }
                        throw new RuntimeException(
                                "Unable to add window -- unknown error code " + res);
                    }
    
                    if (view instanceof RootViewSurfaceTaker) {
                        mInputQueueCallback =
                            ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
                    }
                    if (mInputChannel != null) {
                        if (mInputQueueCallback != null) {
                            mInputQueue = new InputQueue();
                            mInputQueueCallback.onInputQueueCreated(mInputQueue);
                        }
                        mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                                Looper.myLooper());
                    }
    
                    view.assignParent(this);
                    mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
                    mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;
    
                    if (mAccessibilityManager.isEnabled()) {
                        mAccessibilityInteractionConnectionManager.ensureConnection();
                    }
    
                    if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
                        view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
                    }
    
                    // Set up the input pipeline.
                    CharSequence counterSuffix = attrs.getTitle();
                    mSyntheticInputStage = new SyntheticInputStage();
                    InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
                    InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                            "aq:native-post-ime:" + counterSuffix);
                    InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
                    InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                            "aq:ime:" + counterSuffix);
                    InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
                    InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                            "aq:native-pre-ime:" + counterSuffix);
    
                    mFirstInputStage = nativePreImeStage;
                    mFirstPostImeInputStage = earlyPostImeStage;
                    mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
                }
            }
        }
    
    5. 接下来再分析ViewRootImpl里的requestLayout()方法:

    <p>

    @Override
        public void requestLayout() {
            if (!mHandlingLayoutInLayoutRequest) {
    -----------------▶// checkThread()干的活就是检测当前线程是否为主线程
                checkThread();
                mLayoutRequested = true;
                scheduleTraversals();
            }
        }
    
    void checkThread() {
            if (mThread != Thread.currentThread()) {
                throw new CalledFromWrongThreadException(
                        "Only the original thread that created a view hierarchy can touch its views.");
            }
        }
    
    6. 结论:到此我们就明白了,其实checkThread()这个方法是在ActivtiyonResume()方法执行之后执行的,不记得了就回到第2步看看,那也就是说,其实我们在子线程中也是可以更新ui的,前提是还没执行到onResume方法!有木有!!也就是说,我其实可以在onCreate()方法里开一个子线程去更新ui,事实证明,这确实可行:
    public classMainActivity extends Activity{
        private TextView mTextViewUser;
    
        @Override protected void onCreate(BundlesavedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextViewUser = (TextView) findViewById(R.id.textview_user);
            mBtnLogin.setOnClickListener(this);
            new Thread() {@Override public void run() {
                    mTextViewUser.setText(10);
                }
            }.start();
        }
    }
    

    <p>

    7. 最后附上整个流程图
    方法调用流程图

    相关文章

      网友评论

          本文标题:非UI线程不能更新View源码探索

          本文链接:https://www.haomeiwen.com/subject/mvtasttx.html