mypro334 发表于 2023-1-6 13:13

Android面试知识点总结(九)

invalidate()、requestLayout() 区别

结论:


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.javapublic final void runOnUiThread(Runnable action) {    if (Thread.currentThread() != mUiThread) {      mHandler.post(action);    } else {      action.run();    }}// Handler.javapublic final boolean post(@NonNull Runnable r) {   returnsendMessageDelayed(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。
native进程,是由c/c++分配的,/system/bin下面的所有程序运行在native进程中。
dalvik进程,是由java程序的dalvik虚拟机分配的。
- 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
StackOverflowError出现的原因及解决


一般出现这个问题是因为程序里有死循环或递归调用所产生的。
页: [1]
查看完整版本: Android面试知识点总结(九)