/
search.xml
82 lines (39 loc) · 70.8 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Lawrefbook 遇到问题</title>
<link href="/2022/07/01/lawrefbook1/"/>
<url>/2022/07/01/lawrefbook1/</url>
<content type="html"><![CDATA[<p>Lawrefbook 项目开发过程中遇到了不少问题,本篇文章记录面对问题的思考与实践。</p><p>在完成初始版本后,数据源从 2022.5.1 结构由 <a href="https://github.com/LawRefBook/Laws/blob/abe3daaa448d225a37606118eaa4796d05b2c01c/data.json">JSON</a> 变更成 <a href="https://github.com/LawRefBook/Laws/blob/abe3daaa448d225a37606118eaa4796d05b2c01c/laws.db">sqlite3</a> 数据库,因此从 1.1.0 版本开始进行了适配,刚好可以再次熟悉下数据库的相关操作。</p><h2 id="submodule-与-subtree"><a href="#submodule-与-subtree" class="headerlink" title="submodule 与 subtree"></a>submodule 与 subtree</h2><p>早期分别使用过 submodule 和 subtree 对项目中的子项目进行管理,当时也没有细致去区分这两种模式适用的场景</p><table><thead><tr><th></th><th><strong>submodule</strong></th><th><strong>subtree</strong></th><th><strong>结果</strong></th></tr></thead><tbody><tr><td>远程仓库空间占用</td><td>submodule 只是引用,基本不占用额外空间</td><td>子模块 copy,会占用较大的额外空间</td><td>submodule 占用空间较小,略优</td></tr><tr><td>本地空间占用</td><td>可根据需要下载</td><td>会下载整个项目</td><td>所有模块基本都要下载,二者差异不大</td></tr><tr><td>仓库克隆</td><td>克降后所有子模块为空,需要注册及更新,同时更新后还需切换分支</td><td>克隆之后即可使用</td><td>submodule 步骤略多,subtree 占优</td></tr><tr><td>更新本地仓库</td><td>更新后所有子模块后指向最后一次提交,更新后需要重新切回分支,所有子模块只需一条更新语句即可</td><td>所有子模块需要单独更新</td><td>各有优劣,相对 subtree 更好用一些</td></tr><tr><td>提交本地修改</td><td>只需关心子模块即可,子模块的所有操作与普通 git 项目相同</td><td>提交执行命令相对复杂一些</td><td>submodule 操作更简单,submodule 占优</td></tr></tbody></table><p>经过对两种方式的比较</p><ul><li>submodule:适用于<strong>只仅仅</strong>是引用子模块,并不涉及到对子模块项目的修改,例如:这次 Lawrefbook 项目的中的数据源管理,其实就很适合用 submodule 方式</li><li>subtree:适用于引用子模块,除此之外还能在宿主项目中对子模块项目做更改并提交推送到子模块的源仓库</li></ul><p><a href="https://juejin.cn/post/6844904132411654157">Git子库:submodule与subtree</a></p><h2 id="SQLite3-与-Room"><a href="#SQLite3-与-Room" class="headerlink" title="SQLite3 与 Room"></a>SQLite3 与 Room</h2><p>Android 上数据库除了最初的 SQLite 还在 Jetpact 组件中新增了 Room 数据库,因此同步源数据,可以有两个方案</p><ol><li>方案一:依然使用源数据的 SQLite,本质是 Copy assets 路径下数据库到应用内部默认位置 <code>/data/data/<application package name>/databases</code> 下</li><li>方案二:迁移源数据的 SQLite 到 Room 数据库,Room 数据库已经支持了 <a href="https://developer.android.google.cn/training/data-storage/room/sqlite-room-migration">SQLite 数据库的迁移 API</a></li></ol><p>综上两个方案都不难,看自己的选择。</p><blockquote><p>由于应用的收藏功能,就已经用上了 Room 数据库,再使用 Room 提供的 SQLite 迁移 API 如果库名一样会覆盖之前的数据库(当然你可以和之前收藏不在同一个 Room 数据库,就不会覆盖啦),因此我这里使用了方案一,刚好也可以熟悉了 SQLite</p></blockquote><h3 id="列重命名"><a href="#列重命名" class="headerlink" title="列重命名"></a>列重命名</h3><p>在迁移数据之前,我还有一个需求,需要对之前收藏表进行重命名列名,操作步骤如下</p><ol><li><p>在应用的 model 级别的 build.gradle 文件的 defaultConfig 内添加数据库的 schema</p><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">defaultConfig {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 其他配置这里省略</span></span><br><span class="line"> ……</span><br><span class="line"></span><br><span class="line"> <span class="comment">// https://developer.android.google.cn/training/data-storage/room/migrating-db-versions#export-schema</span></span><br><span class="line"> javaCompileOptions {</span><br><span class="line"> annotationProcessorOptions {</span><br><span class="line"> arguments += [<span class="string">"room.schemaLocation"</span>: <span class="string">"$projectDir/database"</span>.toString()]</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>字段重命名策略,如下</p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 原 category 列名,更该为 classify</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@RenameColumn(tableName = "libraries", fromColumnName = "category", toColumnName = "classify")</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Libraries1To2AutoMigration</span> <span class="keyword">implements</span> <span class="title class_">AutoMigrationSpec</span> {</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>修改对应表的实体字段</p></li><li><p>配置数据库的升级策略</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Database(entities = {Libraries.class}, </span></span><br><span class="line"><span class="meta"> // 更新当前数据库的版本</span></span><br><span class="line"><span class="meta"> version = 2,</span></span><br><span class="line"><span class="meta"> // 配置自动迁移策略</span></span><br><span class="line"><span class="meta"> autoMigrations = {@AutoMigration(from = 1, to = 2, spec = AppDatabase.Libraries1To2AutoMigration.class)}</span></span><br><span class="line"><span class="meta"> )</span></span><br></pre></td></tr></table></figure></li></ol><h3 id="数据迁移"><a href="#数据迁移" class="headerlink" title="数据迁移"></a>数据迁移</h3><p>做完了对原 Room 数据库表(<code>libraries</code>)的字段更新,接着分别使用两种方案对数据做迁移</p><h4 id="方案一"><a href="#方案一" class="headerlink" title="方案一"></a>方案一</h4><p>对于源数据我们只需要有查询操作,不涉及到对数据的写入,因此我们只需要关注抽象类 <code>SQLiteOpenHelper</code> 中 <code>getReadableDatabase()</code> 方法来打开我们的数据库</p><ol><li><p>我们先将 assets 路径下的数据库,Copy 存放在应用内部默认位置 <code>/data/data/<application package name>/databases</code> 下,应用启动时调用该方法</p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">packDataBase</span><span class="params">(Context context)</span> {</span><br><span class="line"> <span class="comment">// internal</span></span><br><span class="line"> <span class="meta">@SuppressLint("SdCardPath")</span></span><br><span class="line"> <span class="comment">// specify the path within the app</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">internalPath</span> <span class="operator">=</span> <span class="string">"/data/data/app.incoder.lawrefbook/databases"</span>;</span><br><span class="line"> <span class="comment">// source file</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">sqliteName</span> <span class="operator">=</span> <span class="string">"/db.sqlite3"</span>;</span><br><span class="line"> <span class="comment">// external</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">originPath</span> <span class="operator">=</span> <span class="string">"Laws"</span> + sqliteName;</span><br><span class="line"> <span class="comment">// check internal SQLite is exist</span></span><br><span class="line"> <span class="keyword">if</span> (!(<span class="keyword">new</span> <span class="title class_">File</span>(internalPath + sqliteName)).exists()) {</span><br><span class="line"> <span class="type">File</span> <span class="variable">f</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(internalPath);</span><br><span class="line"> <span class="comment">// if databases category is not exist</span></span><br><span class="line"> <span class="keyword">if</span> (!f.exists()) {</span><br><span class="line"> <span class="keyword">if</span> (f.mkdir()) {</span><br><span class="line"> System.out.println(<span class="string">"create databases"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="type">InputStream</span> <span class="variable">is</span> <span class="operator">=</span> context.getAssets().open(originPath);</span><br><span class="line"> <span class="type">OutputStream</span> <span class="variable">os</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(internalPath + sqliteName);</span><br><span class="line"> <span class="type">byte</span>[] buffer = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">1024</span>];</span><br><span class="line"> <span class="type">int</span> length;</span><br><span class="line"> <span class="keyword">while</span> ((length = is.read(buffer)) > <span class="number">0</span>) {</span><br><span class="line"> os.write(buffer, <span class="number">0</span>, length);</span><br><span class="line"> }</span><br><span class="line"> os.flush();</span><br><span class="line"> os.close();</span><br><span class="line"> is.close();</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>我们需要使用 Android SDK 内自带的 SQLite 相关的 API</p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* Sqlite3Helper</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* <span class="doctag">@author</span> : Jerry xu</span></span><br><span class="line"><span class="comment">* <span class="doctag">@since</span> : 2022/6/5 18:03</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Sqlite3Helper</span> <span class="keyword">extends</span> <span class="title class_">SQLiteOpenHelper</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DATABASE_NAME</span> <span class="operator">=</span> <span class="string">"db.sqlite3"</span>;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">DATABASE_VERSION</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Sqlite3Helper</span><span class="params">(<span class="meta">@Nullable</span> Context context)</span> {</span><br><span class="line"> <span class="built_in">super</span>(context, DATABASE_NAME, <span class="literal">null</span>, DATABASE_VERSION);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onCreate</span><span class="params">(SQLiteDatabase db)</span> {</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onUpgrade</span><span class="params">(SQLiteDatabase db, <span class="type">int</span> oldVersion, <span class="type">int</span> newVersion)</span> {</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>使用单例的方式,创建读取 SQLite 数据库内容的方法</p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* Sqlite3Dao</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* <span class="doctag">@author</span> : Jerry xu</span></span><br><span class="line"><span class="comment">* <span class="doctag">@since</span> : 2022/6/5 18:16</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Sqlite3Dao</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">volatile</span> SQLiteDatabase mLite;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取单例</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> Sqlite3Dao</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> Sqlite3Dao <span class="title function_">getInstance</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> SingletonHolder.INSTANCE;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 在访问数据库时创建单例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">SingletonHolder</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Sqlite3Dao</span> <span class="variable">INSTANCE</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Sqlite3Dao</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Sqlite3Dao</span><span class="params">()</span> {</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> SQLiteDatabase <span class="title function_">getSqlite</span><span class="params">(Context context)</span> {</span><br><span class="line"> <span class="keyword">if</span> (mLite == <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">synchronized</span> (Sqlite3Dao.class) {</span><br><span class="line"> <span class="keyword">if</span> (mLite == <span class="literal">null</span>) {</span><br><span class="line"> mLite = <span class="keyword">new</span> <span class="title class_">Sqlite3Helper</span>(context).getReadableDatabase();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> mLite;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> List<Category> <span class="title function_">categoryList</span><span class="params">(Context context)</span> {</span><br><span class="line"> <span class="keyword">return</span> getCategory(Sqlite3Dao.getInstance().getSqlite(context));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> List<Law> <span class="title function_">lawList</span><span class="params">(Context context, Integer categoryId)</span> {</span><br><span class="line"> <span class="keyword">return</span> getLaw(Sqlite3Dao.getInstance().getSqlite(context), categoryId);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 查询 category 表</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> sqlite sqlite</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 查询结果</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> List<Category> <span class="title function_">getCategory</span><span class="params">(SQLiteDatabase sqlite)</span> {</span><br><span class="line"> <span class="type">Cursor</span> <span class="variable">category</span> <span class="operator">=</span> sqlite.rawQuery(<span class="string">"SELECT * FROM category ORDER BY `order`"</span>, <span class="literal">null</span>);</span><br><span class="line"> List<Category> result = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"> <span class="keyword">if</span> (<span class="literal">null</span> != category) {</span><br><span class="line"> <span class="keyword">if</span> (category.moveToFirst()) {</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">id</span> <span class="operator">=</span> category.getInt(category.getColumnIndexOrThrow(<span class="string">"id"</span>));</span><br><span class="line"> <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> category.getString(category.getColumnIndexOrThrow(<span class="string">"name"</span>));</span><br><span class="line"> <span class="type">String</span> <span class="variable">folder</span> <span class="operator">=</span> category.getString(category.getColumnIndexOrThrow(<span class="string">"folder"</span>));</span><br><span class="line"> <span class="type">Integer</span> <span class="variable">subFolder</span> <span class="operator">=</span> category.getInt(category.getColumnIndexOrThrow(<span class="string">"isSubFolder"</span>));</span><br><span class="line"> <span class="type">String</span> <span class="variable">group</span> <span class="operator">=</span> category.getString(category.getColumnIndexOrThrow(<span class="string">"group"</span>));</span><br><span class="line"> <span class="type">Integer</span> <span class="variable">order</span> <span class="operator">=</span> category.getInt(category.getColumnIndexOrThrow(<span class="string">"order"</span>));</span><br><span class="line"> <span class="type">Category</span> <span class="variable">roomCategory</span> <span class="operator">=</span> Category.builder()</span><br><span class="line"> .id(id)</span><br><span class="line"> .name(name)</span><br><span class="line"> .folder(folder)</span><br><span class="line"> .isSubFolder(subFolder)</span><br><span class="line"> .group(group)</span><br><span class="line"> .order(order)</span><br><span class="line"> .build();</span><br><span class="line"> result.add(roomCategory);</span><br><span class="line"> } <span class="keyword">while</span> (category.moveToNext());</span><br><span class="line"> }</span><br><span class="line"> category.close();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 查询 law 表</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> sqlite sqlite</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 查询结果</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> List<Law> <span class="title function_">getLaw</span><span class="params">(SQLiteDatabase sqlite, Integer categoryIds)</span> {</span><br><span class="line"> Cursor law;</span><br><span class="line"> <span class="keyword">if</span> (categoryIds != <span class="literal">null</span>) {</span><br><span class="line"> law = sqlite.rawQuery(<span class="string">"SELECT * FROM law WHERE category_id = ? ORDER BY `order`"</span>, <span class="keyword">new</span> <span class="title class_">String</span>[]{categoryIds.toString()});</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> law = sqlite.rawQuery(<span class="string">"SELECT * FROM law ORDER BY `order`"</span>, <span class="literal">null</span>);</span><br><span class="line"> }</span><br><span class="line"> List<Law> result = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"> <span class="keyword">if</span> (<span class="literal">null</span> != law) {</span><br><span class="line"> <span class="keyword">if</span> (law.moveToFirst()) {</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> law.getString(law.getColumnIndexOrThrow(<span class="string">"id"</span>));</span><br><span class="line"> <span class="type">String</span> <span class="variable">level</span> <span class="operator">=</span> law.getString(law.getColumnIndexOrThrow(<span class="string">"level"</span>));</span><br><span class="line"> <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> law.getString(law.getColumnIndexOrThrow(<span class="string">"name"</span>));</span><br><span class="line"> <span class="type">String</span> <span class="variable">filename</span> <span class="operator">=</span> law.getString(law.getColumnIndexOrThrow(<span class="string">"filename"</span>));</span><br><span class="line"> <span class="type">String</span> <span class="variable">publish</span> <span class="operator">=</span> law.getString(law.getColumnIndexOrThrow(<span class="string">"publish"</span>));</span><br><span class="line"> <span class="type">String</span> <span class="variable">expired</span> <span class="operator">=</span> law.getString(law.getColumnIndexOrThrow(<span class="string">"expired"</span>));</span><br><span class="line"> <span class="type">Integer</span> <span class="variable">categoryId</span> <span class="operator">=</span> law.getInt(law.getColumnIndexOrThrow(<span class="string">"category_id"</span>));</span><br><span class="line"> <span class="type">Integer</span> <span class="variable">order</span> <span class="operator">=</span> law.getInt(law.getColumnIndexOrThrow(<span class="string">"order"</span>));</span><br><span class="line"> <span class="type">String</span> <span class="variable">subtitle</span> <span class="operator">=</span> law.getString(law.getColumnIndexOrThrow(<span class="string">"subtitle"</span>));</span><br><span class="line"> <span class="type">String</span> <span class="variable">validFrom</span> <span class="operator">=</span> law.getString(law.getColumnIndexOrThrow(<span class="string">"valid_from"</span>));</span><br><span class="line"> <span class="type">Law</span> <span class="variable">roomLaw</span> <span class="operator">=</span> Law.builder()</span><br><span class="line"> .id(id)</span><br><span class="line"> .level(level)</span><br><span class="line"> .name(name)</span><br><span class="line"> .filename(filename)</span><br><span class="line"> .publish(publish)</span><br><span class="line"> .expired(expired)</span><br><span class="line"> .categoryId(categoryId)</span><br><span class="line"> .order(order)</span><br><span class="line"> .subtitle(subtitle)</span><br><span class="line"> .validFrom(validFrom)</span><br><span class="line"> .build();</span><br><span class="line"> result.add(roomLaw);</span><br><span class="line"> } <span class="keyword">while</span> (law.moveToNext());</span><br><span class="line"> }</span><br><span class="line"> law.close();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ol><p>Law,Category 为数据库对应的实体表</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Builder</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Category</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> id;</span><br><span class="line"> <span class="keyword">private</span> String name;</span><br><span class="line"> <span class="keyword">private</span> String folder;</span><br><span class="line"> <span class="keyword">private</span> Integer isSubFolder;</span><br><span class="line"> <span class="keyword">private</span> String group;</span><br><span class="line"> <span class="keyword">private</span> Integer order;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Builder</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Law</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> String id;</span><br><span class="line"> <span class="comment">/*** 法律效力位阶 */</span></span><br><span class="line"> <span class="keyword">private</span> String level;</span><br><span class="line"> <span class="keyword">private</span> String name;</span><br><span class="line"> <span class="keyword">private</span> String filename;</span><br><span class="line"> <span class="comment">/*** 施行日期 */</span></span><br><span class="line"> <span class="keyword">private</span> String publish;</span><br><span class="line"> <span class="comment">/*** 时效性 0-有效,1-已修改(已废止)*/</span></span><br><span class="line"> <span class="keyword">private</span> String expired;</span><br><span class="line"> <span class="keyword">private</span> Integer categoryId;</span><br><span class="line"> <span class="keyword">private</span> Integer order;</span><br><span class="line"> <span class="keyword">private</span> String subtitle;</span><br><span class="line"> <span class="comment">/*** 公布日期 */</span></span><br><span class="line"> <span class="keyword">private</span> String validFrom;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="方案二"><a href="#方案二" class="headerlink" title="方案二"></a>方案二</h4><p>迁移 SQLite 数据到 Room 数据库,可以按照如下步骤进行</p><ol><li><p>项目添加 Room 相关的依赖,由于项目之前使用过 Room,这里就不重复添加 Room 的相关依赖</p></li><li><p>更改源 SQLite 数据库表的实例对象为 Room 数据库对象,按需添加 Room 的 <a href="https://developer.android.google.cn/reference/androidx/room/Entity">androidx.room</a> 路径下相关的注解即可</p><ul><li>Category 数据库对象</li></ul> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Builder</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Category</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> id;</span><br><span class="line"> <span class="keyword">private</span> String name;</span><br><span class="line"> <span class="keyword">private</span> String folder;</span><br><span class="line"> <span class="keyword">private</span> Integer isSubFolder;</span><br><span class="line"> <span class="keyword">private</span> String group;</span><br><span class="line"> <span class="keyword">private</span> Integer order;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>Category 数据库对象</li></ul> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Builder</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Category</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> id;</span><br><span class="line"> <span class="keyword">private</span> String name;</span><br><span class="line"> <span class="keyword">private</span> String folder;</span><br><span class="line"> <span class="keyword">private</span> Integer isSubFolder;</span><br><span class="line"> <span class="keyword">private</span> String group;</span><br><span class="line"> <span class="keyword">private</span> Integer order;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>定义访问 Room 数据库的方法(DAO)</p></li><li><p>创建数据库对象类</p><blockquote><p>由于项目的收藏已创建过 Room 数据库(<code>lawre_room</code>)的实例,因想把法律相关的表和收藏的表放在同一个数据库里,因此这里就不再新建 Room 的数据库对象类</p></blockquote></li><li><p>定义迁移路径</p></li><li><p>更新数据库实例</p></li><li><p>验证数据查看结果</p></li></ol><p>由于迁移数据,只会再首次打开或首次更新后才做数据 SQLite 数据迁移到 Room,因此需要对应用数据迁移的触发机制做好处理</p><h3 id="数据填充"><a href="#数据填充" class="headerlink" title="数据填充"></a>数据填充</h3><p>适用于应用没有历史 Room 数据库,使用 SQLite 数据全量填充 Room 数据库</p><p><a href="https://developer.android.google.cn/training/data-storage/room/prepopulate#from-asset">https://developer.android.google.cn/training/data-storage/room/prepopulate#from-asset</a></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">synchronized</span> AppDatabase <span class="title function_">getInstance</span><span class="params">(Context context)</span> {</span><br><span class="line"> <span class="keyword">if</span> (INSTANCE == <span class="literal">null</span>) {</span><br><span class="line"> INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, <span class="string">"lawre_room"</span>)</span><br><span class="line"> .createFromAsset(<span class="string">"database/db.sqlite3"</span>)</span><br><span class="line"> <span class="comment">// sql log</span></span><br><span class="line"><span class="comment">// .setJournalMode(JournalMode.TRUNCATE)</span></span><br><span class="line"> <span class="comment">// 破坏式迁移</span></span><br><span class="line"><span class="comment">// .fallbackToDestructiveMigration()</span></span><br><span class="line"> <span class="comment">// 迁移策略</span></span><br><span class="line"><span class="comment">// .addMigrations(MIGRATION1_2)</span></span><br><span class="line"> .build();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> INSTANCE;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="执行-SQL-输出"><a href="#执行-SQL-输出" class="headerlink" title="执行 SQL 输出"></a>执行 SQL 输出</h3><p>在开发过程中,我们有时需要输出当前执行的操作和预期的 SQL 语句是否一致</p><h2 id="ScrollingActivity"><a href="#ScrollingActivity" class="headerlink" title="ScrollingActivity"></a>ScrollingActivity</h2><p>文章详情使用了 ScrollingActivity 作为基础布局,它是 Android Studio 提供的一个模版化的 CoordinatorLayout 组合 Activity,这里使用遇到了以下两个问题</p><ol><li>不同文章的标题长度不一样,在页面中如何动态调整显示的字数</li><li>配合目录进行定位文章内容时,滚动的效果</li></ol><h3 id="动态标题"><a href="#动态标题" class="headerlink" title="动态标题"></a>动态标题</h3><h3 id="滚动指定位置"><a href="#滚动指定位置" class="headerlink" title="滚动指定位置"></a>滚动指定位置</h3><p><a href="https://blog.csdn.net/weimingjue/article/details/82805361">纠正:Android RecyclerView滚动到指定位置并置顶(滚动方法、移动置顶、定位滑动到指定位置item)</a><br><a href="https://juejin.cn/post/6844904143560114189">RecyclerView滚动到指定位置的一种姿势。</a></p><p><a href="http://www.javashuo.com/article/p-vjubcjrs-ha.html">RecyclerView 滑动到指定位置的终极方案</a><br><a href="https://blog.csdn.net/HHHceo/article/details/119946385">Android RecyclerView滚动到指定位置并且置顶方案</a><br><a href="https://www.jianshu.com/p/6d5ecfdbb615">RecyclerView滚动到指定位置</a></p><h2 id="动态文本生成图片"><a href="#动态文本生成图片" class="headerlink" title="动态文本生成图片"></a>动态文本生成图片</h2><p>生成图片时,主要需要考虑</p><ol><li>如何自定义 View,且根据内容自适应样式</li><li>如何快速生成图片,避免应用无响应卡死</li><li>如何生成高质量图片文件,且不会超过应用限制</li></ol><p><a href="https://blog.csdn.net/xuqiqiang1993/article/details/67636165">Android StaticLayout实现主流便签内容生成长图功能</a><br><a href="https://blog.csdn.net/u013278940/article/details/52193022">Android 仿各主流便签内容生成长图功能</a><br><a href="https://www.jianshu.com/p/44cc5f3f8de0">Android 根据View生成图片简易参考</a></p><h2 id="更新数据源"><a href="#更新数据源" class="headerlink" title="更新数据源"></a>更新数据源</h2><p>在 1.1 版本的开发过程中,开始使用 submodule 方式来依赖数据源,由于是离线应用,如果数据源更新后,必须得更新应用才能看到新的内容,个人觉得频繁的更新整个应用是不太友好,因此我的方案是做一个自动更新开关</p><ol><li>开启自动更新,则有新版本,就全量更新整个应用</li><li>关闭自动更新,则用户可以自行导入数据源文件,只进行更新应用的数据</li></ol><p><a href="https://www.jianshu.com/p/622f479ac132">如何直接下载 Github 上的某个文件</a></p><p><a href="https://blog.csdn.net/u010168781/article/details/103291193">sqlite中的限制:数据库大小、表数、列数、行数、参数个数、连接数等</a><br><a href="https://blog.csdn.net/libaineu2004/article/details/108815466">sqlite3数据库最大可以是多大?可以存放多少数据?读写性能怎么样?</a><br><a href="https://www.jianshu.com/p/30b18212df8d">SQLite学习八、TEXT类型读取超过2M的字符串</a></p><p><a href="https://raw.githubusercontent.com/IncoderApp/LawRefBook/main/gradlew.bat">https://raw.githubusercontent.com/IncoderApp/LawRefBook/main/gradlew.bat</a></p><p><a href="https://api.github.com/repos/IncoderApp/LawRefBook/tags">https://api.github.com/repos/IncoderApp/LawRefBook/tags</a></p><h3 id="macOS-下-Python-路径问题"><a href="#macOS-下-Python-路径问题" class="headerlink" title="macOS 下 Python 路径问题"></a>macOS 下 Python 路径问题</h3><p><a href="https://www.zhihu.com/question/420273182">为什么 macOS 在 /usr/bin/ 下会有 python3?</a></p><h2 id="Github-Action"><a href="#Github-Action" class="headerlink" title="Github Action"></a>Github Action</h2><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">name:</span> <span class="string">Android</span> <span class="string">CI</span></span><br><span class="line"></span><br><span class="line"><span class="attr">on:</span> [<span class="string">push</span>]</span><br><span class="line"></span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line"> <span class="attr">build:</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">steps:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">uses:</span> <span class="string">actions/checkout@v1</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">set</span> <span class="string">up</span> <span class="string">JDK</span> <span class="number">1.11</span></span><br><span class="line"> <span class="attr">uses:</span> <span class="string">actions/setup-java@v1</span></span><br><span class="line"> <span class="attr">with:</span></span><br><span class="line"> <span class="attr">java-version:</span> <span class="number">1.11</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Build</span> <span class="string">with</span> <span class="string">Gradle</span></span><br><span class="line"> <span class="attr">run:</span> <span class="string">./gradlew</span> <span class="string">build</span> <span class="string">check</span></span><br></pre></td></tr></table></figure><h2 id="Contribution"><a href="#Contribution" class="headerlink" title="Contribution"></a>Contribution</h2><p><a href="https://www.conventionalcommits.org/en/v1.0.0/">Conventional Commits</a><br><a href="https://juejin.cn/post/6844903606815064077">优雅的提交你的 Git Commit Message</a></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ol><li><a href="https://blog.csdn.net/u011974987/article/details/80839758">RecyclerView 滑动到指定位置的终极方案</a></li><li><a href="https://www.cnblogs.com/qynprime/p/9284841.html">RecyclerView 滑动到指定位置,并置顶</a></li><li><a href="https://blog.csdn.net/u012045061/article/details/69568807">利用 CollapsingToolbarLayout 完成联动的动画效果</a></li><li><a href="https://blog.csdn.net/a8341025123/article/details/53006865">CoordinatorLayout与CollapsingToolbarLayout实现视差滚动动画和Toolbar滚动</a></li><li><a href="https://blog.csdn.net/fanhenghao/article/details/105201110">CoordinatorLayout实现页面滚动动画效果</a></li><li><a href="https://blog.csdn.net/xx326664162/article/details/103921480">Github Actions 使用指南和Android 持续集成示例</a></li><li><a href="https://medium.com/androiddevelopers/database-inspector-9e91aa265316">Database Inspector</a></li><li><a href="https://juejin.cn/post/6844903582727143438">从 SQLite 逐步迁移到 Room</a></li></ol>]]></content>
<categories>
<category> Android </category>
</categories>
<tags>
<tag> 中国法律 </tag>
</tags>
</entry>
<entry>
<title>Lawrefbook</title>
<link href="/2022/06/01/lawrefbook/"/>
<url>/2022/06/01/lawrefbook/</url>
<content type="html"><![CDATA[<p>简单来概括《中国法律》应用是一款可以离线查看的法律快查应用</p><table><thead><tr><th align="center">兼容</th><th align="center">JDK</th><th align="center">编译</th></tr></thead><tbody><tr><td align="center"><a href="https://developer.android.google.cn/reference"><img src="https://img.shields.io/badge/Compatibleby-SDK%2024%20~%2031-06?logo=Android&labelColor=02303A"></a></td><td align="center"><a href="https://www.oracle.com/cn/java/technologies/javase/javase-jdk8-downloads.html"><img src="https://img.shields.io/badge/Use%20up%20by-JDK%201.8+-important?logo=openjdk&labelColor=02303A"></a></td><td align="center"><a href="https://docs.gradle.org/7.3.3/release-notes.html"><img src="https://img.shields.io/badge/Build%20up%20by-Gradle%207.3.3%20bin-06A0CE?logo=Gradle&labelColor=02303A"></a></td></tr></tbody></table><div class="note info flat"><p>开发环境说明,<a href="https://developer.android.google.cn/studio/releases/past-releases?hl=zh-cn#4-2-0">Android Studio 4.2 及以上版本,JDK 必须是 JDK11</a> ,因此该项目的 JDK 版本取决于你的运行环境,低版本 Android Studio,要求 JDK8+ 及以上即可</p></div><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=530 height=320 src="//music.163.com/outchain/player?type=4&id=966568618&auto=0&height=320"></iframe><p>五月迷上了网易云一个名为《<a href="http://music.163.com/radio/?id=966568618&userid=34509906">法外狂徒张三</a>》的电台,众所周知 “法外狂徒张三” 是罗老师口中常常为了讲解法律而虚拟的人物,最早看过几个罗老师的视频,印象很深,把枯燥无味的法律教条,以故事结合时事让我这个没有任何法律学习基础的非专业人士,也能听得入神,不得不被罗老师的幽默风趣的文学和对法律的严谨折服。引导了我偶尔也会去看看法律的文字了,翻着翻着,这不巧了嘛,在 Github 上看到了一个整理了相关法律的数据项目,已经有了 iOS 版本的<a href="https://apps.apple.com/app/id1612953870">中国法律快查手册</a>,也有了 <a href="https://lawrefbook.github.io/">Web 网站</a>,就差一个 Android 应用了。那就让我这个曾经的 Android 开发选手来为之添砖加瓦,于是有了现在的这个项目</p><p>项目遵循极简的项目依赖,简约的页面设计,实用的功能,就这样,一鼓作气在业余时间完成了项目开发。当然这里面还有很多的优化完善空间,但依然按捺不住想写一篇文章记录这个项目</p><h2 id="页面预览"><a href="#页面预览" class="headerlink" title="页面预览"></a>页面预览</h2><table><thead><tr><th align="center">主页</th><th align="center">内容页</th><th align="center">个人页</th></tr></thead><tbody><tr><td align="center"><img src="https://res.cloudinary.com/incoder/image/upload/v1654225895/incoderapp/lawrefbook/feed.jpg"></td><td align="center"><img src="https://res.cloudinary.com/incoder/image/upload/v1654225910/incoderapp/lawrefbook/article.jpg"></td><td align="center"><img src="https://res.cloudinary.com/incoder/image/upload/v1654225840/incoderapp/lawrefbook/about.jpg"></td></tr><tr><td align="center">历史</td><td align="center">目录</td><td align="center">收藏</td></tr><tr><td align="center"><img src="https://res.cloudinary.com/incoder/image/upload/v1654225919/incoderapp/lawrefbook/history.jpg"></td><td align="center"><img src="https://res.cloudinary.com/incoder/image/upload/v1654225900/incoderapp/lawrefbook/catalog.jpg"></td><td align="center"><img src="https://res.cloudinary.com/incoder/image/upload/v1654225863/incoderapp/lawrefbook/favorite.jpg"></td></tr><tr><td align="center">搜索文章</td><td align="center">搜索内容</td><td align="center">等等</td></tr><tr><td align="center"><img src="https://res.cloudinary.com/incoder/image/upload/v1654225818/incoderapp/lawrefbook/title-search.jpg"></td><td align="center"><img src="https://res.cloudinary.com/incoder/image/upload/v1654225819/incoderapp/lawrefbook/article-search.jpg"></td><td align="center">……</td></tr></tbody></table><h2 id="应用下载"><a href="#应用下载" class="headerlink" title="应用下载"></a>应用下载</h2><ol><li><a href="https://play.google.com/store/apps/details?id=app.incoder.lawrefbook">Google Play</a></li><li>F-Droid</li><li><a href="https://github.com/IncoderApp/LawRefBook/releases">Github Release</a></li></ol><h2 id="开发寄语"><a href="#开发寄语" class="headerlink" title="开发寄语"></a>开发寄语</h2><ol><li>离线使用;</li><li>遵循 Material Design 风格;</li><li>上架 F-Droid,Google Play 应用市场;</li><li>完全开源,如有需要请遵循开源协议自行提取</li></ol><blockquote><p>应用还有很多需要优化的点,我会继续努力 💪</p></blockquote><h2 id="更新日志"><a href="#更新日志" class="headerlink" title="更新日志"></a>更新日志</h2><h3 id="1-0-0(20220601)"><a href="#1-0-0(20220601)" class="headerlink" title="1.0.0(20220601)"></a>1.0.0(20220601)</h3><ol><li>使用 <a href="https://github.com/LawRefBook/Laws">Laws</a> 项目作为数据源,聚合 <a href="https://flk.npc.gov.cn/">国家法律法规数据库</a>,<a href="https://www.court.gov.cn/">最高人民法院</a> 网站数据</li><li>离线使用</li><li>全文,片段法条收藏</li><li>文本分享</li><li>层级目录</li><li>高亮检索</li></ol><h3 id="1-1-0(20230406)"><a href="#1-1-0(20230406)" class="headerlink" title="1.1.0(20230406)"></a>1.1.0(20230406)</h3><ol><li><input checked="" disabled="" type="checkbox"> 数据 <a href="https://github.com/LawRefBook/Laws/tree/9f3c74b6714e8c3e6514d3b5e56c45d6b2c4065d">9f3c74b6</a> 按照法条解析</li><li><input checked="" disabled="" type="checkbox"> 目录可定位</li><li><input checked="" disabled="" type="checkbox"> 内容标题剧中显示</li><li><input checked="" disabled="" type="checkbox"> 分享内容生成图片(限制数量:3条法条)</li><li><input disabled="" type="checkbox"> 应用上架 F-Droid</li></ol><h3 id="1-2-0"><a href="#1-2-0" class="headerlink" title="1.2.0"></a>1.2.0</h3><ol><li><input disabled="" type="checkbox"> 平板支持</li><li><input disabled="" type="checkbox"> 自定义可展示分类</li><li><input disabled="" type="checkbox"> 目录与内容互相联动</li><li><input disabled="" type="checkbox"> 手动导入数据源选项</li></ol><blockquote><p><a href="https://github.com/IncoderApp/Laws">重构数据源数据结构</a>,解析源数据生成离线 SQLite3 可手动导入数据</p></blockquote><h3 id="1-2-1"><a href="#1-2-1" class="headerlink" title="1.2.1"></a>1.2.1</h3><ol><li><input disabled="" type="checkbox"> 黑夜模式</li><li><input disabled="" type="checkbox"> 可自定义主题</li><li><input disabled="" type="checkbox"> 文字大小调整</li><li><input disabled="" type="checkbox"> 行间距调整</li><li><input disabled="" type="checkbox"> 法条间距调整</li><li><input disabled="" type="checkbox"> 超长文章标题滚动显示</li></ol><h2 id="隐私协议"><a href="#隐私协议" class="headerlink" title="隐私协议"></a>隐私协议</h2><p>项目未集成第三方内库,所以没有任何的隐私问题,且项目完全开源</p><h2 id="致谢"><a href="#致谢" class="headerlink" title="致谢"></a>致谢</h2><p>本项目特别感谢 <a href="https://github.com/LawRefBook/Laws">@RanKKI</a> 提供开源数据,并使用了 <a href="https://github.com/RanKKI/LawRefBook">LawRefBook</a> 项目 icon,以及部分功能参考</p>]]></content>
<categories>
<category> Android </category>
</categories>
<tags>
<tag> 中国法律 </tag>
</tags>
</entry>
<entry>
<title>Github Action 自动部署网站</title>
<link href="/2021/06/29/github-action-auto/"/>
<url>/2021/06/29/github-action-auto/</url>
<content type="html"><![CDATA[<p>简单来说 <a href="https://github.com/features/actions">Github Action</a> 是 <a href="https://github.com/">GitHub</a> 提供的一项支持自动化构建项目,发布项目等一些列自动化的 CI 工具,在一定程度上让我们的开发工作,发布工作更加优雅和安全(只需要配置好相关的执行脚本),让我们更加专注于你所作事情的本身内容</p><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>和之前构建个人博客一样,将源码和生成的静态网站文件都放在同一个 Github 仓库中,master(或 main) 分支存放编译后的产物,hexo 存放项目的源代码,不同的是这次是直接依赖于 <a href="https://github.com/features/actions">Github Action</a> 来进行构建和发布,网站使用 <a href="https://www.yarnpkg.cn/">Yarn</a> 进行包管理</p><h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><h3 id="添加构建脚本"><a href="#添加构建脚本" class="headerlink" title="添加构建脚本"></a>添加构建脚本</h3><p>在项目源码分支的根目录,添加 <mark class="hl-label blue">.github</mark> ,<mark class="hl-label blue">workflows</mark> 文件夹,并添加 <mark class="hl-label pink">file-name.yml</mark> 文件(我这里命名为 action.yml),结构如下</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">incoderapp.github.io/</span><br><span class="line"> ├── .github/workflows/ # action 相关文件夹</span><br><span class="line"> │ └── action.yml # 执行任务配置文件</span><br><span class="line"> ├── scaffolds/</span><br><span class="line"> ├── source/</span><br><span class="line"> ├── themes/</span><br><span class="line"> ├── _config.butterfly.yml</span><br><span class="line"> ├── _config.yml</span><br><span class="line"> ├── .gitignore</span><br><span class="line"> ├── LICENSE</span><br><span class="line"> ├── package.json</span><br><span class="line"> ├── README.md</span><br><span class="line"> └── yarn.lock</span><br></pre></td></tr></table></figure><p>action.yml 文件的配置命令如下所示</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">name:</span> <span class="string">CI</span></span><br><span class="line"><span class="attr">on:</span></span><br><span class="line"> <span class="attr">push:</span></span><br><span class="line"> <span class="attr">branches:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">hexo</span></span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line"> <span class="attr">build:</span></span><br><span class="line"> <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line"> <span class="attr">steps:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Checkout</span> <span class="string">branch</span></span><br><span class="line"> <span class="attr">uses:</span> <span class="string">actions/checkout@v2</span></span><br><span class="line"></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">dependencies</span></span><br><span class="line"> <span class="attr">if:</span> <span class="string">steps.cache.outputs.cache-hit</span> <span class="type">!=</span> <span class="string">'true'</span></span><br><span class="line"> <span class="attr">run:</span> <span class="string">yarn</span></span><br><span class="line"></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Hexo</span> <span class="string">clean</span></span><br><span class="line"> <span class="attr">uses:</span> <span class="string">heowc/action-hexo@main</span></span><br><span class="line"> <span class="attr">with:</span></span><br><span class="line"> <span class="attr">args:</span> <span class="string">clean</span></span><br><span class="line"></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Hexo</span> <span class="string">generate</span></span><br><span class="line"> <span class="attr">uses:</span> <span class="string">heowc/action-hexo@main</span></span><br><span class="line"> <span class="attr">with:</span></span><br><span class="line"> <span class="attr">args:</span> <span class="string">generate</span></span><br><span class="line"></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Deploy</span> <span class="string">to</span> <span class="string">master</span></span><br><span class="line"> <span class="attr">uses:</span> <span class="string">peaceiris/actions-gh-pages@v3</span></span><br><span class="line"> <span class="attr">with:</span></span><br><span class="line"> <span class="attr">personal_token:</span> <span class="string">${{</span> <span class="string">secrets.CI_TOKEN</span> <span class="string">}}</span></span><br><span class="line"> <span class="attr">external_repository:</span> <span class="string">IncoderApp/incoderapp.github.io</span></span><br><span class="line"> <span class="attr">publish_branch:</span> <span class="string">master</span></span><br><span class="line"> <span class="attr">publish_dir:</span> <span class="string">./public</span></span><br></pre></td></tr></table></figure><h3 id="添加相关配置"><a href="#添加相关配置" class="headerlink" title="添加相关配置"></a>添加相关配置</h3><p>我们需要在项目的 Settings 标签中添加一个 Secrets,用于部署访问仓库的 Token,关于生成项目的 Token</p><ul><li><kbd>Github</kbd>-><kbd>头像(右上角)</kbd>-><kbd>Settings</kbd>-><kbd>Developer Settings</kbd>-><kbd>Personal access tokens</kbd></li><li>勾选 <kbd>repo</kbd></li><li>复制生成的 tocken(生成的token只有一次可见机会,请妥善保存),添加到项目的 Settings 标签中 Secrets 选项,并命名为 <mark class="hl-label pink">CI_TOKEN</mark> </li></ul><h3 id="编译及测试"><a href="#编译及测试" class="headerlink" title="编译及测试"></a>编译及测试</h3><p>当我们有 push 动作到 hexo 分支,Github Action 会自动进行安装我们的构建任务执行,我们需要关注项目 Action 标签页,如果有错误,在 Action 标签页中查看相关的错误并解决,当然为了我们方便直观的查看项目的构建情况,我们可以按照官方给定的特殊写法,通过徽章的形式,方便的查看项目的构建结果,比如我这里就按照官方写法 <img src="https://github.com/IncoderApp/incoderapp.github.io/actions/workflows/action.yml/badge.svg?branch=hexo"></img></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://docs.github.com/cn/actions">Github Action 官方文档</a></li><li><a href="https://lovobin.github.io/2021/03/11/55808">Github-Actions-自动化部署总结</a></li><li><a href="https://docs.github.com/cn/actions/managing-workflow-runs/adding-a-workflow-status-badge">添加工作流程状态徽章</a></li></ul>]]></content>
<categories>
<category> Build </category>
</categories>
<tags>
<tag> Github Action </tag>
</tags>
</entry>
</search>