-
invalidate()、requestLayout() 区别
★ requestLayout()方法请求重新布局,会调用measure过程和layout过程,但不会调用draw过程,也不会重新绘制任何View包括该调用者本身。
★ invalidate()系列方法请求重绘视图(View树),如果View大小没有发生变化就不会调用measure和layout过程,相反,View的大小发生改变了就会调用measure和layout的过程,并且哪个View请求invalidate()系列方法,就绘制该View。
结论:
requestLayout方法会导致View的onMeasure、onLayout、onDraw方法被调用;invalidate方法则只会导致View的onDraw方法被调用。
在View的requestLayout方法中,首先会设置View的标记位,PFLAG_FORCE_LAYOUT表示当前View要进行重新布局,PFLAG_INVALIDATED表示要进行重新绘制。invalidate方法没有标记PFLAG_FORCE_LAYOUT,所以不会执行测量和布局流程,而只是对需要重绘的View进行重绘,也就是只会调用onDraw方法。
requestLayout方法中会一层层向上调用父布局的requestLayout方法,最终调用的是ViewRootImpl中的requestLayout方法。
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
scheduleTraversals刷新view,会向messageQueue中发出同步屏障,优先去执行view刷新。scheduleTraversals方法最后会调用performTraversals方法开始执行View的三大流程,会分别调用View的measure、layout、draw方法。
参考:
https://www.cnblogs.com/normalandy/p/12408665.html
-
Bitmap 在内存中有多大?
1.占用内存
2.图片大小
占用内存表示图片被加载进来以后占用的内存空间大小,图片大小则是图片在磁盘存储时占用的大小。
获取一个biemap占用多大内存空间的方法如下:
int sizeOf = bitmap.getRowBytes() * bitmap.getHeight();
//getRowBytes 表示一行的字节数,getHeight可以认为总共有多少行。
不同Android版本时的Bitmap占用内存模型:
我们知道Android系统中,一个进程的内存可以简单分为Java内存和native内存两部分,而Bitmap对象占用的内存,有Bitmap对象内存和像素数据内存两部分,在不同的Android系统版本中,其所存放的位置也有变化。
可以看到,最新的Android O之后,谷歌又把像素存放的位置,从java 堆改回到了 native堆。API 11的那次改动,是源于native的内存释放不及时,会导致OOM,因此才将像素数据保存到Java堆,从而保证Bitmap对象释放时,能够同时把像素数据内存也释放掉。
参考:
https://blog.csdn.net/u011494285/article/details/80523775
https://www.jianshu.com/p/3f6f6e4f1c88
-
dp和px换算关系是怎样的
/*
根据手机的分辨率从 dp 单位转为 px(像素)
*/
public static int dip2px(Context context,float dpValue){
final float scale = context.getResource().getDisplayMetrics().density;
return (int)(dpValue * scale + 0.5f);
}
-
protobuffer是什么
protocol buffers是一个灵活的、高效的、自动化的用于对结构化数据进行序列化的协议,与json、xml相比,protocol buffers序列化后的码流更小、速度更快、操作更简单。
JSON因为有一定的格式,并且是以字符存在的,在数据量上还有可以压缩的空间。protobuf是二进制的、结构化的,所以比json的数据量更小,也更对象化。
对于HTTP协议的交互,用的比较多的是json,而 tcp协议,用的比较多的是protobuffer。
-
为什么view.post()能保证获取到view的宽高?
通过View.post()添加的任务是在View绘制任务里 - 开始绘制阶段时添加到消息队列的尾部的。即View.post() 添加的任务能够保证在所有 View绘制流程结束之后才被执行,所以 执行View.post() 添加的任务时可以正确获取到 View 的宽高。
-
若只是在代码中创建一个 View并调用它的post(),那么post的任务会不会被执行?
不会。主要原因是:每个View中post() 需执行的任务,必须得添加到窗口视图-执行绘制流程,任务才会被post到消息队列里去等待执行,即依赖于dispatchAttachedToWindow ()。
参考:https://www.jianshu.com/p/e2a8cd384eda
-
Kotlin中函数的可见性修饰符是怎样的
-
谈谈你对 Activity.runOnUiThread 的理解?
runOnUiThread过程源码
// Activity.java
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
// Handler.java
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
首先判断当前是不是 UI线程,如果是就直接执行传入的Runnable接口的run方法。
否则就通过Activity类中的Handler实例的post(Runnable)方法来发送消息,最终调用handler的enqueueMessage方法将任务加入到消息队列中,因为在Activity中的Handler对象运行在主线程(主线程中的Handler对象是在ActivityThread类中的main方法中创建的),故而这里就切换到了主线程中。
Intent传输数据的大小有限制吗?如何解决?
Intent传输数据的大小受Binder的限制,上限是1M。不过这个1M并不是安全的上限,Binder可能在处理别的工作,安全上限是多少这个在不同的机型上也不一样。
传 512K 以下的数据的数据可以正常传递。
传 512K~1024K 的数据有可能会出错,闪退。
传 1M以上的数据会报错:TransactionTooLargeException
考虑到 Intent 还包括要启动的 Activity 等信息,实际可以传的数据略小于 512K
解决办法
减少传输数据量
Intent通过绑定一个Bundle来传输,这个可以超过1M,不过也不能过大
通过内存共享,使用静态变量或者使用EventBus等类似的通信工具
通过文件共享
ACTION_CANCEL到底何时触发?滑出子View范围还能触发onClick事件吗?
1.有四种情况会触发ACTION_CANCEL:
在子View处理事件的过程中,父View对事件拦截
ACTION_DOWN初始化操作(系统可能会由于App切换、ANR等原因丢失了up,cancel事件)
在子View处理事件的过程中被从父View中移除时
子View被设置了PFLAG_CANCEL_NEXT_UP_EVENT标记时(该view暂时detached)
2.在view的onTouchEvent()中:
case MotionEvent.ACTION_MOVE:
// Be lenient about moving outside of buttons
// 判断是否超出view的边界
if (!pointInView(x, y, mTouchSlop)) {
// Outside button
if ((mPrivateFlags & PRESSED) != 0) {
// 这里改变状态为 not PRESSED
// Need to switch from pressed to not pressed
mPrivateFlags &= ~PRESSED;
}
}
break;
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
// 可以看到当move出view范围后,这里走不进去了
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
...
performClick();
...
}
mIgnoreNextUpEvent = false;
break;
结论:滑出view范围后,如果父view没有拦截事件,则会继续受到ACTION_MOVE和ACTION_UP等事件。
一旦滑出view范围,view会被移除PRESSED标记,这个是不可逆的,然后在ACTION_UP中不会执行performClick()等逻辑,即不会触发onClick事件。
参考:https://blog.51cto.com/u_15375308/4996238
Android中的bitmap存在哪里
在Android 3.0(API level 11) ~ Android 7.1(API level 25)中无论是Bitmap对象还是像素点数据(Pixel Data),都统一存储在Dalvik Heap。
然而从Android 8.0(API level 26) 开始,截至到2018年3月的版本,素点数据(Pixel Data)被存储到Native Heap。
- BIO、NIO分别是什么
阻塞(Block) / 非租塞(NonBlock)
阻塞:往往需要等待缓冲区中的数据准备好过后才处理其他的事情,否则一直等待在那里。
非阻塞:当我们的进程访问我们的数据缓冲区的时候,如果数据没有准备好则直接返回,不会等待。如果数据已经准备好,也直接返回。
阻塞和非阻塞关注的是程序在等待结果(消息,返回值)时的状态。
同步(Synchronization) / 异步(Asynchronization)
同步:是应用程序要直接参与IO读写的操作。
异步:所有的IO读写交给操作系统去处理,应用程序只需要等待通知。
同步阻塞I/O(BIO)
同步阻塞I/O,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制来改善。
同步非阻塞I/O(NIO)
同步非阻塞I/O,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器。
NIO的3个核心概念:
- 缓冲区Buffer
在NIO中,所有的数据都是用缓冲区处理。IO是面向流的,NIO是面向缓冲区的。 - 通道Channel
Channel是一个通道,可以通过它读取和写入数据,他就像自来水管一样,网络数据通过Channel读取和写入。
通道和流不同之处在于通道是双向的,流只是在一个方向移动,而且通道可以用于读,写或者同时用于读写。 - 多路复用器Selector
Selector选择器可以监听多个Channel通道感兴趣的事情(read、write、accept(服务端接收)、connect,实现一个线程管理多个Channel,节省线程切换上下文的资源消耗。
参考:https://www.jianshu.com/p/91fe446aeb86