平时开发的时候发现了一些小的把戏技巧。整理其中的一些向分享一下
- sdk封装资源
- 非阻塞递归
- 如何获取视图大小
一、sdk封装资源
android的一大难题便是资源文件的绑定。如果我们为了模块化,想要将一个小的功能做成sdk的样式,那么该怎么做呢?
Java普遍的习惯是将功能打包成为一个jar包。其好处很多,解耦合。然而在android开发中,这样子做却有一个大问题。问题便是jar包形式难以提供资源。
Android官方推荐的做法是编写Android Libarary。Android Libarary其实就是一个Android工程,所以可以像普通Android项目一样去引用各种资源。这种方式有它的好处,可包含资源,可自由修改源码。这对于内部工程来说非常合适,可是如果想将功能包装成binary版本,那么还是得依靠jar包形式了。
有没有一种封装方式,能够既提供jar包,又提供资源呢?答案是肯定的。只要在jar包中包含assets文件夹,那么引用jar包的android项目事实上也包含了该资源文件。在编译apk阶段,jar包的资源便被解压进入apk的assets目录了。最终会被拷入私有文件夹data目录。
以腾讯的openapi为例。其打包的jar包中包含了assets目录。Android项目想引用资源非常简单,只消这样一句话即可:
TencentOpenRes.getBigLoginBtn(getAssets())
注:其实jar包中可以通过JarURLConnection来提取资源。不过我觉得此种assets小把戏更符合android编程习惯,很适合用于包含资源的binary sdk情境。
二、非阻塞递归
递归的思维是:在满足退出条件之前,不断重复调用自己。
在Android平台上面,通过使用Handler我们可以实现非阻塞递归。
一个同步版本的递归是这个样子的:
int recursion(int x){if(x==1) return 1;return x*recursion(x-1);}
非阻塞版本的递归则是这个样子的。
int x;int result=1;Handler handler=new Handler();Runnable task=new Runnable() {@Overridepublic void run() {if(x==1) handler.removeCallbacks(task);result=x*x-1;x--;handler.post(task);}};void start(){x=50;//inithandler.post(task);}
看起来,非阻塞的递归用起来很麻烦。其实,只要把task想象成一个时间片,它穿插在UI线程中工作,分时的完成计算任务。例如:UI-Event,View-DRAW,task,UI-Event,task,task。
那么,非阻塞递归在什么情况下使用呢?在配合视图工作时其实很有用。例如,计算滑动重绘视图。当滑动结束时移除重绘回调。
三、安全的获取视图大小
如果你的View设置了WRAP_CONTENT或者MATCH_PARENT。或者是通过weight和Relative布局的,那么View的大小往往需要一轮全局layout才能measure得出。
举个例子,在Activity的onCreate后调用view.getMeasureHeight()也许会得到0。拦截到view的onMeasure似乎是个不错的注意。不过这太麻烦了,用继承做显得小题大做。下面介绍两种方法。
第一种:
view.post(new Runnable() {@Overridepublic void run() {System.out.println(view.getMeasuredHeight());}});
这种方法向该view的执行队列中插入了一段runnable代码。它假设了View的measureHiearachy必定在我们post的Runnable之前。笔者尝试分析一下源码这种做法是否安全,但因为较为复杂,所以目前还没法得出结论。下面介绍的方法相对靠谱些。
第二种:
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {@Overridepublic void onGlobalLayout() {view.getViewTreeObserver().removeGlobalOnLayoutListener(this);System.out.println(view.getMeasuredHeight());}});