<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Android on Haoxiqiang&#39;s Blog</title>
    <link>https://blog.substitute.tech/categories/android/</link>
    <description>Recent content in Android on Haoxiqiang&#39;s Blog</description>
    <generator>Hugo</generator>
    <language>zh</language>
    <lastBuildDate>Mon, 15 Aug 2022 20:53:06 +0000</lastBuildDate>
    <atom:link href="https://blog.substitute.tech/categories/android/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Build AOSP for Pixel 3 XL</title>
      <link>https://blog.substitute.tech/posts/aosp-build-for-pixel3/</link>
      <pubDate>Mon, 15 Aug 2022 20:53:06 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/aosp-build-for-pixel3/</guid>
      <description>&lt;p&gt;AOSP 的构建流程已经比较清晰，大致分为：同步代码、添加对应设备的驱动和内核、构建目标镜像。之前尝试构建 AOSP 来排查一些问题，但 Pixel 3 XL 的官方适配只到 Android 12。最近在 Chromium 开发中需要测试 WebView，因此使用 LineageOS 21 的适配来方便构建。&lt;/p&gt;
&lt;h2 id=&#34;前提条件&#34;&gt;前提条件&lt;/h2&gt;
&lt;p&gt;默认已安装 AOSP 构建环境：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://source.android.com/docs/setup&#34;&gt;AOSP 环境准备&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://source.android.com/docs/setup/reference/build-numbers#source-code-tags-and-builds&#34;&gt;Codenames, Tags, and Build Numbers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;同步-aosp-源码&#34;&gt;同步 AOSP 源码&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir ~/aosp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; ~/aosp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;repo init --partial-clone -b android-12.0.0_r34 -u https://android.googlesource.com/platform/manifest
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;repo sync -c -j8
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;获取驱动&#34;&gt;获取驱动&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;在 &lt;a href=&#34;https://source.android.com/docs/setup/reference/build-numbers#source-code-tags-and-builds&#34;&gt;Codenames, Tags, and Build Numbers&lt;/a&gt; 页面搜索 Pixel 3 XL（代号 &lt;code&gt;crosshatch&lt;/code&gt;），获取最新 Build ID，示例：&lt;code&gt;SP1A.210812.016.C2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在 &lt;a href=&#34;https://developers.google.com/android/drivers&#34;&gt;Google Drivers&lt;/a&gt; 页面下载对应 Build ID 的驱动&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir vendor_backup &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; vendor_backup
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget https://dl.google.com/dl/android/aosp/google_devices-crosshatch-sp1a.210812.016.c2-a4e274b7.tgz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget https://dl.google.com/dl/android/aosp/qcom-crosshatch-sp1a.210812.016.c2-00a7f1f3.tgz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar xvf qcom-crosshatch-*.tgz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar xvf google_devices-crosshatch-*.tgz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./extract-google_devices-crosshatch.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./extract-qcom-crosshatch.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv vendor/ ../
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;构建并刷机&#34;&gt;构建并刷机&lt;/h2&gt;
&lt;p&gt;参考 &lt;a href=&#34;https://source.android.com/docs/setup/build/building&#34;&gt;Building AOSP&lt;/a&gt; 文档：&lt;/p&gt;</description>
    </item>
    <item>
      <title>利用自建 Nexus 仓库优化 Android 构建</title>
      <link>https://blog.substitute.tech/posts/nexus/</link>
      <pubDate>Tue, 31 Dec 2019 21:29:27 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/nexus/</guid>
      <description>&lt;p&gt;在 Android 项目的依赖管理中，通常需要配置多个远程仓库，如 &lt;code&gt;jcenter&lt;/code&gt;、&lt;code&gt;jitpack&lt;/code&gt;、&lt;code&gt;google()&lt;/code&gt; 等。一些大型项目（如&amp;quot;最右&amp;quot;）甚至依赖超过 10 个仓库。当首次初始化项目、依赖发生变化或网络出现问题时，构建过程的排査会变得相当困难。&lt;/p&gt;
