叶金诚的站点
汇贤雅叙
2022-12-03T13:15:36.839Z
http://www.yangchaofan.cn/
叶金诚
Hexo
Android内存优化案例-优化某App的121内存泄漏
http://www.yangchaofan.cn/2022/10/performance-android-121-Memory-leaks/
2022-10-01T13:34:41.000Z
2022-12-03T13:15:36.839Z
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><h1 id="内存泄漏案例5-某app121个内存泄漏"><a href="#内
Android内存泄漏4讲
http://www.yangchaofan.cn/2022/09/android-performance-four-tips/
2022-09-07T12:28:33.000Z
2022-12-03T13:15:32.834Z
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><h1 id="android内存泄漏案例4讲"><a href="#Androi
Android项目Gradle常见用途
http://www.yangchaofan.cn/2022/02/Android-gradle-usage/
2022-02-27T15:21:24.000Z
2022-07-27T15:48:29.511Z
<p>本文介绍了Gradle在Android项目开发中的应用,包括配置SrouceSets、定义全局常量、组件化、定义Apk名称、App包名、依赖项配置等。</p>
Jetpack-生命周期组件库Lifecycle
http://www.yangchaofan.cn/2022/01/jetpack-lifecycle-mmvm-usage/
2022-01-27T15:32:43.000Z
2022-07-27T15:57:59.023Z
<h1 id="Jetpack生命周期组件"><a href="#Jetpack生命周期组件" class="headerlink" title="Jetpack生命周期组件"></a>Jetpack生命周期组件</h1><p>本文主要针对lifecycle引入的背景以及lifecycle比传统监听生命周期优势做了详细对比,也介绍了具体用法。</p>
<h1 id="问题引入"><a href="#问题引入" class="headerlink" title="问题引入"></a>问题引入</h1><p>当组件需要具备生命周期感知能力时,我们传统写法如下</p>
<ul>
<li>定义接口类、定义对象,在Activity、Fragment的生命周期手动回调接口</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyLocationListener</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">MyLocationListener</span><span class="params">(Context context, Callback callback)</span> </span>{</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">start</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">// connect to system location service</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">stop</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">// disconnect from system location service</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyActivity</span> <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> </span>{</span><br><span class="line"> <span class="keyword">private</span> MyLocationListener myLocationListener;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(...)</span> </span>{</span><br><span class="line"> myLocationListener = <span class="keyword">new</span> MyLocationListener(<span class="keyword">this</span>, location -> {</span><br><span class="line"> <span class="comment">// update UI</span></span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onStart</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">super</span>.onStart();</span><br><span class="line"> Util.checkUserStatus(result -> {</span><br><span class="line"> <span class="comment">// what if this callback is invoked AFTER activity is stopped?</span></span><br><span class="line"> <span class="keyword">if</span> (result) {</span><br><span class="line"> myLocationListener.start();</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"> <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">onStop</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">super</span>.onStop();</span><br><span class="line"> myLocationListener.stop();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>传统写法具备如下缺点</p>
<ul>
<li><p>太多组件需要维护生命周期状态,会写太多的生命周期接口回调</p>
</li>
<li><p>组件的回调不一定会按照Activity生命周期的调用顺序执行;Activitiy的onStart中有异步任务,可能出现组件#onStop先执行,组件#onstart后执行的情况,造成内存泄漏</p>
</li>
</ul>
<p>如这么写,会有很明显的问题:</p>
<ol>
<li>每个子组件我们都需要要将myLocationListener#start、#stop调用一遍,如果有十几个组件,代码写出来很酸爽;</li>
<li>myLocationListener#start是异步的,可能会晚于myLocationListener#stop执行</li>
</ol>
<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="class"><span class="keyword">class</span> <span class="title">MyActivity</span> <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> </span>{</span><br><span class="line"> <span class="keyword">private</span> MyLocationListener myLocationListener;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(...)</span> </span>{</span><br><span class="line"> myLocationListener = <span class="keyword">new</span> MyLocationListener(<span class="keyword">this</span>, location -> {</span><br><span class="line"> <span class="comment">// update UI</span></span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onStart</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">super</span>.onStart();</span><br><span class="line"> Util.checkUserStatus(result -> {</span><br><span class="line"> <span class="comment">// what if this callback is invoked AFTER activity is stopped?</span></span><br><span class="line"> <span class="keyword">if</span> (result) {</span><br><span class="line"> myLocationListener.start();</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"> <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">onStop</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">super</span>.onStop();</span><br><span class="line"> myLocationListener.stop();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><a href="https://developer.android.google.cn/reference/androidx/lifecycle/package-summary?hl=zh-cn"><code>androidx.lifecycle</code></a> 软件包提供的类和接口可帮助您以弹性和隔离的方式解决这些问题。</p>
<p>本文主要围绕生命周期感知型组件展开,讲解以这些组件的用法。</p>
Jetpack-生命周期组件库Paging
http://www.yangchaofan.cn/2022/01/jetpack-paging-mmvm-usage/
2022-01-27T13:40:55.000Z
2022-07-27T15:59:13.662Z
<h1 id="Paging"><a href="#Paging" class="headerlink" title="Paging"></a>Paging</h1><h2 id="用途"><a href="#用途" class="headerlink" title="用途"></a>用途</h2><p>对数据按需加载,实现无限滚动,支持从本地存储和网络加载。</p>
<h2 id="优势"><a href="#优势" class="headerlink" title="优势"></a>优势</h2><ul>
<li>支持与Jetpack包下的库组合使用</li>
<li>支持第三方库 RxJava</li>
</ul>
<h2 id="基本用法"><a href="#基本用法" class="headerlink" title="基本用法"></a>基本用法</h2><h3 id="增加依赖"><a href="#增加依赖" class="headerlink" title="增加依赖"></a>增加依赖</h3><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// paging</span></span><br><span class="line">implementation <span class="string">"androidx.paging:paging-runtime:2.1.0-alpha01"</span></span><br><span class="line"></span><br><span class="line">implementation <span class="string">'com.squareup.retrofit2:retrofit:2.0.0'</span></span><br><span class="line">implementation <span class="string">'com.squareup.retrofit2:converter-gson:2.0.0'</span></span><br><span class="line">implementation <span class="string">'com.squareup.retrofit2:adapter-rxjava2:2.2.0'</span></span><br></pre></td></tr></table></figure>
<h3 id="常见API"><a href="#常见API" class="headerlink" title="常见API"></a>常见API</h3><blockquote>
<p>参考<a href="https://segmentfault.com/a/1190000038953354">https://segmentfault.com/a/1190000038953354</a></p>
</blockquote>
<p>使用Paging库进行分页加载时,需要用到几个核心的类,分别是PagedListAdapter、PageList和DataSource。</p>
<table>
<thead>
<tr>
<th>API</th>
<th>用途</th>
</tr>
</thead>
<tbody><tr>
<td>PagedListAdapter</td>
<td>RecyclerView的适配器,扩展了原生的差分更新功能</td>
</tr>
<tr>
<td>PagedList</td>
<td>数据结构集合,存储<code>DataSource</code>中分页不变的内容列表,按条件获取数据</td>
</tr>
<tr>
<td>DataSource</td>
<td>加载数据至<code>PagedList</code></td>
</tr>
<tr>
<td>DataSource.Factory</td>
<td>创建<code>DataSrouce</code></td>
</tr>
<tr>
<td>LivePagedListBuilder</td>
<td>设置分页加载的配置项,设置<code>DataSource.Factory</code>,设置<code>DataSource</code>,返回<code>LiveData</code></td>
</tr>
<tr>
<td><a href="https://developer.android.google.cn/reference/kotlin/androidx/paging/PagingSource.LoadParams?hl=zh-cn"><code>LoadParams</code></a></td>
<td>加载的关键信息,包括加载的键,和加载的页数</td>
</tr>
<tr>
<td><a href="https://developer.android.google.cn/reference/kotlin/androidx/paging/PagingSource.LoadResult?hl=zh-cn"><code>LoadResult</code></a></td>
<td>加载成功返回<code>LoadResult.Page</code>,加载失败返回<code>LoadResult.Error</code></td>
</tr>
</tbody></table>
<h5 id="PagedListAdapter"><a href="#PagedListAdapter" class="headerlink" title="PagedListAdapter"></a>PagedListAdapter</h5><p>众所周知,在<code>Android</code>列表开发中需要使用<code>RecyclerView</code>,并且需要配合自定义<code>Adapter</code>。<code>PagedListAdapter</code>继承于RecyclerView.Adapter,这表明它也是一个<code>RecyclerView.Adapter</code>,并且扩展了<code>RecyclerView.Adapter</code>的支持异步差分更新功能,该功能依赖于匿名内部类<code> DiffUtil.ItemCallback<T></code>,使用时必须复写<code>areItemsTheSame</code>、<code>areContentsTheSame</code>方法。</p>
<h5 id="PageList"><a href="#PageList" class="headerlink" title="PageList"></a>PageList</h5><p><code>PageList</code>是用于通知<code>DataSource</code>何时获取数据,以及如何获取数据。比如,何时获取第一页数据,以及何时开始加载数据等待。并且,<code>DataSource</code>数据源都将通过<code>PageList</code>设置给<code>PagedListAdapter</code>。</p>
<p><strong>LivePagedListBuilder</strong></p>
<p>设置分页加载的配置项,通过传入<code>DataSource.Factory</code>,在<code>Factory</code>的<code>create</code>方法里,返回<code>DataSource</code>,返回<code>Adapter</code>可以使用的<code>LiveData</code>,并通知页面,将新数据刷新到布局上;通过传入<code>PagedList.Config</code>设置分页参数如<code>pageSize</code>、<code>prefetchDistance</code>等</p>
<h5 id="DataSource"><a href="#DataSource" class="headerlink" title="DataSource"></a>DataSource</h5><p>DataSource主要用于执行数据的加载操作,数据载入操作<strong>必须在子线程中进行</strong>,否则会造成主线程的阻塞。DataSource的来源可以是网路,也可以是本地的数据库,如Room。根据分页机制的不同,DataSource可以有3种来源,分别是PageKeyedDataSource、PositionalDataSource和ItemKeyedDataSource。</p>
<ul>
<li><strong>PageKeyedDataSource</strong>:根据传入的页面num获取某一页的数据,比如获取第2页的数据。</li>
<li><strong>PositionalDataSource</strong>:分页时默认显示的第几页。</li>
<li><strong>ItemKeyedDataSource</strong>:封装了上一页、下一页的能力</li>
</ul>
Jetpack-生命周期组件库ViewModel
http://www.yangchaofan.cn/2022/01/jetpack-viewmodel-mmvm-usage/
2022-01-27T06:36:25.000Z
2022-07-27T15:51:13.109Z
<h1 id="Viewmodel"><a href="#Viewmodel" class="headerlink" title="Viewmodel"></a>Viewmodel</h1><h2 id="用途"><a href="#用途" class="headerlink" title="用途"></a>用途</h2><p>用于关注界面相关的数据,关注生命周期期间所需的数据</p>
<p>例如:屏幕旋转、语言、时区发生更改后继续显示的数据。</p>
<blockquote>
<p>关键:对数据的管理、存储数据,是一种管理数据的方式</p>
</blockquote>
<h2 id="优势"><a href="#优势" class="headerlink" title="优势"></a>优势</h2><ul>
<li>销毁或重建后,页面瞬时态的数据会丢失,如rotate后,ViewModel不会被销毁,Activity的成员变了会销毁</li>
<li>比onSaveInstanceState能存的数据量更大,如网络请求查询回来的用户列表接口、bitmap、网络缓存的视频流适合放在Viewmodel里,不适合放在OnSaveInstanceState</li>
<li>避免页面销毁后,异步任务返回造成的内存泄漏;</li>
<li>减少代码维护量,避免手动编写代码管理这些资源的释放</li>
<li>Activity、Fragment不应对关注数据的处理,而只应该关注将数据显示到界面上</li>
<li>从Activity和Fragment中分离出视图所需数据的管理责任,让代码逻辑解耦,让开发者管理业务和界面的关系更高效</li>
<li>在Activity、Fragment之间共享数据,无需持有引用,无需相互调用,无需担心被其他Fragment生命周期影响
Jetpack-生命周期组件库Databinding
http://www.yangchaofan.cn/2022/01/jetpack-daabinding-mmvm-usage/
2022-01-27T05:37:42.000Z
2022-07-27T15:59:41.982Z
<h1 id="Databinding"><a href="#Databinding" class="headerlink" title="Databinding"></a>Databinding</h1><p>本文主要讲了Databinding以下用法:Activity用法、Fragment用法、Recycleview用法、布局引用、更新字段、事件处理等。</p>
<p>参考<a href="https://cloud.tencent.com/developer/article/1909533"><a href="https://juejin.cn/post/6844903609079971854#heading-3">Android DataBinding 从入门到进阶 - 掘金 (juejin.cn)</a></a></p>
<h2 id="用途"><a href="#用途" class="headerlink" title="用途"></a>用途</h2><p>声明式的将视图与数据绑定,而程序式的调用视图组件属性去设置</p>
<p>如程序式的做法</p>
<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">TextView textView = findViewById(R.id.sample_text);</span><br><span class="line">textView.setText(viewModel.getUserName());</span><br></pre></td></tr></table></figure>
<p>声明式的做法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><TextView</span><br><span class="line"> android:text=<span class="string">"@{viewmodel.userName}"</span> /> </span><br><span class="line"></span><br><span class="line">数据绑定类.setViewModel(viewmodel)</span><br></pre></td></tr></table></figure>
<h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><p><strong>问题1:</strong>使用数据绑定类还是视图绑定类?</p>
<p>解决:</p>
<ul>
<li>如果只是取代findViewById,可以使用视图绑定类</li>
</ul>
<p><strong>问题2:</strong></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">If you don<span class="string">'t use an inflation method taking a DataBindingComponent, use DataBindingUtil.setDefaultComponent or make all BindingAdapter methods static.</span></span><br></pre></td></tr></table></figure>
<p>解决:</p>
<p><code>@BindingAdapter</code>注解声明的方法必须是静态的</p>
<p><strong>问题3:</strong>databinding能在子线程直接设置对象的值吗?</p>
<p>写demo验证:起子线程调用databinding设置变量值</p>
<p>button点击事件代码如下,经过验证,每点一下,界面是可以实时刷新的。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">baseObservableInThread</span><span class="params">(View view)</span> </span>{</span><br><span class="line"> <span class="keyword">new</span> Thread(<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"> Log.e(<span class="string">"DatabindingActivity"</span>, <span class="string">"accept: "</span>);</span><br><span class="line"> <span class="comment">// 更新指定字段</span></span><br><span class="line"> user.setUserName(<span class="string">"jordan"</span> + <span class="number">1</span> + <span class="string">"号"</span>);</span><br><span class="line"> user.setPassWord(<span class="string">"jordan-love-"</span> + <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> }).start();</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>官方文档专门描述了:异步设置数据的前提是,数据源不能是集合。——《<a href="https://developer.android.google.cn/topic/libraries/data-binding/generated-binding?hl=zh-cn#viewstubs">生成的绑定类-后台线程</a>》</p>
<p><strong>问题4:</strong>能替代handler通知主线程更新ui的作用吗?</p>
<p>答:确实不需要handler通知主线程刷新UI了。</p>
<p><strong>问题5:</strong>那还需要handler吗?</p>
<p>答:handler仍然可用于多线程通信</p>
<p><strong>问题6:</strong>databinding可观察对象发生更改时,界面是在当前帧显示变化还是下一帧显示变化?</p>
<p>答:当可变或可观察对象发生更改时,绑定会按照计划在下一帧之前发生更改。但有时必须立即执行绑定。要强制执行,请使用 <a href="https://developer.android.google.cn/reference/androidx/databinding/ViewDataBinding?hl=zh-cn#executePendingBindings()"><code>executePendingBindings()</code></a> 方法。</p>
<p><strong>问题7:</strong>双向绑定可能会引入无限循环</p>
<p>使用双向数据绑定时,请注意不要引入无限循环。当用户更改特性时,系统会调用使用 <code>@InverseBindingAdapter</code> 注释的方法,并且该值将分配给后备属性。继而调用使用 <code>@BindingAdapter</code> 注释的方法,从而触发对使用 <code>@InverseBindingAdapter</code> 注释的方法的另一个调用,依此类推。</p>
<p>解决:通过比较使用 <code>@BindingAdapter</code> 注释的方法中的新值和旧值,可以打破可能出现的无限循环。</p>
Jetpack-生命周期组件库LiveData
http://www.yangchaofan.cn/2022/01/jetpack-livedata-mmvm-usage/
2022-01-27T05:34:18.000Z
2022-07-27T15:50:54.993Z
<h1 id="LiveData"><a href="#LiveData" class="headerlink" title="LiveData"></a>LiveData</h1><h2 id="用途"><a href="#用途" class="headerlink" title="用途"></a>用途</h2><p>可观察的数据存储类</p>
<blockquote>
<p>关键: 是一种数据类型</p>
</blockquote>
<h2 id="优势"><a href="#优势" class="headerlink" title="优势"></a>优势</h2><ul>
<li><p>可感知其他组件的生命周期,如Activity、Fragment、Service</p>
</li>
<li><p>只通知处于活跃状态的观察者;非活跃的观察者不会收到通知</p>
</li>
<li><p>确保界面实时响应数据的变化—数据变化,会进入Observer#onStateChange回调</p>
</li>
<li><p>不会发生内存泄漏–Observer会绑定到Lifecycle,其关联的组件销毁后,Observer会自我释放</p>
</li>
<li><p>不会因为Activity停止导致crash–Observer处于非活跃状态,它不会收到任何LiveData事件</p>
</li>
<li><p>数据始终保持最新状态–从非活跃状态变为活跃状态,Observer会收到最新的数据,后台Activity返回前台后会立刻接收最新的数据</p>
</li>
<li><p>兼容系统配置更改–由于设备更改重新创建了Activity、Fragment,Observer会立即收到livedata的最新数据,这一切都不需要额外开发代码</p>
</li>
<li><p>共享资源–使用单例模式扩展LiveData,以便于在整个App内共享数据。具体用法参考本节<strong>基本用法#共享值</strong></p>
viewpager与viewpager2实践总结
http://www.yangchaofan.cn/2022/01/viewpager-viewpager2-usage/
2022-01-22T07:15:44.000Z
2022-07-27T16:01:00.793Z
<h1 id="Code概述"><a href="#Code概述" class="headerlink" title="Code概述"></a>Code概述</h1><p>工程包括2部分</p>
<pre class="mermaid">graph LR
h1(ViewPager知识体系)-->A(常见类和API)
h1-->B(基本用法)
B-->PagerAdapter
B-->FragmentPagerAdapter
B-->FragmetnStatePagerAdpater
B-->RecyclerView.Adapter+FragmentStateAdapter
B-->未完成Fragment+FragmentManager+FragmentTranscation
h1-->C(高阶用法)
C-->P(PageTransformer)
P-->一页多个
P-->层叠
P-->居中缩放
C-->child(未完成-子项控制-5种adapter的用法区别)
child-->remove
child-->update
child-->replace
child-->insert</pre>
<h1 id="常见API"><a href="#常见API" class="headerlink" title="常见API"></a>常见API</h1><table>
<thead>
<tr>
<th>名称</th>
<th>用途</th>
</tr>
</thead>
<tbody><tr>
<td>ViewPager</td>
<td>视图组件</td>
</tr>
<tr>
<td>ViewPager2</td>
<td>视图组件</td>
</tr>
<tr>
<td>PagerAdapter</td>
<td>viewpager1适配器</td>
</tr>
<tr>
<td>FragmentPagerAdapter</td>
<td>viewpager1适配器</td>
</tr>
<tr>
<td>FragmentStatePagerAdapter</td>
<td>viewpager1适配器</td>
</tr>
<tr>
<td>RecyclerView.Adapter</td>
<td>viewpager2适配器</td>
</tr>
<tr>
<td>FragmentStateAdapter</td>
<td>viewpager2适配器</td>
</tr>
<tr>
<td>FragmentTransaction</td>
<td>Fragment事物控制</td>
</tr>
<tr>
<td>FragmentManager</td>
<td>Fragment管理器</td>
</tr>
<tr>
<td>Fragment</td>
<td>视图组件</td>
</tr>
</tbody></table>
<h1 id="标准用法"><a href="#标准用法" class="headerlink" title="标准用法"></a>标准用法</h1><h2 id="ViewPager"><a href="#ViewPager" class="headerlink" title="ViewPager"></a>ViewPager</h2><p>总结</p>
<blockquote>
<p>滑动有哪些方法:</p>
<ol>
<li>setCurrentItem、arrowScroll</li>
</ol>
<p>能监听哪些事件:</p>
<ol>
<li>onPageScrolled、onPageSelected、onPageScrollStateChanged</li>
<li>SCROLL_STATE_IDLE、SCROLL_STATE_DRAGGING、SCROLL_STATE_SETTLING</li>
</ol>
<p>不同事件之间的区别:</p>
<ol>
<li>setAdapter是否进入回调</li>
<li>动作没完成是否进入回调</li>
</ol>
<p>假滑了解吗:</p>
<ol>
<li>Viewpager通过fakeBy来与其他组件联动</li>
</ol>
</blockquote>
<ol>
<li><a href="https://www.apiref.com/android-zh/android/support/v4/view/ViewPager.html">ViewPager - Android中文版 - API参考文档 (apiref.com)</a></li>
<li><a href="https://developer.android.google.cn/reference/androidx/viewpager/widget/ViewPager?hl=zh_cn">ViewPager | Android Developers (google.cn)</a></li>
</ol>
n条用例验证Activity4种启动模式的威力与坑
http://www.yangchaofan.cn/2022/01/launchmode-activity-useage/
2022-01-19T14:16:35.000Z
2022-12-03T13:00:58.437Z
<h1 id="启动模式的使用场景"><a href="#启动模式的使用场景" class="headerlink" title="启动模式的使用场景"></a>启动模式的使用场景</h1><h2 id="标准模式"><a href="#标准模式" class="headerlink" title="标准模式"></a>标准模式</h2><p>特性:</p>
<ul>
<li>任何页面,如无必要,默认为标准模式,start启动一次创建一个新的Activity</li>
</ul>
<p>适合场景:</p>
<ul>
<li>任何页面</li>
</ul>
<h2 id="singleTop"><a href="#singleTop" class="headerlink" title="singleTop"></a>singleTop</h2><p>特性:</p>
<ul>
<li>如果该实例已有,且位于栈顶(是Stack顶部还是TaskRecord顶部?),start启动一次,不会重新创建</li>
<li>如果该实例已有,不位于栈顶(是Stack顶部还是TaskRecord顶部?),start启动一次,会重新创建</li>
</ul>
<p>适合场景:</p>
<ul>
<li>消息推送: 在单聊页面点击消息通知重新进入一个新的单聊页面</li>
<li>商品推荐:在A商品详情页面点击运营推荐进入一个新的B商品详情页面</li>
</ul>
<h2 id="singleTask"><a href="#singleTask" class="headerlink" title="singleTask"></a>singleTask</h2><p>特性:</p>
<ul>
<li>如果该实例已经存在(存在于同一个TaskRecord的?还是存在于Stack中?)但该实例之上还有别的Activity,start启动一次,不会重新创建,会清除该实例之上的所有Activity(清除Stack中其上面的所有?还是TaskRecord内其上面的所有?)</li>
</ul>
<p>适合场景:</p>
<ul>
<li>应用中的主页</li>
</ul>
<h2 id="singleInstance"><a href="#singleInstance" class="headerlink" title="singleInstance"></a>singleInstance</h2><p>特性:</p>
<ul>
<li>创建一个新的栈结构,似的Activity存在于新的TaskRecord中</li>
<li>如果ActivityA已经存在,通过start方式启动ActivityA,则不会重新创建ActivityA</li>
</ul>
<p>适合场景:</p>
<ul>
<li>适用于于App主业务分开的页面</li>
<li>大型App的设置页面</li>
<li>闹铃App,闹铃提醒页面与闹铃设置页面分离</li>
</ul>
Activity各种条件下生命周期小结
http://www.yangchaofan.cn/2022/01/lifecycle-activity/
2022-01-16T14:16:35.000Z
2022-07-27T16:03:23.638Z
<h1 id="Activity生命周期"><a href="#Activity生命周期" class="headerlink" title="Activity生命周期"></a>Activity生命周期</h1><h1 id="应用层管理Activity生命周期的方式"><a href="#应用层管理Activity生命周期的方式" class="headerlink" title="应用层管理Activity生命周期的方式"></a>应用层管理Activity生命周期的方式</h1><p><strong>方法1:</strong></p>
<ol>
<li>在<code>Application</code>中存储一个全局数据集list,子元素是<code>Activity</code></li>
<li><code>BaseActivity#onCreat</code>e调用<code>Application</code>的#add方法添加<code>Activity</code></li>
<li>在退出<code>App</code>时,遍历数据集的所有<code>Activity</code>,调用<code>finsh</code>方法</li>
</ol>
<p><strong>方法2:</strong></p>
<ol>
<li>在<code>Application</code>中调用<code>registerActivityLifecycleCallbacks(activityLifecycleCallbacks);</code></li>
<li>编写<code>ActivityLifecycleCallbacks</code>子类实现,复写接口中生命周期监听方法</li>
<li>在每个监听方法里,对一个全局数据集list进行增删改查,list中的子元素是Activity</li>
</ol>
Android/Java事件过滤方案小结
http://www.yangchaofan.cn/2022/01/throttle-debounce-vie-click/
2022-01-13T10:13:12.000Z
2022-07-27T16:03:51.272Z
<p>什么是防抖动?举一个典型场景:</p>
<p>笔者处理一个bug,【留言板】留言列表点击编辑,多次点击删除键,提示“留言发送失败” ,问题表现为点击删除按钮多次,发出了同一个请求多次,服务端无法响应返回了失败的响应码。</p>
<figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {</span><br><span class="line"> MessageBean bean = mbsAdapter.getData().get(position);</span><br><span class="line"> if (view.getId() == R.id.iv_delete) {</span><br><span class="line"> mbRequest.deleteSingle(bean, position);</span><br><span class="line"> StatisticsUtilFamilyMeals.mbDelete(this, MBHomePageActivity.class.getSimpleName());</span><br><span class="line"> } else if (view.getId() == R.id.iv_resend) {</span><br><span class="line"> mbRequest.updateCacheMessage(bean, position);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>测试人员频繁点击一个Delete按钮,多次发出了同一份Request,该Request中携带了相同的请求体,服务端只能处理一份,先到达的Request将被服务端处理,后到达的Request将被服务端标记为处理失败,客户端会依次收到处理成功、处理失败、处理失败……</p>
<p>以上就是一个典型的点击事件频繁创建的问题,为了防止事件的频繁创建、响应、发出Request、让服务端处理,我们一般会做防抖动——即将点击事件过滤调。</p>
<p>防抖动方案按事件的产生顺序和处理顺序的方向来划分,可分为“先到达先处理型”和“后到达后处理型”</p>
<p>本文包括以下几种</p>
<ol>
<li>传统计算时间间隔</li>
<li>RxJava</li>
<li>RxBinding</li>
<li>同一任务执行最后一次</li>
</ol>
<blockquote>
<p>前三个是先到达先处理型,第四个是厚道的后处理型</p>
</blockquote>
RjxavaSample练习
http://www.yangchaofan.cn/2022/01/Android-rxjava-Practice/
2022-01-06T14:33:57.000Z
2022-07-27T16:04:18.141Z
<h1 id="Rxjava-sample-项目介绍"><a href="#Rxjava-sample-项目介绍" class="headerlink" title="Rxjava sample 项目介绍"></a>Rxjava sample 项目介绍</h1><h2 id="引入依赖"><a href="#引入依赖" class="headerlink" title="引入依赖"></a>引入依赖</h2><h3 id="基础依赖"><a href="#基础依赖" class="headerlink" title="基础依赖"></a>基础依赖</h3><p>依赖后可以使用rxjava操作符</p>
<figure class="highlight groovy"><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">implementation <span class="string">'io.reactivex.rxjava2:rxjava:2.2.2'</span></span><br><span class="line">implementation <span class="string">'io.reactivex.rxjava2:rxandroid:2.1.0'</span></span><br></pre></td></tr></table></figure>
<h3 id="扩展依赖"><a href="#扩展依赖" class="headerlink" title="扩展依赖"></a>扩展依赖</h3><p>网络库,可以返回Observable对象,方便后续操作符操作</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">implementation <span class="string">'com.amitshekhar.android:rx2-android-networking:1.0.2'</span></span><br></pre></td></tr></table></figure>
<p>生命周期库,使用后可以将rxjava与Adnroid组件生命周期关联</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">implementation <span class="string">'com.trello.rxlifecycle4:rxlifecycle:4.0.2'</span></span><br></pre></td></tr></table></figure>
<p>UI布局组件,使用后按钮可以自适应布局,自动换行</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">implementation <span class="string">'com.google.android:flexbox:2.0.1'</span></span><br></pre></td></tr></table></figure>
<h2 id="项目结构"><a href="#项目结构" class="headerlink" title="项目结构"></a>项目结构</h2><p>此项目按目录分为如下多个package,每一个package表示rxjava一类用法,接下来我们一级一级目录来研究这些用法</p>
<figure class="highlight powershell"><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></pre></td><td class="code"><pre><span class="line">├─java</span><br><span class="line">│ └─com</span><br><span class="line">│ └─shaunsheep</span><br><span class="line">│ └─rxjavasample</span><br><span class="line">│ ├─observable // 被观察者</span><br><span class="line">│ ├─operators</span><br><span class="line">│ │ ├─arithmetic // 数学运算符</span><br><span class="line">│ │ ├─combine // 结合</span><br><span class="line">│ │ ├─condition // 条件运算符</span><br><span class="line">│ │ ├─connect // 链接</span><br><span class="line">│ │ ├─create // 创建</span><br><span class="line">│ │ ├─<span class="keyword">filter</span> // 过滤</span><br><span class="line">│ │ ├─model // 模型</span><br><span class="line">│ │ └─transform // 变换</span><br><span class="line">│ ├─scene // 典型场景</span><br><span class="line">│ │ ├─bus // 事件总线</span><br><span class="line">│ │ └─cache // 三级缓存</span><br><span class="line">│ │ ├─controller</span><br><span class="line">│ │ └─model</span><br><span class="line">│ └─schedulers // 调度器</span><br></pre></td></tr></table></figure>
编程人生价值观
http://www.yangchaofan.cn/2021/07/programming-life-values/
2021-07-10T08:10:15.000Z
2021-07-10T09:23:13.710Z
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>7月第一周读完了两本程序访谈类书籍《编程人生》《编程大师访谈录》。这两本书3
Android跨进程通信方案选型
http://www.yangchaofan.cn/2021/07/android-ipc-best-practice/
2021-07-02T12:28:33.000Z
2021-07-02T14:07:58.779Z
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><h1 id="跨进程通信方案"><a href="#跨进程通信方案" class
Android依赖库处理框架——Fataar
http://www.yangchaofan.cn/2021/05/android-fat-aar-useage/
2021-05-27T00:13:12.000Z
2021-06-22T14:36:41.467Z
<h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p><a href="https://github.com/adwiv/android-fat-aar">官方文档</a>中是这样介绍fat-aar的:</p>
<blockquote>
<p>Gradle script that allows you to merge and embed dependencies in generted aar file</p>
</blockquote>
<p>fat-aar是一个允许你合并和嵌入依赖关系到生成的aar文件的gradle脚本。</p>
<p>从fat-aar的用途来讲,它的主要工作就是将project中内部所依赖的aar下载到本地,然后一起发布aar到maven库,外部项目依赖aar时不用去下载project内部依赖的aar</p>
Android Crash实践策略最全面思考
http://www.yangchaofan.cn/2021/05/The-most-comprehensive-thinking-of-crash-practice-strategy/
2021-05-01T07:15:44.000Z
2021-06-11T16:12:48.528Z
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><h1 id="android-crash实践策略最全面思考"><a href="
Android Crash揭秘与实践
http://www.yangchaofan.cn/2021/05/android-crash-who-kill-you/
2021-05-01T03:15:44.000Z
2021-05-17T03:41:40.265Z
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><h1 id="前言"><a href="#前言" class="headerli
职业稳定性杂谈
http://www.yangchaofan.cn/2021/04/Career-stability-experience/
2021-04-03T03:26:22.000Z
2021-04-17T14:22:56.433Z
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>之前在知乎写过一篇回答,<a href="https://www.zhihu.com/question/308110736/answer/628764663">《为什么都在说 90 后的辞职率高?》</a>,纯理性分析获得了300赞。今日在公众号上看到心理学家刘向明教授写的一篇名为<a href="https://mp.weixin.qq.com/s/ZyEWV1g_UzXODkViyFdNlw">《社会招聘,怎样测试稳定性?</a>的文章,让我对职业稳定性有了更深的理解。</p>
<p>先看看我之前的回答,再来看我的新感悟吧。</p>
mtk-log
http://www.yangchaofan.cn/2021/02/mtk-log/
2021-02-27T01:13:12.000Z
2022-12-03T13:00:43.268Z
<h1 id="MTK-log"><a href="#MTK-log" class="headerlink" title="MTK log"></a><a href="https://www.pianshen.com/article/4121163154/">MTK log</a></h1><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>mtklog是由log生成工具MTKLogger生成的一系列问题追踪文件,其主要作用就是对系统或者应用产生的异常进行快速定位,从而解决问题。</p>
<p>log文件名称为:<br>crash_log :崩溃日志,主要输出 程序崩溃造成的crash log<br>events_log:事件日志,主要输出记录各个activity周期及事件<br>kernel_log:底层驱动,按键,低内存相关log<br>sys_log:系统日志,Exception定位点<br>radio_log:输出通话,网络状态变化<br>main_log:详尽输出每一步的log</p>