-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
231 lines (148 loc) · 156 KB
/
atom.xml
File metadata and controls
231 lines (148 loc) · 156 KB
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>小破站</title>
<subtitle>の日常操作</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://keee.top/"/>
<updated>2020-05-22T10:05:53.000Z</updated>
<id>https://keee.top/</id>
<author>
<name>Glimmer</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>60行代码不到用Python下载无水印抖音视频</title>
<link href="https://keee.top/2020/052213760.html"/>
<id>https://keee.top/2020/052213760.html</id>
<published>2020-05-22T10:05:53.000Z</published>
<updated>2020-05-22T10:05:53.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>用到的库 <code>BeautifulSoup</code> ,解析用的是 <code>html.parser</code></p></blockquote><h5 id="模拟-Android-手机浏览器-UA"><a href="#模拟-Android-手机浏览器-UA" class="headerlink" title="模拟 Android 手机浏览器 UA"></a>模拟 <code>Android</code> 手机浏览器 <code>UA</code></h5><figure class="highlight python"><table><tbody><tr><td class="code"><pre><span class="line">ua_phone = {<span class="string">'User-Agent'</span>: <span class="string">'Mozilla/5.0 (Linux; Android 6.0; '</span></span><br><span class="line"> <span class="string">'Nexus 5 Build/MRA58N) AppleWebKit/537.36 ('</span></span><br><span class="line"> <span class="string">'KHTML, like Gecko) Chrome/80.0.3987.116 Mobile Safari/537.36'</span>}</span><br></pre></td></tr></tbody></table></figure><h5 id="模拟-Windows-浏览器-UA"><a href="#模拟-Windows-浏览器-UA" class="headerlink" title="模拟 Windows 浏览器 UA"></a>模拟 <code>Windows</code> 浏览器 <code>UA</code></h5><figure class="highlight python"><table><tbody><tr><td class="code"><pre><span class="line">ua_win = {<span class="string">'User-Agent'</span>: <span class="string">'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '</span></span><br><span class="line"> <span class="string">'AppleWebKit/537.36 (KHTML, like Gecko) '</span></span><br><span class="line"> <span class="string">'Chrome/80.0.3987.116 Safari/537.36'</span>}</span><br></pre></td></tr></tbody></table></figure><h5 id="对分享的链接进行截取,获得标题跟视频播放的链接"><a href="#对分享的链接进行截取,获得标题跟视频播放的链接" class="headerlink" title="对分享的链接进行截取,获得标题跟视频播放的链接"></a>对分享的链接进行截取,获得标题跟视频播放的链接</h5><figure class="highlight python"><table><tbody><tr><td class="code"><pre><span class="line"><span class="comment"># 待解析的视频url</span></span><br><span class="line">parse_url = input(<span class="string">">>请输入抖音分享的完整链接:"</span>)</span><br><span class="line">str_http = <span class="string">"http"</span></span><br><span class="line"><span class="keyword">while</span> parse_url.find(str_http) == <span class="number">-1</span>:</span><br><span class="line"> parse_url = input(<span class="string">"请输入正确的链接:"</span>)</span><br><span class="line">title_index = parse_url.index(str_http)</span><br><span class="line">copy_index = len(parse_url)</span><br><span class="line">str_copy = <span class="string">"复制此链接"</span></span><br><span class="line"><span class="keyword">if</span> parse_url.find(str_copy) != <span class="number">-1</span>:</span><br><span class="line"> copy_index = parse_url.index(str_copy) - <span class="number">1</span></span><br><span class="line">title = parse_url[<span class="number">0</span>:title_index]</span><br><span class="line"><span class="keyword">while</span> len(title) == <span class="number">0</span>:</span><br><span class="line"> title = input(<span class="string">">>请输入视频标题:"</span>)</span><br><span class="line">url = parse_url[title_index:copy_index]</span><br><span class="line">print(<span class="string">f'>>原链接:<span class="subst">{url}</span>'</span>)</span><br></pre></td></tr></tbody></table></figure><a id="more"></a><h5 id="请求原链接进行解析得到有水印的链接跟无水印链接"><a href="#请求原链接进行解析得到有水印的链接跟无水印链接" class="headerlink" title="请求原链接进行解析得到有水印的链接跟无水印链接"></a>请求原链接进行解析得到有水印的链接跟无水印链接</h5><figure class="highlight python"><table><tbody><tr><td class="code"><pre><span class="line"><span class="comment"># 解析url</span></span><br><span class="line">url_source = requests.get(<span class="string">f'<span class="subst">{url}</span>'</span>, headers=ua_win)</span><br><span class="line">soup = BeautifulSoup(url_source.text, <span class="string">'html.parser'</span>)</span><br><span class="line">play_arr = soup.prettify().split(<span class="string">'playAddr: "'</span>, <span class="number">1</span>)</span><br><span class="line">url_origin = <span class="string">""</span></span><br><span class="line"><span class="keyword">if</span> len(play_arr) > <span class="number">1</span>:</span><br><span class="line"> str_temp = play_arr[<span class="number">1</span>]</span><br><span class="line"> index = str_temp.index(<span class="string">"\","</span>)</span><br><span class="line"> url_temp = str_temp[<span class="number">0</span>:index]</span><br><span class="line"> print(<span class="string">f'>>有水印链接:<span class="subst">{url_temp}</span>'</span>)</span><br><span class="line"> url_origin = url_temp.replace(<span class="string">"playwm"</span>, <span class="string">"play"</span>)</span><br><span class="line"> print(<span class="string">f'>>无水印链接:<span class="subst">{url_origin}</span>'</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> print(<span class="string">">>解析无水印链接失败"</span>)</span><br></pre></td></tr></tbody></table></figure><h5 id="进行视频下载保存到本地"><a href="#进行视频下载保存到本地" class="headerlink" title="进行视频下载保存到本地"></a>进行视频下载保存到本地</h5><figure class="highlight python"><table><tbody><tr><td class="code"><pre><span class="line"><span class="comment"># 下载视频</span></span><br><span class="line"><span class="keyword">if</span> len(url_origin) > <span class="number">0</span>:</span><br><span class="line"> print(<span class="string">">>开始下载..."</span>)</span><br><span class="line"> <span class="comment"># 拿到无水印链接</span></span><br><span class="line"> url_video = requests.get(</span><br><span class="line"> url_origin,</span><br><span class="line"> headers=ua_phone)</span><br><span class="line"> <span class="comment"># 无水印链接进行视频下载</span></span><br><span class="line"> save_path = <span class="string">f'<span class="subst">{local_path}</span><span class="subst">{title}</span>.mp4'</span></span><br><span class="line"> <span class="keyword">with</span> open(save_path, <span class="string">'wb'</span>) <span class="keyword">as</span> file:</span><br><span class="line"> file.write(url_video.content)</span><br><span class="line"> file.flush()</span><br><span class="line"> file.close() <span class="comment"># 关闭文件</span></span><br><span class="line"> print(<span class="string">f'>>下载完成:<span class="subst">{save_path}</span>'</span>)</span><br></pre></td></tr></tbody></table></figure><h5 id="效果展示"><a href="#效果展示" class="headerlink" title="效果展示"></a>效果展示</h5><p><img src="https://s1.ax1x.com/2020/05/20/YTZftf.jpg" alt="YTZftf.jpg"></p><p><img src="https://s1.ax1x.com/2020/05/20/YTmqYV.jpg" alt="YTmqYV.jpg"></p><h5 id="完整源码:"><a href="#完整源码:" class="headerlink" title="完整源码:"></a>完整源码:</h5><figure class="highlight python"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">from</span> bs4 <span class="keyword">import</span> BeautifulSoup</span><br><span class="line"></span><br><span class="line"><span class="comment"># 保存的本地路径</span></span><br><span class="line">local_path = <span class="string">"/Volumes/娱乐E/抖音/"</span></span><br><span class="line">ua_phone = {<span class="string">'User-Agent'</span>: <span class="string">'Mozilla/5.0 (Linux; Android 6.0; '</span></span><br><span class="line"> <span class="string">'Nexus 5 Build/MRA58N) AppleWebKit/537.36 ('</span></span><br><span class="line"> <span class="string">'KHTML, like Gecko) Chrome/80.0.3987.116 Mobile Safari/537.36'</span>}</span><br><span class="line">ua_win = {<span class="string">'User-Agent'</span>: <span class="string">'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '</span></span><br><span class="line"> <span class="string">'AppleWebKit/537.36 (KHTML, like Gecko) '</span></span><br><span class="line"> <span class="string">'Chrome/80.0.3987.116 Safari/537.36'</span>}</span><br><span class="line"><span class="comment"># 待解析的视频url</span></span><br><span class="line">parse_url = input(<span class="string">">>请输入抖音分享的完整链接:"</span>)</span><br><span class="line">str_http = <span class="string">"http"</span></span><br><span class="line"><span class="keyword">while</span> parse_url.find(str_http) == <span class="number">-1</span>:</span><br><span class="line"> parse_url = input(<span class="string">"请输入正确的链接:"</span>)</span><br><span class="line">title_index = parse_url.index(str_http)</span><br><span class="line">copy_index = len(parse_url)</span><br><span class="line">str_copy = <span class="string">"复制此链接"</span></span><br><span class="line"><span class="keyword">if</span> parse_url.find(str_copy) != <span class="number">-1</span>:</span><br><span class="line"> copy_index = parse_url.index(str_copy) - <span class="number">1</span></span><br><span class="line">title = parse_url[<span class="number">0</span>:title_index]</span><br><span class="line"><span class="keyword">while</span> len(title) == <span class="number">0</span>:</span><br><span class="line"> title = input(<span class="string">">>请输入视频标题:"</span>)</span><br><span class="line">url = parse_url[title_index:copy_index]</span><br><span class="line">print(<span class="string">f'>>原链接:<span class="subst">{url}</span>'</span>)</span><br><span class="line"><span class="comment"># 解析url</span></span><br><span class="line">url_source = requests.get(<span class="string">f'<span class="subst">{url}</span>'</span>, headers=ua_win)</span><br><span class="line">soup = BeautifulSoup(url_source.text, <span class="string">'html.parser'</span>)</span><br><span class="line">play_arr = soup.prettify().split(<span class="string">'playAddr: "'</span>, <span class="number">1</span>)</span><br><span class="line">url_origin = <span class="string">""</span></span><br><span class="line"><span class="keyword">if</span> len(play_arr) > <span class="number">1</span>:</span><br><span class="line"> str_temp = play_arr[<span class="number">1</span>]</span><br><span class="line"> index = str_temp.index(<span class="string">"\","</span>)</span><br><span class="line"> url_temp = str_temp[<span class="number">0</span>:index]</span><br><span class="line"> print(<span class="string">f'>>有水印链接:<span class="subst">{url_temp}</span>'</span>)</span><br><span class="line"> url_origin = url_temp.replace(<span class="string">"playwm"</span>, <span class="string">"play"</span>)</span><br><span class="line"> print(<span class="string">f'>>无水印链接:<span class="subst">{url_origin}</span>'</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> print(<span class="string">">>解析无水印链接失败"</span>)</span><br><span class="line"><span class="comment"># 下载视频</span></span><br><span class="line"><span class="keyword">if</span> len(url_origin) > <span class="number">0</span>:</span><br><span class="line"> print(<span class="string">">>开始下载..."</span>)</span><br><span class="line"> <span class="comment"># 拿到无水印链接</span></span><br><span class="line"> url_video = requests.get(</span><br><span class="line"> url_origin,</span><br><span class="line"> headers=ua_phone)</span><br><span class="line"> <span class="comment"># 无水印链接进行视频下载</span></span><br><span class="line"> save_path = <span class="string">f'<span class="subst">{local_path}</span><span class="subst">{title}</span>.mp4'</span></span><br><span class="line"> <span class="keyword">with</span> open(save_path, <span class="string">'wb'</span>) <span class="keyword">as</span> file:</span><br><span class="line"> file.write(url_video.content)</span><br><span class="line"> file.flush()</span><br><span class="line"> file.close() <span class="comment"># 关闭文件</span></span><br><span class="line"> print(<span class="string">f'>>下载完成:<span class="subst">{save_path}</span>'</span>)</span><br></pre></td></tr></tbody></table></figure><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<blockquote>
<p>用到的库 <code>BeautifulSoup</code> ,解析用的是 <code>html.parser</code></p>
</blockquote>
<h5 id="模拟-Android-手机浏览器-UA"><a href="#模拟-Android-手机浏览器-UA" class="headerlink" title="模拟 Android 手机浏览器 UA"></a>模拟 <code>Android</code> 手机浏览器 <code>UA</code></h5><figure class="highlight python"><table><tbody><tr><td class="code"><pre><span class="line">ua_phone = {<span class="string">'User-Agent'</span>: <span class="string">'Mozilla/5.0 (Linux; Android 6.0; '</span></span><br><span class="line"> <span class="string">'Nexus 5 Build/MRA58N) AppleWebKit/537.36 ('</span></span><br><span class="line"> <span class="string">'KHTML, like Gecko) Chrome/80.0.3987.116 Mobile Safari/537.36'</span>}</span><br></pre></td></tr></tbody></table></figure>
<h5 id="模拟-Windows-浏览器-UA"><a href="#模拟-Windows-浏览器-UA" class="headerlink" title="模拟 Windows 浏览器 UA"></a>模拟 <code>Windows</code> 浏览器 <code>UA</code></h5><figure class="highlight python"><table><tbody><tr><td class="code"><pre><span class="line">ua_win = {<span class="string">'User-Agent'</span>: <span class="string">'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '</span></span><br><span class="line"> <span class="string">'AppleWebKit/537.36 (KHTML, like Gecko) '</span></span><br><span class="line"> <span class="string">'Chrome/80.0.3987.116 Safari/537.36'</span>}</span><br></pre></td></tr></tbody></table></figure>
<h5 id="对分享的链接进行截取,获得标题跟视频播放的链接"><a href="#对分享的链接进行截取,获得标题跟视频播放的链接" class="headerlink" title="对分享的链接进行截取,获得标题跟视频播放的链接"></a>对分享的链接进行截取,获得标题跟视频播放的链接</h5><figure class="highlight python"><table><tbody><tr><td class="code"><pre><span class="line"><span class="comment"># 待解析的视频url</span></span><br><span class="line">parse_url = input(<span class="string">"&gt;&gt;请输入抖音分享的完整链接:"</span>)</span><br><span class="line">str_http = <span class="string">"http"</span></span><br><span class="line"><span class="keyword">while</span> parse_url.find(str_http) == <span class="number">-1</span>:</span><br><span class="line"> parse_url = input(<span class="string">"请输入正确的链接:"</span>)</span><br><span class="line">title_index = parse_url.index(str_http)</span><br><span class="line">copy_index = len(parse_url)</span><br><span class="line">str_copy = <span class="string">"复制此链接"</span></span><br><span class="line"><span class="keyword">if</span> parse_url.find(str_copy) != <span class="number">-1</span>:</span><br><span class="line"> copy_index = parse_url.index(str_copy) - <span class="number">1</span></span><br><span class="line">title = parse_url[<span class="number">0</span>:title_index]</span><br><span class="line"><span class="keyword">while</span> len(title) == <span class="number">0</span>:</span><br><span class="line"> title = input(<span class="string">"&gt;&gt;请输入视频标题:"</span>)</span><br><span class="line">url = parse_url[title_index:copy_index]</span><br><span class="line">print(<span class="string">f'&gt;&gt;原链接:<span class="subst">{url}</span>'</span>)</span><br></pre></td></tr></tbody></table></figure>
</summary>
<category term="Python" scheme="https://keee.top/categories/Python/"/>
<category term="抖音" scheme="https://keee.top/tags/%E6%8A%96%E9%9F%B3/"/>
</entry>
<entry>
<title>Android常见UI单位</title>
<link href="https://keee.top/2020/052216966.html"/>
<id>https://keee.top/2020/052216966.html</id>
<published>2020-05-22T10:05:53.000Z</published>
<updated>2020-05-22T10:05:53.000Z</updated>
<content type="html"><![CDATA[<h4 id="UI尺寸单位"><a href="#UI尺寸单位" class="headerlink" title="UI尺寸单位"></a>UI尺寸单位</h4><p>Android 中 <code>TypedValue.applyDimension</code> 单位转换的源码:</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TypedValue</span> </span>{</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">float</span> <span class="title">applyDimension</span><span class="params">(<span class="keyword">int</span> unit, <span class="keyword">float</span> value, DisplayMetrics metrics)</span> </span>{</span><br><span class="line"> <span class="keyword">switch</span> (unit) {</span><br><span class="line"> <span class="keyword">case</span> COMPLEX_UNIT_PX:</span><br><span class="line"> <span class="keyword">return</span> value;</span><br><span class="line"> <span class="keyword">case</span> COMPLEX_UNIT_DIP:</span><br><span class="line"> <span class="keyword">return</span> value * metrics.density;</span><br><span class="line"> <span class="keyword">case</span> COMPLEX_UNIT_SP:</span><br><span class="line"> <span class="keyword">return</span> value * metrics.scaledDensity;</span><br><span class="line"> <span class="keyword">case</span> COMPLEX_UNIT_PT:</span><br><span class="line"> <span class="keyword">return</span> value * metrics.xdpi * (<span class="number">1.0f</span>/<span class="number">72</span>);</span><br><span class="line"> <span class="keyword">case</span> COMPLEX_UNIT_IN:</span><br><span class="line"> <span class="keyword">return</span> value * metrics.xdpi;</span><br><span class="line"> <span class="keyword">case</span> COMPLEX_UNIT_MM:</span><br><span class="line"> <span class="keyword">return</span> value * metrics.xdpi * (<span class="number">1.0f</span>/<span class="number">25.4f</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><a id="more"></a><p>涉及到的单位:</p><ul><li><p>px</p><blockquote><p>像素单位,1px 代表一个像素点,例如我们常说的手机屏幕分辨率是 1080x1920,指的就是该手机宽度有 1080 个像素点,高度有 1920 个像素点。</p></blockquote></li><li><p>dp</p><blockquote><p>也叫 dip,全称 device independent pixels,即<code>设备无关像素</code>,这种尺寸单位在不同设备上的物理大小是相同的,就是说在<strong>屏幕尺寸相同</strong>、但不同的分辨率的手机上,用dp标识的东西,显示的大小是一样的。在每英寸 160 点(一个规定的标准值)的显示屏上,1dp = 1px=1/160 inch,px 跟 dp 的关系是 px = density * dp,density 指的是屏幕密度,它的大小跟屏幕尺寸是无关的。</p></blockquote></li><li><p>sp</p><blockquote><p>全称 Scale-independent Pixels ,与 dp 单位相似,会根据用户的字体大小偏好(手机系统里面有统一字体大小设置)进行缩放,谷歌推荐字体单位就是它。</p></blockquote></li><li><p>pt</p><blockquote><p>1pt=1/72 inch</p></blockquote></li><li><p>inch</p><blockquote><p>英寸</p></blockquote></li><li><p>mm</p><blockquote><p>毫米</p></blockquote></li></ul><h4 id="其它相关"><a href="#其它相关" class="headerlink" title="其它相关"></a>其它相关</h4><ul><li><p>dpi</p><blockquote><p>全称 Dots Per Inch,指的是每英寸<strong>点数(Dot)</strong>。计算公式: $$\frac{\sqrt{宽^2(单位px)+高^2(单位px)}}{屏幕尺寸(单位inch)}$$,例如小米 Mix 2s,屏幕分辨率为 1920x1080, 屏幕尺寸为 5.99,那么按照公式计算出它的 dpi 为 368(367.76…),density 为dpi/160=2.29。</p></blockquote><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">val</span> densityDpi = resources.displayMetrics.densityDpi</span><br></pre></td></tr></tbody></table></figure><p><code>ldpi</code>:dpi范围为0 ~ 120</p><p><code>mdpi</code>:dpi范围为121 ~ 160</p><p><code>hdpi</code>:dpi范围为161 ~ 240</p><p><code>xhdpi</code>:dpi范围为241~320</p><p><code>xxhdpi</code>:dpi范围为321~480</p><p><code>xxxhdpi</code>:dpi范围为481及以上</p></li><li><p>xdpi</p><blockquote><p>水平方向上每英寸容纳的点的数量。</p></blockquote></li><li><p>ydpi</p><blockquote><p>垂直方向上每英寸容纳的点的数量。</p></blockquote></li><li><p>ppi</p><blockquote><p>全称 Pixels Per Inch,指的是每英寸<strong>像素数(Pixel)</strong>,数值越大显示越细腻。计算式:ppi = 屏幕对角线像素数 / 屏幕对角线长度。例如上一个例子,小米 Mix 2s,屏幕分辨率为 1920x1080,那么它的对角线像素为 2203,那么它的 ppi=2203/5.99≈368。</p></blockquote><p>DPI 面向的是印刷受体,而PPI面向的是屏幕受体。例如电脑屏幕分辨率为 1920×1080,如果这个电脑屏幕是19.2 英寸,那么电脑屏幕的 PPI=1920/19.2=100PPI;又例如我们现在有张图片分辨率为 1920×1080,如果印刷设备的解析能力刚好是 100DPI,而你要印制的纸张尺寸刚好是 19.2 英寸,那么印刷设备就可以刚好把一个像素作为一个取样点,印刷完成后图片的保真度是百分之百(也就是图片所有的视觉信息都被印刷出来了)。在大多数情况下,这几个数值都不那么整好,因此保真度会产生损失。</p></li><li><p>density</p><blockquote><p>屏幕密度,<strong>它的大小跟手机屏幕大小没有关系</strong>,它与 dpi 的关系是 density=dpi/160。</p></blockquote><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">val</span> density = resources.displayMetrics.density</span><br></pre></td></tr></tbody></table></figure></li></ul><ul><li><p>scaledDensity</p><blockquote><p>默认情况下跟 density 值是一样的,此时 dp 跟 sp 为单位显示的大小是一致的,如果手机系统文字大小设置更改后会发生改变,sp 为单位的时候就会根据这个进行相应的缩放,dp 跟 sp 就不一致了。</p></blockquote><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">val</span> scaledDensity = resources.displayMetrics.scaledDensity</span><br></pre></td></tr></tbody></table></figure></li></ul><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<h4 id="UI尺寸单位"><a href="#UI尺寸单位" class="headerlink" title="UI尺寸单位"></a>UI尺寸单位</h4><p>Android 中 <code>TypedValue.applyDimension</code> 单位转换的源码:</p>
<figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TypedValue</span> </span>{</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">float</span> <span class="title">applyDimension</span><span class="params">(<span class="keyword">int</span> unit, <span class="keyword">float</span> value, DisplayMetrics metrics)</span> </span>{</span><br><span class="line"> <span class="keyword">switch</span> (unit) {</span><br><span class="line"> <span class="keyword">case</span> COMPLEX_UNIT_PX:</span><br><span class="line"> <span class="keyword">return</span> value;</span><br><span class="line"> <span class="keyword">case</span> COMPLEX_UNIT_DIP:</span><br><span class="line"> <span class="keyword">return</span> value * metrics.density;</span><br><span class="line"> <span class="keyword">case</span> COMPLEX_UNIT_SP:</span><br><span class="line"> <span class="keyword">return</span> value * metrics.scaledDensity;</span><br><span class="line"> <span class="keyword">case</span> COMPLEX_UNIT_PT:</span><br><span class="line"> <span class="keyword">return</span> value * metrics.xdpi * (<span class="number">1.0f</span>/<span class="number">72</span>);</span><br><span class="line"> <span class="keyword">case</span> COMPLEX_UNIT_IN:</span><br><span class="line"> <span class="keyword">return</span> value * metrics.xdpi;</span><br><span class="line"> <span class="keyword">case</span> COMPLEX_UNIT_MM:</span><br><span class="line"> <span class="keyword">return</span> value * metrics.xdpi * (<span class="number">1.0f</span>/<span class="number">25.4f</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
</summary>
<category term="Android" scheme="https://keee.top/categories/Android/"/>
<category term="UI布局" scheme="https://keee.top/tags/UI%E5%B8%83%E5%B1%80/"/>
</entry>
<entry>
<title>Java之线程池</title>
<link href="https://keee.top/2020/05228a7d.html"/>
<id>https://keee.top/2020/05228a7d.html</id>
<published>2020-05-22T10:05:53.000Z</published>
<updated>2020-05-22T10:05:53.000Z</updated>
<content type="html"><![CDATA[<h5 id="线程池体系"><a href="#线程池体系" class="headerlink" title="线程池体系"></a>线程池体系</h5><p><img src="https://s1.ax1x.com/2020/05/21/YbAtat.png" alt="YbAtat.png"></p><ul><li><p><code>Executor</code> 作为线程池最顶层的接口,它里面只有一个 <code>execute</code> 方法,用于执行任务。线程的创建、调度等细节均由子类实现。</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Executor</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">execute</span><span class="params">(Runnable command)</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p><code>ExecutorService</code> 继承并拓展了 <code>Executor</code> ,内部提供给了更全面的任务提交机制以及线程池关闭的方法。</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ExecutorService</span> <span class="keyword">extends</span> <span class="title">Executor</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">shutdown</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function">List<Runnable> <span class="title">shutdownNow</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">isShutdown</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">isTerminated</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">awaitTermination</span><span class="params">(<span class="keyword">long</span> timeout, TimeUnit unit)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> InterruptedException</span>;</span><br><span class="line"></span><br><span class="line"> <T> <span class="function">Future<T> <span class="title">submit</span><span class="params">(Callable<T> task)</span></span>;</span><br><span class="line"></span><br><span class="line"> <T> <span class="function">Future<T> <span class="title">submit</span><span class="params">(Runnable task, T result)</span></span>;</span><br><span class="line"></span><br><span class="line"> Future<?> submit(Runnable task);</span><br><span class="line"></span><br><span class="line"> <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)</span><br><span class="line"> <span class="keyword">throws</span> InterruptedException;</span><br><span class="line"></span><br><span class="line"> <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,</span><br><span class="line"> <span class="keyword">long</span> timeout, TimeUnit unit)</span><br><span class="line"> <span class="keyword">throws</span> InterruptedException;</span><br><span class="line"></span><br><span class="line"> <T> <span class="function">T <span class="title">invokeAny</span><span class="params">(Collection<? extends Callable<T>> tasks)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> InterruptedException, ExecutionException</span>;</span><br><span class="line"></span><br><span class="line"> <T> <span class="function">T <span class="title">invokeAny</span><span class="params">(Collection<? extends Callable<T>> tasks,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">long</span> timeout, TimeUnit unit)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> InterruptedException, ExecutionException, TimeoutException</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><a id="more"></a><ul><li><p><code>ThreadPoolExecutor</code> 是 <code>ExecuteService</code> 的默认实现,所谓的线程池机制也大多封装在此类当中。</p></li><li><p><code>ScheduleExecutorService</code> 继承自 <code>ExecutorService</code> ,增加了定时任务相关方法。</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ScheduledExecutorService</span> <span class="keyword">extends</span> <span class="title">ExecutorService</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> ScheduledFuture<?> schedule(Runnable command,</span><br><span class="line"> <span class="keyword">long</span> delay, TimeUnit unit);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <V> <span class="function">ScheduledFuture<V> <span class="title">schedule</span><span class="params">(Callable<V> callable,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">long</span> delay, TimeUnit unit)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> ScheduledFuture<?> scheduleAtFixedRate(Runnable command,</span><br><span class="line"> <span class="keyword">long</span> initialDelay,</span><br><span class="line"> <span class="keyword">long</span> period,</span><br><span class="line"> TimeUnit unit);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,</span><br><span class="line"> <span class="keyword">long</span> initialDelay,</span><br><span class="line"> <span class="keyword">long</span> delay,</span><br><span class="line"> TimeUnit unit);</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p><code>ScheduleThreadPoolExecutor</code> 继承自 <code>ThreadPoolExecutor</code> ,并实现了 <code>ScheduleExecutorService</code> 接口。</p></li><li><p><code>ForkJoinPool</code> 是一种支持分解的线程池,一般配合可分解任务接口 <code>ForkJoinTask</code> 来使用。</p></li></ul><h5 id="如何创建线程池"><a href="#如何创建线程池" class="headerlink" title="如何创建线程池"></a>如何创建线程池</h5><blockquote><p>为了方便开发者可以更方便的使用线程池,<code>JDK</code> 中提供了一个创建线程池的工程类(<code>Executors</code> ,不过此方法在阿里Java开发手册中严禁使用,这是后话),在 <code>Executors</code> 中定义了多个静态方法用来创建不同配置的线程池,常见的有以下几种。</p></blockquote><ul><li><p><strong>newSingleThreadExecutor</strong></p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ExecutorService <span class="title">newSingleThreadExecutor</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> FinalizableDelegatedExecutorService</span><br><span class="line"> (<span class="keyword">new</span> ThreadPoolExecutor(<span class="number">1</span>, <span class="number">1</span>,</span><br><span class="line"> <span class="number">0L</span>, TimeUnit.MILLISECONDS,</span><br><span class="line"> <span class="keyword">new</span> LinkedBlockingQueue<Runnable>()));</span><br><span class="line"> }</span><br></pre></td></tr></tbody></table></figure><p>用来创建一个单线程的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照先进先出的顺序来执行任务。</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadPoolTest</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> newSingleThreadPool();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">newSingleThreadPool</span><span class="params">()</span> </span>{</span><br><span class="line"> ExecutorService threadPool = Executors.newSingleThreadExecutor();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> index = <span class="number">0</span>; index < <span class="number">10</span>; index++) {</span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">int</span> taskId = index;</span><br><span class="line"> threadPool.submit(<span class="keyword">new</span> Runnable() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> System.out.println(<span class="string">"当前线程:"</span> + Thread.currentThread().getName() + <span class="string">",当前任务ID:"</span> + taskId);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException 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><span class="line"> }</span><br><span class="line"> threadPool.shutdown();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>执行结果输出可以看出 <code>Task</code> 始终在同一个线程中被执行的:</p><p><img src="https://s1.ax1x.com/2020/05/21/YbMbo4.jpg" alt="YbMbo4.jpg"></p></li><li><p><strong>newCachedThreadPool</strong></p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ExecutorService <span class="title">newCachedThreadPool</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> ThreadPoolExecutor(<span class="number">0</span>, Integer.MAX_VALUE,</span><br><span class="line"> <span class="number">60L</span>, TimeUnit.SECONDS,</span><br><span class="line"> <span class="keyword">new</span> SynchronousQueue<Runnable>());</span><br><span class="line"> }</span><br></pre></td></tr></tbody></table></figure><p>用来创建一个可缓存的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,如果没有可回收的,则新建线程。</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadPoolTest</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> newSingleThreadPool();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">newCacheThreadPool</span><span class="params">()</span> </span>{</span><br><span class="line"> ExecutorService threadPool = Executors.newCachedThreadPool();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> index = <span class="number">0</span>; index < <span class="number">10</span>; index++) {</span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">int</span> taskId = index;</span><br><span class="line"> threadPool.submit(<span class="keyword">new</span> Runnable() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> System.out.println(<span class="string">"当前线程:"</span> + Thread.currentThread().getName() + <span class="string">",当前任务ID:"</span> + taskId);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException 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><span class="line"> }</span><br><span class="line"> threadPool.shutdown();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>执行输出如下:</p><p><img src="https://s1.ax1x.com/2020/05/21/Yb34NF.jpg" alt="Yb34NF.jpg"></p><p>可以看到新增了10条线程进行执行任务,不是说会缓存吗?来改下代码看看另外的执行情形,我们在每个任务提交前都先休眠2秒钟:</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">newCacheThreadPool</span><span class="params">()</span> </span>{</span><br><span class="line"> ExecutorService threadPool = Executors.newCachedThreadPool();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> index = <span class="number">0</span>; index < <span class="number">10</span>; index++) {</span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">int</span> taskId = index;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">2000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> threadPool.submit(<span class="keyword">new</span> Runnable() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> System.out.println(<span class="string">"当前线程:"</span> + Thread.currentThread().getName() + <span class="string">",当前任务ID:"</span> + taskId);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException 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><span class="line"> }</span><br><span class="line"> threadPool.shutdown();</span><br><span class="line"> }</span><br></pre></td></tr></tbody></table></figure><p>执行输出如下:</p><p><img src="https://s1.ax1x.com/2020/05/21/YbGDQs.jpg" alt="YbGDQs.jpg"></p><p>可以看到,第一次创建的线程被复用了,这是因为我们执行的任务所需时间是1秒,然后我们在进行下次任务的时候是每隔2秒,上次的任务早就执行完成了,所以线程1已经处于空闲状态,可以复用执行任务。</p></li><li><p><strong>newFixedThreadPool</strong></p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ExecutorService <span class="title">newFixedThreadPool</span><span class="params">(<span class="keyword">int</span> nThreads)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> ThreadPoolExecutor(nThreads, nThreads,</span><br><span class="line"> <span class="number">0L</span>, TimeUnit.MILLISECONDS,</span><br><span class="line"> <span class="keyword">new</span> LinkedBlockingQueue<Runnable>());</span><br><span class="line"> }</span><br></pre></td></tr></tbody></table></figure><p>创建一个固定数量的、可重用的线程池</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadPoolTest</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> newSingleThreadPool();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">newCacheThreadPool</span><span class="params">()</span> </span>{</span><br><span class="line"> ExecutorService threadPool = Executors.newFixedThreadPool(<span class="number">5</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> index = <span class="number">0</span>; index < <span class="number">10</span>; index++) {</span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">int</span> taskId = index;</span><br><span class="line"> threadPool.submit(<span class="keyword">new</span> Runnable() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> System.out.println(<span class="string">"当前线程:"</span> + Thread.currentThread().getName() + <span class="string">",当前任务ID:"</span> + taskId);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException 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><span class="line"> }</span><br><span class="line"> threadPool.shutdown();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>执行输出如下:</p><p><a href="https://imgchr.com/i/YbJ0AK" target="_blank" rel="noopener"><img src="https://s1.ax1x.com/2020/05/21/YbJ0AK.jpg" alt="YbJ0AK.jpg"></a></p><p>上面的代码创建了一个固定数量为5的线程池,虽然提交了10个任务,但是只会在这5个线程池进行分配执行。</p></li><li><p><strong>newScheduledThreadPool</strong></p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ScheduledExecutorService <span class="title">newScheduledThreadPool</span><span class="params">(<span class="keyword">int</span> corePoolSize)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> ScheduledThreadPoolExecutor(corePoolSize);</span><br><span class="line"> }</span><br></pre></td></tr></tbody></table></figure><p>创建一个定时线程池,支持定时及周期性任务执行。</p></li></ul><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<h5 id="线程池体系"><a href="#线程池体系" class="headerlink" title="线程池体系"></a>线程池体系</h5><p><img src="https://s1.ax1x.com/2020/05/21/YbAtat.png" alt="YbAtat.png"></p>
<ul>
<li><p><code>Executor</code> 作为线程池最顶层的接口,它里面只有一个 <code>execute</code> 方法,用于执行任务。线程的创建、调度等细节均由子类实现。</p>
<figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Executor</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">execute</span><span class="params">(Runnable command)</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
</li>
<li><p><code>ExecutorService</code> 继承并拓展了 <code>Executor</code> ,内部提供给了更全面的任务提交机制以及线程池关闭的方法。</p>
<figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ExecutorService</span> <span class="keyword">extends</span> <span class="title">Executor</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">shutdown</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function">List&lt;Runnable&gt; <span class="title">shutdownNow</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">isShutdown</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">isTerminated</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">awaitTermination</span><span class="params">(<span class="keyword">long</span> timeout, TimeUnit unit)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> InterruptedException</span>;</span><br><span class="line"></span><br><span class="line"> &lt;T&gt; <span class="function">Future&lt;T&gt; <span class="title">submit</span><span class="params">(Callable&lt;T&gt; task)</span></span>;</span><br><span class="line"></span><br><span class="line"> &lt;T&gt; <span class="function">Future&lt;T&gt; <span class="title">submit</span><span class="params">(Runnable task, T result)</span></span>;</span><br><span class="line"></span><br><span class="line"> Future&lt;?&gt; submit(Runnable task);</span><br><span class="line"></span><br><span class="line"> &lt;T&gt; List&lt;Future&lt;T&gt;&gt; invokeAll(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks)</span><br><span class="line"> <span class="keyword">throws</span> InterruptedException;</span><br><span class="line"></span><br><span class="line"> &lt;T&gt; List&lt;Future&lt;T&gt;&gt; invokeAll(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks,</span><br><span class="line"> <span class="keyword">long</span> timeout, TimeUnit unit)</span><br><span class="line"> <span class="keyword">throws</span> InterruptedException;</span><br><span class="line"></span><br><span class="line"> &lt;T&gt; <span class="function">T <span class="title">invokeAny</span><span class="params">(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> InterruptedException, ExecutionException</span>;</span><br><span class="line"></span><br><span class="line"> &lt;T&gt; <span class="function">T <span class="title">invokeAny</span><span class="params">(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">long</span> timeout, TimeUnit unit)</span></span></span><br><span class="line"><span class="function"> <span class="keyword">throws</span> InterruptedException, ExecutionException, TimeoutException</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
</li>
</ul>
</summary>
<category term="Java" scheme="https://keee.top/categories/Java/"/>
<category term="多线程" scheme="https://keee.top/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
</entry>
<entry>
<title>Jenkins自动化构建Android</title>
<link href="https://keee.top/2020/05227251.html"/>
<id>https://keee.top/2020/05227251.html</id>
<published>2020-05-22T10:05:53.000Z</published>
<updated>2020-05-22T10:05:53.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>基于 Mac 环境,其它环境类似操作</p></blockquote><h4 id="Jenkins-安装"><a href="#Jenkins-安装" class="headerlink" title="Jenkins 安装"></a>Jenkins 安装</h4><ul><li><p>官网下载地址: <a href="https://jenkins.io/zh/download/" target="_blank" rel="noopener">https://jenkins.io/zh/download/</a></p></li><li><p>安装完成后自动跳转网页进行初始化设置</p><p><a href="https://imgchr.com/i/YBIckQ" target="_blank" rel="noopener"><img src="https://s1.ax1x.com/2020/05/14/YBIckQ.png" alt="YBIckQ.png"></a></p><a id="more"></a></li><li><p>找到该目录,改文件夹下是默认锁定不能读写的,可以更改权限,改成可读写就可以进入查看上面的文件了</p><p><a href="https://imgchr.com/i/YB7GwT" target="_blank" rel="noopener"><img src="https://s1.ax1x.com/2020/05/14/YB7GwT.png" alt="YB7GwT.png"></a></p></li></ul><p> <a href="https://imgchr.com/i/YB7wlR" target="_blank" rel="noopener"><img src="https://s1.ax1x.com/2020/05/14/YB7wlR.png" alt="YB7wlR.png"></a></p><ul><li><p>除了以上方法还有一种直接命令行查看的方法,输入以下命令键入电脑密码后进行查看密码</p><figure class="highlight shell"><table><tbody><tr><td class="code"><pre><span class="line">sudo cat /Users/Shared/Jenkins/Home/secrets/initialAdminPassword</span><br></pre></td></tr></tbody></table></figure></li></ul><h4 id="Jenkins-配置"><a href="#Jenkins-配置" class="headerlink" title="Jenkins 配置"></a>Jenkins 配置</h4><ul><li><p>安装完成自动跳转网页配置</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583892762685-d64b5622-9877-44d3-8d9b-aed8cb4d8204.png#align=left&display=inline&height=402&margin=%5Bobject%20Object%5D&originHeight=534&originWidth=991&size=0&status=done&style=stroke&width=746" alt=""></p></li><li><p>直接选择安装推荐的插件即可,当然你想自己选择安装哪些插件也行,那就选择左边的进行自定义安装,选择后就是漫长的等待下载插件安装过程,插件安装失败可以重试多几次╮( ̄▽ ̄)╭</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583892815909-eb7262fa-f790-41d7-8041-bb6a2a34e259.png#align=left&display=inline&height=467&margin=%5Bobject%20Object%5D&originHeight=620&originWidth=990&size=0&status=done&style=stroke&width=746" alt=""></p></li></ul><ul><li><p>插件安装完成后,创建用户,也可以直接继续使用 admin 用户进行管理</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583892867202-c22b9fac-2a67-4edc-aad2-f028dfecd8ff.png#align=left&display=inline&height=718&margin=%5Bobject%20Object%5D&originHeight=954&originWidth=991&size=0&status=done&style=stroke&width=746" alt=""></p></li></ul><ul><li><p>创建 <code>URL</code>,<code>localhost</code> 可以改为本机 <code>ip</code> 地址</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583892925715-3cce9aa0-fd75-4055-8306-fa909b34d19e.png#align=left&display=inline&height=271&margin=%5Bobject%20Object%5D&originHeight=359&originWidth=989&size=0&status=done&style=stroke&width=746" alt=""></p></li></ul><ul><li><p>初始化完成</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583892944805-1740edca-42ca-4051-93d1-0c720068beed.png#align=left&display=inline&height=284&margin=%5Bobject%20Object%5D&originHeight=377&originWidth=991&size=0&status=done&style=stroke&width=746" alt=""></p></li></ul><p> <img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583892977396-c1dff7a2-85b0-43ee-978e-f454191548a5.png#align=left&display=inline&height=532&margin=%5Bobject%20Object%5D&originHeight=532&originWidth=684&size=0&status=done&style=stroke&width=684" alt=""></p><ul><li><p>插件管理</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583893005678-fe69e919-7bf9-49d6-899e-932d7ba5a2b5.png#align=left&display=inline&height=369&margin=%5Bobject%20Object%5D&originHeight=661&originWidth=1336&size=0&status=done&style=stroke&width=746" alt=""></p></li></ul><ul><li><p>【Git Parameter】插件,git 仓库的参数,例如分支选择 </p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583893087500-119cfa61-45da-4df4-a392-644bc8242141.png#align=left&display=inline&height=102&margin=%5Bobject%20Object%5D&originHeight=216&originWidth=1585&size=0&status=done&style=stroke&width=746" alt=""></p></li></ul><ul><li><p>【Build Name and Description Setter】插件,设置构建历史描述信息,例如可是设置描述显示二维码</p></li><li><p>【Upload to pgyer】插件,蒲公英上传插件,上传到外网,二维码扫码安装</p></li><li><p>更改设置显示富文本</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583898796234-c02a69d0-33b0-45f2-8e34-5770579f3eeb.png#align=left&display=inline&height=250&margin=%5Bobject%20Object%5D&originHeight=367&originWidth=1093&size=0&status=done&style=stroke&width=746" alt=""></p></li></ul><p> <img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583898807937-c94ac540-93aa-4f28-8129-425a720c7de7.png#align=left&display=inline&height=478&margin=%5Bobject%20Object%5D&originHeight=655&originWidth=1022&size=0&status=done&style=stroke&width=746" alt=""></p><ul><li><p>修改默认的构建目录,该文件跟上面一开始的初始化密码文件在一起的</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583898827768-ef59b5ac-21bd-4424-ae85-bb85020ee014.png#align=left&display=inline&height=374&margin=%5Bobject%20Object%5D&originHeight=374&originWidth=413&size=0&status=done&style=stroke&width=413" alt=""></p></li></ul><p> <img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583898875162-c82044c6-a324-4204-b65d-2a36d5beb4b4.png#align=left&display=inline&height=126&margin=%5Bobject%20Object%5D&originHeight=186&originWidth=1102&size=0&status=done&style=stroke&width=746" alt=""></p><p> 修改成你所想修改的目录即可,只需更改前半部分,后面的变量不需要更改,是根据项目自动生成文件夹的</p><p> <img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583898894960-8d7a4373-ab4c-4961-9196-d094306ff5d7.png#align=left&display=inline&height=57&margin=%5Bobject%20Object%5D&originHeight=87&originWidth=1139&size=0&status=done&style=stroke&width=746" alt=""></p><p> 修改完成后需要重新启动 Jenkins 加载配置</p> <figure class="highlight http"><table><tbody><tr><td class="code"><pre><span class="line"><span class="attribute">http://localhost:8080/reload</span></span><br></pre></td></tr></tbody></table></figure><ul><li><p>配置 <code>ANDROID_HOME</code></p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583899082207-c053f743-b830-4ada-8450-5802bfd089f7.png#align=left&display=inline&height=285&margin=%5Bobject%20Object%5D&originHeight=371&originWidth=972&size=0&status=done&style=stroke&width=746" alt=""></p></li></ul><p> <img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583903517381-e23ee694-043e-4b68-a87f-4768c0655433.png#align=left&display=inline&height=285&margin=%5Bobject%20Object%5D&originHeight=371&originWidth=972&size=0&status=done&style=stroke&width=746" alt=""></p><p> <img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583904961151-1d021923-64a7-4a43-a20f-47657591958e.png#align=left&display=inline&height=200&margin=%5Bobject%20Object%5D&originHeight=276&originWidth=1027&size=0&status=done&style=stroke&width=746" alt=""></p><ul><li><p>配置 Git,填写 Mac 系统安装的地址</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583905021439-4e8aff43-4258-4d03-81ca-2fa9bc939e91.png#align=left&display=inline&height=464&margin=%5Bobject%20Object%5D&originHeight=546&originWidth=878&size=0&status=done&style=stroke&width=746" alt=""></p></li></ul><h4 id="项目配置"><a href="#项目配置" class="headerlink" title="项目配置"></a>项目配置</h4><ul><li><p>新建项目</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583905073512-a9a3aea3-e9ad-4d8d-9bba-bd91b51de139.png#align=left&display=inline&height=283&margin=%5Bobject%20Object%5D&originHeight=283&originWidth=732&size=0&status=done&style=stroke&width=732" alt=""></p></li></ul><p> <img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583905202422-b3965a29-939a-4ac5-8d21-05e7b0c88f9b.png#align=left&display=inline&height=327&margin=%5Bobject%20Object%5D&originHeight=362&originWidth=826&size=0&status=done&style=stroke&width=746" alt=""></p><ul><li><p>参数化构建</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583905490920-bf9112aa-d17b-4a55-8a4e-ef613be3d6f4.png#align=left&display=inline&height=514&margin=%5Bobject%20Object%5D&originHeight=514&originWidth=1446&size=0&status=done&style=stroke&width=1446" alt=""></p></li></ul><p> <img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583905506494-59ff145b-2ed1-4abb-a9fa-e2800fda8897.png#align=left&display=inline&height=808&margin=%5Bobject%20Object%5D&originHeight=808&originWidth=769&size=0&status=done&style=stroke&width=769" alt=""></p><ul><li><p>添加 <code>Git</code> 仓库,添加仓库的凭证</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583905535995-f8e160d4-a40a-4b87-b7e9-70c114cc861b.png#align=left&display=inline&height=534&margin=%5Bobject%20Object%5D&originHeight=544&originWidth=760&size=0&status=done&style=stroke&width=746" alt=""></p></li></ul><p> <img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583905553680-862d4f99-a67f-4b6a-9cff-e507ff332a6b.png#align=left&display=inline&height=529&margin=%5Bobject%20Object%5D&originHeight=529&originWidth=681&size=0&status=done&style=stroke&width=681" alt=""></p><p> <img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583905565642-8c131664-d454-46d5-a7b0-6af778236dfc.png#align=left&display=inline&height=455&margin=%5Bobject%20Object%5D&originHeight=604&originWidth=990&size=0&status=done&style=stroke&width=746" alt=""></p><blockquote><p> <code>${Branch}</code> 是前面步骤 <code>Git Parameter</code> 定义分支参数。</p></blockquote><ul><li><p>选择 Shell 命令构建</p><p><a href="https://imgchr.com/i/YBqO9e" target="_blank" rel="noopener"><img src="https://s1.ax1x.com/2020/05/14/YBqO9e.png" alt="YBqO9e.png"></a></p></li></ul><blockquote><p>命令如下:</p></blockquote> <figure class="highlight shell"><table><tbody><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash">!/bin/bash</span></span><br><span class="line"></span><br><span class="line">this_dir=`pwd`</span><br><span class="line">project_name="/QMEasy"</span><br><span class="line">project_full_dir="${this_dir}${project_name}"</span><br><span class="line">build_type=$BuildType</span><br><span class="line">build_type_debug="Debug"</span><br><span class="line">build_type_release="Release"</span><br><span class="line"></span><br><span class="line">echo '***********************************************.开始执行.*********************************************'</span><br><span class="line">cd $project_full_dir</span><br><span class="line">this_dir=`pwd`</span><br><span class="line">echo ">当前目录:${this_dir}"</span><br><span class="line"></span><br><span class="line">echo '***********************************************gradle 版本********************************************'</span><br><span class="line"><span class="meta">$</span><span class="bash">this_dir/gradlew -v</span></span><br><span class="line">echo '*****************************************************************************************************'</span><br><span class="line"></span><br><span class="line">echo '************************************************当前构建环境*******************************************'</span><br><span class="line">if [ "$build_type" = "$build_type_debug" ]</span><br><span class="line">then</span><br><span class="line"> echo ">测试环境"</span><br><span class="line">elif [ "$build_type" = "$build_type_release" ]</span><br><span class="line">then</span><br><span class="line"> echo ">正式环境"</span><br><span class="line">else</span><br><span class="line"> echo ">环境配置错误,已退出构建..."</span><br><span class="line"> exit</span><br><span class="line">fi</span><br><span class="line">echo '*****************************************************************************************************'</span><br><span class="line"></span><br><span class="line">echo '************************************************当前项目分支*******************************************'</span><br><span class="line">echo ">${Branch}"</span><br><span class="line">echo '*****************************************************************************************************'</span><br><span class="line"></span><br><span class="line">echo ">clean 项目..."</span><br><span class="line"><span class="meta">$</span><span class="bash">this_dir/gradlew clean</span></span><br><span class="line">echo '*****************************************************************************************************'</span><br><span class="line"></span><br><span class="line">echo ">开始构建..."</span><br><span class="line"><span class="meta">$</span><span class="bash">this_dir/gradlew assemble<span class="variable">$BuildType</span></span></span><br><span class="line">echo '*****************************************************************************************************'</span><br><span class="line"></span><br><span class="line">apk_dest_dir=/Volumes/数据/Jenkins/APK/分期易/</span><br><span class="line">echo ">移动apk到指定目录 ---> $apk_dest_dir"</span><br><span class="line">rm -rf $apk_dest_dir</span><br><span class="line">mkdir $apk_dest_dir</span><br><span class="line">apk_src_dir=${WORKSPACE}$project_name/app/build/outputs/apk/${BuildType}/*.apk</span><br><span class="line">mv -f $apk_src_dir $apk_dest_dir</span><br><span class="line">echo ">移动完成..."</span><br><span class="line">echo '*****************************************************************************************************'</span><br><span class="line"></span><br><span class="line">echo ">构建结束,开始上传到蒲公英..."</span><br><span class="line">echo '*****************************************************************************************************'</span><br></pre></td></tr></tbody></table></figure><ul><li><p>配置蒲公英上传</p><blockquote><p>key可以在蒲公英说明找到,scandir指的是扫描上传的apk目录,Build Description是构建后的描述信息,这里我配置的是本地的共享目录,img用的是蒲公英的二维码。</p></blockquote><p><a href="https://imgchr.com/i/YBLk9g" target="_blank" rel="noopener"><img src="https://s1.ax1x.com/2020/05/14/YBLk9g.png" alt="YBLk9g.png"></a></p></li></ul><ul><li><p>蒲公英key的获取地址 <a href="https://www.pgyer.com/doc/view/api" target="_blank" rel="noopener">https://www.pgyer.com/doc/view/api</a></p></li><li><p>配置完成后可以开始构建项目</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583905961338-336b6358-6353-4a4c-9238-d95648bf5c2f.png#align=left&display=inline&height=453&margin=%5Bobject%20Object%5D&originHeight=499&originWidth=821&size=0&status=done&style=stroke&width=746" alt=""></p></li></ul><p> <img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583905980688-13435365-360f-4f8a-aa59-df5868fb0f61.png#align=left&display=inline&height=326&margin=%5Bobject%20Object%5D&originHeight=326&originWidth=472&size=0&status=done&style=stroke&width=472" alt=""></p><ul><li><p>对应的构建输出信息可以查看对应项目的控制台输出</p><p><a href="https://imgchr.com/i/YBLf58" target="_blank" rel="noopener"><img src="https://s1.ax1x.com/2020/05/14/YBLf58.png" alt="YBLf58.png"></a></p></li></ul><h4 id="局域网文件共享"><a href="#局域网文件共享" class="headerlink" title="局域网文件共享"></a>局域网文件共享</h4><blockquote><p>利用 Mac 本身自带的 Apache 进行局域网文件共享,无需传到外网蒲公英等第三方</p></blockquote><ul><li><p>命令行输入 httpd -v 查看对应的版本信息</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583906185585-a2e16188-7ee8-4f68-915a-d6ddbaed673a.png#align=left&display=inline&height=79&margin=%5Bobject%20Object%5D&originHeight=79&originWidth=312&size=0&status=done&style=none&width=312" alt=""></p></li><li><p>命令行输入 sudo apachectl start 启动服务,需要输入 Mac 的登录密码开启</p></li><li><p>浏览器 <a href="">http:/127.0.0.1</a> 进行访问,可以看到网页输出</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583906185562-1a9f51c8-fce7-447b-a2bd-4655858bbf9a.png#align=left&display=inline&height=67&margin=%5Bobject%20Object%5D&originHeight=67&originWidth=139&size=0&status=done&style=none&width=139" alt=""></p></li><li><p>修改文件存放的目录</p></li><li><p>原始目录</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583906185546-90cbe9d3-d390-4010-aeac-95c5769b23be.png#align=left&display=inline&height=342&margin=%5Bobject%20Object%5D&originHeight=376&originWidth=820&size=0&status=done&style=stroke&width=746" alt=""></p></li></ul><ul><li><p>配置文件目录</p><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583906185577-4369f5f2-e7a3-49ab-9c3b-9e71ad7c8e2e.png#align=left&display=inline&height=379&margin=%5Bobject%20Object%5D&originHeight=379&originWidth=634&size=0&status=done&style=none&width=634" alt=""></p></li></ul><ul><li><p>打开该文件,查找 DocumentRoot 节点,修改为你需要的目录。</p></li><li><p>Root 节点下的文件类型也需要修改,然后输入命令 sudo apachectl restart 重启服务,再次访问 <a href="http://127.0.0.1" target="_blank" rel="noopener">http://127.0.0.1</a> 就会看到你刚才更改目录下的文件。</p><figure class="highlight html"><table><tbody><tr><td class="code"><pre><span class="line">DocumentRoot "/Volumes/文档/apk"</span><br><span class="line"><span class="tag"><<span class="name">Directory</span> "/<span class="attr">Volumes</span>/文档/<span class="attr">apk</span>"></span></span><br><span class="line"> Options Indexes FollowSymLinks Multiviews</span><br><span class="line"> MultiviewsMatch Any</span><br><span class="line"> AllowOverride All</span><br><span class="line"> Require all granted</span><br><span class="line"><span class="tag"></<span class="name">Directory</span>></span></span><br></pre></td></tr></tbody></table></figure><p><img src="https://cdn.nlark.com/yuque/0/2020/png/407428/1583906185615-623fa570-19f1-41cc-8a64-b645d5784ad6.png#align=left&display=inline&height=199&margin=%5Bobject%20Object%5D&originHeight=199&originWidth=378&size=0&status=done&style=none&width=378" alt=""></p></li><li><p>局域网访问直接输入 <a href="http://本机ip地址" target="_blank" rel="noopener">http://本机ip地址</a></p></li></ul><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<blockquote>
<p>基于 Mac 环境,其它环境类似操作</p>
</blockquote>
<h4 id="Jenkins-安装"><a href="#Jenkins-安装" class="headerlink" title="Jenkins 安装"></a>Jenkins 安装</h4><ul>
<li><p>官网下载地址: <a href="https://jenkins.io/zh/download/" target="_blank" rel="noopener">https://jenkins.io/zh/download/</a></p>
</li>
<li><p>安装完成后自动跳转网页进行初始化设置</p>
<p><a href="https://imgchr.com/i/YBIckQ" target="_blank" rel="noopener"><img src="https://s1.ax1x.com/2020/05/14/YBIckQ.png" alt="YBIckQ.png"></a></p></li></ul>
</summary>
<category term="Android" scheme="https://keee.top/categories/Android/"/>
<category term="Jenkins" scheme="https://keee.top/tags/Jenkins/"/>
<category term="自动化" scheme="https://keee.top/tags/%E8%87%AA%E5%8A%A8%E5%8C%96/"/>
</entry>
<entry>
<title>Kotlin基础</title>
<link href="https://keee.top/2020/0522bfb2.html"/>
<id>https://keee.top/2020/0522bfb2.html</id>
<published>2020-05-22T10:05:53.000Z</published>
<updated>2020-05-22T10:05:53.000Z</updated>
<content type="html"><![CDATA[<h3 id="函数声明"><a href="#函数声明" class="headerlink" title="函数声明"></a>函数声明</h3><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">max</span><span class="params">(a: <span class="type">Int</span>, b: <span class="type">Int</span>)</span></span>: <span class="built_in">Int</span> = <span class="keyword">if</span>(a>b) a <span class="keyword">else</span> b</span><br></pre></td></tr></tbody></table></figure><p>关键字<code>fun</code>,名称<code>max</code>,括号内的参数列表,先参数名,冒号后面是参数类型,括号后面的冒号跟着返回值类型,如果没有返回值就把冒号跟返回值类型直接去掉,<code>kotlin</code>里面<code>if</code>是有结果的表达式。</p><h3 id="变量声明"><a href="#变量声明" class="headerlink" title="变量声明"></a>变量声明</h3><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">val</span> a = <span class="number">1</span></span><br><span class="line"><span class="keyword">val</span> a: <span class="built_in">Int</span> = <span class="number">1</span></span><br><span class="line"><span class="keyword">var</span> b = <span class="number">2</span></span><br><span class="line"><span class="keyword">var</span> b: <span class="built_in">Int</span> = <span class="number">2</span></span><br></pre></td></tr></tbody></table></figure><p>声明变量的关键字有两个不可变引用<code>val</code>、可变引用<code>var</code>,默认情况下尽可能用<code>val</code>去声明变量,仅在必要的时候把变量声明为<code>var</code>,这样可以让你的代码更接近函数式编程风格。声明变量的时候可以显式的指定变量的类型,也可以直接隐藏去掉(如果已经可以推断出类型)</p><h3 id="字符串格式化模板"><a href="#字符串格式化模板" class="headerlink" title="字符串格式化模板"></a>字符串格式化模板</h3><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">val</span> name = <span class="string">"bob"</span></span><br><span class="line"><span class="keyword">val</span> sex = <span class="string">"男"</span></span><br><span class="line"><span class="keyword">val</span> age = <span class="number">100</span></span><br><span class="line">print(<span class="string">"<span class="subst">${name}</span>的性别为:<span class="variable">$sex</span>,年龄为:<span class="variable">$age</span>"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果:bob的性别为:男,年龄为:100</span></span><br></pre></td></tr></tbody></table></figure><p>可以直接用<code>$</code>符号对变量进行引用,还可以用<code>${}</code>这种对表达式进行引用,就像刚才的例子那样,<code>name</code>可以换成表达式进行使用。</p><a id="more"></a><h3 id="类和属性"><a href="#类和属性" class="headerlink" title="类和属性"></a>类和属性</h3><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span></span>(<span class="keyword">val</span> name: String)</span><br></pre></td></tr></tbody></table></figure><p>在<code>Java</code>中,声明一个<code>JavaBean</code>类的<code>Person</code>除了属性<code>name</code>外,一般还需要设置<code>setter</code>跟<code>getter</code>方法,在<code>Kotlin</code>中可以直接就像上面的例子那样就可以了,<code>Kotlin</code>中<code>public</code>是默认的可见性,可以忽略它,如果这个<code>bean</code>是不需要更改里面的属性的,上面的代码例子就已经完成了,如果需要像<code>Java</code>那样有<code>setter</code>方法对属性进行更改,那么可以把<code>val</code>改成<code>var</code>即可。</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span></span>(<span class="keyword">var</span> name: String)</span><br></pre></td></tr></tbody></table></figure><p>我们还可以自定义类属性的访问器</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span></span>(<span class="keyword">var</span> name: String) {</span><br><span class="line"> <span class="keyword">val</span> isMarried: <span class="built_in">Boolean</span></span><br><span class="line"> <span class="keyword">get</span>() = name == <span class="string">"Bob"</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>单独声明一个变量<code>isMarried</code>,然后在<code>get()</code>方法中对其进行判断返回。</p><h3 id="枚举类enum-class和when"><a href="#枚举类enum-class和when" class="headerlink" title="枚举类enum class和when"></a>枚举类<code>enum class</code>和<code>when</code></h3><h4 id="枚举类"><a href="#枚举类" class="headerlink" title="枚举类"></a>枚举类</h4><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="class"><span class="keyword">class</span> <span class="title">MyColor</span> </span>{</span><br><span class="line"> RED, GREEN, BLUE</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><code>Kotlin</code>中用了<code>enum class</code>两个关键字来声明枚举类,我们还可以给枚举类声明属性和方法。</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="class"><span class="keyword">class</span> <span class="title">MyColor</span></span>(<span class="keyword">val</span> r: <span class="built_in">Int</span>, <span class="keyword">val</span> g: <span class="built_in">Int</span>, <span class="keyword">val</span> b: <span class="built_in">Int</span>) {</span><br><span class="line"> RED(<span class="number">255</span>, <span class="number">0</span>, <span class="number">0</span>), GREEN(<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), BLUE(<span class="number">0</span>, <span class="number">0</span>, <span class="number">255</span>);</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">rgb</span><span class="params">()</span></span> = (r * <span class="number">256</span> + g) * <span class="number">256</span> + b</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>然后可以使用该变量、方法</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line">MyColor.BLUE.rgb()</span><br></pre></td></tr></tbody></table></figure><h4 id="when"><a href="#when" class="headerlink" title="when"></a><code>when</code></h4><p><code>kotlin</code>中的<code>when</code>代替了<code>Java</code>中的<code>switch</code>,跟<code>if</code>一样,<code>when</code>也是一个有返回值的表达式</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">getMyColor</span><span class="params">(color: <span class="type">MyColor</span>)</span></span> = <span class="keyword">when</span> (color) {</span><br><span class="line"> MyColor.RED -> <span class="string">"红色"</span></span><br><span class="line"> MyColor.GREEN -> <span class="string">"绿色"</span></span><br><span class="line"> MyColor.BLUE -> <span class="string">"蓝色"</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>对于分支还可以用逗号合并起来的,例如下面例子</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">getMyColor</span><span class="params">(color: <span class="type">MyColor</span>)</span></span> = <span class="keyword">when</span> (color) {</span><br><span class="line"> MyColor.RED -> <span class="string">"红色"</span></span><br><span class="line"> MyColor.GREEN, MyColor.BLUE -> <span class="string">"绿色、蓝色"</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><code>when</code>还可以使用<code>else</code>作为分支,<code>when</code>的条件不像<code>Java</code>的<code>switch</code>只能使用枚举常量、字符串或者数字面值这种,<code>when</code>可以使用任何对象作为分支条件</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">getMyColor</span><span class="params">(color: <span class="type">MyColor</span>)</span></span> = <span class="keyword">when</span> (color) {</span><br><span class="line"> setOf(MyColor.RED) -> <span class="string">"红色"</span></span><br><span class="line"> setOf(MyColor.GREEN, MyColor.BLUE) -> <span class="string">"绿色、蓝色混合"</span></span><br><span class="line"> <span class="keyword">else</span> -> <span class="string">"其它颜色混合"</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><code>when</code>还可以不带参数条件,分支条件结果为<code>true</code>或者<code>false</code>就行</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">getMyColor</span><span class="params">(color: <span class="type">MyColor</span>)</span></span> = <span class="keyword">when</span> {</span><br><span class="line"> color == MyColor.RED -> <span class="string">"红色"</span></span><br><span class="line"> color == MyColor.GREEN || color == MyColor.BLUE -> <span class="string">"绿色、蓝色混合"</span></span><br><span class="line"> <span class="keyword">else</span> -> <span class="string">"其它颜色混合"</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="智能转换,合并类型检查和转换"><a href="#智能转换,合并类型检查和转换" class="headerlink" title="智能转换,合并类型检查和转换"></a>智能转换,合并类型检查和转换</h3><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">getTextViewText</span><span class="params">(cc : <span class="type">View</span>)</span></span>: String? {</span><br><span class="line"> <span class="keyword">if</span> (cc <span class="keyword">is</span> TextView) {</span><br><span class="line"> <span class="keyword">return</span> cc.text.toString()</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>上面的例子中,在<code>if</code>中使用了<code>is</code>关键字进行类型判断跟转换,所以在条件内部中可以直接使用<code>TextView</code>的内部方法,相对于已经把<code>cc</code>转换为<code>TextView</code>了,另外<code>String?</code>是<code>Kotlin</code>表示可以为空的变量表达方法。</p><h3 id="迭代:while、for"><a href="#迭代:while、for" class="headerlink" title="迭代:while、for"></a>迭代:<code>while</code>、<code>for</code></h3><p>这个跟<code>Java</code>中基本上一致,没什么新内容。<code>for</code>迭代可以使用区间和数列,用<code>..</code>运算符表示区间,一个代表起始值,一个表示结束值:</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">val</span> oneToTen = <span class="number">1</span>..<span class="number">10</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">for</span> (index <span class="keyword">in</span> <span class="number">1</span>..<span class="number">5</span>) {</span><br><span class="line"> print(index)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出:1、2、3、4、5</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">for</span> (index <span class="keyword">in</span> <span class="number">1</span> until <span class="number">5</span>) {</span><br><span class="line"> print(index)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出:1、2、3、4</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">for</span> (index <span class="keyword">in</span> <span class="number">5</span> downTo <span class="number">1</span>) {</span><br><span class="line"> print(index)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出:5、4、3、2、1</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">for</span> (index <span class="keyword">in</span> <span class="number">5</span> downTo <span class="number">1</span> step <span class="number">2</span>) {</span><br><span class="line"> print(index)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出:5、3、1</span></span><br></pre></td></tr></tbody></table></figure><p><code>step</code>表示步长</p><h3 id="关键字in"><a href="#关键字in" class="headerlink" title="关键字in"></a>关键字<code>in</code></h3><p>使用<code>in</code>运算符来检查一个值是否在某个区间中或者它的逆运算<code>!in</code>检查这个值是否不在某个区间内。</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">isLetter</span><span class="params">(c: <span class="type">Char</span>)</span></span> = c <span class="keyword">in</span> <span class="string">'a'</span>..<span class="string">'z'</span> || c <span class="keyword">in</span> <span class="string">'A'</span>..<span class="string">'Z'</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">isNotDigit</span><span class="params">(c: <span class="type">Char</span>)</span></span> = c <span class="keyword">in</span> <span class="string">'0'</span>..<span class="string">'9'</span></span><br></pre></td></tr></tbody></table></figure><h3 id="函数定义与调用"><a href="#函数定义与调用" class="headerlink" title="函数定义与调用"></a>函数定义与调用</h3><p><code>Kotlin</code>没有使用自己的集合类,用的是标准的<code>Java</code>集合类,使得<code>Kotlin</code>可以更好的与<code>Java</code>代码交互,下面例子创建一些基本的集合类。</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">val</span> <span class="keyword">set</span> = hashSetOf(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line"><span class="keyword">val</span> list = listOf(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line"><span class="keyword">val</span> map = hashMapOf(<span class="number">1</span> to <span class="string">"one"</span>, <span class="number">2</span> to <span class="string">"two"</span>, <span class="number">3</span> to <span class="string">"three"</span>)</span><br></pre></td></tr></tbody></table></figure><h3 id="顶层函数"><a href="#顶层函数" class="headerlink" title="顶层函数"></a>顶层函数</h3><p><code>Kotlin</code>可以定义顶层函数,创建一个名为<code>KtEx</code>的<code>kt</code>文件,直接创建函数:</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">join</span><span class="params">(source: <span class="type">String</span>, suffix: <span class="type">String</span>)</span></span>: String {</span><br><span class="line"> <span class="keyword">return</span> source.plus(suffix)</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>在<code>kotlin</code>的其它地方就可以直接调用该<code>join</code>函数,如果是<code>Java</code>中调用的话,系统已经自动生成名为<code>KtExKt</code>的类去引用该静态方法,<code>KtExKt.join(source, suffix)</code></p><h3 id="扩展函数和属性"><a href="#扩展函数和属性" class="headerlink" title="扩展函数和属性"></a>扩展函数和属性</h3><p>扩展函数很简单,理论上来说,它就是一个类的成员函数,不过是定义在类的外部而已,<code>Kotlin</code>为我们定义了很多扩展函数,例如集合类<code>List</code>中有<code>last()、max()</code>等扩展函数。我们也可以自己自定义扩展函数,为String来扩展一个成员函数,获取<code>String</code>的最后一个字符返回</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> com.glimmer</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">fun</span> String.<span class="title">lastChar</span><span class="params">()</span></span>: <span class="built_in">Char</span> = <span class="keyword">get</span>(length - <span class="number">1</span>)</span><br></pre></td></tr></tbody></table></figure><p>然后我们就可以用<code>String</code>类型的变量直接调用该方法了,<code>Java</code>调用跟刚才一样</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="string">"abc"</span>.lastChar()</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line">KtExKt.lastChar(“abc”);</span><br></pre></td></tr></tbody></table></figure><p>在扩展函数中,可以直接访问被扩展的类的其它方法和属性,注意,扩展函数并不允许你打破它的封装性,跟在内部定义的方法不同的是,扩展函数不能访问私有的或者是受保护的成员。</p><h4 id="导入扩展函数"><a href="#导入扩展函数" class="headerlink" title="导入扩展函数"></a>导入扩展函数</h4><p>扩展函数有时候会有命名冲突,<code>Kotlin</code>支持用和导入类一样的语法来导入某个函数</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> com.glimmer.lastChar</span><br></pre></td></tr></tbody></table></figure><p>还可以导入全部</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> com.glimmer.*</span><br></pre></td></tr></tbody></table></figure><p>可以使用关键字<code>as</code>来修改导入的名称,后面就可以直接使用该名称来进行调用</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> com.glimmer.lastChar <span class="keyword">as</span> last</span><br><span class="line"></span><br><span class="line"><span class="string">"abc"</span>.last()</span><br></pre></td></tr></tbody></table></figure><h4 id="扩展属性"><a href="#扩展属性" class="headerlink" title="扩展属性"></a>扩展属性</h4><p>扩展属性提供了一种方法用来扩展类的<code>API</code>,可以用来访问属性,用的是属性语法而不是函数的语法,尽管它们被称为属性,但是它们可以没有任何状态,因为没有合适的地方来存储它。扩展属性跟扩展函数一样,也像接收者的一个普通的成员属性一样,这里必须定义<code>getter</code>函数,因为没有支持的字段,因此没有默认的<code>getter</code>实现,同理,初始化也不可可以的,因为没有地方存储初始值。</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">val</span> String.last: <span class="built_in">Char</span></span><br><span class="line"> <span class="keyword">get</span>() = <span class="keyword">get</span>(length - <span class="number">1</span>)</span><br></pre></td></tr></tbody></table></figure><p>然后我们就可以直接用它的扩展属性了</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="string">"abc"</span>.last</span><br></pre></td></tr></tbody></table></figure><p>如果是在<code>StringBuilder</code>上定义一个相同的属性,这就可以设置为<code>var</code>,因为<code>StringBuilder</code>的内容是可变的。</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> StringBuilder.lastChar: <span class="built_in">Char</span></span><br><span class="line"> <span class="keyword">get</span>() = <span class="keyword">get</span>(length - <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">set</span>(filed) = setCharAt(length - <span class="number">1</span>, filed)</span><br></pre></td></tr></tbody></table></figure><h3 id="可变参数"><a href="#可变参数" class="headerlink" title="可变参数"></a>可变参数</h3><p>当你调用函数来创建一个列表的时候,可以传递任意个的参数给它进行初始化:</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">val</span> list = listOf(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br></pre></td></tr></tbody></table></figure><p>该函数在库中的声明是:</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">fun</span> <span class="type"><T></span> <span class="title">listOf</span><span class="params">(<span class="keyword">vararg</span> elements: <span class="type">T</span>)</span></span>: List<T> = {...}</span><br></pre></td></tr></tbody></table></figure><p>可以发现<code>Kotlin</code>中的可变参数关键字是<code>vararg</code></p><h3 id="中缀调用和解构声明"><a href="#中缀调用和解构声明" class="headerlink" title="中缀调用和解构声明"></a>中缀调用和解构声明</h3><p>我们可以使用<code>mapOf</code>函数来创建<code>map</code></p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">val</span> map = mapOf(<span class="number">1</span> to <span class="string">"one"</span>, <span class="number">2</span> to <span class="string">"two"</span>)</span><br></pre></td></tr></tbody></table></figure><p>以上代码中的关键字’to’不是内置的结构,而是一种特殊的函数调用,被称为中缀调用。<br>在中缀调用中,没有添加额外的分隔符,函数名称是直接放在目标对象名称和参数之间的,就像以下的两种调用方式,它们是等价的:</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="number">1</span> to <span class="string">"one"</span></span><br><span class="line"><span class="number">1</span>.to(<span class="string">"one"</span>)</span><br></pre></td></tr></tbody></table></figure><p>中缀调用可以与只有一个参数的函数一起使用,无论是普通的函数还是扩展函数,要允许使用中缀符号调用函数需要使用<code>infix</code>修饰符来标记,下面是一个简单的<code>to</code>函数的声明:</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">infix</span> <span class="function"><span class="keyword">fun</span> Any.<span class="title">to</span><span class="params">(other: <span class="type">Any</span>)</span></span> = Pair(<span class="keyword">this</span>, other)</span><br></pre></td></tr></tbody></table></figure><p>在<code>Koltin</code>中可以把一个对象赋值给多个变量,这种操作叫做解构声明。</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">val</span>(number, name) = <span class="number">1</span> to <span class="string">"one"</span></span><br><span class="line"><span class="comment">// 然后我们可以直接调用打印number跟name</span></span><br><span class="line">print(number)</span><br><span class="line">print(name)</span><br></pre></td></tr></tbody></table></figure><p>数据类会默认生成解构声明</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">data</span> <span class="class"><span class="keyword">class</span> <span class="title">Book</span></span>(<span class="keyword">var</span> name: String, <span class="keyword">var</span> price: <span class="built_in">Float</span>)</span><br></pre></td></tr></tbody></table></figure><p>然后就可以调用<code>Book</code>的解构声明<code>component1()</code>对应<code>name</code>,<code>component2()</code>对应<code>price</code><br>我们还可以为普通类自定义解构声明</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span></span>(<span class="keyword">var</span> name: String, <span class="keyword">var</span> price: <span class="built_in">Float</span>) {</span><br><span class="line"> <span class="keyword">operator</span> <span class="function"><span class="keyword">fun</span> <span class="title">component1</span><span class="params">()</span></span>: String {</span><br><span class="line"> <span class="keyword">return</span> name</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">operator</span> <span class="function"><span class="keyword">fun</span> <span class="title">component2</span><span class="params">()</span></span>: <span class="built_in">Float</span> {</span><br><span class="line"> <span class="keyword">return</span> price</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><hr><h3 id="类、对象、接口"><a href="#类、对象、接口" class="headerlink" title="类、对象、接口"></a>类、对象、接口</h3><p><code>Kotlin</code>的类和接口与<code>Java</code>的还是有点区别的,例如<code>Kotlin</code>的接口是可以包含属性声明的,默认声明都是<code>final</code>和<code>public</code>,父类中的方法如果需要在子类中重写的话需要添加<code>open</code>关键字。嵌套的类默认不是内部类,也没有包含对其外部类的隐式引用。<code>kotlin</code>除了<code>public</code>、<code>protected</code>、<code>private</code>修饰符外,还新增了<code>internal</code>表示模块内部中可见修饰符,一个模块就是一组一起编译的<code>kotlin</code>文件。</p><h4 id="内部类和嵌套类:默认是嵌套类"><a href="#内部类和嵌套类:默认是嵌套类" class="headerlink" title="内部类和嵌套类:默认是嵌套类"></a>内部类和嵌套类:默认是嵌套类</h4><p>跟<code>Java</code>一样,在<code>Kotlin</code>中也可以在一个类中声明另一个类,但是<code>kotlin</code>的嵌套类不能访问外部类的实例,它并没有像<code>Java</code>那样包含了对外部类的隐式引用,除非你特别做出了要求,在<code>class</code>前面加上<code>inner</code>关键字声明这是一个内部类。</p><h4 id="密封类sealed"><a href="#密封类sealed" class="headerlink" title="密封类sealed"></a>密封类<code>sealed</code></h4><p>当我们使用<code>when</code>作为表达式来赋值的时候,我们必须提供一个else分支来处理没有其它任何分支能匹配的情况。</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">Expr</span></span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Num</span></span>(<span class="keyword">val</span> value: <span class="built_in">Int</span>) : Expr</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sum</span></span>(<span class="keyword">val</span> left: Expr, <span class="keyword">val</span> right: Expr) : Expr</span><br></pre></td></tr></tbody></table></figure><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">eval</span><span class="params">(e: <span class="type">Expr</span>)</span></span>: <span class="built_in">Int</span> = </span><br><span class="line"> <span class="keyword">when</span>(e) {</span><br><span class="line"> <span class="keyword">is</span> Num -> e.value</span><br><span class="line"> <span class="keyword">is</span> Sum -> eval(e.left) + eval(e.right)</span><br><span class="line"> <span class="keyword">else</span> -> -<span class="number">1</span></span><br><span class="line"> }</span><br></pre></td></tr></tbody></table></figure><p>加上密封类关键字后,<code>Kotlin</code>就会对可能创建的子类做出了一个的限制,所有的直接子类必须嵌套在父类中。</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">sealed</span> <span class="class"><span class="keyword">class</span> <span class="title">Expr</span> </span>{</span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Num</span></span>(<span class="keyword">val</span> value: <span class="built_in">Int</span>) : Expr()</span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Sum</span></span>(<span class="keyword">val</span> left: Expr, <span class="keyword">val</span> right: Expr) : Expr()</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">eval</span><span class="params">(e: <span class="type">Expr</span>)</span></span>: <span class="built_in">Int</span> = </span><br><span class="line"> <span class="keyword">when</span>(e) {</span><br><span class="line"> <span class="keyword">is</span> Num -> e.value</span><br><span class="line"> <span class="keyword">is</span> Sum -> eval(e.left) + eval(e.right)</span><br><span class="line"> }</span><br></pre></td></tr></tbody></table></figure><p>加入<code>sealed</code>关键字后你就不需要再加上<code>else</code>分支处理其它情况了,<code>sealed</code>修饰符隐含的这个类是一个<code>open</code>类,不需要显示的添加<code>open</code>修饰符。</p><h4 id="声明一个带非默认构造方法或属性的类"><a href="#声明一个带非默认构造方法或属性的类" class="headerlink" title="声明一个带非默认构造方法或属性的类"></a>声明一个带非默认构造方法或属性的类</h4><p>一个简单的类的声明</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span></span>(<span class="keyword">val</span> nickname: String)</span><br></pre></td></tr></tbody></table></figure><p>类所有的声明都在括号内,这段被括号围起来的语句块也叫做主构造方法,我们也可以自己显式的去声明一个构造方法。</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span> <span class="keyword">constructor</span></span>(_nickname: String) {</span><br><span class="line"> <span class="keyword">val</span> nickname: String</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">init</span> {</span><br><span class="line"> nickname = _nickname</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><code>constructor</code>用来声明构造方法,<code>init</code>是用来引入一个初始化语句块,这种语句块在类被创建的时候就会执行,可以与主构造方法一起使用,下面声明一个私有的构造方法。</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span> <span class="keyword">private</span> <span class="keyword">constructor</span></span>() {}</span><br></pre></td></tr></tbody></table></figure><p>一个类也可以有多个构造方法,像<code>Java</code>一样。</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">open</span> <span class="class"><span class="keyword">class</span> <span class="title">View</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>(context: Context) {}</span><br><span class="line"> <span class="keyword">constructor</span>(context: Context, attr: Attributeset) {}</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>调用父类的构造方法</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyView</span> : <span class="type">View {</span></span></span><br><span class="line"> <span class="keyword">constructor</span>(context: Context) : <span class="keyword">super</span>(context) {}</span><br><span class="line"> <span class="keyword">constructor</span>(context: Context, attr: Attributeset) : <span class="keyword">super</span>(context, attr) {}</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>也可以使用<code>this</code>关键字调用本类中的构造方法。</p><h4 id="通过getter、setter访问支持的字段"><a href="#通过getter、setter访问支持的字段" class="headerlink" title="通过getter、setter访问支持的字段"></a>通过<code>getter</code>、<code>setter</code>访问支持的字段</h4><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span> <span class="keyword">constructor</span></span>(_nickname: String) {</span><br><span class="line"> <span class="keyword">val</span> nickname: String = _nickname</span><br><span class="line"> <span class="keyword">var</span> sex: String = <span class="string">""</span></span><br><span class="line"> <span class="keyword">set</span>(value) {</span><br><span class="line"> field = <span class="string">"<span class="variable">$value</span>.com"</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">get</span>() = <span class="string">"@<span class="variable">$field</span>"</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>关键字<code>field</code>用来访问支持的字段的值</p><h4 id="数据类"><a href="#数据类" class="headerlink" title="数据类"></a>数据类</h4><p><code>Kotlin</code>中的数据类会帮忙自动生成通用的方法,例如<code>setter</code>、<code>getter</code>等。数据类的属性推荐使用只读属性<code>val</code>,如果需要更改对象数据,也提供了一个允许<code>copy</code>类的实例方法,在<code>copy</code>的同时修改某些属性的值。</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">data</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</span></span>(<span class="keyword">val</span> name: String)</span><br></pre></td></tr></tbody></table></figure><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">val</span> pp = Person(<span class="string">"张三"</span>)</span><br><span class="line"><span class="keyword">val</span> pp2 = pp.copy(name = <span class="string">"李四"</span>)</span><br><span class="line">showToast(pp2.name)</span><br><span class="line"><span class="comment">// 弹框输出:李四</span></span><br></pre></td></tr></tbody></table></figure><h4 id="类的委托"><a href="#类的委托" class="headerlink" title="类的委托"></a>类的委托</h4><p>通过关键字<code>by</code>实现</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">Base</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">showTag</span><span class="params">(tag: <span class="type">String</span>)</span></span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BaseImpl</span> : <span class="type">Base {</span></span></span><br><span class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">showTag</span><span class="params">(tag: <span class="type">String</span>)</span></span> {</span><br><span class="line"> println(tag)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DriverBaseImpl</span></span>(<span class="keyword">private</span> <span class="keyword">val</span> base: BaseImpl) : Base <span class="keyword">by</span> base</span><br></pre></td></tr></tbody></table></figure><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line">DriverBaseImpl(BaseImpl()).showTag(<span class="string">"tag"</span>)</span><br></pre></td></tr></tbody></table></figure><h4 id="object关键字"><a href="#object关键字" class="headerlink" title="object关键字"></a><code>object</code>关键字</h4><p><code>Kotlin</code>中<code>object</code>关键字在多种情况下会出现,这个关键字定义一个类并同时创建一个实例:</p><ul><li>对象声明是定义单例的一种方式。</li><li>伴生对象可以持有工厂方法和其它与这个类先关,但在调用时并不依赖类实例的方法,它们的成员可以通过类名来访问,</li><li>对象表达式用来替代<code>Java</code>的匿名内部类。</li></ul><h5 id="对象声明,创建单例"><a href="#对象声明,创建单例" class="headerlink" title="对象声明,创建单例"></a>对象声明,创建单例</h5><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">object</span> PayType {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">val</span> allType = arrayListOf(<span class="string">"支付宝"</span>, <span class="string">"微信"</span>, <span class="string">"银联"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">printAllPayType</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">for</span> (payType <span class="keyword">in</span> allType) {</span><br><span class="line"> println(payType)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line">PayType.printAllPayType()</span><br></pre></td></tr></tbody></table></figure><p>与类一样,一个对象声明可以包含属性、方法、初始化语句块等的声明,唯一不允许的是构造方法,与普通类实例不一样的是对象声明在定义的时候就立即创建了,不需要再代码的其它地方调用构造方法之类的。</p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<h3 id="函数声明"><a href="#函数声明" class="headerlink" title="函数声明"></a>函数声明</h3><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">max</span><span class="params">(a: <span class="type">Int</span>, b: <span class="type">Int</span>)</span></span>: <span class="built_in">Int</span> = <span class="keyword">if</span>(a&gt;b) a <span class="keyword">else</span> b</span><br></pre></td></tr></tbody></table></figure>
<p>关键字<code>fun</code>,名称<code>max</code>,括号内的参数列表,先参数名,冒号后面是参数类型,括号后面的冒号跟着返回值类型,如果没有返回值就把冒号跟返回值类型直接去掉,<code>kotlin</code>里面<code>if</code>是有结果的表达式。</p>
<h3 id="变量声明"><a href="#变量声明" class="headerlink" title="变量声明"></a>变量声明</h3><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">val</span> a = <span class="number">1</span></span><br><span class="line"><span class="keyword">val</span> a: <span class="built_in">Int</span> = <span class="number">1</span></span><br><span class="line"><span class="keyword">var</span> b = <span class="number">2</span></span><br><span class="line"><span class="keyword">var</span> b: <span class="built_in">Int</span> = <span class="number">2</span></span><br></pre></td></tr></tbody></table></figure>
<p>声明变量的关键字有两个不可变引用<code>val</code>、可变引用<code>var</code>,默认情况下尽可能用<code>val</code>去声明变量,仅在必要的时候把变量声明为<code>var</code>,这样可以让你的代码更接近函数式编程风格。声明变量的时候可以显式的指定变量的类型,也可以直接隐藏去掉(如果已经可以推断出类型)</p>
<h3 id="字符串格式化模板"><a href="#字符串格式化模板" class="headerlink" title="字符串格式化模板"></a>字符串格式化模板</h3><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">val</span> name = <span class="string">"bob"</span></span><br><span class="line"><span class="keyword">val</span> sex = <span class="string">"男"</span></span><br><span class="line"><span class="keyword">val</span> age = <span class="number">100</span></span><br><span class="line">print(<span class="string">"<span class="subst">${name}</span>的性别为:<span class="variable">$sex</span>,年龄为:<span class="variable">$age</span>"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果:bob的性别为:男,年龄为:100</span></span><br></pre></td></tr></tbody></table></figure>
<p>可以直接用<code>$</code>符号对变量进行引用,还可以用<code>${}</code>这种对表达式进行引用,就像刚才的例子那样,<code>name</code>可以换成表达式进行使用。</p>
</summary>
<category term="Android" scheme="https://keee.top/categories/Android/"/>
<category term="kotlin" scheme="https://keee.top/tags/kotlin/"/>
</entry>
<entry>
<title>Kotlin常见作用域函数</title>
<link href="https://keee.top/2020/052242399.html"/>
<id>https://keee.top/2020/052242399.html</id>
<published>2020-05-22T10:05:53.000Z</published>
<updated>2020-05-22T10:05:53.000Z</updated>
<content type="html"><![CDATA[<ul><li>let函数</li></ul><blockquote><p>定义一个变量在一个特定的作用域内,避免写一些判空操作</p></blockquote><h5 id="使用:"><a href="#使用:" class="headerlink" title="使用:"></a>使用:</h5><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line">非空变量,使用it代替<span class="keyword">object</span>对象去访问其公有属性:</span><br><span class="line"><span class="keyword">object</span>.let {</span><br><span class="line">it.xxx()</span><br><span class="line"><span class="comment">// 999</span></span><br><span class="line"> <span class="comment">// return 888</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">可空变量,判空操作,函数内it不为空:</span><br><span class="line"><span class="keyword">object</span>?.let {</span><br><span class="line">it.xxx()</span><br><span class="line"><span class="comment">// 999</span></span><br><span class="line"> <span class="comment">// return 888</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>返回值:最后一行 return 的表达式</p><a id="more"></a><ul><li>with函数</li></ul><blockquote><p>调用一个对象的多个方法 or 属性时,可以省去对象名的重复,直接调用方法 or 属性即可</p></blockquote><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line">with(<span class="keyword">object</span>) {</span><br><span class="line"> <span class="comment">// object本身的一些方法或属性,不需要再object.xxx</span></span><br><span class="line"><span class="comment">// 999</span></span><br><span class="line"> <span class="comment">// return 888</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>返回值:最后一行 return 的表达式</p><ul><li>apply函数</li></ul><blockquote><p>结合了 <code>let</code>、<code>with</code> 两个函数的作用,适合链式调用</p></blockquote><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">object</span>.apply {</span><br><span class="line"> <span class="comment">// 返回值是object本身</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">object</span>?.apply {</span><br><span class="line"> <span class="comment">// 返回值是object本身</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>返回值:返回调用者本身</p><ul><li>run函数</li></ul><blockquote><p>作用类似与 <code>apply</code> 函数,两者区别于返回值</p></blockquote><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">object</span>.run {</span><br><span class="line"> <span class="comment">// 999</span></span><br><span class="line"> <span class="comment">// return 888</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">object</span>?.run {</span><br><span class="line"> <span class="comment">// 999</span></span><br><span class="line"> <span class="comment">// return 888</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>返回值:返回最后一行 return 的表达式</p><ul><li>also函数</li></ul><blockquote><p>类似与 <code>let</code> 函数,区别在于返回值</p></blockquote><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">object</span>.also {</span><br><span class="line"> <span class="comment">// 返回值是object本身</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">object</span>?.also {</span><br><span class="line"> <span class="comment">// 返回值是object本身</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>返回值:返回调用者本身</p><h4 id="总结:"><a href="#总结:" class="headerlink" title="总结:"></a>总结:</h4><ol><li>返回本身的函数有 <code>also</code>、<code>apply</code>,其它的 <code>let</code>、<code>with</code>、<code>run</code> 返回的都是末行或者 <code>return</code> 表达式。</li><li>除了 <code>with</code> 方法没有判空作用,其它均可以进行判空处理。</li></ol><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<ul>
<li>let函数</li>
</ul>
<blockquote>
<p>定义一个变量在一个特定的作用域内,避免写一些判空操作</p>
</blockquote>
<h5 id="使用:"><a href="#使用:" class="headerlink" title="使用:"></a>使用:</h5><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line">非空变量,使用it代替<span class="keyword">object</span>对象去访问其公有属性:</span><br><span class="line"><span class="keyword">object</span>.let {</span><br><span class="line"> it.xxx()</span><br><span class="line"> <span class="comment">// 999</span></span><br><span class="line"> <span class="comment">// return 888</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">可空变量,判空操作,函数内it不为空:</span><br><span class="line"><span class="keyword">object</span>?.let {</span><br><span class="line"> it.xxx()</span><br><span class="line"> <span class="comment">// 999</span></span><br><span class="line"> <span class="comment">// return 888</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>返回值:最后一行 return 的表达式</p>
</summary>
<category term="Android" scheme="https://keee.top/categories/Android/"/>
<category term="kotlin" scheme="https://keee.top/tags/kotlin/"/>
</entry>
<entry>
<title>Synchronize & ReentrantLock</title>
<link href="https://keee.top/2020/052261209.html"/>
<id>https://keee.top/2020/052261209.html</id>
<published>2020-05-22T10:05:53.000Z</published>
<updated>2020-05-22T10:05:53.000Z</updated>
<content type="html"><![CDATA[<h5 id="synchronized"><a href="#synchronized" class="headerlink" title="synchronized"></a>synchronized</h5><p><code>synchronized</code> 可以用来修饰以下 3 个层面:</p><ol><li><p>修饰实例方法</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">int</span> count = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">log</span><span class="params">(String text)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> index = <span class="number">0</span>; index < <span class="number">5</span>; index++) {</span><br><span class="line"> count++;</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">":"</span> + text + <span class="string">":"</span> + count);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>这种情况下的锁对象是 <code>当前实例对象</code> ,因此只有同一个实例对象调用此方法才会产生互斥效果,不同实例对象之间不会有互斥效果。</p><figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> static void main(String[] args) {</span><br><span class="line"> LockTest t1 = new LockTest();</span><br><span class="line"> LockTest t2 = new LockTest();</span><br><span class="line"> Thread thread1 = new Thread(() -> t1.log(<span class="string">"测试1"</span>));</span><br><span class="line"> Thread thread2 = new Thread(() -> t2.log(<span class="string">"测试2"</span>));</span><br><span class="line"> thread1.start();</span><br><span class="line"> thread2.start();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>上面代码例子,调用的是不通对象的实例方法,所以彼此之间是不会有排斥的,运行输出如下:</p><p><img src="https://s1.ax1x.com/2020/05/18/YfXwYF.png" alt="YfXwYF.png"></p></li><li><p>修饰静态方法</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> index = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">print</span><span class="params">(String text)</span> </span>{</span><br><span class="line"> index++;</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">":"</span> + text + <span class="string">":"</span> + index);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><a id="more"></a></li><li><p>修饰代码块</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">int</span> count = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">println</span><span class="params">(String text)</span> </span>{</span><br><span class="line"> <span class="keyword">synchronized</span> (<span class="keyword">this</span>) {</span><br><span class="line"> count++;</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">":"</span> + text + <span class="string">":"</span> + count);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>或者</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">int</span> count = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Object lock = <span class="keyword">new</span> Object();</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">println</span><span class="params">(String text)</span> </span>{</span><br><span class="line"> <span class="keyword">synchronized</span> (lock) {</span><br><span class="line"> count++;</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">":"</span> + text + <span class="string">":"</span> + count);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ol><h5 id="ReentrantLock"><a href="#ReentrantLock" class="headerlink" title="ReentrantLock"></a>ReentrantLock</h5><p><code>ReentrantLock</code> 的使用同 <code>synchronized</code> 有点不同,它的加锁和解锁操作都需要<code>手动完成</code>:</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">int</span> count = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">private</span> ReentrantLock reentrantLock = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">print</span><span class="params">(String text)</span> </span>{</span><br><span class="line"> reentrantLock.lock();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">":"</span> + text + <span class="string">":"</span> + count);</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> reentrantLock.unlock();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p> <code>lock()</code> 和 <code>unlock()</code> 分别是加锁和解锁操作。<code>ReentrantLock</code> 与 <code>synchronized</code> 不同,当异常发生时 <code>synchronized</code> 会自动释放锁,但是 <code>ReentrantLock</code> 并不会自动释放锁。因此好的方式是将 <code>unlock</code> 操作放在 <code>finally</code> 代码块中,保证任何时候锁都能够被正常释放掉。</p><p>默认情况下,<code>synchronized</code> 和 <code>ReentrantLock</code> 都是<code>非公平锁</code>。但是 <code>ReentrantLock</code> 可以通过传入 <code>true</code> 来创建一个公平锁。所谓公平锁就是通过同步队列来实现多个线程按照<code>申请锁的顺序</code>获取锁。</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* Creates an instance of {<span class="doctag">@code</span> ReentrantLock} with the</span></span><br><span class="line"><span class="comment">* given fairness policy.</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> fair {<span class="doctag">@code</span> true} if this lock should use a fair ordering policy</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ReentrantLock</span><span class="params">(<span class="keyword">boolean</span> fair)</span> </span>{</span><br><span class="line"> sync = fair ? <span class="keyword">new</span> FairSync() : <span class="keyword">new</span> NonfairSync();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>创建一个公平锁:</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">int</span> count = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">private</span> ReentrantLock reentrantLock = <span class="keyword">new</span> ReentrantLock(<span class="keyword">true</span>);</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">print</span><span class="params">(String text)</span> </span>{</span><br><span class="line"> reentrantLock.lock();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">":"</span> + text + <span class="string">":"</span> + count);</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> reentrantLock.unlock();</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="读写锁(ReentrantReadWriteLock)"><a href="#读写锁(ReentrantReadWriteLock)" class="headerlink" title="读写锁(ReentrantReadWriteLock)"></a>读写锁(ReentrantReadWriteLock)</h5><p>创建读写锁:</p><figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> ReentrantReadWriteLock reentrantReadWriteLock = <span class="keyword">new</span> ReentrantReadWriteLock();</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">// 读操作</span></span><br><span class="line"> reentrantReadWriteLock.readLock().lock();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> reentrantReadWriteLock.readLock().unlock();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 写操作</span></span><br><span class="line"> reentrantReadWriteLock.writeLock().lock();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> reentrantReadWriteLock.writeLock().unlock();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<summary type="html">
<h5 id="synchronized"><a href="#synchronized" class="headerlink" title="synchronized"></a>synchronized</h5><p><code>synchronized</code> 可以用来修饰以下 3 个层面:</p>
<ol>
<li><p>修饰实例方法</p>
<figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">int</span> count = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">log</span><span class="params">(String text)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> index = <span class="number">0</span>; index &lt; <span class="number">5</span>; index++) {</span><br><span class="line"> count++;</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">":"</span> + text + <span class="string">":"</span> + count);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>这种情况下的锁对象是 <code>当前实例对象</code> ,因此只有同一个实例对象调用此方法才会产生互斥效果,不同实例对象之间不会有互斥效果。</p>
<figure class="highlight kotlin"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> static void main(String[] args) {</span><br><span class="line"> LockTest t1 = new LockTest();</span><br><span class="line"> LockTest t2 = new LockTest();</span><br><span class="line"> Thread thread1 = new Thread(() -&gt; t1.log(<span class="string">"测试1"</span>));</span><br><span class="line"> Thread thread2 = new Thread(() -&gt; t2.log(<span class="string">"测试2"</span>));</span><br><span class="line"> thread1.start();</span><br><span class="line"> thread2.start();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>上面代码例子,调用的是不通对象的实例方法,所以彼此之间是不会有排斥的,运行输出如下:</p>
<p><img src="https://s1.ax1x.com/2020/05/18/YfXwYF.png" alt="YfXwYF.png"></p>
</li>
<li><p>修饰静态方法</p>
<figure class="highlight java"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> index = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">print</span><span class="params">(String text)</span> </span>{</span><br><span class="line"> index++;</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">":"</span> + text + <span class="string">":"</span> + index);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ol>
</summary>
<category term="Java" scheme="https://keee.top/categories/Java/"/>
<category term="多线程" scheme="https://keee.top/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
</entry>
</feed>