每日一题(Android,Java,Kotlin)

每日一题,书接上文每日一题(Android相关)(答案次日更新)

2018.09.13

Q:在Java中,请说说finally与return的执行顺序?

A:

  • 首先,finally是不一定被执行的,因为当你的程序没有进入try或者在try中直接强行终止(执行System.out.exit(0))时,finally是不会执行的;
  • 然后当finally中有return时,此时函数的返回值就是finally中的return,这时无论try或者catch中的return都是无效的,而且函数的return也是无效的(finally中有return的话函数如果还有return编译器会报错)
  • 然后当finally中没有return语句,finally中的语句是可以改变函数返回值的,这里涉及到一个值传递和引用传递的问题了,详细可以看看下面这两个函数:
public class Test {

    public static void main(String[] args) {
        System.out.println(test1());
        System.out.println(test2());
    }

    public static int test1() {
        int a = 10;
        try {
            a += 5;
            return a;
        } catch (Exception e) {
            a += 10;
            return a;
        } finally {
            a += 20;
        }
    }

    public static A test2() {
        A aClass = new A(10);
        try {
            aClass.a += 5;
            return aClass;
        } catch (Exception e) {
            aClass.a++;
            return aClass;
        } finally {
            aClass.b=10;
            aClass.a += 20;
        }
    }

    private static class A {
        int a;
        int b;

        public A(int a) {
            this.a = a;
        }

        @Override
        public String toString() {
            return "a=" + a + "\nb=" + b;
        }
    }
}


return:
15
a=35
b=10
复制代码

看到这里相信有部分童鞋可能不理解了,为什么会这样呢?在Java中有着值传递,而非引用传递这一说,简单来说,就是基本类型作为参数传递时,是传递值的拷贝,无论你怎么改变这个拷贝,原值是不会改变的,test1中先执行了一个return语句,返回栈中有了一个a,当执行到finally中时,做了a = a +· 20的操作,此时的a已经不是返回栈中的a了,a的引用发生了改变,所以不回传给返回栈中的a了,所以test1返回值是15;而对象作为参数传递时,是把对象在内存中的地址拷贝了一份传给了参数,在test2中,我们把A直接返回了,而在finally中操作的是A对象的属性,所以这样是可以成功的。

总结:finally块的语句在try或catch中的return语句执行之后,在函数return语句之前执行的

2018.09.25

Q:请从源码的角度谈谈Activity,View,Window之间的关系?

A:详解参考

从源码的角度浅谈Activity、Window、View之间的关系

2018.09.26

Q:Android子线程可以更新UI吗?为什么?

A:相信我们大多数人在刚开始学习Android的时候已经知道Android UI操作不是线程安全的,更新UI的操作一定要在主线程中进行,但事实上真的是这样吗?我们不妨来看看下面这段代码:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_test);
	new Thread(() -> {
		textView.setText("你好啊");
	}
}
复制代码

不知道大家有没有试过,这样是可以的,程序不会报错,相信很多人会有疑问了,为什么会这样呢?是不是感觉幼小的心灵受到了欺骗 >_< 别急,我们再来改造一下,请看下面这段代码:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_test);
	new Thread(() -> {
		try {
			Thread.sleep(1000);
			textView.setText("你好啊");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
复制代码

咦~怎么报错了Only the original thread that created a view hierarchy can touch its views.为什么会这样呢?不延迟就不报错,延迟了就报错。下面我们来分析一下,先找到这个错是在哪里报的,这个应该不难找吧ctrl + shift + f全部搜索一下这个报错信息,我们发现是在ViewRootImpl这个类中的checkMain方法中,刚好我们昨天分析从源码的角度浅谈Activity、Window、View之间的关系的时候有介绍过:

handleResumeActivity中调用了addView方法,在这之前,ActivityonCreate方法已经被调用了,而ViewRootImpl的初始化是在WindowManagerGlobal中的addView方法中:

WindowManagerGlobal类:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
	...
	ViewRootImpl root;
	View panelParentView = null;
	
	...
	
	root = new ViewRootImpl(view.getContext(), display);
	view.setLayoutParams(wparams);
	mViews.add(view);
	mRoots.add(root);
	mParams.add(wparams);
	
	// do this last because it fires off messages to start doing things
	try {
		root.setView(view, wparams, panelParentView);
	} catch (RuntimeException e) {
	
	}
	...
}
复制代码

初始化完ViewRootImpl之后,会调用它的setView方法:

ViewRootImpl类:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
	
	...
	requestLayout();
	...
	
}
@Override
public void requestLayout() {
	if (!mHandlingLayoutInLayoutRequest) {
		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.");
	}
}
复制代码

看到这里相信大家应该就明白了,因为onCreate方法在ViewRootImpl初始化之前就已经被调用了;还有一个问题就是,我在下面那个方法中更新UI了,ViewRootImpl中的requestLayout是怎么被调用的呢?因为这里调用了setText改变了UI,这个方法会触发ViewRootImpl中的requestLayout,有疑问的同学可以参考我的另外一篇文章带着问题去看源码——TextView篇

总结:在UI线程中能更新UI吗?答案是能的,只不过我们在日常开发中很少这样干,大多都是请求后台之后然后再去更新UI,这个时候我们就不能在子线程中更新UI了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值