探究onAttachedToWindow和onDetachedFromWindow

QuibblerQuibbler 2022-02-09 约 17 分钟 2391 次阅读

探究onAttachedToWindow和onDetachedFromWindow

ProgressBar,是很多开发者一开始接触安卓开发就使用过的控件,用来显示加载进度。查看ProgressBar源码,在onAttachedToWindow()方法中开启进度条加载动画:

@Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (mIndeterminate) {
            startAnimation();
        }
        if (mRefreshData != null) {
            synchronized (this) {
                final int count = mRefreshData.size();
                for (int i = 0; i < count; i++) {
                    final RefreshData rd = mRefreshData.get(i);
                    doRefreshProgress(rd.id, rd.progress, rd.fromUser, true, rd.animate);
                    rd.recycle();
                }
                mRefreshData.clear();
            }
        }
        mAttached = true;
    }

在与之对应的onDetachedFromWindow()方法中停止动画:

@Override
    protected void onDetachedFromWindow() {
        if (mIndeterminate) {
            stopAnimation();
        }
        if (mRefreshProgressRunnable != null) {
            removeCallbacks(mRefreshProgressRunnable);
            mRefreshIsPosted = false;
        }
        // This should come after stopAnimation(), otherwise an invalidate message remains in the
        // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation
        super.onDetachedFromWindow();
        mAttached = false;
    }

onAttachedToWindow()和onDetachedFromWindow()类似Activity的onCreate()和onDestroy()。作为成对匹配的方法:可以在attach的时候做一些初始化、启动操作;在detach的时候释放资源、暂停操作。

非常有必要了解onAttachedToWindow()和onDetachedFromWindow()这两个方法的来龙去脉。

1、View的onAttachedToWindow、onDetachedFromWindow

在View类中就定义了onAttachedToWindow()和onDetachedFromWindow()这两个方法,它们不是继承来的。子类重写的时候注意需要调用super父类方法。

/**
     * This is called when the view is attached to a window.  At this point it
     * has a Surface and will start drawing.  Note that this function is
     * guaranteed to be called before {@link #onDraw(android.graphics.Canvas)},
     * however it may be called any time before the first onDraw -- including
     * before or after {@link #onMeasure(int, int)}.
     *
     * @see #onDetachedFromWindow()
     */
    @CallSuper
    protected void onAttachedToWindow() {
        if ((mPrivateFlags & PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
            mParent.requestTransparentRegion(this);
        }
        mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
        jumpDrawablesToCurrentState();
        AccessibilityNodeIdManager.getInstance().registerViewWithId(this, getAccessibilityViewId());
        resetSubtreeAccessibilityStateChanged();
        // rebuild, since Outline not maintained while View is detached
        rebuildOutline();
        if (isFocused()) {
            notifyFocusChangeToImeFocusController(true /* hasFocus */);
        }
    }
    
    /**
     * This is called when the view is detached from a window.  At this point it
     * no longer has a surface for drawing.
     *
     * @see #onAttachedToWindow()
     */
    @CallSuper
    protected void onDetachedFromWindow() {
    }

1.1、onAttachedToWindow流程

找到源码的切入点,从Activity的resume开始:当View被添加到ViewRootImpl的时候,会通过dispatchAttachedToWindow(AttachInfo info, int visibility)方法逐级分发View被添加到Window的事件。

源码就不贴上来了,用PlantUML“手写”View的onAttachedToWindow()回调时序图如下:


1.2、onDetachedFromWindow流程

对应的,是在Activity销毁destroy时会将View从ViewRootImpl移除,通过dispatchDetachedFromWindow()完成事件分发给每个View。

源码较多太占篇幅,对应的时序图简化如下:


2、Activity的onAttachedToWindow、onDetachedFromWindow

在Activity中也有一对onAttachedToWindow()和onDetachedFromWindow()方法,是Activity实现Window.Callback接口而定义的空方法:

/**
     * Called when the main window associated with the activity has been
     * attached to the window manager.
     * See {@link View#onAttachedToWindow() View.onAttachedToWindow()}
     * for more information.
     * @see View#onAttachedToWindow
     */
    public void onAttachedToWindow() {
    }
    
    /**
     * Called when the main window associated with the activity has been
     * detached from the window manager.
     * See {@link View#onDetachedFromWindow() View.onDetachedFromWindow()}
     * for more information.
     * @see View#onDetachedFromWindow
     */
    public void onDetachedFromWindow() {
    }

Activity的onAttachedToWindow()比View的onAttachedToWindow()早;Activity的onDetachedFromWindow()比View的onDetachedFromWindow()晚;通过Log可以验证:

2.1、onAttachedToWindow流程

Activity的onAttachedToWindow()从哪里回调的呢?在DecorView的onAttachedToWindow()中回调:

@Override
    protected void onAttachedToWindow() {
        //先回调View的onAttachedToWindow()
        super.onAttachedToWindow();
        
        //Window中的Callback正是Activity,Activity实现了Window.Callback接口
        final Window.Callback cb = mWindow.getCallback();
        
        //再这里回调Activity的onAttachedToWindow()
        if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
            cb.onAttachedToWindow();
        }
        
        ...
    }

在DecorView添加到Window的流程一文中提到相关成员的初始化:

@Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, ...) {
            ...
            //获取Activity中的Window
            r.window = r.activity.getWindow();
            
            //获取DecorView
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            
            //获取WindowManager
            ViewManager wm = a.getWindowManager();
            
            ...
            //将DecorView添加到Window中
            wm.addView(decor, l);
            ....
    }

其中Activity中的Window在attach()创建的时候就初始化为PhoneWindow,并且给PhoneWindow设置的Window.Callback就是当前Activity:

@UnsupportedAppUsage
    final void attach(Context context, ActivityThread aThread...) {
        attachBaseContext(context);
        
        mFragments.attachHost(null /*parent*/);
        
        //创建一个PhoneWindow
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(mWindowControllerCallback);
        
        //给Window设置Callback,就是当前Activity
        mWindow.setCallback(this);
        ...
    }

2.2、onDetachedFromWindow流程

同样的,Activity的onDetachedFromWindow()方法也是借助DecorView回调:

@Override
    protected void onDetachedFromWindow() {
        //ViewGroup分发onDetachedFromWindow()
        super.onDetachedFromWindow();
        
        //拿到当前Window的Activity
        final Window.Callback cb = mWindow.getCallback();
        
        //回调Activity的onDetachedFromWindow()方法
        if (cb != null && mFeatureId < 0) {
            cb.onDetachedFromWindow();
        }
        
        ...
    }

再看一下Log,各周期执行情况和前面所看源码顺序一致:

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w