-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
364 lines (336 loc) · 55.9 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[canaan's blog]]></title>
<subtitle><![CDATA[If it ain't fun, don't do it]]></subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://yoursite.com/"/>
<updated>2017-02-12T16:12:32.000Z</updated>
<id>http://yoursite.com/</id>
<author>
<name><![CDATA[canaan]]></name>
<email><![CDATA[canaanyjn@gmail.com]]></email>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title><![CDATA[RecyclerView 复用机制]]></title>
<link href="http://yoursite.com/2017/01/30/RecyclerView%20%E5%A4%8D%E7%94%A8%E6%9C%BA%E5%88%B6/"/>
<id>http://yoursite.com/2017/01/30/RecyclerView 复用机制/</id>
<published>2017-01-30T11:39:00.000Z</published>
<updated>2017-02-12T16:12:32.000Z</updated>
<content type="html"><![CDATA[<p>随着需求的变化,在一个列表中的内容和列表的使用率也不断增加,如何保证列表在滑动过程中的流畅性对于用户体验有很大的影响,而 RecyclerView 对于 ListView 的一大优势就是其复用机制更加完善,有两层复用,而且给程序员更多的发挥空间。</p>
<h2 id="LayoutManager">LayoutManager</h2><p>LayoutManager 主要是测量和安置并且复用 RecyclerView 中的 item。LayoutManager 既然是管理者安置 item,就不得不去管理 item 的复用,因为视图的复用能保证最快速的将界面呈现在用户面前,优化性能。 所以这部分中我们主要是来简单的看下 LayoutManager 的复用逻辑。</p>
<h3 id="LayoutManager如何回收与利用">LayoutManager如何回收与利用</h3><p>LayoutManager 是一个抽象的类,我们如果要写自定义的LayoutManager 就必须得处理其复用逻辑。可以按照这样的逻辑来管理视图的复用。 要做的就是在所有的子 View 中找出属于屏幕范围外的,然后将其通过<figure class="highlight"><figcaption><span>Recycler 的 getViewForPosition() 来复用回收的 View 之后分别调用以下几个方法来布局</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"> * ```measureChildWithMargins()```:测量子 View * ```addView()```:将子 View 加到 RecyclerView 中 * ```layoutDecorated()```:布局在屏幕上的子 View ## Recycler Recycler 类主要是负责管理被标记为零碎(Scrapped)和被分离(detached)的 view,并且复用这些 view。 Recycler 主要负责回收缓存和复用两部分的功能,就从这两方面来分析。在分析之前,我们还是有必要来理解下一些 Recycler 中的背景。 ###Detach vs. Remove 布局更新时有两个方法处理已存在的子视图:detach 和 remove (分离和移除)。Detach 是一个轻量的记录 view 操作。 被 detach 的视图在你的代码返回前能够重新连接。可以通过 Recycler 在不 重新绑定/重新构建 子视图的情况下修改已连接子视图的索引。 Remove 意味着这个 view 已经不需要了。任何被永久移除的 view 都应该 放到 Recycler 中,方便以后重用,不过 API 并没有强制要求。 被 remove 的视图是否被回收取决于你。 ###Scrap vs. Recycle Recycler 有两级视图缓存系统: scrap heap 和 recycle pool (垃圾堆和回收池), Scrap heap 是一个轻量的集合,视图可以不经过适配器直接返回给 LayoutManager 。通常被 detach 但会在同一布局重新使用的视图会临时储存在这里。Recycle pool 存放的 是那些假定并没有得到正确数据(相应位置的数据)的视图, 因此它们都要经过适配器重新绑定后才能返回给 LayoutManager。 ### RecyclerView 如何做到 Recycle 一个废弃的 view 虽然被标记为废弃的,但它仍然与依附于 RecyclerView,只是做了个移除或者复用的标记。 LayoutManager 的 ```removeAndRecycleView()``` 方法调用的就是 Recycler 的```recycleView()```来完成视图的回收。 **Recycler::recycleView(View view)** ```java public void recycleView(View view) { // This public recycle method tries to make view recycle-able since layout manager // intended to recycle this view (e.g. even if it is in scrap or change cache) ViewHolder holder = getChildViewHolderInt(view); if (holder.isTmpDetached()) { removeDetachedView(view, false); } if (holder.isScrap()) { holder.unScrap(); } else if (holder.wasReturnedFromScrap()){ holder.clearReturnedFromScrapFlag(); } recycleViewHolderInternal(holder); }</span><br></pre></td></tr></table></figure></p>
<p>这个方法先通过 view 获取了 viewHolder 然后调用 recycleViewHolderInternal()。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">recycleViewHolderInternal</span><span class="params">(ViewHolder holder)</span> </span>{</span><br><span class="line"> ···</span><br><span class="line"> <span class="comment">//noinspection unchecked</span></span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">boolean</span> transientStatePreventsRecycling = holder</span><br><span class="line"> .doesTransientStatePreventRecycling();</span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">boolean</span> forceRecycle = mAdapter != <span class="keyword">null</span></span><br><span class="line"> && transientStatePreventsRecycling</span><br><span class="line"> && mAdapter.onFailedToRecycleView(holder);</span><br><span class="line"> <span class="keyword">boolean</span> cached = <span class="keyword">false</span>;</span><br><span class="line"> <span class="keyword">boolean</span> recycled = <span class="keyword">false</span>;</span><br><span class="line"> ···</span><br><span class="line"> <span class="keyword">if</span> (forceRecycle || holder.isRecyclable()) {</span><br><span class="line"> <span class="keyword">if</span> (!holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED</span><br><span class="line"> | ViewHolder.FLAG_UPDATE)) {</span><br><span class="line"> <span class="comment">// Retire oldest cached view</span></span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">int</span> cachedViewSize = mCachedViews.size();</span><br><span class="line"> <span class="keyword">if</span> (cachedViewSize == mViewCacheMax && cachedViewSize > <span class="number">0</span>) {</span><br><span class="line"> recycleCachedViewAt(<span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (cachedViewSize < mViewCacheMax) {</span><br><span class="line"> mCachedViews.add(holder);</span><br><span class="line"> cached = <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (!cached) {</span><br><span class="line"> addViewHolderToRecycledViewPool(holder);</span><br><span class="line"> recycled = <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (DEBUG) {</span><br><span class="line"> Log.d(TAG, <span class="string">"trying to recycle a non-recycleable holder. Hopefully, it will "</span></span><br><span class="line"> + <span class="string">"re-visit here. We are still removing it from animation lists"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// even if the holder is not removed, we still call this method so that it is removed</span></span><br><span class="line"> <span class="comment">// from view holder lists.</span></span><br><span class="line"> mViewInfoStore.removeViewHolder(holder);</span><br><span class="line"> <span class="keyword">if</span> (!cached && !recycled && transientStatePreventsRecycling) {</span><br><span class="line"> holder.mOwnerRecyclerView = <span class="keyword">null</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>如果一个 ViewHolder 被标记为 Scrap、tmpDetached 或者 ignore,是不会被回收的,这类的视图是仅仅被加到 mChangedScrap 队列中,因为这些视图还可能马上就会被复用,没有必要将其加到缓存中。<br>Recycler 在回收时会先去判断已经缓存的 View 的数量是否已经达到上限,如果达到上限了,就将最先缓存的 View 加到回收池中。如果此时缓存的 View 的数量没有达到上限就要加到缓存队列中先,这是 RecyclerView 的一级缓存。如果最终没有被成功加到缓存中,就把当前的 ViewHolder 加到下一级缓存中 —— RecycledViewPool。</p>
<p><strong>Recycler::addViewHolderToRecycledViewPool(holder)</strong></p>
<figure class="highlight openscad"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">void addViewHolderToRecycledViewPool<span class="params">(ViewHolder holder)</span> {</span><br><span class="line"> ViewCompat.setAccessibilityDelegate<span class="params">(holder.itemView, null)</span>;</span><br><span class="line"> dispatchViewRecycled<span class="params">(holder)</span>;</span><br><span class="line"> holder.mOwnerRecyclerView = null;</span><br><span class="line"> getRecycledViewPool<span class="params">()</span>.putRecycledView<span class="params">(holder)</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>同时告知相关的组件这个 view 被回收了。所以,最后都是将 View 加到 RecycledViewPool 中。</p>
<h4 id="RecycledViewPool">RecycledViewPool</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> SparseArray<ArrayList<ViewHolder>> mScrap =</span><br><span class="line"> <span class="keyword">new</span> SparseArray<ArrayList<ViewHolder>>();</span><br></pre></td></tr></table></figure>
<p>RecycledViewPool 实际上就是一个二维链表,其中一个 ViewType 对应于一个列表,每个列表的默认最大值是5。</p>
<p>RecycledViewPool 最大的特点是能在多个 RecyclerView 之间共享 View 对象。对于一些 ViewPager 加上 RecyclerView 的界面,能够共用相同的 View,减少 View 的创建,能提高不少的性能。</p>
<h3 id="Recycler_如何复用">Recycler 如何复用</h3><p>复用主要看两个方法</p>
<ul>
<li>getViewForPosition(int position, boolean dryRun)<br> 这个方法在 LayoutManager 部分中讲过,对于获取复用的视图都是通过这个方法。<br> 先从一级缓存中拿取 ViewHolder, 如果不存在则从缓存池中获取,从缓存中获取的视图是已经 detached 了,所以必须对该 ViewHolder 重新绑定。最后如果仍然没有获取到 ViewHolder,则通过适配器调用 createViewHolder() 方法。</li>
<li><p>bindViewToPosition(View view, int position)<br> 一般来说我们应该通过 getViewForPosition() 来获取 View,在这个方法中已经处理了缓存逻辑,并且在这个方法中已经对视图做了绑定,所以基本上 bindViewToPosition()基本用不到。</p>
<h2 id="如何知道一个_itemView_的回收">如何知道一个 itemView 的回收</h2><p> RecyclerView::Adapter 中有一个方法叫做<code>onViewRecycled(VH holder)</code>,这个函数会被回调。</p>
</li>
</ul>
<h2 id="参考">参考</h2><p><a href="http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0517/2880.html" target="_blank" rel="external">http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0517/2880.html</a></p>
]]></content>
<summary type="html">
<![CDATA[<p>随着需求的变化,在一个列表中的内容和列表的使用率也不断增加,如何保证列表在滑动过程中的流畅性对于用户体验有很大的影响,而 RecyclerView 对于 ListView 的一大优势就是其复用机制更加完善,有两层复用,而且给程序员更多的发挥空间。</p>
<h2 id="L]]>
</summary>
<category term="android" scheme="http://yoursite.com/tags/android/"/>
</entry>
<entry>
<title><![CDATA[通过学习ViewCompat实现向下兼容]]></title>
<link href="http://yoursite.com/2016/01/28/ViewCompat%E5%AD%A6%E4%B9%A0/"/>
<id>http://yoursite.com/2016/01/28/ViewCompat学习/</id>
<published>2016-01-28T03:43:00.000Z</published>
<updated>2017-02-12T16:12:32.000Z</updated>
<content type="html"><![CDATA[<p>今天在做PickerColorFB的时候想做一个阴影效果,于是去看FAB的源码,他的阴影是通过<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mImpl.setElevation(elevation);</span><br></pre></td></tr></table></figure></p>
<p>来实现,其中的mIpl是<strong>FloatingActionButtonImpl.class</strong>的引用,因为FAB基类是ImageView,类FloatingActionButtonImpl抽象了FAB的独有功能。而mIpl也是根据SDK版本来实现。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="keyword">int</span> sdk = Build.VERSION.SDK_INT;</span><br><span class="line"> <span class="keyword">if</span> (sdk >= <span class="number">21</span>) {</span><br><span class="line"> mImpl = <span class="keyword">new</span> FloatingActionButtonLollipop(<span class="keyword">this</span>, delegate);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (sdk >= <span class="number">12</span>) {</span><br><span class="line"> mImpl = <span class="keyword">new</span> FloatingActionButtonHoneycombMr1(<span class="keyword">this</span>, delegate);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> mImpl = <span class="keyword">new</span> FloatingActionButtonEclairMr1(<span class="keyword">this</span>, delegate);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></p>
<p>我们先去看FloatingActionButtonLollipop.class的代码<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="annotation">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setElevation</span><span class="params">(<span class="keyword">float</span> elevation)</span> </span>{</span><br><span class="line"> ViewCompat.setElevation(mView, elevation);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>他是通过调用ViewCompat.setElevation()来实现。那ViewCompat又是一个什么类呢。从名字就可以看出来,这是一个View类的兼容辅助类。</p>
<blockquote>
<p>Helper for accessing features in {@link View} introduced after API<br> level 4 in a backwards compatible fashion.</p>
</blockquote>
<p>从该类的注释也可以看出来,这是一个帮助API4以后的方法实现向下兼容的类。<br>ViewCompat跟其他类很不一样的一点就是SDK版本判断。当然方式是一样的,但位置不同。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> {</span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">int</span> version = android.os.Build.VERSION.SDK_INT;</span><br><span class="line"> <span class="keyword">if</span> (version >= <span class="number">23</span>) {</span><br><span class="line"> IMPL = <span class="keyword">new</span> MarshmallowViewCompatImpl();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (version >= <span class="number">21</span>) {</span><br><span class="line"> IMPL = <span class="keyword">new</span> LollipopViewCompatImpl();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (version >= <span class="number">19</span>) {</span><br><span class="line"> IMPL = <span class="keyword">new</span> KitKatViewCompatImpl();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (version >= <span class="number">17</span>) {</span><br><span class="line"> IMPL = <span class="keyword">new</span> JbMr1ViewCompatImpl();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (version >= <span class="number">16</span>) {</span><br><span class="line"> IMPL = <span class="keyword">new</span> JBViewCompatImpl();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (version >= <span class="number">15</span>) {</span><br><span class="line"> IMPL = <span class="keyword">new</span> ICSMr1ViewCompatImpl();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (version >= <span class="number">14</span>) {</span><br><span class="line"> IMPL = <span class="keyword">new</span> ICSViewCompatImpl();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (version >= <span class="number">11</span>) {</span><br><span class="line"> IMPL = <span class="keyword">new</span> HCViewCompatImpl();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (version >= <span class="number">9</span>) {</span><br><span class="line"> IMPL = <span class="keyword">new</span> GBViewCompatImpl();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (version >= <span class="number">7</span>) {</span><br><span class="line"> IMPL = <span class="keyword">new</span> EclairMr1ViewCompatImpl();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> IMPL = <span class="keyword">new</span> BaseViewCompatImpl();</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></p>
<p>将SDK版本初始化在一个static块中,而不是我们常用的方法内。这样在整个应用的生命周期中只用判断一次就可以了。这样我们就得到了不同版本对应的类。但是,很多方法虽然SDK版本不同,但是他们并没有改变,那怎么办。<br>ViewCompat类中定义了一个接口ViewCompatImpl,它包含了基本的方法。同时也定义了一个BaseViewCompatImpl类,它实现了ViewCompatImpl。这就是基本的ViewCompatImpl,然后每个版本的ViewCompatImpl会继承上一个版本的类,如果修改的方法,就覆盖原来的方法。需要新加的就定义一个,这样就实现了向下兼容。<br>每一个最新的版本都含有了原来的方法,这样在一些新版本系统上的机器安装旧版本的应用也不至于找不到原来的方法。如果在旧版本上某些方法有新的实现,则可以修改,这样新版本自然也会改变,保持一致。<br>在每一个版本实现的ViewCompatImpl中,各个新增的方法并不是自己实现,他只是代理,最后实现的是通过(比如说Lollipop)ViewCompatLollipop来实现,这就是复合。<br>回到之前的话题,设置阴影。在Lollipop版本中,最终到最后会由View类的setElevation()方法实现,其中也是通过mRenderNode来实现<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span><br><span class="line"> * Sets the base elevation of this view, in pixels.</span><br><span class="line"> *</span><br><span class="line"> * <span class="doctag">@attr</span> ref android.R.styleable#View_elevation</span><br><span class="line"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setElevation</span><span class="params">(<span class="keyword">float</span> elevation)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (elevation != getElevation()) {</span><br><span class="line"> invalidateViewProperty(<span class="keyword">true</span>, <span class="keyword">false</span>);</span><br><span class="line"> mRenderNode.setElevation(elevation);</span><br><span class="line"> invalidateViewProperty(<span class="keyword">false</span>, <span class="keyword">true</span>);</span><br><span class="line"></span><br><span class="line"> invalidateParentIfNeededAndWasQuickRejected();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>这里关系到硬件加速渲染,毕竟阴影是3d的,以后有时间再学习。<br>在SDK 12-20之间,FloatingActionButtonHoneycombMr1类没有重写setElevation(),所以直接调用父类FloatingActionButtonEclairMr1的setElevation()<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="annotation">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">setElevation</span><span class="params">(<span class="keyword">float</span> elevation)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (mElevation != elevation && mShadowDrawable != <span class="keyword">null</span>) {</span><br><span class="line"> mShadowDrawable.setShadowSize(elevation, elevation + mPressedTranslationZ);</span><br><span class="line"> mElevation = elevation;</span><br><span class="line"> updatePadding();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>在这个类中实现是通过ShadowDrawableWrapper实现。这个也在以后说,毕竟不是本文重点。</p>
<p><strong>总结一下</strong></p>
<ul>
<li>SDK版本的初始化,如果在应用中很多地方需要判断SDK版本号,不如尝试在工具类中的静态代码块中先初始化,这样就只用初始化一次。尤其是最新的Marshmallow,需要动态获取权限,这就需要判断是否是6.0从而判断需不需要获取权限。</li>
<li>实现向下兼容,可以先定义一个接口,声明目前的方法,然后通过一个基类来实现该接口。之后每个版本只用实现上一个版本就够了。Android中还有很多Compat类,都是实现向下兼容的辅助类。</li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<p>今天在做PickerColorFB的时候想做一个阴影效果,于是去看FAB的源码,他的阴影是通过<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1]]>
</summary>
<category term="android" scheme="http://yoursite.com/tags/android/"/>
</entry>
<entry>
<title><![CDATA[RxJava —— Subject]]></title>
<link href="http://yoursite.com/2015/12/20/RxJava%20%E2%80%94%E2%80%94%20Subject/"/>
<id>http://yoursite.com/2015/12/20/RxJava —— Subject/</id>
<published>2015-12-20T15:03:42.000Z</published>
<updated>2017-02-12T16:12:32.000Z</updated>
<content type="html"><![CDATA[<p>这是个很奇怪的类,可以说是Observable和Observer的代理<br>Subject类继承于Observable,同时实现了Observer接口。因此既是一个Observable也是个Observe,所以能被Observer订阅,也可以订阅Observable,也可以将接收来的数据再转发出去。<br>文档中说有四种子类,但是在源码中显示有7种</p>
<h2 id="AsyncSubject">AsyncSubject</h2><p>AsyncSubject只会在源Observable完成之后,将源Observable的最后一个数据传给Observer</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Observe不会收到数据,因为Subjec没有调用onComplete()</span></span><br><span class="line"> AsyncSubject<Object> subject = AsyncSubject.create();</span><br><span class="line"> subject.subscribe(observer);</span><br><span class="line"> subject.onNext(<span class="string">"one"</span>);</span><br><span class="line"> subject.onNext(<span class="string">"two"</span>);</span><br><span class="line"> subject.onNext(<span class="string">"three"</span>);</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Observer 只接收到"three"</span></span><br><span class="line"> AsyncSubject<Object> subject = AsyncSubject.create();</span><br><span class="line"> subject.subscribe(observer);</span><br><span class="line"> subject.onNext(<span class="string">"one"</span>);</span><br><span class="line"> subject.onNext(<span class="string">"two"</span>);</span><br><span class="line"> subject.onNext(<span class="string">"three"</span>);</span><br><span class="line"> subject.onCompleted();</span><br></pre></td></tr></table></figure>
<h2 id="BehaviorSubject">BehaviorSubject</h2><p>BehaviorSubject只会发送离他被订阅最近的一个数据,然后发送之后全部的数据给Observer</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Observer 接收到所有事件</span></span><br><span class="line"> BehaviorSubject<Object> subject = BehaviorSubject.create(<span class="string">"default"</span>);</span><br><span class="line"> subject.subscribe(observer);</span><br><span class="line"> subject.onNext(<span class="string">"one"</span>);</span><br><span class="line"> subject.onNext(<span class="string">"two"</span>);</span><br><span class="line"> subject.onNext(<span class="string">"three"</span>);</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Observer 只接收到"one","two","three",没收到"zero"</span></span><br><span class="line"> BehaviorSubject<Object> subject = BehaviorSubject.create(<span class="string">"default"</span>);</span><br><span class="line"> subject.onNext(<span class="string">"zero"</span>);</span><br><span class="line"> subject.onNext(<span class="string">"one"</span>);</span><br><span class="line"> subject.subscribe(observer);</span><br><span class="line"> subject.onNext(<span class="string">"two"</span>);</span><br><span class="line"> subject.onNext(<span class="string">"three"</span>);</span><br></pre></td></tr></table></figure>
<p>BehaviorSubject.oncreate()中传入一个defaultValue,这个认值会第一个被发送给Observer只要BahaviorSubject还没有接收到任何来自源Observable。</p>
<h2 id="PublishSubject">PublishSubject</h2><p>PublishSubject只会把在订阅时间点之后的源Observable的数据发送给Observer。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">PublishSubject<Object> subject = PublishSubject.create();</span><br><span class="line"> <span class="comment">// observer1 收到所有数据</span></span><br><span class="line"> subject.subscribe(observer1);</span><br><span class="line"> subject.onNext(<span class="string">"one"</span>);</span><br><span class="line"> subject.onNext(<span class="string">"two"</span>);</span><br><span class="line"> <span class="comment">// observer2 只收到"three"和onCompeleted()</span></span><br><span class="line"> subject.subscribe(observer2);</span><br><span class="line"> subject.onNext(<span class="string">"three"</span>);</span><br><span class="line"> subject.onCompleted();</span><br></pre></td></tr></table></figure>
<p>这里要注意一点是observer2的这种情况,容易造成数据的丢失。</p>
<h2 id="ReplaySubject">ReplaySubject</h2><p>ReplaySubject会发射所有的数据给Observer,无论是何时订阅.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">ReplaySubject<Object> subject = ReplaySubject.create();</span><br><span class="line"> subject.onNext(<span class="string">"one"</span>);</span><br><span class="line"> subject.onNext(<span class="string">"two"</span>);</span><br><span class="line"> subject.onNext(<span class="string">"three"</span>);</span><br><span class="line"> subject.onCompleted();</span><br><span class="line"> <span class="comment">// 两个observer都会接收到所有的数据</span></span><br><span class="line"> subject.subscribe(observer1);</span><br><span class="line"> subject.subscribe(observer2);</span><br></pre></td></tr></table></figure>
<p>ReplaySubject有多种构造方法,每种决定了不同的策略来决定何时抛弃掉一些缓存</p>
<ul>
<li>create();<br>默认设定缓存列表大小为16,如果达到了这么大,将会按需增长,一般增长50%。但是随着不断有新的数据加进来,队列重新分配,复制频率太高等会影响效率。</li>
<li>create();<br>根据你的估计来设定队列的大小,能较好的避免队列的重分配频率 </li>
<li>createUnbounded();<br>主要是测试用的一个方法,让你估计队列需要的大小,不需要去考虑抛弃策略</li>
<li>createWithSize(int size);<br>设定队列最大容量,如果超出,就抛弃掉最老的数据。如果订阅的ReplaySubject已经停止了,会发送停止前size大小的数据</li>
<li>createWithTime();<ul>
<li>以时间为边界的队列,每个数据会有一个时间戳,如果超过这个时间戳那就会被抛弃掉。</li>
<li>如果该subject停止了,那么之后订阅的Observer会收到在停止之后仍存留在缓存中的数据,并且无视时间。</li>
<li>如果一个Observer订阅了还未停止的Subject,他将只接收到在规定时间内的数据</li>
<li>注意,onError()和onCompleted()也会触发丢弃数据的动作。</li>
</ul>
</li>
</ul>
<h2 id="SerializedSubject">SerializedSubject</h2><p>封装Subject,这样能避免在不同线程下调用onNext()的不同步危险。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mySafeSubject = <span class="keyword">new</span> SerializedSubject( myUnsafeSubject );</span><br></pre></td></tr></table></figure>
<h2 id="TestSubject">TestSubject</h2><p>一种Subject,主要是测试用。通过使用TestScheduler来精准的控制一定时间的延迟来通知Subject的订阅者相关事件。</p>
]]></content>
<summary type="html">
<![CDATA[<p>这是个很奇怪的类,可以说是Observable和Observer的代理<br>Subject类继承于Observable,同时实现了Observer接口。因此既是一个Observable也是个Observe,所以能被Observer订阅,也可以订阅Observable,也可]]>
</summary>
<category term="android" scheme="http://yoursite.com/tags/android/"/>
</entry>
<entry>
<title><![CDATA[运行时保存配置]]></title>
<link href="http://yoursite.com/2015/11/13/restain%20fragment/"/>
<id>http://yoursite.com/2015/11/13/restain fragment/</id>
<published>2015-11-13T03:13:47.000Z</published>
<updated>2017-02-12T16:12:32.000Z</updated>
<content type="html"><![CDATA[<p>一些设备的配置会在运行时发生改变,比如说屏幕的方向,键盘是否可获取,或者语言等。当这些改变发生时,Android会重启正在运行的Activity(先后调用<code>onDestroy</code>,<code>onCreate</code>);<br>重启能够使Activity重新加载新的参数,但是有的时候有的数据是比较庞大的,比如Bitmap。如果重新加载体验会比较差。Android官方提出了两种解决方式。 </p>
<ul>
<li><h3 id="在Retained_Fragment中管理对象">在Retained Fragment中管理对象</h3> 调用<code>setRetaininstance()</code>方法,能控制一个fragment对象在所关联的Activity重启过程中是否保持。这样对象就被加载下来了。默认情况下fragment会随着宿主Activity重启,但是设为true之后,fragment的<code>onDestroy()</code>和<code>onCreate(Bundle)</code>会被跳过,但是<code>onDetach()</code>、<code>onAttach()</code>和<code>onActivityCreated()</code>仍然会被调用。</li>
<li><h3 id="自己处理参数的改变">自己处理参数的改变</h3> 在AndroidManifest文件中,在activit的<code>android:configChanges=""</code>中配置你所想要的参数。被设置的参数在改变之后,activity不会重启,这是个很简单的方法,但是配置不同有时候会需要不同的参数,必须自己手动去设置。<code>onConfigurationChanged (Configuration newConfig)</code>中监测新发生的改变,然后手动设置资源。</li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<p>一些设备的配置会在运行时发生改变,比如说屏幕的方向,键盘是否可获取,或者语言等。当这些改变发生时,Android会重启正在运行的Activity(先后调用<code>onDestroy</code>,<code>onCreate</code>);<br>重启能够使Activ]]>
</summary>
<category term="android" scheme="http://yoursite.com/tags/android/"/>
</entry>
<entry>
<title><![CDATA[Service 整理]]></title>
<link href="http://yoursite.com/2015/11/01/Service/"/>
<id>http://yoursite.com/2015/11/01/Service/</id>
<published>2015-11-01T09:33:04.000Z</published>
<updated>2017-02-12T16:12:32.000Z</updated>
<content type="html"><![CDATA[<p>—</p>
<h3 id="功能:">功能:</h3><ul>
<li>在后台执行长时间的操作 </li>
<li>提供某些功能给其他应用</li>
</ul>
<h3 id="使用Service:">使用Service:</h3><ul>
<li><h4 id="创建Service:">创建Service:</h4><ul>
<li><strong>AndroidMainfest中注册service</strong> </li>
<li><strong>start</strong> or <strong>bind</strong> or <strong>either</strong><ul>
<li>started的Service能一直在后台运行,即使start该Service的组件已经被销毁了。started的Service一般只进行一个操作,并且不返回结果给caller。startServices()中传入的Intent最后到达onStartCommand()中。 </li>
<li>bound的Service能同时绑定多个组件,并且当所有组件都解绑之后,该Service被销毁。该Service能与绑定的组件交互。 </li>
<li>如果Service同时实现两个回调方法:<code>onStartCommand()</code>(允许组件start该Service),<code>onBind()</code>(允许组件bind该Service)。</li>
</ul>
</li>
<li><strong>使用</strong><code>context.startService()</code><br>如果Service已经在运行了,就会直接调用onStartCommand(),否则会先调用onCreate()再调用onStartCommand()。如果Service没有提供binding,则组件想获得结果必须通过PendingIntent和BroadCast来与传递结果。</li>
<li><strong>使用</strong><code>context.bindService()</code><br>如果想让组件与Service交互或者让其他应用调用系统的某些功能,使用Bound Service。一定要实现onBind(),并且返回一个继承Binder的IBinder,其中定义了Service与client之间的交互。在client里创建一个ServiceConnection对象,来获取service对象。<br>如果只是想service与activity在可见的时候交互,则在onStart(),onStop()里绑定和解绑。如果需要activity即使在后台停止了也可以接收到来自service的答复,则在onCreate()和onDestroy()中绑定和解绑。一般不要在onResume()和onPause()中绑定和解绑。 </li>
</ul>
</li>
<li><h4 id="Stopping_a_Service">Stopping a Service</h4><ul>
<li>bound的Service在所有绑定的组件解绑之后就会自动消除,调用onUnBind(),如果返回true,则在下一个client绑定该Service时调用onRebind()方法。</li>
<li>started的Service的生命周期需要手动去管理,通过Service自己调用stopSelf()或者其他组件调用。在Service的所有任务完成之后就要销毁该Service,否则会一直在后台运行占用着资源,消耗电池。当停止Service时,如果还有任务正在执行,就会造成丢失,所以停止Service可以选择调用stopSelf(),并且传开始的请求的id,如果正在执行的请求的id与传入的id不符,Service将不会停止。</li>
</ul>
</li>
<li><h4 id="Service_生命周期">Service 生命周期</h4><img src="http://developer.android.com/images/service_lifecycle.png" alt=""></li>
</ul>
<h3 id="Service与IntentService">Service与IntentService</h3><ul>
<li><p>继承IntentService </p>
<ul>
<li>IntendService中维护着一个工作线程去执行所有onStartCommand()中的任务 </li>
<li>维护着一个工作队列一次发送一个intent到onHandleIntent(),所有的任务完成之后就会停止Service</li>
<li>onBind()方法中默认返回null,说明其他组件无法bind该Service</li>
<li><p>实现了onStartCommand(),发送intent到工作队列,然后到onHandleIntent()。</p>
<p><strong>总之我们只用实现onHandleIntent()</strong>,如果需要重写其他回调函数,记得要调用父类的实现,免得功能失效。</p>
</li>
</ul>
</li>
<li>继承Service<ul>
<li>如果需要有多线程,使用继承Service</li>
<li>onStartCommand()一定要返回一个Integer,其值为三个值之一: <ul>
<li>START_NOT_STICKY(被杀了不重启) </li>
<li>START_STICKY(被杀了重启但不再次发送最后个intent) </li>
<li>START_REDELIVER_INTENT(被杀了重启但且再次发送最后个intent)</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="AIDL">AIDL</h3><p>只有当你允许不同的应用访问你的Service来进行进程间通信并且在service中需要维护多线程时才使用AIDL。如果不需要在不同应用中执行并发的进程间通信,你就该通过实现Binder来创建自己的接口或者如果需要执行进程间通信,但是不需要在service处理中多线程,只需要使用Messenger来实现你的接口。 </p>
<h3 id="TIPs:">TIPs:</h3><ul>
<li>如果Service中要进行CPU密集型操作或者阻塞式操作,需要维护一个Service自己的线程来做这些操作。IntentService有自己的线程。</li>
<li>不要直接调用onStartCommand(),这是系统的事儿。</li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<p>—</p>
<h3 id="功能:">功能:</h3><ul>
<li>在后台执行长时间的操作 </li>
<li>提供某些功能给其他应用</li>
</ul>
<h3 id="使用Service:">使用Service:</h3><ul>
<li><h4 id="创建S]]>
</summary>
<category term="android" scheme="http://yoursite.com/tags/android/"/>
</entry>
<entry>
<title><![CDATA[volley 源码阅读]]></title>
<link href="http://yoursite.com/2015/10/10/volley%20%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/"/>
<id>http://yoursite.com/2015/10/10/volley 源码阅读/</id>
<published>2015-10-10T03:13:47.000Z</published>
<updated>2017-02-12T16:12:32.000Z</updated>
<content type="html"><![CDATA[<h4 id="流程:">流程:</h4><ul>
<li>调用Volley.newRequestQueue获得requestQueue对象<ul>
<li>如果不传入httpStack参数,则根据系统版本来创建httpStcak</li>
<li>根据创建的httpStack创建NetWork</li>
<li>queue.start(),其中默认创建了四个NetWorkDispatcher和一个CacheDispatcher</li>
</ul>
</li>
<li>add() 创建request对象加入到requestQueue中<ul>
<li>判断是否可以缓存</li>
<li>可以则加入到缓存队列,否则加入网络请求队列<ul>
<li>若加入缓存队列,先在缓存中找相应的缓存结果。如果没有命中,加入到网络请求队列中</li>
<li>若是命中,判断缓存是否过期,若过期,加入到网络请求队列</li>
<li>若没有过期,则直接使用缓存数据</li>
</ul>
</li>
<li>若加入到网络请求队列<ul>
<li>发送网络请求是通过BasicNetwork的performRequest()</li>
<li>其中通过httpStack来请求网络,并返回一个NetworkResponse对象</li>
<li>解析数据交给request中的parseNetworkResponse()方法,该方法需要重写,对于不同的返回对象有不同的解析方式</li>
<li>解析完成之后,调用ExecutorDelivery的postResponse(),回调解析的数据。通过在一个关联主线程的Runnable对象中,保证run()在主线程运行</li>
<li>在run()中调用了Request的deliverResponse()方法,在该方法中奖相应的数据回调给Response.Listener的onResponse()方法</li>
</ul>
</li>
</ul>
</li>
<li><strong>over</strong></li>
</ul>
<h4 id="优点和特点:">优点和特点:</h4><ul>
<li>扩展性高,可以自定义httpStack,request,缓存。框架的耦合度很低。</li>
<li>会自动重试</li>
<li>维护了一个byte[]回收池,减少了内存的分配回收</li>
<li>缓存: <ul>
<li>根据Cache-Control和Expires首部来计算缓存的过期时间。如果两个首部都存在情况下,以Cache-Control为准。</li>
<li>利用If-None-Match和If-Modified-Since对过期缓存或者不新鲜缓存,进行请求再验证,并处理 304 响应,更新缓存。</li>
<li>默认的缓存实现,将缓存以文件的形式存储在 Disk,程序退出后不会丢失。 </li>
</ul>
</li>
</ul>
<h4 id="缺点:">缺点:</h4><ul>
<li>只适合小数据的传输</li>
<li>重试容易造成多次请求,在一次请求超时之后应该取消该请求。</li>
</ul>
<h4 id="tips:">tips:</h4><ul>
<li>ResponseDelivery里维护了一个<strong>关联UI线程消息队列</strong>的Handler:<br><code>Handler mResponseHandler = new Handler(Looper.getMainLooper());</code></li>
<li><p>HUrlStack中存在一个URLRewriter接口,用于更改之前提供的url。如果参数为null,则表示之前的url已经没用了。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (mUrlRewriter != <span class="keyword">null</span>) { </span><br><span class="line"> String rewritten = mUrlRewriter.rewriteUrl(url); </span><br><span class="line"> <span class="keyword">if</span> (rewritten == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> IOException(<span class="string">"URL blocked by rewriter: "</span> + url);</span><br><span class="line"> }</span><br><span class="line"> url = rewritten;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p>ByteArrayPool 是一个byte[]回收池,减少了内存的分配和回收。其维护了两个ArrayList,一个根据byte[]的加入时间排序,一个根据byte[]的大小排序。当需要一定大小的内存时,根据大小排序的byte[]数组寻找可去除正好大小的空间,当回收时如果大小超过上限,则从时间list上去掉第一个byte[]。</p>
</li>
</ul>
<blockquote>
<p>参考:<br>volley源码<br><a href="http://blog.csdn.net/guolin_blog/article/details/17656437" target="_blank" rel="external">http://blog.csdn.net/guolin_blog/article/details/17656437</a><br><a href="http://www.codekk.com/blogs/detail/54cfab086c4761e5001b2542" target="_blank" rel="external">http://www.codekk.com/blogs/detail/54cfab086c4761e5001b2542</a></p>
</blockquote>
]]></content>
<summary type="html">
<![CDATA[<h4 id="流程:">流程:</h4><ul>
<li>调用Volley.newRequestQueue获得requestQueue对象<ul>
<li>如果不传入httpStack参数,则根据系统版本来创建httpStcak</li>
<li>根据创建的httpStack创]]>
</summary>
<category term="android" scheme="http://yoursite.com/tags/android/"/>
</entry>
<entry>
<title><![CDATA[面对GFW,更新SDK新方法]]></title>
<link href="http://yoursite.com/2015/09/24/sdk/"/>
<id>http://yoursite.com/2015/09/24/sdk/</id>
<published>2015-09-24T15:04:05.000Z</published>
<updated>2017-02-12T16:12:32.000Z</updated>
<content type="html"><![CDATA[<p>这是一种简单又方便的方法,速度还不错。<br>首先在<strong>ping.chinaz.com/</strong>上ping”<strong>g.cn/</strong>“获取谷歌中国的ip地址,选择最快的一条线路就好了。<br>在SDK配置中,server填ip地址,端口填80。<br>通过这方法,终于能愉快的更新了~<br>参考知乎。</p>
]]></content>
<summary type="html">
<![CDATA[<p>这是一种简单又方便的方法,速度还不错。<br>首先在<strong>ping.chinaz.com/</strong>上ping”<strong>g.cn/</strong>“获取谷歌中国的ip地址,选择最快的一条线路就好了。<br>在SDK配置中,server填ip地址,]]>
</summary>
<category term="android" scheme="http://yoursite.com/tags/android/"/>
<category term="android studio" scheme="http://yoursite.com/tags/android-studio/"/>
</entry>
<entry>
<title><![CDATA[使用SharedPreferences 需要注意的一点]]></title>
<link href="http://yoursite.com/2015/07/31/SharedPreferences%20tips/"/>
<id>http://yoursite.com/2015/07/31/SharedPreferences tips/</id>
<published>2015-07-31T03:13:47.000Z</published>
<updated>2017-02-12T16:12:32.000Z</updated>
<content type="html"><![CDATA[<p>SharedPreferences是十分常用的一个工具。前几天在写一个功能的时候,发现在一个Activity中保存的信息在另一个Activity中找不到。一直为空,后来看源码才发现获取SharedPreferences是有两种方式</p>
<blockquote>
<ul>
<li>getPreferences(int mode)</li>
<li>getSharedPreferences(String name, int mode)</li>
</ul>
</blockquote>
<p>我们先看getPreferences(int mode)源码</p>
<!-- lang:java-->
<pre><code>/<span class="keyword">*</span><span class="keyword">*</span>
<span class="keyword">*</span> Retrieve a {<span class="comment">@link SharedPreferences} object for accessing preferences</span>
<span class="keyword">*</span> that are private to this activity. This simply calls the underlying
<span class="keyword">*</span> {<span class="comment">@link #getSharedPreferences(String, int)} method by passing in this activity's</span>
<span class="keyword">*</span> class name as the preferences name.
<span class="keyword">*</span>
<span class="keyword">*</span> <span class="comment">@param mode Operating mode. Use {@link #MODE_PRIVATE} for the default</span>
<span class="keyword">*</span> operation, {<span class="comment">@link #MODE_WORLD_READABLE} and</span>
<span class="keyword">*</span> {<span class="comment">@link #MODE_WORLD_WRITEABLE} to control permissions.</span>
<span class="keyword">*</span>
<span class="keyword">*</span> <span class="comment">@return Returns the single SharedPreferences instance that can be used</span>
<span class="keyword">*</span> to retrieve and modify the preference values.
<span class="keyword">*</span>/
public SharedPreferences getPreferences(int mode) {
return getSharedPreferences(getLocalClassName(), mode);
}
</code></pre><p>注释中说明了,这个方法获取了一个是当前Activity的私有SharedPreferences对象,并且是获得该类的名字后调用getSharedPreferences(String name, int mode),将名字作为第一个参数—-名字传入,所以其实在其他activity中使用了当前的activity的类名(不包括包名)也是可以获取的.</p>
<p>再来讲讲getSharedPreferences(String name, int mode)的第二个参数mode</p>
<blockquote>
<ul>
<li>MODE_PRIVATE(只能自己的应用获取的到,如果已存在就替换)</li>
<li>MODE_APPEND(只能自己的应用获取的到,如果已存在就接在目前数据之后)</li>
<li>MODE_WORLD_READABLE(其他应用都能获取的到,可读,但是由于数据不安全,已被废弃)</li>
<li>MODE_WORLD_WRITEABLE(其他应用都能获取的到,可写,但是由于数据不安全,已被废弃)</li>
<li>MODE_MULTI_PROCESS(在多进程同时访问一个SharedPreferces使用,在Android2.3之前会自动调用,但是2.3之后就必须手动声明)</li>
</ul>
</blockquote>
]]></content>
<summary type="html">
<![CDATA[<p>SharedPreferences是十分常用的一个工具。前几天在写一个功能的时候,发现在一个Activity中保存的信息在另一个Activity中找不到。一直为空,后来看源码才发现获取SharedPreferences是有两种方式</p>
<blockquote>
<ul>]]>
</summary>
<category term="android" scheme="http://yoursite.com/tags/android/"/>
</entry>
</feed>