&lt;p&gt;很早之前就发现了这个问题，但一直因为懒没有处理。本文记录 Nexus 的搭建与配置过程。&lt;/p&gt;
&lt;h2 id=&#34;安装并配置-nexus&#34;&gt;安装并配置 Nexus&lt;/h2&gt;
&lt;p&gt;前置条件：JDK 8+。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 下载并解压 Nexus&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir /app &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /app
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget -O nexus.tar.gz https://download.sonatype.com/nexus/3/latest-unix.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar -xvf nexus.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv nexus-3* nexus
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 创建专用用户&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;adduser nexus
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 修改目录权限&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chown -R nexus:nexus /app/nexus
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chown -R nexus:nexus /app/sonatype-work
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;配置运行用户：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vi /app/nexus/bin/nexus.rc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 添加以下内容&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;run_as_user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;nexus&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;如需修改存储路径等，编辑 JVM 参数：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android 的 MediaStore</title>
      <link>https://blog.substitute.tech/posts/android%E7%9A%84mediastore/</link>
      <pubDate>Fri, 31 Mar 2017 11:19:26 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/android%E7%9A%84mediastore/</guid>
      <description>&lt;p&gt;最近在写一个类似微信的相册功能，需要读取照片和视频，支持多文件夹切换，且速度要比微信快。调研后发现基于 &lt;code&gt;MediaStore&lt;/code&gt; 的方案最为合适。以前用得不多，特此记录。&lt;/p&gt;
