美文网首页
Android 冷知识

Android 冷知识

作者: 阿木Robert | 来源:发表于2022-01-05 14:10 被阅读0次

1.binder oneway

oneway:调用方(Client)非阻塞(non-block)
非 oneway:调用方(Client)阻塞(休眠)

2.Handler之IdleHandler

使用和原理:

Looper.myQueue().addIdleHandler(new IdleHandler(){ 
  @Override
  public boolean queueIdle() { 
    // TODO Auto-generated method stub
    //你想做的任何事情
    //
    doSomeThing()
    //需要注意,当queueIdle返回true,将会在每次空闲的时候都会调用 queueIdle,
    //反之要是返回false,则只调用一次。
    return false; 
  }
});

目前可以想到的场景:
1.Activity启动优化:onCreate,onStart,onResume中耗时较短但非必要的代码可以放到IdleHandler中执行,减少启动时间
2.想要一个View绘制完成之后添加其他依赖于这个ViewView,当然这个View#post()也能实现,区别就是前者会在消息队列空闲时执行。
3.发生一个返回trueIdleHandler,在里面让某个View不停闪烁,这样当用户发呆时就可以诱导用户点击这个View,这也是种很酷的操作。
4.一些第三方库中使用,比如LeakCanary,Glide中使用到,做内存泄漏检测。

3文件锁

FileLock 用来表示文件区域锁定标记,是锁操作的辅助类,可以通过 FileChannel.lock() 或者 FileChannel.tryLock() 等方法获取文件锁,一旦获取锁则持续有效,直到被 release() 或 JVM 退出,可以通过 FileLock.isValid() 方法来检测文件锁的有效性。
文件锁要么是共享锁,要么是独占锁。可以通过 FileLock.isShared() 方法来检测锁的模式。
共享锁指的是允许多个并发进行文件的读取操作,独占锁指的是只允许一个进行文件的读/写操作。

FileChannel channel = FileChannel.open(path);
//阻塞直至可获得锁。
FileLock lock = channel.lock();
//立即返回,要么返回锁,要么在锁不可获得的情况下返回null。
FileLock lock = channel.tryLock();

//如果shared为false,则锁定文件的目的是独占锁。
//如果shared为true,则是一个共享锁,允许多个进程从文件中读入,并阻止任何进程获得独占的锁。
FileLock.isShared();

//如果锁定了文件的尾部,而这个文件的长度随后增长超过了锁定的部分,那么增长出来的额外区域是未锁定的。
//想要锁定所有的字节可以使用 Long.MAX_VALUE 来表示 size。
FileLock lock = channel.lock(start, size, shared);
FileLock lock = channel.tryLock(start, size, shared);
//锁释放
lock.release();
lock = null;

应用:App保活
原理如下:
1.利用Linux文件锁的原理,使用2个进程互相监听各自的文件锁,来感知彼此的死亡。
2.通过 fork 产生子进程,fork 的进程同属一个进程组,一个被杀之后会触发另外一个进程被杀,从而被文件锁感知。

具体来说,创建 2 个进程 p1, p2,这两个进程通过文件锁互相关联,一个被杀之后拉起另外一个;同时 p1 经过 2 次 fork 产生孤儿进程 c1,p2 经过 2 次 fork 产生孤儿进程 c2,c1 和 c2 之间建立文件锁关联。这样假设 p1 被杀,那么 p2 会立马感知到,然后 p1 和 c1 同属一个进程组,p1 被杀会触发 c1 被杀,c1 死后 c2 立马感受到从而拉起 p1,因此这四个进程三三之间形成了铁三角,从而保证了存活率。

拉起进程方案:
过 native code 给 ams 发送 binder 调用;
当然,如果再底层一点,我们甚至可以通过 ioctl 直接给 binder 驱动发送数据进而完成调用。

4.日志库核心

1.存储,使用mmap,优化io. java提供了map的方法,但是没有unmap,可以反射调用。
MappedByteBuffer map unmap

rafi = new RandomAccessFile(cacheFile, "rw");
            rafo = new RandomAccessFile(logFile, "rw");

            fci = rafi.getChannel();
            fco = rafo.getChannel();

            long cacheSize = fci.size();
            long logSize = fco.size();

            MappedByteBuffer mbbi = fci.map(FileChannel.MapMode.READ_WRITE, 0, cacheSize);
            MappedByteBuffer mbbo = fco.map(FileChannel.MapMode.READ_WRITE, logSize, cacheSize);
            for (int i = 0; i < cacheSize; i++) {
                mbbo.put(mbbi.get(i));
            }
            //解除内存映射
            unmap(mbbi);
            unmap(mbbo);

2.加解密

5.IO

1.NIO
多线程阻塞式 I/O 会增加系统开销

非阻塞的 NIO 将 I/O 以事件的方式通知,减少线程切换的开销.
应用程序的实现会变得更复杂,有的时候异步改造并不容易.

NIO 的最大作用不是减少读取文件的耗时,而是最大化提升应用整体的 CPU 利用率。在 CPU 繁忙的时候,我们可以将线程等待磁盘 I/O 的时间来做部分 CPU 操作
读写分离,Buffer 的大小一般推荐使用 4KB 以上

