【Android】不可不知的开发技巧之View.Post()

2017-01-14 10:45:33来源:http://www.jianshu.com/p/b1d5e31e2011作者:原来是控控人点击

稍微有点经验的安卓开发人员应该都知道View类的post和postDelayed方法。我们知道调用这个方法可以保证在UI线程中进行需要的操作,方便得进行异步通信。以下是官方文档对该方法的注释及源码。(postDelayed类似,不再赘述)


Causes the Runnable to be added to the message queue.The runnable will be run on the user interface thread.


public boolean post(Runnable action) {    
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().post(action);
return true;
}

从本质上说,它还是依赖于以Handle、Looper、MessageQueue、Message为基础的异步消息处理机制。相比于自己创建Handler进行处理更加便捷,但是它的作用并不仅限于此,比如典型的在onCreate方法中获取某个view的宽高。
下面我举一个自己近期碰到的例子来说明,使用过SwipeRefreshLayout的同学,应该曾经被这个问题困扰过,假如你想实现知乎那样打开页面后自动刷新的效果。你在onCreate中直接调用SwipeRefreshLayout.setRefreshing(true),但是你很快发现这并没有什么卵用。于是你求助于Google,终于在stackoverflow找到了答案:


mSwipeRefreshLayout.post(new Runnable() {
@Override public void run() {
mSwipeRefreshLayout.setRefreshing(true);
}
});

但是stackoverflow这些答案里却没有说为什么,关于这点我简单解释一下,观察setRefreshing方法源码我们知道该方法调用的变量中有一个mOriginalOffsetTop,但是在onCreate/onCreateView时,该变量没有取得正确的值,导致无法实现刷新。这点和获取某个view的宽高是类似的,都是因为该View还是成功初始化。那为什么调用post方法就能起作用呢?
我们知道Android的通信有两大机制,其一是以Binber为基础的IPC机制,另一个就是上面说过的消息机制。Android是以java语言为基础的,但是日常开发中我们并没有见过java的main方法,关于这点我们先看AndroidDeveloper官网上一段话


Unlike other programming paradigms in which apps are launched with a main()method, the Android system initiates code in an Activity
instance by invoking specific callback methods that correspond to specific stages of its lifecycle. There is a sequence of callback methods that start up an activity and a sequence of callback methods that tear down an activity.


在启动安卓应用时,会调用ActivityThread中的main方法


  public static void main(String[] args) {
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Security.addProvider(new AndroidKeyStoreProvider());
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}

可以看到在这个方法中创建了一个主线程的Looper和系统级的Handler。现在我们再回去看post方法


public boolean post(Runnable action) {    
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().post(action);
return true;
}

这里先做了一个判断,如果attachInfo不为空,则调用attachInfo中的Handler来处理任务,否则就将该任务添加到ViewRootImpl的RunQueue中。mAttachInfo是View类的一个全局变量,对于AttachInfo这个类,官方解释是


A set of information given to a view when it is attached to its parent window.


这里先讲解一个知识点,Android中所有的视图都是通过Window来呈现的,不管是Activity、Dialog还是Toast和Snackbar,他们的视图其实都是附着于Window,所以管理View的最终是Wondow。查看源码,我们发现attachinfo的赋值只有一处:dispatchAttachedToWindow方法。也就是说,View在和Window连接的时候,得到了一系列的信息,其中就包括对系统级的Handler的引用。所以调用view的post方法,其实就是发送了一个任务到MessageQueue的队尾。前面也说了,由于Android的消息驱动机制,View的layout,draw等过程都有涉及。因此不仅是View的初始化,你对View做调整可见性,背景图等操作最后都有涉及到异步消息机制,该方法返回了,但该方法却不一定已经执行成功 大家可以到View类的源码中查看这几个方法。
下面我再举一个例子,加入你有一个需求,需要在某个页面中加入一个点击事件:从AppBar下滑出一个控件,并且在该控件出现的同时依然可以电机AppBar上的MenuItem。因此不能使用popupWindow,只能将该空间放置于布局内,同时为了不阻碍其他控件的交互,需要将该view的可见性设置为gone。于是你写下下面的代码


private void show(){
if(myView.getVisibility()==View.GONE){
myView.setVisibility(View.VISIBLE);
}
//对view执行动画
}

但运行后却发现view是一瞬间显现的,并没有执行动画,我想你也猜到了,问题在于动画开始时该View还未成功设置为可见,导致动画失效。
所以我们可以这么写:


private void show(){
if(myView.getVisibility()==View.GONE){
myView.setVisibility(View.VISIBLE);
}
myView.post(new Runnable() {
@Override
public void run() {
//对view执行动画
}
});
}

这样就相当于保证了动画是在设置可见性操作成功后执行的。
最后的结论,调用View.post()可以保证指定方法的执行是在之前调用的方法处理完成之后的 而不仅仅是方法调用已经返回。
另外提醒一下,在onCreate中mAttachInfo其实是空值,post方法会走向另一个分支,限于篇幅,这里不做展开,大家可以去做个实验验证下。




最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台