<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Android on Website of SauceWu</title><link>https://saucewu.github.io/tags/android/</link><description>Recent content in Android on Website of SauceWu</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Fri, 10 Apr 2026 10:00:00 +0000</lastBuildDate><atom:link href="https://saucewu.github.io/tags/android/index.xml" rel="self" type="application/rss+xml"/><item><title>Flutter 手势系统与竞技场机制详解</title><link>https://saucewu.github.io/posts/flutter-%E6%89%8B%E5%8A%BF%E7%B3%BB%E7%BB%9F%E4%B8%8E%E7%AB%9E%E6%8A%80%E5%9C%BA%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3/</link><pubDate>Fri, 10 Apr 2026 10:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/flutter-%E6%89%8B%E5%8A%BF%E7%B3%BB%E7%BB%9F%E4%B8%8E%E7%AB%9E%E6%8A%80%E5%9C%BA%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3/</guid><description>&lt;h1 id="flutter-手势系统与竞技场机制详解"&gt;Flutter 手势系统与竞技场机制详解&lt;/h1&gt;
&lt;h2 id="目录"&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;一、Gesture Arena（手势竞技场）运行原理&lt;/li&gt;
&lt;li&gt;二、与 Android 触摸分发机制的核心区别&lt;/li&gt;
&lt;li&gt;三、OneSequenceGestureRecognizer 生命周期详解&lt;/li&gt;
&lt;li&gt;四、MultiTapGestureRecognizer 使用场景与区别&lt;/li&gt;
&lt;li&gt;五、GestureArenaTeam 实际应用场景&lt;/li&gt;
&lt;li&gt;六、总结&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h1 id="一gesture-arena手势竞技场运行原理"&gt;一、Gesture Arena（手势竞技场）运行原理&lt;/h1&gt;
&lt;h2 id="1-两层触摸模型"&gt;1. 两层触摸模型&lt;/h2&gt;
&lt;p&gt;Flutter 的触摸处理分为两层：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;层级&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;th&gt;对应类&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pointer&lt;/td&gt;
&lt;td&gt;原始触摸事件（down / move / up）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PointerEvent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gesture&lt;/td&gt;
&lt;td&gt;语义手势（tap、drag、scale 等）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GestureRecognizer&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Pointer 层不做任何裁决，&lt;strong&gt;所有命中节点都会收到完整的 PointerEvent 序列&lt;/strong&gt;。Gesture Arena 工作在 Gesture 层，负责在多个识别器之间做出唯一裁决。&lt;/p&gt;</description></item><item><title>从 setTint 引发的Drawable 的缓存问题</title><link>https://saucewu.github.io/posts/drawable%E7%9A%84%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6/</link><pubDate>Sun, 10 May 2020 15:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/drawable%E7%9A%84%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6/</guid><description>&lt;h1 id="从-settint-引发的drawable-的缓存问题"&gt;从 setTint 引发的Drawable 的缓存问题&lt;/h1&gt;
&lt;p&gt;这两天在做一个新需求是突然发现 DrawableCompat.setTint()更改drawable 渲染颜色的方法居然是全局生效的。变相说明getResource().getDrawable() 每次取出drawable的时候都是从一个缓存池里面出去的。&lt;/p&gt;
&lt;h3 id="避免缓存的解决方案"&gt;避免缓存的解决方案&lt;/h3&gt;
&lt;p&gt;第一反应是clone一个新的对象出来进行修改 但是貌似drawable并没有实现Cloneable的借口0那看来得想想别的办法了&lt;/p&gt;
&lt;p&gt;看了一会源码果然google在drawable类中已经给我们提供了一个 mutate方法 看注释看似乎这个方法可以返回一个新的drawable对象&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * Make this drawable mutable. This operation cannot be reversed. A mutable
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * drawable is guaranteed to not share its state with any other drawable.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * This is especially useful when you need to modify properties of drawables
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * loaded from resources. By default, all drawables instances loaded from
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * the same resource share a common state; if you modify the state of one
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * instance, all the other instances will receive the same modification.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * Calling this method on a mutable Drawable will have no effect.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * @return This drawable.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * @see ConstantState
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * @see #getConstantState()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;@NonNull&lt;/span&gt; Drawable &lt;span style="color:#a6e22e"&gt;mutate&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最终的实现是根据不同的drawable类型有不同的实现方式 这里就用bitmapdrawable作为例子 不仅clone了的对象 还禁止了这个对象再次被复制 猜测是为了方式无限clone导致oom&lt;/p&gt;</description></item><item><title>WebView实现离线缓存</title><link>https://saucewu.github.io/posts/android-webview%E5%AE%9E%E7%8E%B0%E7%A6%BB%E7%BA%BF%E7%BC%93%E5%AD%98/</link><pubDate>Tue, 10 Mar 2020 20:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/android-webview%E5%AE%9E%E7%8E%B0%E7%A6%BB%E7%BA%BF%E7%BC%93%E5%AD%98/</guid><description>&lt;h1 id="webview实现离线缓存"&gt;WebView实现离线缓存&lt;/h1&gt;
&lt;h3 id="场景"&gt;场景&lt;/h3&gt;
&lt;p&gt;在App在长期发展之中，对动态性要求很高的 活动页面 或是 一些带有简单功能的详情页面都可能会有大量Webview使用的情况。但是webview初始化时极有可能遇到网络波动的影响导致加载不出 或者 会重复下载一些公共资源造成性能问题。这时我们希望有一种缓存方案能够暂时解决这些初始化变慢的问题&lt;/p&gt;
&lt;h3 id="原理"&gt;原理&lt;/h3&gt;
&lt;p&gt;android WebViewClient提供了shouldInterceptRequest的接口供我们使用这个接口会拦截webview所有请求。如果错误缓存了资源，可能会出现web页面无法更新的情况。所以用的时候要谨慎只对我们需要使用缓存的部分进行拦截&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;override&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;shouldInterceptRequest&lt;/span&gt;(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (view &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; request &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//判断需要使用缓存的url
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;WebViewCacheUtils&lt;/span&gt;.needCache(request.url.toString())) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//从缓存池中获取缓存
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; cache = &lt;span style="color:#a6e22e"&gt;WebViewCacheUtils&lt;/span&gt;.getCache(request)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (cache &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; cache
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//未找到缓存文件或者不需要缓存 还是正常走请求
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;super&lt;/span&gt;.shouldInterceptRequest(view, request)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getCache&lt;/span&gt;(webResourceRequest: WebResourceRequest): WebResourceResponse? {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; uri = webResourceRequest.url
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//获取加载资源类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; mimeType: String? = &lt;span style="color:#a6e22e"&gt;MimeTypeMapUtils&lt;/span&gt;.getMimeTypeFromUrl(uri.toString())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; type: String
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; header = HashMap&amp;lt;String, String&amp;gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//我们可能对多个域名进行缓存 先设置跨域
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; header[&lt;span style="color:#e6db74"&gt;&amp;#34;Access-Control-Allow-Origin&amp;#34;&lt;/span&gt;] = &lt;span style="color:#e6db74"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; header[&lt;span style="color:#e6db74"&gt;&amp;#34;Access-Control-Allow-Headers&amp;#34;&lt;/span&gt;] = &lt;span style="color:#e6db74"&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (mimeType &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (uri.path&lt;span style="color:#f92672"&gt;!!&lt;/span&gt;.contains(&lt;span style="color:#e6db74"&gt;&amp;#34;js&amp;#34;&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; type = &lt;span style="color:#e6db74"&gt;&amp;#34;js&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mimeType = &lt;span style="color:#e6db74"&gt;&amp;#34;application/javascript&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; header[&lt;span style="color:#e6db74"&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;] = &lt;span style="color:#e6db74"&gt;&amp;#34;application/javascript; charset=utf-8&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mimeType = &lt;span style="color:#e6db74"&gt;&amp;#34;text/html&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; type = &lt;span style="color:#e6db74"&gt;&amp;#34;html&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; header[&lt;span style="color:#e6db74"&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;] = &lt;span style="color:#e6db74"&gt;&amp;#34;text/html; charset=utf-8&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (mimeType.contains(&lt;span style="color:#e6db74"&gt;&amp;#34;img&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;||&lt;/span&gt; mimeType.contains(&lt;span style="color:#e6db74"&gt;&amp;#34;image&amp;#34;&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; type = &lt;span style="color:#e6db74"&gt;&amp;#34;img&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//这里主要是css 格式是 text/css
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; type = mimeType.split(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;.toRegex()).dropLastWhile { &lt;span style="color:#66d9ef"&gt;it&lt;/span&gt;.isEmpty() }.toTypedArray()[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//确定是否获取了资源类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (!&lt;span style="color:#a6e22e"&gt;TextUtils&lt;/span&gt;.isEmpty(mimeType)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; name = (&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (type &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;html&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//由于多个url可能使用的是同一个html 这里需要判断下 返回的是 该html的md5
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; needCacheHtml(uri.path)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; MD5Utils.encode(uri.path)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//从缓存池中获取缓存
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; cacheSteam=getWebCache(name, type)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//构造响应体 并返回
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; WebResourceResponse(mimeType, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;, header, cacheSteam)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; (e: FileNotFoundException) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e.printStackTrace()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="注意事项"&gt;注意事项&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;HTML的缓存一定要小心 最好是由前端同学出一份目录 的接口并且做好版本管理 防止误操作 否则线上可能会出严重问题&lt;/li&gt;
&lt;li&gt;存储的文件名都是用md5过的 防止有特殊字符影响持久化&lt;/li&gt;
&lt;li&gt;如果有大文件缓存 最好需要有文件完整性验证&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>关于 Androidx 的5.x版本 webview crash</title><link>https://saucewu.github.io/posts/androidx-webview%E5%B4%A9%E6%BA%83/</link><pubDate>Tue, 10 Mar 2020 15:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/androidx-webview%E5%B4%A9%E6%BA%83/</guid><description>&lt;h1 id="关于-androidx-的5x版本-webview-crash"&gt;关于 Androidx 的5.x版本 webview crash&lt;/h1&gt;
&lt;p&gt;这两更新了androidx compat 在5.x一下版本手机上遇到crash问题&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;android.content.res.Resources$NotFoundException: String resource ID #0x2040002
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;确定是androidx 1.1.0版本问题 &lt;a href="https://issuetracker.google.com/issues/141132133"&gt;google issue&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;而且是只有 1.1.0 正式包才会有问题。以后还是少升级这种基础包为好&lt;/p&gt;</description></item><item><title> WebView页面 软键盘弹出无法正确折叠页面的问题</title><link>https://saucewu.github.io/posts/%E6%B2%89%E6%B5%B8%E5%BC%8F%E7%8A%B6%E6%80%81%E6%A0%8F%E7%9A%84%E8%BD%AF%E9%94%AE%E7%9B%98%E5%BC%B9%E5%87%BA%E9%97%AE%E9%A2%98/</link><pubDate>Thu, 03 May 2018 18:30:00 +0000</pubDate><guid>https://saucewu.github.io/posts/%E6%B2%89%E6%B5%B8%E5%BC%8F%E7%8A%B6%E6%80%81%E6%A0%8F%E7%9A%84%E8%BD%AF%E9%94%AE%E7%9B%98%E5%BC%B9%E5%87%BA%E9%97%AE%E9%A2%98/</guid><description>&lt;h1 id="webview页面-软键盘弹出无法正确折叠页面的问题"&gt;WebView页面 软键盘弹出无法正确折叠页面的问题&lt;/h1&gt;
&lt;p&gt;问题出在webview页面 选中webview中edit控件弹出的软键盘 无法正确折叠页面 遮挡了输入框。一开始认为是webview的适配问题，想当然的加上了&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;activity&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;android:name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;.WebViewActivity&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;android:windowSoftInputMode=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;adjustPan&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;/activity&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;结果当然是毫！无！反！应！&lt;/p&gt;
&lt;p&gt;在一番调试之后 发现时隐藏了状态栏之后 屏幕尺寸计算出现问题的缘故 也是个老&lt;a href="https://code.google.com/p/android/issues/detail?id=5497"&gt;google issue&lt;/a&gt;了&lt;/p&gt;
&lt;p&gt;网上有很多解决办法 最通用的当然还是 原理很简单就是每当页面有变动的时候自行去计算页面高度- -&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AndroidBug5497Workaround&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;assistActivity&lt;/span&gt;(Activity activity) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; AndroidBug5497Workaround(activity);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; View mChildOfContent;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; usableHeightPrevious;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; FrameLayout.&lt;span style="color:#a6e22e"&gt;LayoutParams&lt;/span&gt; frameLayoutParams;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; contentHeight;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;boolean&lt;/span&gt; isfirst &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; statusBarHeight;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AndroidBug5497Workaround&lt;/span&gt;(Activity activity) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (activity &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//获取状态栏的高度&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; resourceId &lt;span style="color:#f92672"&gt;=&lt;/span&gt; activity.&lt;span style="color:#a6e22e"&gt;getResources&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;getIdentifier&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;status_bar_height&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;dimen&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;android&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; statusBarHeight &lt;span style="color:#f92672"&gt;=&lt;/span&gt; activity.&lt;span style="color:#a6e22e"&gt;getResources&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;getDimensionPixelSize&lt;/span&gt;(resourceId);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FrameLayout content &lt;span style="color:#f92672"&gt;=&lt;/span&gt; activity.&lt;span style="color:#a6e22e"&gt;findViewById&lt;/span&gt;(android.&lt;span style="color:#a6e22e"&gt;R&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;content&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mChildOfContent &lt;span style="color:#f92672"&gt;=&lt;/span&gt; content.&lt;span style="color:#a6e22e"&gt;getChildAt&lt;/span&gt;(0);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//界面出现变动都会调用这个监听事件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mChildOfContent.&lt;span style="color:#a6e22e"&gt;getViewTreeObserver&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;addOnGlobalLayoutListener&lt;/span&gt;(() &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (isfirst) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; contentHeight &lt;span style="color:#f92672"&gt;=&lt;/span&gt; mChildOfContent.&lt;span style="color:#a6e22e"&gt;getHeight&lt;/span&gt;();&lt;span style="color:#75715e"&gt;//兼容华为等机型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; isfirst &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; possiblyResizeChildOfContent();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; frameLayoutParams &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (FrameLayout.&lt;span style="color:#a6e22e"&gt;LayoutParams&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mChildOfContent.&lt;span style="color:#a6e22e"&gt;getLayoutParams&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//重新调整跟布局的高度&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;possiblyResizeChildOfContent&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; usableHeightNow &lt;span style="color:#f92672"&gt;=&lt;/span&gt; computeUsableHeight();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//当前可见高度和上一次可见高度不一致 布局变动&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (usableHeightNow &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; usableHeightPrevious) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//int usableHeightSansKeyboard2 = mChildOfContent.getHeight();//兼容华为等机型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; usableHeightSansKeyboard &lt;span style="color:#f92672"&gt;=&lt;/span&gt; mChildOfContent.&lt;span style="color:#a6e22e"&gt;getRootView&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;getHeight&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; heightDifference &lt;span style="color:#f92672"&gt;=&lt;/span&gt; usableHeightSansKeyboard &lt;span style="color:#f92672"&gt;-&lt;/span&gt; usableHeightNow;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (heightDifference &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; (usableHeightSansKeyboard &lt;span style="color:#f92672"&gt;/&lt;/span&gt; 4)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// keyboard probably just became visible&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; frameLayoutParams.&lt;span style="color:#a6e22e"&gt;height&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; usableHeightSansKeyboard &lt;span style="color:#f92672"&gt;-&lt;/span&gt; heightDifference &lt;span style="color:#f92672"&gt;+&lt;/span&gt; statusBarHeight;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; frameLayoutParams.&lt;span style="color:#a6e22e"&gt;height&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; contentHeight;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mChildOfContent.&lt;span style="color:#a6e22e"&gt;requestLayout&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; usableHeightPrevious &lt;span style="color:#f92672"&gt;=&lt;/span&gt; usableHeightNow;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * 计算mChildOfContent可见高度
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; ** @return 高度
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;computeUsableHeight&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Rect r &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Rect();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mChildOfContent.&lt;span style="color:#a6e22e"&gt;getWindowVisibleDisplayFrame&lt;/span&gt;(r);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (r.&lt;span style="color:#a6e22e"&gt;bottom&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; r.&lt;span style="color:#a6e22e"&gt;top&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Android 编译期的黑科技（三）-字节码篇</title><link>https://saucewu.github.io/posts/android-%E7%BC%96%E8%AF%91%E6%9C%9F%E7%9A%84%E9%BB%91%E7%A7%91%E6%8A%80%E4%B8%89-%E5%AD%97%E8%8A%82%E7%A0%81%E7%AF%87/</link><pubDate>Thu, 05 Apr 2018 19:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/android-%E7%BC%96%E8%AF%91%E6%9C%9F%E7%9A%84%E9%BB%91%E7%A7%91%E6%8A%80%E4%B8%89-%E5%AD%97%E8%8A%82%E7%A0%81%E7%AF%87/</guid><description>&lt;h1 id="android-编译期的黑科技三--字节码篇"&gt;Android 编译期的黑科技（三）- 字节码篇&lt;/h1&gt;
&lt;h2 id="字节码织入"&gt;字节码织入&lt;/h2&gt;
&lt;p&gt;可以绕过编译，直接操作字节码，从而实现代码注入。所以使用 Javassist 的时机就是在构建工具 Gradle 将源 文件编译成 .class 文件之后，在将 .class 打包成 .dex 文件之前。也有两个相当成熟的字节码框架可供使用。使用起来大同小异这节主要介绍ASM&lt;/p&gt;
&lt;h3 id="asm"&gt;ASM&lt;/h3&gt;
&lt;p&gt;ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件，也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里，这些类文件拥有足够的元数据来解析类中的所有元素：类名称、方法、属性以及 Java 字节码（指令）。ASM 从类文件中读入信息后，能够改变类行为，分析类信息，甚至能够根据用户要求生成新类。&lt;/p&gt;</description></item><item><title>Android 编译期的黑科技（二）- AOP 篇</title><link>https://saucewu.github.io/posts/android-%E7%BC%96%E8%AF%91%E6%9C%9F%E7%9A%84%E9%BB%91%E7%A7%91%E6%8A%80%E4%BA%8C-aop%E7%AF%87/</link><pubDate>Sat, 17 Mar 2018 19:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/android-%E7%BC%96%E8%AF%91%E6%9C%9F%E7%9A%84%E9%BB%91%E7%A7%91%E6%8A%80%E4%BA%8C-aop%E7%AF%87/</guid><description>&lt;h1 id="android-编译期的黑科技二--aop-篇"&gt;Android 编译期的黑科技（二）- AOP 篇&lt;/h1&gt;
&lt;h2 id="aop-定义"&gt;AOP 定义&lt;/h2&gt;
&lt;p&gt;AOP 是 Aspect Oriented Programming 的缩写，即“面向切面编程”。使用 AOP，可以在编译期间对代码进行动态管理， 以达到统一维护的目的。AOP 是 OOP 编程的一种延续，也是 Spring 框架中的一个重要模块。利用 AOP 可以对业务逻辑 的各个模块进行隔离，从而使得业务逻辑各个部分之间的耦合度降低，提高程序的可重用性，同时提高开发的效率。利用 AOP，我们可以在无浸入的在宿主中插入一些代码逻辑，从而可以实现一些特殊的功能，比如日志埋点、性能监控、动态 权限控制、代码调试等。&lt;/p&gt;
&lt;h3 id="优点"&gt;优点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;织入的代码都是Java代码没有过多的学习难度&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="缺点"&gt;缺点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;无法织入第三方的库&lt;/li&gt;
&lt;li&gt;由于定义的切点依赖编程语言，该方案无法兼容 Lambda 语法&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="使用"&gt;使用&lt;/h2&gt;
&lt;p&gt;AOP只是个概念的定义有很多库都可以实现。例如Javapoet/AspectJ。使用的方法都很相似这里只介绍Javapoet。
防不胜防来啦这部分会用&lt;a href="https://github.com/SauceWu/InjectExtra"&gt;InjectExtra&lt;/a&gt;为例解释&lt;/p&gt;</description></item><item><title>Android 编译期的黑科技（一）基础篇</title><link>https://saucewu.github.io/posts/android-%E7%BC%96%E8%AF%91%E6%9C%9F%E7%9A%84%E9%BB%91%E7%A7%91%E6%8A%80%E4%B8%80%E5%9F%BA%E7%A1%80%E7%AF%87/</link><pubDate>Sat, 10 Mar 2018 19:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/android-%E7%BC%96%E8%AF%91%E6%9C%9F%E7%9A%84%E9%BB%91%E7%A7%91%E6%8A%80%E4%B8%80%E5%9F%BA%E7%A1%80%E7%AF%87/</guid><description>&lt;h1 id="android-编译期的黑科技一基础篇"&gt;Android 编译期的黑科技（一）基础篇&lt;/h1&gt;
&lt;h2 id="序言"&gt;序言&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;为什么需要这些编译期的黑科技
使用这些编译期的小工具可以有效减少 重复代码和重复逻辑 在android中大量运用的ButterKnife Gilde Room和DataBind都大量的时候用编译期生成代码的技术&lt;/li&gt;
&lt;li&gt;哪里可以用到这些
应用场景很多 最经典的应用场景是无痕埋点技术和解决重复逻辑&lt;/li&gt;
&lt;li&gt;为什么需要这个基础篇
直接上工具当然也可以使用，但毕竟写代码 知其然还要知其所以然 不然除了各种问题无法解决就很尴尬了&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="什么是编译"&gt;什么是编译&lt;/h2&gt;
&lt;p&gt;它主要的目的是将便于人编写、阅读、维护的高级计算机语言所写作的源代码程序，翻译为计算机能解读、运行的低阶机器语言的程序，也就是可执行文件。编译器将原始程序（source program）作为输入，翻译产生使用目标语言（target language）的等价程序。源代码一般为高级语言（High-level language），如Pascal、C、C++、C# 、Java等，而目标语言则是汇编语言或目标机器的目标代码（Object code），有时也称作机器代码（Machine code）。
java编译专指 .java &amp;mdash;&amp;gt;.class&lt;/p&gt;</description></item><item><title>Fragment 踩坑实录</title><link>https://saucewu.github.io/posts/fragment-%E8%B8%A9%E5%9D%91%E5%AE%9E%E5%BD%95/</link><pubDate>Sat, 10 Feb 2018 15:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/fragment-%E8%B8%A9%E5%9D%91%E5%AE%9E%E5%BD%95/</guid><description>&lt;h1 id="fragment-踩坑实录"&gt;Fragment 踩坑实录&lt;/h1&gt;
&lt;h3 id="不要轻易使用commitnowallowingstateloss"&gt;不要轻易使用commitNowAllowingStateLoss&lt;/h3&gt;
&lt;p&gt;在很多情况下使用不正确使用Fragment 会导致 java.lang.IllegalStateException 网上很多推荐commitNowAllowingStateLoss 来治标
但使用commitNowAllowingStateLoss 会导致当前Fragment 以其子Fragment 丢失状态 如 getVisibility() isHidden() 等等 而且会出现很多不可预知的问题&lt;/p&gt;
&lt;p&gt;出现问题:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1 在不正确的时机commit 如 onActivityResult 回调方法中
fragmentManager的 commit方法只能在onResume-onPause 的状态中使用
如必须在其他状态下改变状态 建议将状态记录下来 在onResume中执行操作&lt;/li&gt;
&lt;li&gt;2 重复add 同一个Fragment
由于commit属于异步操作 可能由于commit 过快导致 判断fragment是否为空的方法失效
应先使用executePendingTransactions让上一次commit执行完成后再执行新的操作&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="不要在初始化方法中直接生成新的fragment"&gt;不要在初始化方法中直接生成新的Fragment&lt;/h3&gt;
&lt;p&gt;在很多Activity 被重建的情况下 直接生成新的Fragment 会导致旧Fragment 无法回收导致泄露 如果在这些fragment中还有轮训或者socket 回调会导致崩溃
解决方案:
为每个Fragment添加tag
使用findFragmentByTag() 获取Fragment 若为null 再创建新的Fragment&lt;/p&gt;</description></item></channel></rss>