面试时容易忽略的细节

2019-02-14  本文已影响28人  affyzh
  1. 热更新原理
    客户端向服务器请求时,在请求头部带上range: bytes=2000070-,表示文件请求从2000070开始。服务端收到请求时,在响应中带上content-range=bytes 2000070-106786027/106786028,同时将return code从200改为206。
    而文件的续写,可以使用io包中的RandomAccessFile
Url url = new Url("http://www.jizhuomi.com/android/down.zip");  
Httpurlconnection httpconnection = (httpurlconnection)url.openconnection();  
//设置user-agent  
httpconnection.setrequestproperty("user-agent","netfox");  
//设置断点续传的开始位置  
httpconnection.setrequestproperty("range","bytes=2000070");  
//获得输入流  
Inputstream input = httpconnection.getinputstream(); 
//从输入流中取出的字节流就是down.zip文件从2000070开始的字节流。

randomaccess osavedfile = new randomaccessfile("local_down.zip","rw");  
long npos = 2000070;  
//定位文件指针到npos位置  
osavedfile.seek(npos);  
byte[] b = new byte[1024];  
int nread;  
//从输入流中读入字节流,然后写到文件中  
while((nread=input.read(b,0,1024)) > 0) {  
  osavedfile.write(b,0,nread);  
} 
  1. ANR
    anr最近三天的trace.txt会保存到/data/system/dropbox目录下。这是由DropBoxManager实现的,该类可以理解为一个持久化的logcat。读源码时发现,anr的logcat是在ActivityManagerService的appNotResponding()输出的,其中用到了一个很实用的工具类Os,里面有个Os.chmod(path,mode)方法可以便利地设置文件权限。而trace.txt的写入必须在20s内完成,ActivityManagerService通过FileObserver实现对文件读写的监听。一旦文件有修改,会触发FileObserver.onEvent()通知监听者。

3.热更新原理
Java运行时的类加载是通过抽象类ClassLoader实现的,而Android中其默认实现是PathClassLoader。后者的父类是BaseDexClassLoaderPathClassLoader只是简单地从文件系统中加载类文件,而BaseDexClassLoader重写了findClass()方法,并在其中委托DexPathList去调用它的findClass()

public Class findClass(String name, List<Throwable> suppressed) {
    for (Element element : dexElements) {
        DexFile dex = element.dexFile;

        if (dex != null) {
            Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }
    }
    if (dexElementsSuppressedExceptions != null) {
        suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
    }
    return null;
}

要实现热更新,就需要我们在运行时去更改PathClassLoader.pathList.dexElements.并将我们的hotpacth dex文件出现在dexElements列表的前面。

  1. 阅读错误报告(bugreport)

  2. 今日头条屏幕适配原理:屏幕宽度(px)/设计稿宽度(dp) = 目标屏幕宽度(px)/目标值(dp)。利用的是动态修改屏幕密度,使得两者屏幕密度相等(density = px / dp)。
    参考:
    Android 目前最稳定和高效的UI适配方案
    Android屏幕适配方案(出自今日头条)

  3. 显示大图,但是不能压缩BitmapRegionDecoder

BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
bitmapRegionDecoder.decodeRegion(rect, options);