&lt;h2 id=&#34;contentresolver-对-group-by-的特殊处理&#34;&gt;ContentResolver 对 GROUP BY 的特殊处理&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ContentResolver.query()&lt;/code&gt; 没有提供 &lt;code&gt;groupBy&lt;/code&gt; 参数（与 &lt;code&gt;SQLiteQueryBuilder.query()&lt;/code&gt; 不同），但可以通过在 &lt;code&gt;selection&lt;/code&gt; 参数中嵌入 &lt;code&gt;GROUP BY&lt;/code&gt; 来实现类似效果。&lt;/p&gt;
&lt;p&gt;原理是 &lt;code&gt;ContentResolver&lt;/code&gt; 会在编译 SQL 时给 &lt;code&gt;selection&lt;/code&gt; 自动加上括号包裹，形成 &lt;code&gt;WHERE ( ... )&lt;/code&gt;。利用这一点，可以在 selection 中提前闭合括号，然后追加 &lt;code&gt;GROUP BY&lt;/code&gt; 子句。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 常规写法 — selection 会被包装成 WHERE (mime_type IS NOT NULL)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;MediaStore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Images&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ImageColumns&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;MIME_TYPE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34; IS NOT NULL &amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Hack 写法 — 利用闭合括号注入 GROUP BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;MediaStore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Images&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ImageColumns&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;MIME_TYPE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34; IS NOT NULL &amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;) GROUP BY (&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MediaStore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Images&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ImageColumns&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;BUCKET_DISPLAY_NAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;生成的 SQL 变为：&lt;/p&gt;</description>
    </item>
    <item>
      <title>问题整理（二）</title>
      <link>https://blog.substitute.tech/posts/%E9%97%AE%E9%A2%98%E6%95%B4%E7%90%86%E4%BA%8C/</link>
      <pubDate>Tue, 19 Jan 2016 16:50:36 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/%E9%97%AE%E9%A2%98%E6%95%B4%E7%90%86%E4%BA%8C/</guid>
      <description>&lt;p&gt;第二篇问题整理，主要涉及 WebView 的内存管理和 Cookie 同步，以及一些其他细节。&lt;/p&gt;</description>
    </item>
    <item>
      <title>使用 RxJava 写一个应用</title>
      <link>https://blog.substitute.tech/posts/rxjava/</link>
      <pubDate>Thu, 07 Jan 2016 14:05:45 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/rxjava/</guid>
      <description>&lt;p&gt;最近准备写一个知乎日报客户端，主要是练习和验证 RxJava 在实际项目中的价值。RxJava 在国外社区很火热，目前支持的编程语言包括 Java、JavaScript、C#、Scala、Clojure、C++、Python、Ruby、Kotlin、Swift 等。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android 开发中遇到的问题整理</title>
      <link>https://blog.substitute.tech/posts/%E9%97%AE%E9%A2%98%E6%95%B4%E7%90%86/</link>
      <pubDate>Tue, 29 Dec 2015 18:41:05 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/%E9%97%AE%E9%A2%98%E6%95%B4%E7%90%86/</guid>
      <description>&lt;p&gt;读到 Xiaoke&amp;rsquo;s Blog 上的一些问题整理，发现自己很多也遇到过，抽空整理一下。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android Resource Shrinking 资源清理</title>
      <link>https://blog.substitute.tech/posts/resourceshrinking%E8%B5%84%E6%BA%90%E6%B8%85%E7%90%86/</link>
      <pubDate>Wed, 23 Dec 2015 17:43:07 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/resourceshrinking%E8%B5%84%E6%BA%90%E6%B8%85%E7%90%86/</guid>
      <description>&lt;p&gt;构建项目时发现 &lt;code&gt;shrinkResources&lt;/code&gt; 这个属性，用于删除项目中未使用的资源文件。记录一下使用中的问题和配置方法。&lt;/p&gt;</description>
    </item>
    <item>
      <title>RecyclerView 实现树形菜单</title>
      <link>https://blog.substitute.tech/posts/treeview%E6%A0%91%E5%BD%A2%E8%8F%9C%E5%8D%95/</link>
      <pubDate>Wed, 16 Dec 2015 18:15:33 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/treeview%E6%A0%91%E5%BD%A2%E8%8F%9C%E5%8D%95/</guid>
      <description>&lt;p&gt;很早想写这样一个 Demo。以前实现树形菜单使用 &lt;code&gt;TreeViewList&lt;/code&gt;（继承 &lt;code&gt;ListView&lt;/code&gt; 的封装），或者用 &lt;code&gt;ExpandableListView&lt;/code&gt; 实现多级菜单。后来发现根本不需要自定义控件——直接使用 &lt;code&gt;RecyclerView&lt;/code&gt;，只需要控制数据源的展平转换即可。&lt;/p&gt;
&lt;p&gt;核心思路：以递归方式将嵌套数据结构展平为线性列表，通过 &lt;code&gt;notifyItemRangeInserted&lt;/code&gt; / &lt;code&gt;notifyItemRangeRemoved&lt;/code&gt; 控制展开和收起。&lt;/p&gt;</description>
    </item>
    <item>
      <title>如何根据错误日志解决问题 —— Bugly 博客整理</title>
      <link>https://blog.substitute.tech/posts/%E5%A6%82%E4%BD%95%E6%A0%B9%E6%8D%AE%E9%94%99%E8%AF%AF%E6%97%A5%E5%BF%97%E8%A7%A3%E5%86%B3%E9%97%AE%E9%A2%98/</link>
      <pubDate>Wed, 29 Jul 2015 12:00:39 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/%E5%A6%82%E4%BD%95%E6%A0%B9%E6%8D%AE%E9%94%99%E8%AF%AF%E6%97%A5%E5%BF%97%E8%A7%A3%E5%86%B3%E9%97%AE%E9%A2%98/</guid>
      <description>&lt;p&gt;项目中使用腾讯 Bugly 做崩溃监控。同类工具功能相似，选择 Bugly 主要是因为统计界面友好、品牌可靠。以下整理的 Bugly 技术博客文章，包含开发中遇到的 case 和很有启发性的分析思路。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Volley 自定义中的几个问题</title>
      <link>https://blog.substitute.tech/posts/volley%E8%87%AA%E5%AE%9A%E4%B9%89%E4%B8%AD%E7%9A%84%E5%87%A0%E4%B8%AA%E9%97%AE%E9%A2%98%E7%9A%84%E5%8E%9F%E5%9B%A0/</link>
      <pubDate>Tue, 09 Jun 2015 17:30:15 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/volley%E8%87%AA%E5%AE%9A%E4%B9%89%E4%B8%AD%E7%9A%84%E5%87%A0%E4%B8%AA%E9%97%AE%E9%A2%98%E7%9A%84%E5%8E%9F%E5%9B%A0/</guid>
      <description>&lt;p&gt;项目中使用 Volley 作为网络库，封装过程中遇到几个常见问题，记录如下。以下内容不兼容 Android 2.3 及以下版本。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android 源码下载</title>
      <link>https://blog.substitute.tech/posts/android%E6%BA%90%E7%A0%81%E4%B8%8B%E8%BD%BD/</link>
      <pubDate>Thu, 04 Jun 2015 15:45:04 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/android%E6%BA%90%E7%A0%81%E4%B8%8B%E8%BD%BD/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;时效性说明&lt;/strong&gt;：本文涉及的镜像地址仅适用于特定时期的版本。清华 TUNA 和中科大 USTC 的 AOSP 镜像地址已多次变更，请以各镜像站官方帮助页为准。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;AOSP 源码体积庞大（约 70GB），通过 VPN 从 Google 官方源下载极其缓慢。国内镜像可以大幅提升下载速度。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Make Your App the Default SMS App</title>
      <link>https://blog.substitute.tech/posts/defaultapp/</link>
      <pubDate>Mon, 18 May 2015 16:49:21 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/defaultapp/</guid>
      <description>&lt;p&gt;使用 Google Messenger 时发现它有设置默认短信应用的功能，于是研究了一下实现方式。&lt;/p&gt;
&lt;p&gt;Android 4.4 KitKat 开始，Google 推出了默认短信应用的机制。官方对此的解释是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Some of you have built SMS apps using hidden APIs — a practice we discourage because hidden APIs may be changed or removed and new devices are not tested against them for compatibility. So, to provide you with a fully supported set of APIs for building SMS apps.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;简而言之，做得足够好、用户量足够大，Google 就会重视并官方支持。&lt;/p&gt;</description>
    </item>
    <item>
      <title>最近读文档发现的几个注意点</title>
      <link>https://blog.substitute.tech/posts/%E6%9C%80%E8%BF%91%E5%8F%91%E7%8E%B0%E7%9A%84%E5%87%A0%E4%B8%AA%E6%B3%A8%E6%84%8F%E7%82%B9/</link>
      <pubDate>Mon, 11 May 2015 11:18:26 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/%E6%9C%80%E8%BF%91%E5%8F%91%E7%8E%B0%E7%9A%84%E5%87%A0%E4%B8%AA%E6%B3%A8%E6%84%8F%E7%82%B9/</guid>
      <description>&lt;p&gt;阅读 Android 官方文档时发现几个容易忽略的细节，整理如下。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Dart Web Apps on Android</title>
      <link>https://blog.substitute.tech/posts/dart/</link>
      <pubDate>Tue, 05 May 2015 17:46:58 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/dart/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;历史说明&lt;/strong&gt;：本文基于 2015 年的 Dart 开发环境编写。当时的 Dart 还以 Dart Editor + Dartium 浏览器为核心工具链。如今的 Dart 已全面转向 Flutter 生态和 Dart SDK 命令行工具链，内容仅供参考。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;您可以直接在 Android 设备上启动和调试 Dart Web 应用，无需预编译为 JavaScript。需要安装 &lt;strong&gt;Dart Editor&lt;/strong&gt; 和 &lt;strong&gt;Dart Content Shell&lt;/strong&gt;。Dart Content Shell 会自动安装到 Android 设备上。&lt;/p&gt;</description>
    </item>
    <item>
      <title>最近遇到的一些奇怪的问题</title>
      <link>https://blog.substitute.tech/posts/recent-strangeproblem/</link>
      <pubDate>Tue, 21 Apr 2015 14:41:34 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/recent-strangeproblem/</guid>
      <description>&lt;p&gt;Android 开发中偶尔会遇到一些看似莫名其妙的问题，记录在这里，方便以后查阅。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android Data Storage</title>
      <link>https://blog.substitute.tech/posts/androiddatastorage/</link>
      <pubDate>Wed, 08 Apr 2015 11:38:49 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/androiddatastorage/</guid>
      <description>&lt;p&gt;Android 提供了多种数据持久化方案,不同的方案适用于不同的场景。选择合适的存储方式对于应用的性能、安全性和用户体验至关重要。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android App Widgets</title>
      <link>https://blog.substitute.tech/posts/androidappwidgets/</link>
      <pubDate>Wed, 01 Apr 2015 10:37:26 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/androidappwidgets/</guid>
      <description>&lt;p&gt;App Widget 是 Android 中的一种微型应用视图,可以嵌入到其他应用(如桌面 Home Screen)中,并支持周期性更新。最常见的例子就是桌面 Widget,比如天气小部件、音乐播放器控件等。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;appwidget&#34; loading=&#34;lazy&#34; src=&#34;https://blog.substitute.tech/images/appwidget.png&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android Developer Requirements</title>
      <link>https://blog.substitute.tech/posts/androiddeveloperrequirements/</link>
      <pubDate>Wed, 01 Apr 2015 09:50:52 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/androiddeveloperrequirements/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;:本文写于 2015 年,部分工具链和版本信息已过时,仅供历史参考。当前 Android 开发请直接使用 &lt;a href=&#34;https://developer.android.com/studio&#34;&gt;Android Studio&lt;/a&gt; 和 &lt;a href=&#34;https://developer.android.com/&#34;&gt;Android Developers 官方文档&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;</description>
    </item>
    <item>
      <title>Remote Debugging on Android with Chrome</title>
      <link>https://blog.substitute.tech/posts/remotedebuggingonandroidwithchrome/</link>
      <pubDate>Tue, 24 Mar 2015 14:54:02 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/remotedebuggingonandroidwithchrome/</guid>
      <description>&lt;p&gt;Chrome DevTools 支持远程调试 Android 设备上的网页和 WebView 应用。这一功能对于移动端前端开发和混合应用开发来说非常实用,可以让我们使用桌面 Chrome 的完整开发者工具来调试移动端页面。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Bezier Curve Practice</title>
      <link>https://blog.substitute.tech/posts/beziercurvepractice/</link>
      <pubDate>Wed, 04 Mar 2015 14:11:48 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/beziercurvepractice/</guid>
      <description>&lt;p&gt;贝塞尔曲线(Bézier Curve)被广泛应用于计算机图形学中,用于为平滑曲线建立模型。它以法国工程师 Pierre Bézier (雷诺汽车公司) 和 Paul de Casteljau (雪铁龙汽车公司) 命名,两人在 1960 年代独立开发了这一曲线表示方法。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;Bézier_3_big&#34; loading=&#34;lazy&#34; src=&#34;https://blog.substitute.tech/images/Be%CC%81zier_3_big.svg.png&#34;&gt;&lt;img alt=&#34;Bézier_3_big_gif&#34; loading=&#34;lazy&#34; src=&#34;https://blog.substitute.tech/images/Be%CC%81zier_3_big.gif&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android Property Animation</title>
      <link>https://blog.substitute.tech/posts/androidpropertyanimation/</link>
      <pubDate>Tue, 03 Mar 2015 10:31:21 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/androidpropertyanimation/</guid>
      <description>&lt;p&gt;&lt;code&gt;Property Animation&lt;/code&gt; 是 Android 提供的一套非常强大的动画框架,可以在运行时动态改变任意 View (可见或不可见) 的属性.相比传统的 View 动画(补间动画),属性动画真正改变了对象的属性,而不仅仅是绘制效果.&lt;/p&gt;</description>
    </item>
    <item>
      <title>SQLite 限制详解：从一次 SQLiteFullException 说起</title>
      <link>https://blog.substitute.tech/posts/limitsinsqlite/</link>
      <pubDate>Sat, 28 Feb 2015 22:48:55 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/limitsinsqlite/</guid>
      <description>&lt;p&gt;最近排查一个 BUG 时遇到一个奇怪的问题：&lt;code&gt;android.database.sqlite.SQLiteFullException: database or disk is full (code 13)&lt;/code&gt;。查阅了很多资料也没能完全弄清楚根因，这里把整理的资料分享出来，希望遇到类似问题的朋友共同探讨。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;limitsinsqlite01&#34; loading=&#34;lazy&#34; src=&#34;https://blog.substitute.tech/images/limitsinsqlite01.png&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android 通知机制详解</title>
      <link>https://blog.substitute.tech/posts/androidnotification/</link>
      <pubDate>Thu, 29 Jan 2015 15:26:07 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/androidnotification/</guid>
      <description>&lt;p&gt;通知系统是 Android 平台上用户与应用交互的重要通道——它能在应用不处于前台时告知用户重要事件，如来消息或日历提醒。&lt;code&gt;Notification&lt;/code&gt; 本身在 Android 4.1 (Jelly Bean) 经历过一次重大升级，后续在 5.0 (Lollipop) 又有诸多细节改进。从 4.1 开始，Android 支持在通知底部附加操作按钮，用户无需打开应用即可直接执行常见任务，配合滑出清除，使通知抽屉的体验更加顺滑。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：本文基于 Android 4.1—5.0 时代的 API 编写。自 Android 8.0 (API 26) 起，所有通知必须归属到通知渠道（Notification Channel）；Android 13 (API 33) 起需要运行时权限 &lt;code&gt;POST_NOTIFICATIONS&lt;/code&gt;。下文代码示例使用 &lt;code&gt;NotificationCompat&lt;/code&gt; 以保证对低版本的兼容性，在不同设备上效果可能略有差异。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt=&#34;notification01&#34; loading=&#34;lazy&#34; src=&#34;https://blog.substitute.tech/images/notification01.jpg&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android 学习资料指南</title>
      <link>https://blog.substitute.tech/posts/android%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%99%E6%8C%87%E5%8D%97/</link>
      <pubDate>Mon, 26 Jan 2015 16:35:07 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/android%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%99%E6%8C%87%E5%8D%97/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;注：此文写于 2015 年，文中部分链接可能已失效。资源推荐反映了当时的生态，许多内容已有更新版本，但核心的学习路径和方法论仍然有参考价值。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;
&lt;p&gt;谁无年少时。对于一门技术而言，大家的起点都是 Hello World。让人困惑的是，一年下来有些人的技术提升是 100%，有些人是 20%，有些人或许悲催地没有什么变化。是智商的原因吗？在我看来，可能是学习方法不对。&lt;/p&gt;
&lt;p&gt;前几天结婚休假，我一直在写博客。找资料的过程中发现，单用百度效率太低，至少有一半的时间浪费在了无用页面上。我准备花几个晚上的时间整理收集的资料，尽可能保证在国内可以访问。以下列举初中级开发者的资料整合，至于高级——我大约还没有什么发言权。&lt;/p&gt;</description>
    </item>
    <item>
      <title>轻松搞定 Android 环境、源码与工具（国内镜像方案）</title>
      <link>https://blog.substitute.tech/posts/%E8%BD%BB%E6%9D%BE%E6%90%9E%E5%AE%9Aandroid%E7%9A%84%E7%8E%AF%E5%A2%83%E6%BA%90%E7%A0%81%E5%B7%A5%E5%85%B7/</link>
      <pubDate>Fri, 16 Jan 2015 08:35:07 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/%E8%BD%BB%E6%9D%BE%E6%90%9E%E5%AE%9Aandroid%E7%9A%84%E7%8E%AF%E5%A2%83%E6%BA%90%E7%A0%81%E5%B7%A5%E5%85%B7/</guid>
      <description>&lt;p&gt;&lt;strong&gt;注：此文写于 2015 年，部分镜像地址可能已变更或失效。当前国内推荐使用清华大学 TUNA、中科大 USTC 等镜像站，或直接通过 Android Studio SDK Manager 下载。截至 2025 年，本文提到的部分高校镜像可能已停止服务。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;此篇为国内网络环境下的 Android 开发者准备，涵盖以下内容：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Android SDK 更新&lt;/li&gt;
&lt;li&gt;Android Studio 下载&lt;/li&gt;
&lt;li&gt;AOSP 源码下载&lt;/li&gt;
&lt;li&gt;Android NDK 下载&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    <item>
      <title>Android Fragment$InstantiationException 分析与解决</title>
      <link>https://blog.substitute.tech/posts/android-fragmentinstantiationexception/</link>
      <pubDate>Wed, 07 Jan 2015 08:35:07 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/android-fragmentinstantiationexception/</guid>
      <description>&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-gdscript3&#34; data-lang=&#34;gdscript3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;android&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;support&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v4&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Fragment&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;InstantiationException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Unable&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;to&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;instantiate&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fragment&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;make&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sure&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;exists&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;an&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;empty&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;constructor&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;that&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;public&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;以前偶尔碰到这个错误，量不大没在意。最近突然暴增，仔细研究了一番，整理出几个原因和解决办法。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android EBUSY Exception</title>
      <link>https://blog.substitute.tech/posts/android-ebusy-exception/</link>
      <pubDate>Mon, 22 Dec 2014 15:35:07 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/android-ebusy-exception/</guid>
      <description>&lt;p&gt;最近写一个图片上传功能时，在某些手机上遇到了运行时异常：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;java&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;FileNotFoundException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mnt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sdcard&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Android&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;com&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;xxxxxx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;android&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;files&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;xxxx&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;failed&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EBUSY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Device&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;or&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;resource&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;busy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;libcore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;IoBridge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IoBridge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;java&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;406&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;java&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;FileOutputStream&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FileOutputStream&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;java&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;88&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Caused&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;by&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;libcore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ErrnoException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;failed&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EBUSY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Device&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;or&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;resource&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;busy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;libcore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Posix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Native&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;libcore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;BlockGuardOs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;BlockGuardOs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;java&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;110&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
    </item>
    <item>
      <title>Android 使用 Wireless 调试</title>
      <link>https://blog.substitute.tech/posts/android%E4%BD%BF%E7%94%A8wireless%E8%B0%83%E8%AF%95/</link>
      <pubDate>Fri, 05 Dec 2014 11:35:07 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/android%E4%BD%BF%E7%94%A8wireless%E8%B0%83%E8%AF%95/</guid>
      <description>&lt;p&gt;自从换了 Nexus 设备，就不太想用 USB 连电脑了。Android 支持通过 WiFi 进行 ADB 调试，完全可以摆脱数据线的束缚。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android&#39;s SQLite</title>
      <link>https://blog.substitute.tech/posts/androids-sqlite/</link>
      <pubDate>Mon, 01 Dec 2014 22:11:22 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/androids-sqlite/</guid>
      <description>&lt;p&gt;SQLite 是一个轻量级的关系型数据库引擎，Android 内置对其支持，非常适合本地数据持久化。无需额外配置，通过 &lt;code&gt;android.database.sqlite&lt;/code&gt; 包下的 API 即可使用。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android&#39;s Palette</title>
      <link>https://blog.substitute.tech/posts/androids-palette/</link>
      <pubDate>Mon, 03 Nov 2014 10:01:22 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/androids-palette/</guid>
      <description>&lt;p&gt;Palette 是 Android Support Library（现已迁移至 AndroidX &lt;code&gt;palette&lt;/code&gt; 库）提供的取色工具，可以从 Bitmap 中自动提取一组代表色。在 Material Design 中，从图片提取色彩并应用到 UI 元素是常见的设计手法，Palette 让这一过程变得非常简单。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;palette01&#34; loading=&#34;lazy&#34; src=&#34;https://blog.substitute.tech/images/palette01.png&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android 的 RecyclerView（二）：视差滚动</title>
      <link>https://blog.substitute.tech/posts/androids-recyclerview2-%E8%A7%86%E5%B7%AE%E6%BB%9A%E5%8A%A8/</link>
      <pubDate>Sun, 02 Nov 2014 14:01:22 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/androids-recyclerview2-%E8%A7%86%E5%B7%AE%E6%BB%9A%E5%8A%A8/</guid>
      <description>&lt;p&gt;视差滚动（Parallax Scrolling）是一种常见的 UI 效果：滚动时不同层次的元素以不同速度移动，营造立体感。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;parallaxrecycler&#34; loading=&#34;lazy&#34; src=&#34;https://blog.substitute.tech/images/parallaxrecycler.gif&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android 的 RecyclerView</title>
      <link>https://blog.substitute.tech/posts/androids-recyclerview/</link>
      <pubDate>Sat, 01 Nov 2014 17:03:26 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/androids-recyclerview/</guid>
      <description>&lt;p&gt;RecyclerView 是一个比 ListView 更灵活的滚动列表控件。官方文档指出，它能高效维护数量有限的滚动数据集合，当 View 需要与用户行为和网络数据交互时，推荐使用 RecyclerView。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;RecyclerView01&#34; loading=&#34;lazy&#34; src=&#34;https://blog.substitute.tech/images/RecyclerView01.png&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android 的 Canvas（二）：裁剪</title>
      <link>https://blog.substitute.tech/posts/android-canvas2/</link>
      <pubDate>Fri, 03 Oct 2014 14:19:06 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/android-canvas2/</guid>
      <description>&lt;p&gt;Canvas 的裁剪（Clip）用于限定绘制区域：只有裁剪区域范围内的内容才会被显示出来。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android 的 Canvas（一）</title>
      <link>https://blog.substitute.tech/posts/android-canvas1/</link>
      <pubDate>Thu, 02 Oct 2014 13:19:06 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/android-canvas1/</guid>
      <description>&lt;p&gt;View 能将内容显示出来，本质上是&amp;quot;画&amp;quot;出来的——在画板上使用画笔绘制。这里的画布是 &lt;code&gt;Canvas&lt;/code&gt;，画笔是 &lt;code&gt;Paint&lt;/code&gt;。通过 &lt;code&gt;onDraw&lt;/code&gt; 方法获取到的 Canvas 内容可以直接反映到 View 上。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;Canvas01&#34; loading=&#34;lazy&#34; src=&#34;https://blog.substitute.tech/images/android_canvas01.png&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Android 的自定义 View（二）：测量模式</title>
      <link>https://blog.substitute.tech/posts/customview2/</link>
      <pubDate>Wed, 01 Oct 2014 19:19:06 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/customview2/</guid>
      <description></description>
    </item>
    <item>
      <title>Android 的自定义 View（一）</title>
      <link>https://blog.substitute.tech/posts/customview1/</link>
      <pubDate>Wed, 01 Oct 2014 17:19:06 +0000</pubDate>
      <guid>https://blog.substitute.tech/posts/customview1/</guid>
      <description>&lt;p&gt;刚开始编写自定义 View 时，难免不知道如何下手。一般说来有两种实现方式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;从零开始&lt;/strong&gt;：继承 &lt;code&gt;View&lt;/code&gt;，通过计算和绘制实现所需的外观。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;扩展现有 View&lt;/strong&gt;：在已有控件基础上增加子 View，或重写方法改变原有逻辑。&lt;/li&gt;
&lt;/ol&gt;</description>
    </item>
  </channel>
</rss>