2.Okio:
http://www.javashuo.com/article/p-hlddrbjq-cd.html

3.读写分离+Buffer IO+阻塞队列

6.网络优化

DNS优化核心需要解决的问题有两点:

【1】由于DNS劫持或故障造成的服务不可用,进而影响用户体验,影响公司的收入。

【2】由于DNS调度不准确导致的性能退化,进而影响用户体验。


解决方案:HttpDns



要点:
1.IPv4/IPv6协议栈探测:使用UDP探测
2.使用客户端重试3次机制;
3.使用缓存;

TCP

  1. 提供面向连接的,可靠的字节流服务
  2. tcp的可靠性:
    分块传送:数据被分割成最合适的数据块(UDP的数据报长度不变)
    等待确认:通过定时器等待接收端发送确认请求,收不到确认则重发
    确认回复:收到确认后发送确认回复(不是立即发送,通常推迟几分之一秒)
    数据校验:保持首部和数据的校验和,检测数据传输过程有无变化
    乱序排序:接收端能重排序数据,以正确的顺序交给应用端
    重复丢弃:接收端能丢弃重复的数据包
    滑动窗口:两端有固定大小的缓冲区(滑动窗口),防止速度不匹配丢数据;
    3.三次握手


    image

    1)客户端发送SYN,表明要向服务器建立连接。同时带上序列号ISN
    2)服务器返回ACK(序号为客户端序列号+1)作为确认。同时发送SYN作为应答(SYN的序列号为服务端唯一的序号)
    3)客户端发送ACK确认收到回复(序列号为服务端序列号+1);
    why?
    4.四次挥手


    image
    tcp连接是全双工的,数据在两个方向上能同时传递。要确保双方,同时能发数据和收数据
    第一次握手:证明了发送方能发数据
    第二次握手:ack确保了接收方能收数据,syn确保了接收方能发数据
    第三次握手:确保了发送方能收数据
    四次握手浪费,两次握手不能保证“双方同时具备收发功能”.

ime_wait状态
也称为2MSL等待状态,报文段最大生存时间。常用值为30s,1min,2min。linux一般为30s。
主动关闭的一方发送最后一个ack所处的状态
这个状态必须维持2MSL等待时间.
目的:预留足够的时间给接收端收ack。同时保证,这个连接不会和后续的连接乱套
1)保证客户端或关闭方发送的最后一个ACK报文能够到达服务器或接受方,因为这个ACK报文可能丢失。
服务器的角度看来,S发送了FIN+ACK报文请求断开,C没有回应,应该是S发送的请求断开报文C没有收到,于是S又会重新发送一次,而C就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。如果客户端收到服务端的FIN+ACK报文后,发送一个ACK给服务端之后就“自私”地立马进入CLOSED状态,可能会导致服务端无法确认收到最后的ACK指令,无法进入CLOSED状态.
2)防止失效请求。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。

5.重要特性

确认应答机制(ACK机制):

每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你要从哪里开始发.

超时重传机制

主机A发送数据给B之后, 可能因为网络拥堵等原因, 数据无法到达主机B
如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发
但是主机A没收到确认应答也可能是ACK丢失了.这种情况下, 主机B会收到很多重复数据.
这时候利用前面提到的序列号, 就可以做到去重.
超时时间如何确定?
动态计算:以500ms为一个单位进行控制, 每次判定超时重发的超时时间都是500ms的整数倍.
如果重发一次之后, 仍然得不到应答, 等待 2500ms 后再进行重传. 如果仍然得不到应答, 等待 4500ms 进行重传.
依次类推, 以指数形式递增. 累计到一定的重传次数, TCP认为网络异常或者对端主机出现异常, 强制关闭连接.

滑动窗口

如果出现了丢包, 那么该如何进行重传呢?
此时分两种情况讨论:
1, 数据包已经收到, 但确认应答ACK丢了.这种情况下, 部分ACK丢失并无大碍, 因为还可以通过后续的ACK来确认对方已经收到了哪些数据包.

  1. 数据包丢失:
    当某一段报文丢失之后, 发送端会一直收到 1001 这样的ACK, 就像是在提醒发送端 “我想要的是 1001”
    如果发送端主机连续三次收到了同样一个 “1001” 这样的应答, 就会将对应的数据 1001 - 2000 重新发送
    这个时候接收端收到了 1001 之后, 再次返回的ACK就是7001了
    因为2001 - 7000接收端其实之前就已经收到了, 被放到了接收端操作系统内核的接收缓冲区中.
    这种机制被称为 “高速重发控制” ( 也叫 “快重传” )

流量控制

接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 “窗口大小” 字段,
通过ACK通知发送端;
窗口大小越大, 说明网络的吞吐量越高;
接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端;
发送端接受到这个窗口大小的通知之后, 就会减慢自己的发送速度;
如果接收端缓冲区满了, 就会将窗口置为0;
这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 让接收端把窗口大小再告诉发送端.

拥塞控制:是TCP协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案