7.本地缓存DiskLruCache
主要记住有一个journal.temp文件去保存每次缓存读写状态:DIRTY(写入缓存,但是没有commit),CLEAN(缓存已被commit),REMOVE,READ。同时内部LRU逻辑是通过LikedHashMap来实现,trimToSize()触发回收。

  1. 几个重要的自定义View用法
  1. 两个Activity跳转,必然会执行什么方法?
    一般地,Activity A 跳转到Activity B,A会先调用onPause(),然后B会调用onCreate(),onStart(),onResume()。
    如果此时B已经完全遮挡A,A会调用onStop();否则,如果B是个透明窗口,或者是对话框主题,则B不会调用onPause()。
    如果B已经存在于Activity栈中,则不会调用onCreate()。

  2. 如果同时使用startService 与bindService 方法启动Service,需要终止该Service时,要调用stopService 和unbindService 方法(unbindService 依附于启动它的Context,startServicec 并不依附于启动它的Context。如果先调用unbindService ,这时服务并不会被终止,当调用stopService 后,服务才会被终止;如果先调用stopService ,服务也不会被终止,当调用unbindService 或者之前调用bindService 的Context不存在了(如Activity被finish掉了)服务才会自动停止)

  3. 属性动画原理:属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的过渡则是通过ValueAnimator来负责计算。其中TypeEvaluator决定了初始值过渡到结束值的计算算法(y = sin(x)),TimeInterpolator决定了初始值过渡到结束值的节奏。

  4. SharedPreferences是线程安全的,但不是进程安全的。
    第一次getSharedPreferences()会开启一个单线程池去异步加载。读取磁盘文件,get/set之前有awaitLoadedLocked()同步操作,在磁盘文件加载完成前会卡住调用线程。加载完成之后会从内存中读取。
    apply是内存同步操作,但是会开启一个异步任务放到单线程队列中,更新磁盘缓存。commit同apply类似,但是会等磁盘任务完成才返回,会有boolean结果返回。每次 apply / commit 都会把全部的数据一次性写入磁盘, 所以单个的配置文件不应该过大, 影响整体性能。

  5. 在浏览器中输入一个网址,并得到网页呈现结果。这个过程中经历了什么?
    DNS域名解析->TCP三次握手->TCP建立并发送HTTP请求->服务器响应HTTP请求->浏览器解析HTTP代码,同时获取css,js,图片等资源->浏览器渲染页面并呈现结果

  6. 为什么是三次握手,四次挥手?
    关闭连接时,当Server端收到FIN报文时,很可能数据信息没有传完并不会立即关闭连接,所以只能先回复一个ACK报文(告诉Client端,"你发的FIN报文我收到了")。只有等到Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步挥手。

  7. Webview与js交互总结

类型 调用方式 优点 缺点 使用场景
Android调用js WebView.loadUrl("javascript:jsMethod()") 简洁方便 效率低,获取返回值麻烦 不需要返回值,对性能要求不高
Android调用js WebView.evaluateJavascript() 效率高 只兼容Android4.4以上 Android4.4以上
js调用Android WebView.addJavascriptInterface(new Object(), "objectName") 简洁方便 Android4.2以下存在安全漏洞 Android4.2以上相对简单的互调
js调用Android WebViewClient.shouldOverrideUrlLoading()回调拦截 url 不存在安全漏洞 使用复杂,需要进行协议约定;从Native层往Webview传值比较繁琐 不需要返回值的互调(IOS主要使用该方式)
js调用Android WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息回调拦截 url 不存在安全漏洞 使用复杂,需要进行协议约定 满足大多数情况下的互调

16.进程保活

  1. notify和notifyAll的区别
    首先区分两个概念,锁池和等待池:
    锁池:等待当前锁的拥有者线程释放锁,以便执行该对象的synchronized方法的线程队列。
    等待池:线程调用wait方法释放锁,就会进入等待池。等待池中的线程不会去竞争该对象的锁。
    当拥有锁的线程调用notify时,会从等待池中随机将某一条线程移动到锁池中,去竞争锁的拥有权。而调用notifyAll,所有等待池中的线程都会被移动到锁池中竞争锁。优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

  2. 屏幕适配
    dp(dip,也就是密度无关像素)解决了同一控件数值在不同分辨率中展示相同大小的问题(也就是屏幕像素密度匹配,dpi),但是没法解决屏幕尺寸大小带来的匹配问题(也就是屏幕尺寸匹配)。

Nexus5的总宽度为360dp,我们现在在水平方向上放置两个按钮,一个是150dp左对齐,另外一个是200dp右对齐,那么中间留有10dp间隔;
但假如同样地设置在Nexus S(屏幕宽度是320dp),会发现,两个按钮会重叠,因为320dp<200+150dp

本质上是希望使得布局组件在不同屏幕密度上显示相同的像素效果,对于屏幕尺寸匹配,可以考虑百分比匹配

上一篇下一篇

猜你喜欢

热点阅读