android中view刷新界面,笔记:android子线程刷新UI界面

最近在写一些demo时写了下面这样一段代码

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

final ImageView iv = (ImageView) findViewById(R.id.iv);

new Thread(new Runnable() {

@Override

public void run() {

iv.setImageResource(R.mipmap.ic_launcher);

}

}).start();

}

按照我以前的理解,在子线程中更新UI,应该会报错,程序会crash的啊,为什么这样就没有问题呢?

于是结合一些网上资料还有对源码的跟踪学习,终于知道原因了,现在以ImageView为例

public void setImageResource(@DrawableRes int resId) {

...

invalidate();

}

void invalidate(boolean invalidateCache) {

invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop,invalidateCache, true);

}

void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,

boolean fullInvalidate) {

if (mGhostView != null) {

...

if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)

|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)

|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED

|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {

...

// Propagate the damage rectangle to the parent view.

final AttachInfo ai = mAttachInfo;

final ViewParent p = mParent;

if (p != null && ai != null && l < r && t < b) {

final Rect damage = ai.mTmpInvalRect;

damage.set(l, t, r, b);

p.invalidateChild(this, damage);

}

...

}

}

主要是这段代码

final ViewParent p = mParent;

if (p != null && ai != null && l < r && t < b) {

final Rect damage = ai.mTmpInvalRect;

damage.set(l, t, r, b);

p.invalidateChild(this, damage);

}

我们再看invalidateChild方法,这个方法的实现在ViewRootImpl类中

public void invalidateChild(View child, Rect dirty) {

invalidateChildInParent(null, dirty);

}

@Override

public ViewParent invalidateChildInParent(int[] location, Rect dirty) {

checkThread();

...

return null;

}

void checkThread() {

if (mThread != Thread.currentThread()) {

throw new CalledFromWrongThreadException(

"Only the original thread that created a view hierarchy can touch its views.");

}

}

就是这个checkThread抛出的异常,那么这个mThread又是哪来的呢,原来在ViewRootImpl的构造方法中有这样一句

mThread = Thread.currentThread();

那么这个ViewRootImpl又是什么时候创建的呢?经过源码查看,原来是在ActivityThread的handleResumeActivity方法中创建的,在handleResumeActivity中有这样一段代码

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;

if (r.mPreserveWindow) {

a.mWindowAdded = true;

r.mPreserveWindow = false;

ViewRootImpl impl = decor.getViewRootImpl();

if (impl != null) {

impl.notifyChildRebuilt();

}

}

if (a.mVisibleFromClient && !a.mWindowAdded) {

a.mWindowAdded = true;

wm.addView(decor, l);

}

实际上就是在这个addView方法中,那这个addView的具体实现是在哪呢,一步步找吧,首先是这个wm对象,这个vm对象是通过Activity的getWindowManager方法获取到的,代码如下

public WindowManager getWindowManager() {

return mWindowManager;

}

继续看这个mWindowManager对象是怎么来的,最终找到在attach里面有这样一段

mWindow = new PhoneWindow(this, window);

mWindow.setWindowControllerCallback(this);

mWindow.setCallback(this);

mWindow.setOnWindowDismissedCallback(this);

mWindow.getLayoutInflater().setPrivateFactory(this);

if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {

mWindow.setSoftInputMode(info.softInputMode);

}

if (info.uiOptions != 0) {

mWindow.setUiOptions(info.uiOptions);

}

...

mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),

mToken, mComponent.flattenToString(),

(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);

if (mParent != null) {

mWindow.setContainer(mParent.getWindow());

}

mWindowManager = mWindow.getWindowManager();

应该是从PhoneWindow这个对象中获取的,继续看PhoneWindow源码,好吧,PhoneWindow中没getWindowManager这个方法,是它父类Window的,那就继续到Window中寻找

public WindowManager getWindowManager() {

return mWindowManager;

}

继续看这个mWindowManager是什么时候创建的,记得上面PhoneWindow创建成功后调用过这样一个方法吗?

mWindow.setWindowManager

就是在这个方法中创建的

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,

boolean hardwareAccelerated) {

mAppToken = appToken;

mAppName = appName;

mHardwareAccelerated = hardwareAccelerated|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);

if (wm == null) {

wm =(WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);

}

mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

}

那么就是到WindowManagerImpl类中找addView方法了

public void addView(View view, ViewGroup.LayoutParams params) {

mGlobal.addView(view, params, mDisplay, mParentWindow);

}

继续看这个mGlobal对象

public void addView(View view, ViewGroup.LayoutParams params,

Display display, Window parentWindow) {

...

ViewRootImpl root;

View panelParentView = null;

synchronized (mLock) {

...

root = new ViewRootImpl(view.getContext(), display);

view.setLayoutParams(wparams);

mViews.add(view);

mRoots.add(root);

mParams.add(wparams);

}

}

终于找到了,但是这些都是onResume执行完后才会生成,所以如果在此生命周期后子线程中刷新UI是会报错的,除非自己通过WindowManager的addView方法创建一个ViewRootImpl对象,修改mThread对象的值,使其最终checkThread时能通过。

但是我的demo是在onCreate中,按照道理说mThread对象应该是空啊,怎么通过的呢?其实要看这段代码

final ViewParent p = mParent;

if (p != null && ai != null && l < r && t < b) {

final Rect damage = ai.mTmpInvalRect;

damage.set(l, t, r, b);

p.invalidateChild(this, damage);

}

在onCreate中,这时的p对象是空,所以是不会执行的,那界面又是怎么刷新的呢?我们再看ViewRootImpl的构造方法,其中有这样一句

loadSystemProperties();

看它的实现

public void loadSystemProperties() {

mHandler.post(new Runnable() {

@Override

public void run() {

// Profiling

mProfileRendering = SystemProperties.getBoolean(PROPERTY_PROFILE_RENDERING, false);

profileRendering(mAttachInfo.mHasWindowFocus);

// Hardware rendering

if (mAttachInfo.mHardwareRenderer != null) {

if(mAttachInfo.mHardwareRenderer.loadSystemProperties()) {

invalidate();

}

}

// Layout debugging

boolean layout = SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false);

if (layout != mAttachInfo.mDebugLayout) {

mAttachInfo.mDebugLayout = layout;

if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) {

mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200);

}

}

}

});

}

void invalidate() {

mDirty.set(0, 0, mWidth, mHeight);

if (!mWillDrawSoon) {

scheduleTraversals();

}

}

void scheduleTraversals() {

if (!mTraversalScheduled) {

mTraversalScheduled = true;

mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

mChoreographer.postCallback(

Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

if (!mUnbufferedInputDispatch) {

scheduleConsumeBatchedInput();

}

notifyRendererOfFramePending();

pokeDrawLockIfNeeded();

}

}

所以是在ViewRootImpl创建成功后又主动刷新了下界面,但这时是不会检查thread情况的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值