TCP引入 慢启动 机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态以后, 再决定按照多大的速度传输数据.
拥塞窗口:
发送开始的时候, 定义拥塞窗口大小为1;
每次收到一个ACK应答, 拥塞窗口加1;
每次发送数据包的时候, 将拥塞窗口和接收端主机反馈的窗口大小做比较, 取较小的值作为实际发送的窗口
慢启动的阈值, 当拥塞窗口的大小超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长.

延迟应答

如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小.
假设接收端缓冲区为1M. 一次收到了500K的数据;
如果立刻应答, 返回的窗口大小就是500K;
但实际上可能处理端处理的速度很快, 10ms之内就把500K数据从缓冲区消费掉了; 在这种情况下, 接收端处理还远没有达到自己的极限, 即使窗口再放大一些, 也能处理过来;

数量限制: 每隔N个包就应答一次
时间限制: 超过最大延迟时间就应答一次
一般 N 取2, 最大延迟时间取200ms

捎带应答

ACK就可以搭顺风车, 和服务器回应的 “Fine, thank you” 一起发送给客户端。

面向字节流

创建一个TCP的socket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区;
调用write时, 数据会先写入发送缓冲区中;
如果发送的字节数太大, 会被拆分成多个TCP的数据包发出;
如果发送的字节数太小, 就会先在缓冲区里等待, 等到缓冲区大小差不多了, 或者到了其他合适的时机再发送出去;
接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区;
然后应用程序可以调用read从接收缓冲区拿数据;
另一方面, TCP的一个连接, 既有发送缓冲区, 也有接收缓冲区,
那么对于这一个连接, 既可以读数据, 也可以写数据, 这个概念叫做 全双工

粘包问题

站在应用层的角度, 应用层的数据包,是一串连续的字节数据,没有明确的边界。

客户端心跳包使用

方案一
最简单的策略当然是客户端定时n秒发送心跳包,服务端收到心跳包后,回复客户端的心跳,如果客户端连续m秒没有收到心跳包,则主动断开连接,然后重连,将正常的业务请求暂时不发送的该台服务器上。

方案二
这样传送一些无效的数据包有点多,可以做些优化。因为心跳就是一种探测请求,业务上的正常请求除了做业务处理外,还可以用作探测的功能,比如此时有请求需要发送到服务端,这个请求就可以当作是一次心跳,服务端收到请求,处理后回复,只要服务端有回复,就表明链路还是通的,如果客户端请求比较空闲的时候,服务端一直没有数据回复,就使用心跳进行探测,这样就有效利用了正常的请求来作为心跳的功能,减少无效的数据传输。

6.滑动窗口
TCP滑动窗口技术通过动态改变窗口大小来调节两台主机间数据传输。每个TCP/IP主机支持全双工数据传输,因此TCP有两个滑动窗口:一个用于接收数据,另一个用于发送数据。

7.为什么用socket而不是binder fork进程

fork不允许存在多线程。而非常巧的是Binder通讯偏偏就是多线程,所以干脆父进程(Zgote)这个时候就不使用binder线程

作者:kinnylee
链接:https://juejin.cn/post/6844903685563105293
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

————————————————
版权声明:本文为CSDN博主「朱小厮」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013256816/article/details/84001583

————————————————
版权声明:本文为CSDN博主「rugu-sco」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sinat_36629696/article/details/80740678

相关文章

  • Android 冷知识

    1.binder oneway oneway:调用方(Client)非阻塞(non-block)非 oneway:...

  • Android面试知识整理-java篇

    Android面试知识整理-android基础知识 Android面试知识整理-性能优化 Android面试知识整...

  • Android的一些冷知识

    这里介绍一些Android的一些冷知识。 App进程是从哪里开始启动的 ActivityManagerServic...

  • 冷知识

    刚刚看到了一篇文章,简要的提取了一下信息,在这里分享了一下,可能有的是假的不必当真 1,约三分之一的人类都是寄生兽...

  • 冷知识

    “把手放在盐水中,半小时后会起皱,6小时后皮肤开始剥落,22小时后指甲会脱离……” “家猫从9楼以上跌落,死亡率1...

  • 冷知识

    1、在荷兰当乞丐,必须事先向政府申请,有关当局经过调查,证明有行乞的必要,才发给执照。不过这名乞丐的收入所得是需要...

  • 冷知识

    贵州的天文射电望远镜,是世界最大的天文射电望远镜,如果用来盛米饭的话,可以保证世界上每一个人都能吃到两碗。 在20...

  • 冷知识

    1.没有一张纸可对折超过9次 2.根据统计每年骡子所杀的人比飞机失事所死的人还多 3.人睡觉比坐着看电视所消耗的卡...

  • 冷知识

    1、蓝宝石不一定是蓝色的 2、刚刚被蚊子咬完时、涂上肥皂就不会痒了; 3、19世纪的欧洲男同性恋会戴着一朵绿色康乃...

  • 冷知识

    1.近些年来,日本每100万人中博士的数量发生了明显的下滑,高质量论文数量也在减少,很多人担忧日本的科研实力将会被...

网友评论

      本文标题:Android 冷知识

      本文链接:https://www.haomeiwen.com/subject/jiwbeltx.html