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

来源:互联网 发布:js获取img标签的src 编辑:程序博客网 时间:2024/03/29 15:17

非UI线程不能更新View,相信对每个Android开发者来说都不陌生,那大家有没有想过为什么非UI线程就不能更新View呢?

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

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

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类里:

 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类里:

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()

 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()方法:

@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();    }}

7. 最后附上整个流程图

方法调用流程图

0 0
原创粉丝点击