<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
  <channel>
    <title>Samuelnotes</title>
    <link>http://www.samuelnotes.cn</link>
    <description>Samuelnotes</description>
    
      <item>
        <title>Android 7.0  viewgroup cast exception</title>
        <link>http://www.samuelnotes.cn/2017/04/07/android70-viewgroup-cast-expection.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2017/04/07/android70-viewgroup-cast-expection.html</guid>
        <pubDate>Fri, 07 Apr 2017 00:00:00 +0000</pubDate>
        <description>&lt;h1 id=&quot;android-70--viewgroup-强制转换相关异常&quot;&gt;Android 7.0  viewgroup 强制转换相关异常&lt;/h1&gt;

&lt;p&gt;android 7.0 ListView 添加FootView 动态设置 LayoutParams  导致强制转换异常，这里列一下异常和解决方法。
（这里的错误仅在7.0及以上出现，其他版本暂无发现该问题）&lt;/p&gt;

&lt;p&gt;该部分代码是出现在ListView  动态添加FootView的时候。&lt;/p&gt;

&lt;p&gt;用代码添加了一个View ， LayoutParams 没有使用ListView父类AbsListView而是使用ViewGroup.LayoutParams 引起的。&lt;/p&gt;

&lt;p&gt;具体的异常信息如下：
&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
java.lang.ClassCastException: android.view.ViewGroup$LayoutParams cannot be cast to android.widget.AbsListView$LayoutParams
	at android.widget.ListView.removeUnusedFixedViews(ListView.java:1908)
	at android.widget.ListView.layoutChildren(ListView.java:1769)
	at android.widget.AbsListView.onLayout(AbsListView.java:2162)
	at android.view.View.layout(View.java:17637)
	at android.view.ViewGroup.layout(ViewGroup.java:5575)
	at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1079)
	at android.view.View.layout(View.java:17637)
	at android.view.ViewGroup.layout(ViewGroup.java:5575)
	at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1079)
	at android.view.View.layout(View.java:17637)
	at android.view.ViewGroup.layout(ViewGroup.java:5575)
	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
	at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
	at android.view.View.layout(View.java:17637)
	at android.view.ViewGroup.layout(ViewGroup.java:5575)
	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1741)
	at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1585)
	at android.widget.LinearLayout.onLayout(LinearLayout.java:1494)
	at android.view.View.layout(View.java:17637)
	at android.view.ViewGroup.layout(ViewGroup.java:5575)
	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
	at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
	at android.view.View.layout(View.java:17637)
	at android.view.ViewGroup.layout(ViewGroup.java:5575)
	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1741)
	at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1585)
	at android.widget.LinearLayout.onLayout(LinearLayout.java:1494)
	at android.view.View.layout(View.java:17637)
	at android.view.ViewGroup.layout(ViewGroup.java:5575)
	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
	at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
	at com.android.internal.policy.DecorView.onLayout(DecorView.java:726)
	at android.view.View.layout(View.java:17637)
	at android.view.ViewGroup.layout(ViewGroup.java:5575)
	at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2346)
	at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2068)
	at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1254)
	at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6337)
	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:874)
	at android.view.Choreographer.doCallbacks(Choreographer.java:686)
	at android.view.Choreographer.doFrame(Choreographer.java:621)
	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:860)
	at android.os.Handler.handleCallback(Handler.java:751)
	at android.os.Handler.dispatchMessage(Handler.java:95)
	at android.os.Looper.loop(Looper.java:154)
	at android.app.ActivityThread.main(ActivityThread.java:6119)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)


&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;很简洁的异常信息，除了系统sdk 中的代码错误，并没有发现自己代码中的异常部分。&lt;/p&gt;

&lt;p&gt;出现的机型：&lt;/p&gt;

&lt;p&gt;而且是Android 7.0 的新机器才会出现的问题（华为P9， 小米 5c 等 安装Android7.0 系统及其相关7.0 定制rom）&lt;/p&gt;

&lt;p&gt;解决办法 ：&lt;/p&gt;

&lt;p&gt;很无奈的办法&lt;/p&gt;

&lt;p&gt;&lt;b&gt;注意将ViewGroup.LayoutParams 改成 ListView 需要的 AbsListView.LayoutParams  &lt;/b&gt;&lt;/p&gt;

&lt;p&gt;看图：
&lt;img src=&quot;https://raw.githubusercontent.com/samuelhehe/samuelhehe.github.io/master/res/android7.0_listview_viewgroup_cast_abslistview_exception.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;问题已解决， 具体问什么Android7.0 才出现这样的问题。 详细的原因还没有深入研究，随后续上。&lt;/p&gt;

&lt;p&gt;续：&lt;/p&gt;

&lt;p&gt;看图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/samuelhehe/samuelhehe.github.io/master/res/android7.0_listview_viewgroup_cast_abslistview_layoutparams.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;个人估计是 7.0 AbsListView.LayoutParams 虽然继承了ViewGroup.LayoutParams ， 但是AbsListView.LayoutParams 添加了ViewType 类型， 并且在上图中的1911行进行强制的转换操作。
并且有相关的注释：
我们尊重在需要添加的 FixedViewInfo中View的LayoutParams 参数。 否则我们将为你添加，没有进行类型强制转换检查的操作。&lt;/p&gt;

&lt;p&gt;并且在1913行的generateDefaultLayoutParams()中添加了生成了AbsListView.LayoutParams&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
@Override
    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
        return new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT, 0);
    }

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 1015行重新添加了 对viewType的赋值&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;p.viewType = mAdapter.getItemViewType(position);

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;github上相关 issue ：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/idunnololz/AnimatedExpandableListView/issues/3&quot;&gt;https://github.com/idunnololz/AnimatedExpandableListView/issues/3&lt;/a&gt;&lt;/p&gt;

</description>
      </item>
    
      <item>
        <title>2016阅读书单</title>
        <link>http://www.samuelnotes.cn/2016/03/30/book-list-of-2016.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2016/03/30/book-list-of-2016.html</guid>
        <pubDate>Wed, 30 Mar 2016 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;effective-java-中文版-第二版&quot;&gt;Effective Java 中文版 第二版&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;status: 已读&lt;/li&gt;
  &lt;li&gt;publisher: 机械工业出版社&lt;/li&gt;
  &lt;li&gt;language: 中文&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;第一行代码-电子版&quot;&gt;第一行代码 电子版&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;status: 已读&lt;/li&gt;
  &lt;li&gt;author: 郭霖&lt;/li&gt;
  &lt;li&gt;publisher: 人民邮电出版社&lt;/li&gt;
  &lt;li&gt;language: 中文&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;android开发艺术探索&quot;&gt;Android开发艺术探索&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;status: 已读&lt;/li&gt;
  &lt;li&gt;author: 任玉刚&lt;/li&gt;
  &lt;li&gt;publisher: 电子工业出版社&lt;/li&gt;
  &lt;li&gt;language: 中文&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;python-核心编程&quot;&gt;Python 核心编程&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;status: 部分&lt;/li&gt;
  &lt;li&gt;author: wesley chun&lt;/li&gt;
  &lt;li&gt;publisher: 人民邮电出版社&lt;/li&gt;
  &lt;li&gt;language: 中文&lt;/li&gt;
&lt;/ul&gt;

</description>
      </item>
    
      <item>
        <title>Android 低版本 multidex 65536 异步加载</title>
        <link>http://www.samuelnotes.cn/2016/01/22/android-multidex-asyncload-solution.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2016/01/22/android-multidex-asyncload-solution.html</guid>
        <pubDate>Fri, 22 Jan 2016 00:00:00 +0000</pubDate>
        <description>&lt;h1 id=&quot;android-低版本-multidex-65536加载问题-异步加载解决方案&quot;&gt;Android 低版本 multidex 65536加载问题 异步加载解决方案。&lt;/h1&gt;

&lt;p&gt;使用子进程异步MultiDex Install 方法 介绍：&lt;/p&gt;

&lt;p&gt;官网镇楼：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.android.com/studio/build/multidex.html&quot;&gt;https://developer.android.com/studio/build/multidex.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;现在将主要代码块 粘贴于此，以便回头查看。&lt;/p&gt;

&lt;p&gt;AndroidManifest 中子进程配置如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/samuelhehe/samuelhehe.github.io/master/res/multidex_loadresactivity.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到 process 处 设置 “:mini”  这里的”:mini” 代表的是启动另外的进程，该进程以“applicaitonId:mini”命名，以”:”为开头 这种写法是简写方式，其属性属于当前应用的私有进程，代表了其他应用的组件不可以和它跑在进程中。  参考文档： Android开发艺术探索 第二章 IPC机制的2.2.1中指出，开启多进程看似简单，实则暗藏杀机。经过实际测试的确如此，首先Application 的onCreate 会被调用N次 ，N的次数 包含了各种进程的名称数和主进程数。 
我们的操作就放在了attachBaseContext中 ，这是Android提供的方案，或者延伸为在attachBaseContext中同步加载dex的方案，它的好处是非常简单，所需的依赖集也非常少。 也就是说我们在启动加载过程中几乎不会出现NoClassFoundError , 话虽说如此，但实际测试中，碰到因此崩溃还是有的，测试工具是Testin ，但是测试报告，其中出现的错误机型，系统分布 确实是存在的。 这里我就不放截图了（因为我确实找不到了，登上testin 测试报告均已被清空，不知去向）&lt;/p&gt;

&lt;p&gt;下边看一下在Application 子类 重写的attachBaseContext 方法的主要逻辑。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/samuelhehe/samuelhehe.github.io/master/res/multidex_attach.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;因为application多次启动 这里就是为了抓取以mini结尾的进程。
&lt;img src=&quot;https://raw.githubusercontent.com/samuelhehe/samuelhehe.github.io/master/res/multidex_quickstart.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;为了防止App 启动时出现ANR问题 采取了启动子进程 异步加载 class2.dex 方法， LoadResActivity 是启动的另外一个进程，看代码：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/samuelhehe/samuelhehe.github.io/master/res/multidex_loadres_activity.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/samuelhehe/samuelhehe.github.io/master/res/multidex_install_finish.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在上边的代码中 ， 第29行启动 异步load MultiDex install  ，第 36行便是 install 过程 ， 38行 是安装成功后调用Application中的installFinish 通过SP文件通信 告知主进程 子进程已经加载完毕。 后边是计时 log ， 退出当前进程 不再赘述。
&lt;img src=&quot;https://raw.githubusercontent.com/samuelhehe/samuelhehe.github.io/master/res/multidex_need_wait.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这里可以看到 installFinish 将classes2.dex 的sha1-digest 码保存至SP文件中，通过waitForDexopt 方法中 的needWait 方法来终止等待死循环。
&lt;img src=&quot;https://raw.githubusercontent.com/samuelhehe/samuelhehe.github.io/master/res/multidex_need_wait.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这里可以看到 SP 文件的读写属性 是MODE_MULTI_PROCESS的 ，也就是跨进程。  虽然说这种跨进程通信方式很low ，但有时候虽然他不是最好的方案，确实最有效的解决办法。但在某种意义上讲还是合适的，因为这种方法对即时性要求不高，交换一些简单的数据，对实时性要求不好，毕竟我们在里边sleep了200毫秒。&lt;/p&gt;

&lt;p&gt;下边是在gradle中配置：
 &lt;img src=&quot;https://raw.githubusercontent.com/samuelhehe/samuelhehe.github.io/master/res/multidex_gradle.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第57行  添加 android官方 multidex lib 依赖 ，以便导入multidex 相关class ， 第39， 40行 便是对class 的分割， method数量达到 48000 会进行下一个class.dex 分包。 当然如果打包时候发现方法数 48000&amp;lt; methodcount &amp;lt;= 65536 这样的情况 是不会进行分包的。&lt;/p&gt;

&lt;p&gt;注意： 第42行 注释行， 添加的条件是 需要指定main dex ， 该dex 分包的class 需要根据App的前期加载进行选择性打包。以防止前期使用而找不到Class的情况 ，接下来就是如何分包，如何将main class 加入到Maindex中去，这些东西已经有人帮我们做好了。看官网multiDexKeepProguard 部分 ， 也可以手动配置，这里就不在多说了。&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结：&lt;/h2&gt;

&lt;p&gt;就目前我们上边所述的方法 并不是完美的，采取的也是Android提供的方案，虽然采用多进程异步加载方案，但是同时也会有问题， 那就是 如果dex 比较大的情况下，我们的App会出现长时间加载 ， 在2.3 某些机型上测试 虽然不崩溃，但启动时间长达10s 。 通过testin 统计数据查看 基本上时间比较长的 或者NoclassFoundError的机型 一般都是2.3 ，3.0 的系统。再者可能就是机身内存比较小。 很遗憾，现在都已经6.0了，我觉得只要不崩溃，时间长点就长点吧。 
	最后一点需要提的是 在LoadResActivity 中的配置中可以看到 style ，内容一般就是透明的背景。 这就是为了防止出现长时间加载黑屏的办法。
以上代码已经整理好放在github 上 &lt;a href=&quot;https://github.com/samuelhehe/MultiDexSample&quot;&gt;https://github.com/samuelhehe/MultiDexSample&lt;/a&gt;欢迎批评指正，拍砖。&lt;/p&gt;

&lt;h2 id=&quot;参考链接&quot;&gt;参考链接：&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.csdn.net/t12x3456/article/details/40837287&quot;&gt;Android 使用android-support-multidex解决Dex超出方法数的限制问题,让你的应用不再爆棚&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://segmentfault.com/a/1190000004053072&quot;&gt;dex分包变形记&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.tuicool.com/articles/rEBVNfY&quot;&gt;Android dex分包方案&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://tech.meituan.com/mt-android-auto-split-dex.html&quot;&gt;美团Android DEX自动拆包及动态加载简介&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.csdn.net/zhaokaiqiang1992/article/details/50412975&quot;&gt;关于『65535问题』的一点研究与思考&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/creativepsyco/secondary-dex-gradle/&quot;&gt;secondary-dex-gradle&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/23614095/using-gradle-to-split-external-libraries-in-separated-dex-files-to-solve-android&quot;&gt;Using Gradle to split external libraries in separated dex files to solve Android Dalvik 64k methods limit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      </item>
    
      <item>
        <title>Android 6.0 sdk  apache 相关包移除异常解决</title>
        <link>http://www.samuelnotes.cn/2015/12/22/android60-apacheentity-expection.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/12/22/android60-apacheentity-expection.html</guid>
        <pubDate>Tue, 22 Dec 2015 00:00:00 +0000</pubDate>
        <description>&lt;h1 id=&quot;android-60-apache-相关包移除异常解决&quot;&gt;Android 6.0 apache 相关包移除异常解决&lt;/h1&gt;

&lt;p&gt;android 6.0 sdk  apache httpclient 相关包移除后 导致相关依赖包的库不能使用，这里列一下异常和解决方法。&lt;/p&gt;

&lt;p&gt;官网发布的changes 我当时没有怎么关注，后来在使用友盟统计的时候碰到了这个问题，才猛地想起来是这个原因
看一下log截图：&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
 &lt;img src=&quot;https://raw.githubusercontent.com/samuelhehe/samuelhehe.github.io/master/res/android6_apache_entity_error.png&quot; alt=&quot;image&quot; /&gt;
将 下边代码配置gradle 文件 dependencies 中&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
 &lt;img src=&quot;https://raw.githubusercontent.com/samuelhehe/samuelhehe.github.io/master/res/android6_apache_entity_config.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;问题已解决， 还是希望以后多注意下依赖包的依赖项，并且考虑系统版本。
&lt;br /&gt;
附 官网的相关链接，&lt;a href=&quot;https://developer.Android.com/preview/behavior-changes.html&quot;&gt;https://developer.Android.com/preview/behavior-changes.html&lt;/a&gt;&lt;/p&gt;

</description>
      </item>
    
      <item>
        <title>Android应用与系统安全防御(转)</title>
        <link>http://www.samuelnotes.cn/2015/11/20/android-app-safesolution.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/11/20/android-app-safesolution.html</guid>
        <pubDate>Fri, 20 Nov 2015 00:00:00 +0000</pubDate>
        <description>&lt;h1 id=&quot;android应用与系统安全防御&quot;&gt;Android应用与系统安全防御&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;工作这段做应用，让我来负责App的安全 ， 他们说就你做的时间比较久，了解的也比较多，安全部分交给你做吧。 这让一直做应用部分，从来没有考虑过安全的猿，无从下手。经过疯狂的Google ， 百度， 发现了不少好的资源与文章。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在这里分享一篇，在原文的基础之上添加了一些自己的想法与处理方式&lt;/p&gt;

&lt;p&gt;原文链接： &lt;a href=&quot;http://www.cnblogs.com/goodhacker/p/3864680.html&quot;&gt;http://www.cnblogs.com/goodhacker/p/3864680.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;##Android应用安全防御&lt;/p&gt;

&lt;p&gt;Android应用的安全隐患包括三个方面：代码安全、数据安全和组件安全。&lt;/p&gt;

&lt;h3 id=&quot;1-代码安全&quot;&gt;1. 代码安全&lt;/h3&gt;

&lt;p&gt;代码安全主要是指Android apk有被篡改、盗版等风险，产生代码安全的主要原因是apk很容易被反编译、重打包。我们可以采用以下方法对apk进行保护：&lt;/p&gt;

&lt;h4 id=&quot;11-代码混淆&quot;&gt;1.1 代码混淆&lt;/h4&gt;

&lt;p&gt;代码混淆可以在一定程度上增加apk逆向分析的难度。Android SDK从2.3开始就加入了ProGuard代码混淆功能，开发者只需进行简单的配置就可以实现对代码的混淆。&lt;/p&gt;

&lt;p&gt;如果大家想学习混淆配置请转官方文档：http://developer.android.com/tools/help/proguard.html#configuring&lt;/p&gt;

&lt;p&gt;或参考我的文章：&lt;a href=&quot;http://www.samuelnotes.cn/2015/10/02/android-code-safe.html&quot;&gt;Android 代码安全（代码混淆）&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;12-apk签名校验&quot;&gt;1.2 Apk签名校验&lt;/h4&gt;

&lt;p&gt;每一个软件在发布时都需要开发人员对其进行签名，而签名使用的密钥文件时开发人员所独有的，破解者通常不可能拥有相同的密钥文件，因此可以使用签名校验的方法保护apk。Android SDK中PackageManager类的getPackageInfo()方法就可以进行软件签名检测。&lt;/p&gt;

&lt;h4 id=&quot;13-dex文件校验&quot;&gt;1.3 Dex文件校验&lt;/h4&gt;

&lt;p&gt;重编译apk其实就是重编译了classes.dex文件，重编译后，生成的classes.dex文件的hash值就改变了，因此我们可以通过检测安装后classes.dex文件的hash值来判断apk是否被重打包过。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;读取应用安装目录下/data/app/xxx.apk中的classes.dex文件并计算其哈希值，将该值与软件发布时的classes.dex哈希值做比较来判断客户端是否被篡改。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;读取应用安装目录下/data/app/xxx.apk中的META-INF目录下的MANIFEST.MF文件，该文件详细记录了apk包中所有文件的哈希值，因此可以读取该文件获取到classes.dex文件对应的哈希值，将该值与软件发布时的classes.dex哈希值做比较就可以判断客户端是否被篡改。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;为了防止被破解，软件发布时的classes.dex哈希值应该存放在服务器端。&lt;/p&gt;

&lt;p&gt;另外由于逆向c/c++代码要比逆向Java代码困难很多，所以关键代码部位应该使用Native C/C++来编写。&lt;/p&gt;

&lt;h4 id=&quot;14-逆向工具对抗&quot;&gt;1.4 逆向工具对抗&lt;/h4&gt;

&lt;p&gt;对apk进行重打包常用的工具是apktool，apktool对于后缀为PNG的文件，会按照PNG格式进行处理，如果我们将一个非PNG格式文件的文件后缀改为PNG，再使用apktool重打包则会报错。&lt;/p&gt;

&lt;p&gt;以上是使用比较多的几种保护方法，单独使用其中一种效果不大，应该综合运用。&lt;/p&gt;

&lt;h4 id=&quot;15-调试器检测&quot;&gt;1.5 调试器检测&lt;/h4&gt;

&lt;p&gt;为了防止apk被动态调试，可以检测是否有调试器连接。在Application类中提供了isDebuggerConnected()方法用于检测是否有调试器连接，如果发现有调试器连接，可以直接退出程序。&lt;/p&gt;

&lt;p&gt;####　1.6 加壳保护&lt;/p&gt;

&lt;p&gt;使用加壳程序防止apk逆向是一种非常有效的方式，也是一个趋势。Jack_Jia在《Android APK加壳技术方案》一文中详细阐述了Android apk加壳原理以及几种加壳方案的具体实现。我们可以利用这几种方案对apk进行加壳。&lt;/p&gt;

&lt;p&gt;不过这种加壳方式是在Java层实现的，被反编译的风险仍然很大。为了克服这个缺点，今后可以研究采用如下思路来进行保护：&lt;/p&gt;

&lt;p&gt;将核心业务逻辑代码放入加密的.jar或者.apk文件中，在需要调用时使用Native C/C++代码进行解密，同时完成对解密后文件的完整性校验。如果需要更加安全的保护方法，可以考虑对so文件（Native C/C++代码编译得到的文件）进行加壳。Android so加壳主要需要解决两个问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;对ELF文件加壳；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;对Android SO的加载、调用机制做特殊处理。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这将是以后Android应用安全研究的一个方向。&lt;/p&gt;

&lt;h3 id=&quot;2-数据安全&quot;&gt;2. 数据安全&lt;/h3&gt;

&lt;h4 id=&quot;21-存储安全问题&quot;&gt;2.1 存储安全问题&lt;/h4&gt;

&lt;p&gt;关于数据存储可能出现的问题包括如下几点：&lt;/p&gt;

&lt;p&gt;（1）明文存储敏感数据，导致直接被攻击者复制或篡改。&lt;/p&gt;

&lt;p&gt;将隐私数据明文保存在外部存储
将系统数据明文保存在外部存储
将软件运行时依赖的数据保存在外部存储
将软件安装包或者二进制代码保存在外部存储
全局可读写的内部文件存储
（2）不恰当存储登陆凭证，导致攻击者利用此数据窃取网络账户隐私数据。&lt;/p&gt;

&lt;h4 id=&quot;解决方案&quot;&gt;解决方案：&lt;/h4&gt;

&lt;p&gt;对这些数据进行加密，密码保存在内部存储，由系统托管或者由用户使用时输入。
对应用配置文件，较安全的方法是保存到内部存储；如果必须存储到SD卡，则应该在每次使用前检验它是否被篡改，与预先保存在内部的文件哈希值进行比较。
应用如果需要安装或加载位于SD卡的任何文件，应该先对其完整性做验证，判断其与实现保存在内部存储中的（或从服务器下载来的）哈希值是否一致。
如果要跨应用进行数据共享，有种较好的方法是实现一个Content Provider 组件，提供数据的读写接口并为读写操作分别设置一个自定义的权限。
对于登录凭证的存储，使用基于凭据而不是密码的协议满足这种资源持久访问的需求，例如OAuth。&lt;/p&gt;

&lt;h4 id=&quot;22-传输安全问题&quot;&gt;2.2 传输安全问题&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;不使用加密传输&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;使用加密传输但忽略证书验证环节&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如开发者在代码中不检查服务器证书的有效性，或选择接受所有的证书时，这种做法可能会导致中间人攻击。&lt;/p&gt;

&lt;p&gt;我们在对敏感数据进行传输时应该采用基于SSL/TLS的HTTPS进行传输。由于移动软件大多只和固定的服务器通信，我们可以采用“证书锁定”（certificate pinning）方式在代码更精确地直接验证服务器是否拥有某张特定的证书。&lt;/p&gt;

&lt;h3 id=&quot;3-组件安全&quot;&gt;3. 组件安全&lt;/h3&gt;

&lt;p&gt;android应用内部的Activity、Service、Broadcast Receiver等组件是通过Intent通信的，组件间需要通信就需要在Androidmanifest.xml文件中配置，不恰当的组件配置则会带来风险。&lt;/p&gt;

&lt;p&gt;可能产生的风险：&lt;/p&gt;

&lt;p&gt;（1）恶意调用&lt;/p&gt;

&lt;p&gt;（2）恶意接受数据&lt;/p&gt;

&lt;p&gt;（3）仿冒应用，例如（恶意钓鱼，启动登录界面）&lt;/p&gt;

&lt;p&gt;（4）恶意发送广播、启动应用服务。&lt;/p&gt;

&lt;p&gt;（5）调用组件，接受组件返回的数据&lt;/p&gt;

&lt;p&gt;（6）拦截有序广播&lt;/p&gt;

&lt;h3 id=&quot;解决办法&quot;&gt;解决办法：&lt;/h3&gt;

&lt;p&gt;（1）最小化组件暴露&lt;/p&gt;

&lt;p&gt;不参与跨应用调用的组件添加android:exported=”false”属性，这个属性说明它是私有的，只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务。&lt;/p&gt;

&lt;p&gt;（2）设置组件访问权限&lt;/p&gt;

&lt;p&gt;对参与跨应用调用的组件或者公开的广播、服务设置权限。只有具有该权限的组件才能调用这个组件。&lt;/p&gt;

&lt;p&gt;（3）暴露组件的代码检查&lt;/p&gt;

&lt;p&gt;Android 提供各种API来在运行时检查、执行、授予和撤销权限。这些 API 是 android.content.Context 类的一部分，这个类提供有关应用程序环境的全局信息。&lt;/p&gt;

&lt;p&gt;另外，Android应用也会存在很多传统web漏洞，比如SQL注入，xss漏洞等，代码级防止出现这些漏洞的方法与web应用防御方法相同。&lt;/p&gt;

&lt;h2 id=&quot;android系统安全防御&quot;&gt;Android系统安全防御&lt;/h2&gt;

&lt;h3 id=&quot;1-操作系统安全问题&quot;&gt;1. 操作系统安全问题&lt;/h3&gt;

&lt;p&gt;Android root问题
系统漏洞，补丁更新不及时
认证机制问题&lt;/p&gt;
&lt;h3 id=&quot;2-系统安全解决方案&quot;&gt;2. 系统安全解决方案&lt;/h3&gt;

&lt;h4 id=&quot;21-权限管理与隔离&quot;&gt;2.1 权限管理与隔离&lt;/h4&gt;

&lt;p&gt;对运行在Android系统上的应用程序进行权限的细粒度管理和隔离，防止越权行为的发生和滥用权限获取敏感数据。&lt;/p&gt;

&lt;p&gt;可以采用MAC（Mandatory Access Control）强制访问控制模型实现。它是一个针对Linux的安全加强系统SELinux中使用的安全模型，即任何进程想在SELinux系统中干任何事情，都必须先在安全策略配置文件中赋予权限。凡是没有出现在安全策略配置文件中的权限，进程就没有该权限。Google在Android 4.4上正式推出了一套以SELinux为基础的系统安全机制SEAndroid。所以如果我们要定制一个Android系统，可以采用具有SEAndroid安全机制的Android 4.4版本。&lt;/p&gt;

&lt;h4 id=&quot;22-内核与应用层漏洞防护&quot;&gt;2.2 内核与应用层漏洞防护&lt;/h4&gt;

&lt;p&gt;增加补丁更新功能，如果发现漏洞，及时提醒用户进行系统补丁更新。&lt;/p&gt;

&lt;h4 id=&quot;23-恶意程序检测与防护&quot;&gt;2.3 恶意程序检测与防护&lt;/h4&gt;

&lt;p&gt;建立一套恶意代码防护模型，对运行在Android系统上的恶意程序进行检测，抵御恶意代码的入侵。&lt;/p&gt;

&lt;h4 id=&quot;24-数据安全存储与传输&quot;&gt;2.4 数据安全存储与传输：&lt;/h4&gt;

&lt;p&gt;对Android系统上的数据存储和数据传输进行加密保护，保证终端上数据能够安全地使用。&lt;/p&gt;

&lt;p&gt;更多资源： &lt;a href=&quot;http://blog.csdn.net/column/details/android-safe.html&quot;&gt;http://blog.csdn.net/column/details/android-safe.html&lt;/a&gt;&lt;/p&gt;
</description>
      </item>
    
      <item>
        <title>Android 5.0 权限冲突导致安装失败的问题</title>
        <link>http://www.samuelnotes.cn/2015/11/10/android-permission-issue.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/11/10/android-permission-issue.html</guid>
        <pubDate>Tue, 10 Nov 2015 00:00:00 +0000</pubDate>
        <description>&lt;blockquote&gt;
  &lt;p&gt;最近把运行的项目放在云测上测试, 发现Android 5.0 系统上频频出现安装失败的问题, 最先考虑的问题是JNI cpu兼容问题,更换so文件之后,便排除了该问题. 具体错误是Android5.0 应用权限导致的安装问题.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;问题描述&quot;&gt;问题描述&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  Failure [INSTALL_FAILED_DUPLICATE_PERMISSION perm=android.permission.BAIDU_LOCATION_SERVICE pkg=com.bmcc.ms.ui] 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于项目中引用的有百度地图SDK jar 包, 所以直接定位百度地图的引用,以及各种配置. 结果并没有发现什么异常,  而且大部分手机测试结果是没有问题的.于是在网络上找到了一篇帖子, 是在百度贴吧中找到的, 晒出来看一下:&lt;a href=&quot;http://tieba.baidu.com/p/3237226241&quot;&gt;http://tieba.baidu.com/p/3237226241&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;仔细阅读后发现,这是Android 5.0加强权限控制导致的, 让我继续拿百度Google 得到原因.&lt;/p&gt;

&lt;h2 id=&quot;解决办法&quot;&gt;解决办法&lt;/h2&gt;

&lt;p&gt;经过查找了Google,发现了一些提示, 并且在国内的网站上也找到了类似的提示:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://stackoverflow.com/questions/26491251/android-5-0-lollipop-app-install-shows-unknown-error-code-during-application-ins&quot;&gt;http://stackoverflow.com/questions/26491251/android-5-0-lollipop-app-install-shows-unknown-error-code-during-application-ins&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://stackoverflow.com/questions/27043933/install-failed-duplicate-permission-c2d-message&quot;&gt;http://stackoverflow.com/questions/27043933/install-failed-duplicate-permission-c2d-message&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://www.weste.net/2015/07-27/104873.html&quot;&gt;http://www.weste.net/2015/07-27/104873.html&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://lmbj.net/blog/solve-android-5.0-installed-app-failed-problem/&quot;&gt;http://lmbj.net/blog/solve-android-5.0-installed-app-failed-problem/&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;&lt;strong&gt;正要安装的App的自定义权限与手机上已有App的自定义权限名字相同，但两个App具有不同的签名信息导致安装失败。&lt;/strong&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;很明显, 这样就意味着,我们安装了同一应用权限的签名的应用的话, 只能N选一了, 例如说安装了引用了百度地图sdk的应用,也就只能安装一个, 怪不得安装不成功, 问题找到了.&lt;/p&gt;

&lt;h3 id=&quot;继续查找&quot;&gt;继续查找:&lt;/h3&gt;

&lt;p&gt;这么大的问题,百度肯定有解决的办法,继续查找百度地图SDK文档, 找到了答案:
    +个链接:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://developer.baidu.com/map/index.php?title=android-locsdk/guide/v5-0&quot;&gt;http://developer.baidu.com/map/index.php?title=android-locsdk/guide/v5-0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;【重要提醒】&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;定位SDKv3.1版本之后，以下权限已不需要，请取消声明，否则将由于Android 5.0多帐户系统加强权限管理而导致应用安装失败。&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &amp;lt;uses-permission android:name=&quot;android.permission.BAIDU_LOCATION_SERVICE&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;这也没什么好总结的, 总之我觉得还有有点要记住的:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;自己不要瞎定义权限, 搞不好,又被Google的另一个东西给限制了. 不过总的来说, 对我Android 开发的也未尝不是一件好事.&lt;/li&gt;
  &lt;li&gt;接下来就是不要瞎引用第三方,特别是小公司的jar包, 搞不好,自己的App就栽了, 好在百度也不算是个小公司,靠得住.开发者甚幸, 民族甚幸&lt;/li&gt;
  &lt;li&gt;没了, 自己瞎写的,仅供个人记录之用, 参考资料,大概就是上边的几个链接,谢谢百度地图.&lt;/li&gt;
&lt;/ul&gt;

</description>
      </item>
    
      <item>
        <title>Android 代码安全（代码混淆）</title>
        <link>http://www.samuelnotes.cn/2015/10/02/android-code-safe.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/10/02/android-code-safe.html</guid>
        <pubDate>Fri, 02 Oct 2015 00:00:00 +0000</pubDate>
        <description>&lt;h1 id=&quot;android-代码安全代码混淆&quot;&gt;Android 代码安全（代码混淆）&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;之前学习这一部分的时候，也废了很大的劲，工作中遇到了，就网上乱搜一通，配置好了以后就忘了各个参数的意思，为了方便进一步提升，觉得有必要将此部分记录下来。废话少说转正文&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;加混淆就是为了防止别人反编译自己辛辛苦苦写的代码，保护自己的劳动成果。上官网链接：&lt;a href=&quot;http://developer.android.com/tools/help/proguard.html&quot;&gt;http://developer.android.com/tools/help/proguard.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;proguard配置方法与解释链接：&lt;a href=&quot;https://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/usage.html&quot;&gt;https://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/usage.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;###简单的描述一下我自己对该配置的认知&lt;/p&gt;

&lt;p&gt;删除无效代码（Shrinking 收缩），和代码进行优化 （Optimization Options）。默认proguard 会混淆所有代码，但是下面几种情况是不能改变java 元素的名称，否则就会这样就会导致程序出错。&lt;/p&gt;

&lt;p&gt;动态加载类或者字段的时候，例如用到反射的地方。（如果添加混淆则会导致引用错误，无法找到相关对象，从而报异常）
 我们代码依赖于系统的接口。例如重写系统的方法。
如果我们使用了JNI ，如果我们添加了混淆则导致引用不到lib而报错。
系统默认的并不能够满足我们所有的需求，所以我们实际开发中我们需要定制适合自己App的混淆配置文件。&lt;/p&gt;

&lt;h3 id=&quot;proguard-的配置&quot;&gt;proguard 的配置&lt;/h3&gt;

&lt;h4 id=&quot;最常用的配置选项&quot;&gt;最常用的配置选项&lt;/h4&gt;

&lt;p&gt;-dontwarn  缺省proguard 会检查每一个引用是否正确，但是第三方库里面往往有些不会用到的类，没有正确引用。如果不配置的话，系统就会报错。
-keep 指定的类和类成员被保留作为 入口 。
-keepclassmembers 指定的类成员被保留。
-keepclasseswithmembers 指定的类和类成员被保留，假如指定的类成员存在的话。&lt;/p&gt;

&lt;p&gt;为了防止混淆出问题，你需要熟悉你所有的code ，系统的架构 ，以及系统和你code的集成的接口，并细心分析。 同时你必须需要一轮全面的测试。 所以混淆也还是有一定风险的。 为了避免风险，你可以只是混淆部分关键的代码，但是这样你的混淆的效果也会有所降低。&lt;/p&gt;

&lt;h4 id=&quot;常见的不能混淆的代码&quot;&gt;常见的不能混淆的代码&lt;/h4&gt;

&lt;p&gt;被Android Resource 文件引用到的。名字已经固定的也不能混淆，比如自定义的View ，好像混淆不通过，不要在Resource中添加Onclick事件，有一次因为这个耽误了我两个小时，原因竟然是因为把Onclick事件添加到了resource中。&lt;/p&gt;

&lt;p&gt;Android Parcelable ，需要使用android 序列化的 ，系统默认配置文件就包含这个。其他Anroid 官方建议 不混淆的，如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;android.app.backup.BackupAgentHelper&lt;/li&gt;
  &lt;li&gt;android.preference.Preference com.android.vending.licensing.ILicensingService&lt;/li&gt;
  &lt;li&gt;Java序列化方法，系统序列化需要固定的方法。&lt;/li&gt;
  &lt;li&gt;枚举 ，系统需要处理枚举的固定方法。&lt;/li&gt;
  &lt;li&gt;本地方法，不能修改本地方法名&lt;/li&gt;
  &lt;li&gt;annotations 注释&lt;/li&gt;
  &lt;li&gt;数据库驱动&lt;/li&gt;
  &lt;li&gt;有些resource 文件&lt;/li&gt;
  &lt;li&gt;Android系统组件&lt;/li&gt;
  &lt;li&gt;自定义View&lt;/li&gt;
  &lt;li&gt;Android Parcelable&lt;/li&gt;
  &lt;li&gt;Android R 文件&lt;/li&gt;
  &lt;li&gt;Android Parcelable&lt;/li&gt;
  &lt;li&gt;各个开发人员必须检查自己的code 是否用到反射 ，和其他不能混淆的地方。
告诉我来修改配置文件（已经保留的就不需要了）&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;常见的目前系统不检查的第三方库为&quot;&gt;常见的目前系统不检查的第三方库为&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-dontwarn android.support.**
-dontwarn com.tencent.**
-dontwarn org.dom4j.**
-dontwarn org.slf4j.**
-dontwarn org.http.mutipart.**
-dontwarn org.apache.**
-dontwarn org.apache.log4j.**
-dontwarn org.apache.commons.logging.**
-dontwarn org.apache.commons.codec.binary.**
-dontwarn weibo4android.**
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;proguard-参数&quot;&gt;proguard 参数&lt;/h4&gt;
&lt;p&gt;-include {filename}    从给定的文件中读取配置参数&lt;/p&gt;

&lt;p&gt;-basedirectory {directoryname}    指定基础目录为以后相对的档案名称&lt;/p&gt;

&lt;p&gt;-injars {class_path}    指定要处理的应用程序jar,war,ear和目录&lt;/p&gt;

&lt;p&gt;-outjars {class_path}    指定处理完后要输出的jar,war,ear和目录的名称&lt;/p&gt;

&lt;p&gt;-libraryjars {classpath}    指定要处理的应用程序jar,war,ear和目录所需要的程序库文件&lt;/p&gt;

&lt;p&gt;-dontskipnonpubliclibraryclasses    指定不去忽略非公共的库类。&lt;/p&gt;

&lt;p&gt;-dontskipnonpubliclibraryclassmembers    指定不去忽略包可见的库类的成员。&lt;/p&gt;

&lt;h4 id=&quot;保留选项&quot;&gt;保留选项&lt;/h4&gt;

&lt;p&gt;-keep {Modifier} {class_specification}    保护指定的类文件和类的成员&lt;/p&gt;

&lt;p&gt;-keepclassmembers {modifier} {class_specification}    保护指定类的成员，如果此类受到保护他们会保护的更好&lt;/p&gt;

&lt;p&gt;-keepclasseswithmembers {class_specification}    保护指定的类和类的成员，但条件是所有指定的类和类成员是要存在。&lt;/p&gt;

&lt;p&gt;-keepnames {class_specification}    保护指定的类和类的成员的名称（如果他们不会压缩步骤中删除）&lt;/p&gt;

&lt;p&gt;-keepclassmembernames {class_specification}    保护指定的类的成员的名称（如果他们不会压缩步骤中删除）&lt;/p&gt;

&lt;p&gt;-keepclasseswithmembernames {class_specification}    保护指定的类和类的成员的名称，如果所有指定的类成员出席（在压缩步骤之后）&lt;/p&gt;

&lt;p&gt;-printseeds {filename}    列出类和类的成员-keep选项的清单，标准输出到给定的文件&lt;/p&gt;

&lt;h4 id=&quot;压缩&quot;&gt;压缩&lt;/h4&gt;
&lt;p&gt;-dontshrink    不压缩输入的类文件&lt;/p&gt;

&lt;p&gt;-printusage {filename}&lt;/p&gt;

&lt;p&gt;-whyareyoukeeping {class_specification}  不懂&lt;/p&gt;

&lt;h4 id=&quot;优化&quot;&gt;优化&lt;/h4&gt;
&lt;p&gt;-dontoptimize    不优化输入的类文件&lt;/p&gt;

&lt;p&gt;-assumenosideeffects {class_specification}    优化时假设指定的方法，没有任何副作用&lt;/p&gt;

&lt;p&gt;-allowaccessmodification    优化时允许访问并修改有修饰符的类和类的成员&lt;/p&gt;

&lt;h4 id=&quot;混淆&quot;&gt;混淆&lt;/h4&gt;
&lt;p&gt;-dontobfuscate    不混淆输入的类文件&lt;/p&gt;

&lt;p&gt;-printmapping {filename} 打印映射&lt;/p&gt;

&lt;p&gt;-applymapping {filename}    重用映射增加混淆&lt;/p&gt;

&lt;p&gt;-obfuscationdictionary {filename}    使用给定文件中的关键字作为要混淆方法的名称&lt;/p&gt;

&lt;p&gt;-overloadaggressively    混淆时应用侵入式重载&lt;/p&gt;

&lt;p&gt;-useuniqueclassmembernames    确定统一的混淆类的成员名称来增加混淆&lt;/p&gt;

&lt;p&gt;-flattenpackagehierarchy {package_name}    重新包装所有重命名的包并放在给定的单一包中&lt;/p&gt;

&lt;p&gt;-repackageclass {package_name}    重新包装所有重命名的类文件中放在给定的单一包中&lt;/p&gt;

&lt;p&gt;-dontusemixedcaseclassnames    混淆时不会产生形形色色的类名&lt;/p&gt;

&lt;p&gt;-keepattributes {attribute_name,…}    保护给定的可选属性，例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses.&lt;/p&gt;

&lt;p&gt;-renamesourcefileattribute {string}    设置源文件中给定的字符串常量&lt;/p&gt;

&lt;h4 id=&quot;解决export打包的报错&quot;&gt;解决export打包的报错&lt;/h4&gt;

&lt;p&gt;这个时候export提示“conversion to Dalvik format failed with error 1”错误，
网上说法有好多种，最后我还是把proguard从4.4升级到4.8就解决了。官方地址是&lt;a href=&quot;http://proguard.sourceforge.net&quot;&gt;http://proguard.sourceforge.net&lt;/a&gt;。上面的配置文件参数可以在这里查阅。&lt;/p&gt;

&lt;p&gt;升级办法很简单，就是把android sdk目录下的tool/proguard目录覆盖一下即可。&lt;/p&gt;

&lt;p&gt;打包出来的程序如何调试
一旦打包出来，就不能用eclipse的logcat去看了，这里可以用android sdk中ddms.bat的tool来看，一用就发现和logcat其实还是一个东西，就是多了个设备的选择。&lt;/p&gt;

&lt;p&gt;使用 gson 需要的配置
当Gson用到了泛型就会有报错，这个真给郁闷了半天，提示“Missing type parameter”。最后找到一个资料给了一个解决办法，参考：http://stackoverflow.com/questio … sing-type-parameter。&lt;/p&gt;

&lt;p&gt;另外我又用到了JsonObject，提交的Object里面的members居然被改成了a。所以上面给的东西还不够，还要加上&lt;/p&gt;
&lt;h4 id=&quot;用到自己拼接的jsonobject&quot;&gt;用到自己拼接的JsonObject&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-keep class com.google.gson.JsonObject { *; }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;个人建议减少这些依赖包混淆带来的麻烦，干脆都全部保留不混淆。例如&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-keep class com.badlogic.** { *; }
-keep class * implements com.badlogic.gdx.utils.Json*
-keep class com.google.** { *; }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;使用libgdx需要的配置参考&lt;a href=&quot;http://code.google.com/p/libgdx-users/wiki/Ant&quot;&gt;http://code.google.com/p/libgdx-users/wiki/Ant&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;验证打包效果&quot;&gt;验证打包效果&lt;/h4&gt;

&lt;p&gt;利用了apktool的反编译工具，把打包文件又解压了看了一下，如果包路径、类名、变量名、方法名这些变化和你期望一致，那就OK了。命令：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apktool.bat  d xxx.apk destdir
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;配置实例&quot;&gt;配置实例&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
-injars  androidtest.jar【jar包所在地址】

-outjars  out【输出地址】

-libraryjars    ‘D:\android-sdk-windows\platforms\android-9\android.jar’ 【引用的库的jar，用于解析injars所指定的jar类】

-optimizationpasses 5    性能优化

-dontusemixedcaseclassnames 【混淆时不会产生形形色色的类名 】

-dontskipnonpubliclibraryclasses 【指定不去忽略非公共的库类。 】

-dontpreverify 【不预校验】

-verbose

-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 【优化】

-keep public class * extends android.app.Activity　　【不进行混淆保持原样】

-keep public class * extends android.app.Application

-keep public class * extends android.app.Service

-keep public class * extends android.content.BroadcastReceiver

-keep public class * extends android.content.ContentProvider

-keep public class * extends android.app.backup.BackupAgentHelper

-keep public class * extends android.preference.Preference

-keep public class com.android.vending.licensing.ILicensingService

-keep public abstract interface com.asqw.android.Listener{
public protected &amp;lt;methods&amp;gt;;  【所有方法不进行混淆】
}

-keep public class com.asqw.android{
public void Start(java.lang.String); 【对该方法不进行混淆】
}

-keepclasseswithmembernames class * { 【保护指定的类和类的成员的名称，如果所有指定的类成员出席（在压缩步骤之后）】
native &amp;lt;methods&amp;gt;;
}

-keepclasseswithmembers class * { 【保护指定的类和类的成员，但条件是所有指定的类和类成员是要存在。】
public &amp;lt;init&amp;gt;(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembers class * {
public &amp;lt;init&amp;gt;(android.content.Context, android.util.AttributeSet, int);
}

-keepclassmembers class * extends android.app.Activity {【保护指定类的成员，如果此类受到保护他们会保护的更好 】
public void *(android.view.View);
}

-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}

-keep class * implements android.os.Parcelable {【保护指定的类文件和类的成员】
public static final android.os.Parcelable$Creator *;
}
//不混淆指定包下的类
-keep class com.abcd.**

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;proguard-问题和风险&quot;&gt;proguard 问题和风险&lt;/h3&gt;

&lt;p&gt;代码混淆后虽然有混淆优化的好处，但是它往往也会带来如下的几点问题&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1，混淆错误，用到第三方库的时候，必须告诉 proguard 不要检查，否则proguard 会报错。&lt;/li&gt;
  &lt;li&gt;2，运行错误，当code 不能混淆的时候，我们必须要正确配置，否则程序会运行出错，这种情况问题最多。&lt;/li&gt;
  &lt;li&gt;3，调试难，出错了，错误堆栈是混淆后的代码 ，自己也看不懂,最后苦的还是开发的,泪奔。官网上提出的使用&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;retrace.bat|retrace.sh [-verbose] mapping.txt [&amp;lt;stacktrace_file&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;工具进行反mapping文件的操作：&lt;a href=&quot;http://developer.android.com/tools/help/proguard.html#decoding&quot;&gt;http://developer.android.com/tools/help/proguard.html#decoding&lt;/a&gt;&lt;/p&gt;
</description>
      </item>
    
      <item>
        <title>雷军一刻演讲稿：只要拥有梦想，终将与众不同</title>
        <link>http://www.samuelnotes.cn/2015/09/15/leijun-we-need-have-dream.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/09/15/leijun-we-need-have-dream.html</guid>
        <pubDate>Tue, 15 Sep 2015 00:00:00 +0000</pubDate>
        <description>&lt;blockquote&gt;
  &lt;p&gt;大家好，非常感谢在“一刻演讲”跟大家交流，这么一个大的话题。昨天我在乌镇参加了全球互联网峰会，在这个会议上有马云，也有苹果公司的高级副总裁，主持人抛出了一个问题，说雷军你说你有一个目标，要用5到10年的时间做智能手机市场风格全球第一。我忙着点头，我的确说过，但是他们又问我，他去问苹果公司的高管，说你怎么看？这个苹果公司的高管也很厉害，他说Easy to say hard to do，我英文很差，正好我听懂了。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;在那一刻我觉得很尴尬，主持人说雷军你怎么想？那一瞬间我非常非常的尴尬，我冷静了一下，我说马云在阿里巴巴上市的那一刻说过一句话，梦想还是要有的，万一实现了呢？我的演说水平远远没办法跟马云相比，马云的号召力和演说水平，我是望尘莫及。除了我湖北普通话之外，我觉得我作为一个技术和理工科的人来说，跟他的口才比不了，因为马云也是毕业于非常非常有名的名校，不亚于北京大学，叫杭州师范大学，专门培养老师的，真的比我能说。
尤其是我听说了马云还讲过，像他这样的人都能成功的话，80%的中国人都可以成功，听得我们每个人都热血沸腾，他说我高考几次落榜，好不容易上了杭州师范大学，还找不到工作。马云今天有资格讲这个话，讲得也特别震撼，每一个，尤其每一个屌丝都渴望像马云一样逆袭。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;讲完马云这句名言以后，我又补了一段话，我说4年多前，小米刚刚创业，在中关村十来个人、七八条枪要去做手机，有谁相信我们能赢呢？手机这个行业是刀山火海，前面有三星、有苹果，后面有联想、有华为，我们看到摩托罗拉不行了，诺基亚也不行了，黑莓也不行了，连HTC也不行了，最近索尼也不行了，这个市场竞争极为激烈。一个正常人想到智能手机，就觉得这个市场竞争很激烈。
    三年前，我们产品刚刚发布，仅仅用了三年时间，谁又想过这十来个人的小公司，用了不到三年时间，有苹果、有三星、有华为、有联想这样的市场里面，杀到了全中国第一、全球第三。当我们今天有这样的业绩，有这样的起跑线，我觉得我们总应该有这一点点梦想，用5到10年时间杀到全球第一吧。所以梦想还是要有的。4年前我梦想的动力源于哪里呢？
其实办小米对我来说是一个很难很难的事情，为什么呢？是因为我在办小米之前，我有幸参与了金山软件的创办，今天我依然是金山软件的董事长和大股东，而且我还有幸的办过一个电商公司叫卓越网，应该来说我的人生也足够了。在IPO之后，我退休了，还干了三四年投资，而且做天使投资，业绩还不错，绝对能进入中国天使投资界的第一排系列，肯定没问题。
    你说做投资总不用天天这么操心吧，是什么样的动力使我下定决心去干这么累的一件事情呢？这件事真的很累。我在那个阶段，在我做天使投资，在我从金山退休的那个阶段，我有天晚上从梦中醒来，我问了我自己一个问题，我40岁了，在别人眼里功成名就，已经退休了，还干着人人都很羡慕的投资。我还有没有勇气去追寻我小时候的梦想，我觉得岁数越大，谈梦想越难，我觉得大家现在都是最有梦想的时候，你们到了40岁的时候，还有梦想吗？面对残酷的现实，还有几个人能笑对今天、笑对明天呢？
    我当时问我自己，我还有没有勇气去试一把？这么试下去风险很高，有可能身败名裂，有可能倾家荡产，而且更重要的是，我在别人眼里已经是个成功者，我需要冒那么大的风险去做这么艰难的一件事情吗？其实我真的犹豫了半年时间，最后我觉得这一种梦想激励我自己一定要去赌一把，我说只有这样做，我的人生才是圆满的，至少当我老了的时候，我还可以很自豪的说，我曾经有过梦想，我曾经去试过，哪怕输了。所以我最后下定了决心，办了小米，办小米刚开始，我认为我100%会输，我想的全部是我怎么死，我真的很庆幸，我们竟然只有了三年，完成了一个令我自己都无法相信的结果。
我曾经在武汉大学的操场上沿着400米的跑道走了一天又一天，走了好几个通宵，我怎么能塑造与众不同的人生。我想在我们中国这个土壤上，我们能不能像乔布斯一样，办一家世界一流的公司，我觉得只有这样你才无愧于你的人生，才会使你自己觉得人生是有价值、有意义、有追求的。&lt;/p&gt;

&lt;p&gt;那么我的梦想是什么？我为什么会有这样的梦想？是因为我跟大家同样岁数的时候，在我18岁的那一年，曾经青春年少，无意之中在图书馆里看了一本书，改变了我的一生，那是我大学一年级第一个学期，我在武汉大学的图书馆里面看了一本书，这本书叫《硅谷之火》，那是1987。这本书讲述的是70年代末、80年代初，那些硅谷的英雄创业的故事，其中主要的篇章就是讲苹果乔布斯的，书中说乔布斯在70年代末、80年代初，代表着美国式的创业。
我记得90年代的时候，比尔·盖茨而成功的时候，比尔·盖茨讲说我不过是乔布斯第二而已。乔布斯在80年代就已经如日中天，当时看了这本书激动的我自己心情久久难以平静，我清晰的记得看完这本书以后，我在武汉大学的操场上沿着400米的跑道走了一天又一天，走了好几个通宵，我怎么能塑造与众不同的人生。我想在我们中国这个土壤上，我们能不能像乔布斯一样，办一家世界一流的公司，我觉得只有这样你才无愧于你的人生，才会使你自己觉得人生是有价值、有意义、有追求的。
    当然，在二十六七年前的中国，条件比今天差很多，要做点事情远没有今天容易，有这样的梦想，Easy to say hard to do，当我有这样的梦想以后，我认为放到语言上是没有用的，怎么能落实到实际的学习和工作中。我当时给我制订的第一个计划，在二十六七年前，两年修完大学所有过程。
    我真的是武汉大学在80年代不多的两年修完所有课程的人，而且我绝大部分的成绩应该都是年级第一，几乎所有专业课程。我记得我两年修完了成绩，在我们同学里面排到第六，我们全年级100多人。所以怎么能够落实到第一个梦想，我能够在一个赛道上把学习弄好，因为我认为你还是要有基本功的沉淀，有了第一个基本功的沉淀，我们又给出了第二个目标，大家说你没有本事在一级学报上发表论文，我用两年时间，在我大二的时候，在一级学报上发表了论文。所以有梦想是件简单的事情，关键是有了梦想以后，你能不能把这个东西付诸实践，你怎么去实践，你怎么给自己设定一个又一个可行的目标。当然，有了这样的目标还是不够的，因为要成功还不是一件简单的事情，他需要你长时间的坚忍不拔、百折不挠。就像我自己，到了我40岁退休以后，我还有没有勇气去试一把，所以非常感谢一刻演讲给了我这个机会。今天我可以很自豪的跟大家分享，我在40岁的时候，没有忘记我18岁的梦想，我去试了。虽然我知道今天的小米说成功为时过早，谈击败苹果为时过早，但是梦想总是要有的，万一实现了呢？&lt;strong&gt;我们做任何事情都需要看五年以后的事情，想三年，认认真真做好一两年。&lt;/strong&gt;
   我比大家大概大两轮的年纪，20多岁，我也跟很多年轻人经常交流梦想，我自己特别特别喜欢的一句话叫“人因梦想而伟大”，在21日下午开会讨论的时候，还有人说这是美国哪个总统讲的，我说我抄来的。我觉得我们每一个人，只要你有了梦想，你就变得与众不同。周星弛也讲过一句名言，叫人没有梦想，和咸鱼有什么差别。所以你关键要有梦想，有了梦想，是你迈向成功的第一步，有了第一步以后，你一定要为自己的梦想去准备各种坚实的基础。
那么谈到梦想的实现，我最近还有一句话挺出名的，也是我抄来的，叫“台风来的时候，猪都会飞”，听说过吗？你要成功了，要找台风口，当台风飞过来的时候，猪都可以飞。你想是猪都可以飞，当然了，这个话我其实想表达两层的意思，尤其是给在座的同学们，我觉得第一个，没有坚实的基本功，没有勤奋是成功不了的。我觉得第二点，有了勤奋，有了坚实的基础，也不一定能成功，还需要什么呢？还需要台风口。还需要把握这个大的发展机遇，把这个机遇把握好，抓住这个机会，你才有机会成功。
   那么小米把握的是什么机会呢？为什么这个台风这么厉害呢？其实小米精准的踏到了智能手机换机的时间点，诺基亚不行了，苹果刚刚起来，小米应运而生，用了一套全新的模式，在短短的三年时间里面成了中国第一。其实整个成长速度远超想象，小米三年做下来，今年大概有多大的规模呢？今年大概销售6500万只手机，营业额大概会在700亿人民币到800亿人民币，这是一个三四年的创业公司。这个应该已经创造了全球的奇迹，这背后是什么呢？
这背后是坚实的基本功和非常好、非常好的对时机的把握。所以我自己工作了20多年，我对各位同学们的建议就是，我觉得第一条要有梦想，第二条要设定step by step的努力的目标，要制订阶段性的目标，不要着急。第三点就是要重视机遇的重要性，我觉得很聪明的一些同学都觉得聪明加勤奋天下无敌，其实仅有聪明和勤奋是远远不够的，怎么把握时代的机遇，怎么在大方向上正确，我觉得这点也非常关键。
   今天的一刻演讲，我就讲到这里，谢谢大家！&lt;/p&gt;
</description>
      </item>
    
      <item>
        <title>面向对象五大基本原则</title>
        <link>http://www.samuelnotes.cn/2015/09/05/object-oriented-design-principles.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/09/05/object-oriented-design-principles.html</guid>
        <pubDate>Sat, 05 Sep 2015 00:00:00 +0000</pubDate>
        <description>&lt;p&gt;面向对象五大基本原则&lt;/p&gt;

&lt;p&gt;以前一直认为程序中的类有使用到封装继承多态就是面向对象设计,其实不然
封装,继承,多态只是面向对象的三大特性,但是在设计程序的时候并不是说类的结构使用到了(或是体现出了)这三个特性就是面向对象,
其实真正的面向对象设计是要符合下面的五大原则,&lt;/p&gt;

&lt;p&gt;面向对象的五大基本原则&lt;/p&gt;

&lt;p&gt;1 单一职责原则（SRP）&lt;/p&gt;

&lt;p&gt;2 开放封闭原则（OCP）&lt;/p&gt;

&lt;p&gt;3 里氏替换原则（LSP）&lt;/p&gt;

&lt;p&gt;4 依赖倒置原则（DIP）&lt;/p&gt;

&lt;p&gt;5 接口隔离原则（ISP）&lt;/p&gt;

&lt;p&gt;单一职责原则（SRP）&lt;/p&gt;

&lt;p&gt;• 一个类应该仅有一个引起它变化的原因(最简单，最容易理解却最不容易做到的一个设计原则)
职员类例子：
  比如在职员类里，将工程师、销售人员、销售经理这些情况都放在职员类里考虑，其结果将会非常混乱，在这个假设下，职员类里的每个方法都要if else判断是哪种情况，从类结构上来说将会十分臃肿，并且上述三种的职员类型，不论哪一种发生需求变化，都会改变职员类！这个是大家所不愿意看到的！&lt;/p&gt;

&lt;p&gt;开放封闭原则（OCP）&lt;/p&gt;

&lt;p&gt;• 既开放又封闭，对扩展是开放的，对更改是封闭的！&lt;/p&gt;

&lt;p&gt;• 扩展即扩展现行的模块，当我们软件的实际应用发生改变时，出现新的需求，就需要我们对模块进行扩展，使其能够满足新的需求！&lt;/p&gt;

&lt;p&gt;更改封闭即是在我们对模块进行扩展时，勿需对源有程序代码和DLL进行修改或重新编译文件！
这个原则对我们在设计类的时候很有帮助，坚持这个原则就必须尽量考虑接口封装，抽象机制和多态技术！&lt;/p&gt;

&lt;p&gt;里氏替换原则（LSP）&lt;/p&gt;

&lt;p&gt;• 子类可以替换父类并且出现在父类能够出现的任何地方&lt;/p&gt;

&lt;p&gt;• 这个原则也是在贯彻GOF倡导的面向接口编程！&lt;/p&gt;

&lt;p&gt;在这个原则中父类应尽可能使用接口或者抽象类来实现！&lt;/p&gt;

&lt;p&gt;子类通过实现了父类接口，能够替父类的使用地方！
通过这个原则，我们客户端在使用父类接口的时候，通过子类实现！
意思就是说我们依赖父类接口，在客户端声明一个父类接口，通过其子类来实现
这个时候就要求子类必须能够替换父类所出现的任何地方，这样做的好处就是，在根据新要求扩展父类接口的新子类的时候而不影响当前客户端的使用！&lt;/p&gt;

&lt;p&gt;依赖倒置原则（DIP）&lt;/p&gt;

&lt;p&gt;• 传统的结构化编程中，最上层的模块通常都要依赖下面的子模块来实现，也
称为高层依赖低层！
所以DIP原则就是要逆转这种依赖关系，让高层模块不要依赖低层模块，所以称之为依赖倒置原则！&lt;/p&gt;

&lt;p&gt;ISP 接口隔离原则&lt;/p&gt;

&lt;p&gt;• 这个原则的意思是：使用多个专门的接口比使用单个接口要好的多！&lt;/p&gt;

&lt;p&gt;这个我有体会，在我实际编程中，为了减少接口的定义，将许多类似的方法都放在一个接口中，最后发现，维护和实现接口的时候花了太多精力，而接口所定义的操作相当于对客户端的一种承诺，这种承诺当然是越少越好，越精练越好，过多的承诺带来的就是你的大量精力和时间去维护！&lt;/p&gt;
</description>
      </item>
    
      <item>
        <title>数据结构-页面常用置换算法</title>
        <link>http://www.samuelnotes.cn/2015/08/10/common-page-replacement-algorithms.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/08/10/common-page-replacement-algorithms.html</guid>
        <pubDate>Mon, 10 Aug 2015 00:00:00 +0000</pubDate>
        <description>&lt;blockquote&gt;
  &lt;p&gt;做应用做了两年多了，发现自己曾经认为最重要的是各种各样的api，各种各样的效果图，这些都算法编出来的。  再后来就发现编程到最后就是属于算法的扩充。 听起来好牛气的样子，不过的确是这样，后悔在大学里没有好好地学习数据结构这门课程，后来凭记忆从网上搜集了几种页面置换算法，整理记录一下。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;缓存算法页面置换算法-fifolfulru&quot;&gt;缓存算法（页面置换算法）-FIFO、LFU、LRU&lt;/h1&gt;

&lt;p&gt;　　在前一篇文章中通过leetcode的一道题目了解了LRU算法的具体设计思路，下面继续来探讨一下另外两种常见的Cache算法：FIFO、LFU&lt;/p&gt;

&lt;h2 id=&quot;1-fifo算法&quot;&gt;1. FIFO算法&lt;/h2&gt;

&lt;p&gt;　　FIFO（First in First out），先进先出。其实在操作系统的设计理念中很多地方都利用到了先进先出的思想，比如作业调度（先来先服务），为什么这个原则在很多地方都会用到呢？因为这个原则简单、且符合人们的惯性思维，具备公平性，并且实现起来简单，直接使用数据结构中的队列即可实现。&lt;/p&gt;

&lt;p&gt;　　在FIFO Cache设计中，核心原则就是：如果一个数据最先进入缓存中，则应该最早淘汰掉。也就是说，当缓存满的时候，应当把最先进入缓存的数据给淘汰掉。在FIFO Cache中应该支持以下操作;&lt;/p&gt;

&lt;p&gt;　　get(key)：如果Cache中存在该key，则返回对应的value值，否则，返回-1；&lt;/p&gt;

&lt;p&gt;　　set(key,value)：如果Cache中存在该key，则重置value值；如果不存在该key，则将该key插入到到Cache中，若Cache已满，则淘汰最早进入Cache的数据。&lt;/p&gt;

&lt;p&gt;　　举个例子：假如Cache大小为3，访问数据序列为set(1,1),set(2,2),set(3,3),set(4,4),get(2),set(5,5)&lt;/p&gt;

&lt;p&gt;　　则Cache中的数据变化为：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
　　(1,1)                           set(1,1)

　　(1,1) (2,2)                     set(2,2)

　　(1,1) (2,2) (3,3)               set(3,3)

　　(2,2) (3,3) (4,4)               set(4,4)

　　(2,2) (3,3) (4,4)               get(2)

　　(3,3) (4,4) (5,5)               set(5,5)

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;　　那么利用什么数据结构来实现呢？&lt;/p&gt;

&lt;p&gt;　　下面提供一种实现思路：&lt;/p&gt;

&lt;p&gt;　　利用一个双向链表保存数据，当来了新的数据之后便添加到链表末尾，如果Cache存满数据，则把链表头部数据删除，然后把新的数据添加到链表末尾。在访问数据的时候，如果在Cache中存在该数据的话，则返回对应的value值；否则返回-1。如果想提高访问效率，可以利用hashmap来保存每个key在链表中对应的位置。&lt;/p&gt;

&lt;h2 id=&quot;2-lfu算法&quot;&gt;2. LFU算法&lt;/h2&gt;

&lt;p&gt;　　LFU（Least Frequently Used）最近最少使用算法。它是基于 &lt;strong&gt;如果一个数据在最近一段时间内使用次数很少，那么在将来一段时间内被使用的可能性也很小&lt;/strong&gt;的思路。&lt;/p&gt;

&lt;p&gt;　　注意LFU和LRU算法的不同之处，LRU的淘汰规则是基于访问时间，而LFU是基于访问次数的。举个简单的例子：&lt;/p&gt;

&lt;p&gt;　　假设缓存大小为3，数据访问序列为set(2,2),set(1,1),get(2),get(1),get(2),set(3,3),set(4,4)，&lt;/p&gt;

&lt;p&gt;　　则在set(4,4)时对于LFU算法应该淘汰(3,3)，而LRU应该淘汰(1,1)。&lt;/p&gt;

&lt;p&gt;　　那么LFU Cache应该支持的操作为：&lt;/p&gt;

&lt;p&gt;　　get(key)：如果Cache中存在该key，则返回对应的value值，否则，返回-1；&lt;/p&gt;

&lt;p&gt;　　set(key,value)：如果Cache中存在该key，则重置value值；如果不存在该key，则将该key插入到到Cache中，若Cache已满，则淘汰最少访问的数据。&lt;/p&gt;

&lt;p&gt;　　为了能够淘汰最少使用的数据，因此LFU算法最简单的一种设计思路就是 利用一个数组存储 数据项，用hashmap存储每个数据项在数组中对应的位置，然后为每个数据项设计一个访问频次，当数据项被命中时，访问频次自增，在淘汰的时候淘汰访问频次最少的数据。这样一来的话，在插入数据和访问数据的时候都能达到O(1)的时间复杂度，在淘汰数据的时候，通过选择算法得到应该淘汰的数据项在数组中的索引，并将该索引位置的内容替换为新来的数据内容即可，这样的话，淘汰数据的操作时间复杂度为O(n)。&lt;/p&gt;

&lt;p&gt;　　另外还有一种实现思路就是利用 小顶堆+hashmap，小顶堆插入、删除操作都能达到O(logn)时间复杂度，因此效率相比第一种实现方法更加高效。&lt;/p&gt;

&lt;h2 id=&quot;3-lru算法--最近最少使用页面置换算法&quot;&gt;3. LRU算法  最近最少使用页面置换算法&lt;/h2&gt;

&lt;p&gt;最近最不常用调度算法总是根据一段时间内页面的访问次数开选择淘汰页面，没次淘汰访问次数最少的页面。算法实现是需要为每个页面设置计数器，记录访问次数。计数器有硬件或操作系统自动定时清零。&lt;/p&gt;

&lt;h2 id=&quot;4lru和lfu的区别&quot;&gt;4.LRU和LFU的区别&lt;/h2&gt;

&lt;p&gt;LRU是最近最少使用页面置换算法(Least Recently Used),也就是&lt;strong&gt;首先淘汰最长时间未被使用的页面&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;LFU是最近最不常用页面置换算法(Least Frequently Used),也就是&lt;strong&gt;首先淘汰一定时期内被访问次数最少&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;比如,第二种方法的时期T为10分钟,如果每分钟进行一次调页,主存块为3,若所需页面走向为2 1 2 1 2 3 4&lt;/p&gt;

&lt;p&gt;注意,当调页面4时会发生缺页中断&lt;/p&gt;

&lt;p&gt;若按LRU算法,应换页面1(1页面最久未被使用) 但按LFU算法应换页面3(十分钟内,页面3只使用了一次)&lt;/p&gt;

&lt;p&gt;可见LRU关键是看页面最后一次被使用到发生调度的时间长短,&lt;/p&gt;

&lt;p&gt;而LFU关键是看一定时间段内页面被使用的频率!&lt;/p&gt;

&lt;h2 id=&quot;5-先进先出调度算法fifo&quot;&gt;5. 先进先出调度算法(FIFO)&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;先进先出调度算法&lt;/strong&gt;根据页面进入内存的时间 先后选择滔滔页面，本算法实现时需要将页面按照进入的时间先后组成一个队列，每次调度队首页面予以淘汰。他的优点是比较容易实现，能够利用主存储器中页面调度情况的历史信息，但是，他没有反映程序的局部性，因为最先调入主存的页面，很可能也是经常要使用的页面。&lt;/p&gt;

&lt;h2 id=&quot;6-最优替换算法-opt&quot;&gt;6. 最优替换算法 OPT&lt;/h2&gt;

&lt;p&gt;前面几种页面调度算法主要是以主存出奇中页面调度情况的历史信息为依据的，他假设将来主存出器中页面调度情况的历史信息为依据的，他假设将来主存储器中的页面调度情况与过去一段时间内主存储器的页面调度情况是相同的。显然，这种假设不总是真确的，
  最好的算法应该是选择将来最久不被方位的页面最为被替换的页面，这种算法命中率一定是最高的。就是最优替换算法，也实现opt算法，唯一的方法就是让程序先执行一边，记录下实际的页地址流情况，根据这个页地址流才能找出当前要被替换的页面，显然，这样做是不现实的。因此，&lt;strong&gt;OPT算法只是一种理想化的算法&lt;/strong&gt; ，然而，这也是一种很有用的算法。
  实际上，经常把这种算法用来作为评价其他页面调度算法好坏的标准，在其他条件相同的情况下，哪中页面调度算法的命中率与opt相近，那么，他就是一种比较好的页面置换算法。&lt;/p&gt;

&lt;p&gt;缺页调度次数和缺页中断率，缺页置换率计算：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;缺页中断次数是缺页是发出缺页中的次数&lt;/li&gt;
  &lt;li&gt;缺页中断率=缺页中断次数/中的页面引用次数&lt;/li&gt;
  &lt;li&gt;缺页调度次数是调入新页是需要进行页面调度的次数&lt;/li&gt;
  &lt;li&gt;缺页置换率=缺页调度次数/总的页面引用次数&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;总结&quot;&gt;总结&lt;/h1&gt;

&lt;h2 id=&quot;判断一个页面调度算法好坏&quot;&gt;判断一个页面调度算法好坏&lt;/h2&gt;
&lt;p&gt;一是命中率要高，二是效率高，三是算法要容易实现。&lt;/p&gt;

&lt;h2 id=&quot;应用&quot;&gt;应用&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;虚拟存储器中，贮存页面的替换&lt;/li&gt;
  &lt;li&gt;Cache中的块替换&lt;/li&gt;
  &lt;li&gt;虚拟存储器中的快慢表中，块表的替换&lt;/li&gt;
  &lt;li&gt;虚拟存储其中，用户基地址寄存器的替换&lt;/li&gt;
&lt;/ol&gt;

</description>
      </item>
    
      <item>
        <title>开源项目源码分析-android-async-http</title>
        <link>http://www.samuelnotes.cn/2015/08/06/openproj-android-async-http-analysis.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/08/06/openproj-android-async-http-analysis.html</guid>
        <pubDate>Thu, 06 Aug 2015 00:00:00 +0000</pubDate>
        <description>&lt;blockquote&gt;
  &lt;p&gt;An asynchronous, callback-based Http client for Android built on top of Apache’s HttpClient libraries.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;android-async-http-源码解析&quot;&gt;android-async-http 源码解析&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;本文为 &lt;a href=&quot;https://github.com/android-cn/android-open-project-analysis&quot;&gt;Android 开源项目源码解析&lt;/a&gt; 中 android-async-http 部分&lt;br /&gt;
 项目地址：&lt;a href=&quot;https://github.com/loopj/android-async-http&quot;&gt;android-async-http&lt;/a&gt;，分析的版本：[1.4.8] ，Demo 地址：&lt;a href=&quot;https://github.com/loopj/android-async-http/tree/1.4.8/sample/src/main/java/com/loopj/android/http/sample&quot;&gt;sample&lt;/a&gt;  &lt;br /&gt;
 分析者：&lt;a href=&quot;https://github.com/samuelhehe&quot;&gt;samuelhehe&lt;/a&gt;，分析状态：未完成，校对者：&lt;a href=&quot;https://github.com/trinea&quot;&gt;Trinea&lt;/a&gt;，校对状态：未开始&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;1-功能介绍&quot;&gt;1. 功能介绍&lt;/h1&gt;

&lt;h2 id=&quot;功能介绍&quot;&gt;功能介绍：&lt;/h2&gt;

&lt;p&gt;android-async-http  是一个基于回调的并基于Apache’s httpClient libraries 为Android 开发的一个框架。 所有的请求在非UI线程中处理，而回调是在Handler里面处理。你也可以在Service或者后台线程使用， lib 会自动识别 并在context里处理。  具体使用方法可见 &lt;a href=&quot;http://loopj.com/android-async-http/&quot;&gt;http://loopj.com/android-async-http/&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;概念&quot;&gt;概念&lt;/h2&gt;

&lt;p&gt;Http请求 (AsyncHttpRequest)：又可以成为Http请求的单线程对象，通过将该单线程模型submit至 线程池中统一管理。&lt;/p&gt;

&lt;p&gt;Http响应处理者(HttpResponseHandler)： 可以针对性的生成不同数据类型的相应处理者，包含Json， String， binary， file …的处理操作。&lt;/p&gt;

&lt;p&gt;重试处理者(RetryHandler)：可以针对不同的异常情况根据自身预设定的white&amp;amp;black exception List 进行智能筛选，选择需要重试的请求。&lt;/p&gt;

&lt;p&gt;拦截器(Interceptor): 在该框架中的应用类似于Spring中的 aop(Aspect Oriented Programming), 多次设定与Httpclient属性中。&lt;/p&gt;

&lt;h2 id=&quot;特点&quot;&gt;特点：&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;异步发送HTTP请求，在回调函数中处理响应&lt;/li&gt;
  &lt;li&gt;HTTP请求过程不在UI线程进行&lt;/li&gt;
  &lt;li&gt;使用线程池来管理并发数&lt;/li&gt;
  &lt;li&gt;支持GET/POST请求参数单独设置&lt;/li&gt;
  &lt;li&gt;无需其他库上传序列化JSON数据&lt;/li&gt;
  &lt;li&gt;处理重定向&lt;/li&gt;
  &lt;li&gt;体积小，只有90K&lt;/li&gt;
  &lt;li&gt;针对不同的网络连接对重试次数进行智能优化&lt;/li&gt;
  &lt;li&gt;支持gzip&lt;/li&gt;
  &lt;li&gt;二进制通信协议使用BinaryHttpResponseHandler处理&lt;/li&gt;
  &lt;li&gt;内置Json解析，使用JsonHttpResponseHandler对响应进行处理&lt;/li&gt;
  &lt;li&gt;使用FileAsyncHttpResponseHandler直接将响应保存到文件中&lt;/li&gt;
  &lt;li&gt;动态保存Cookie，将Cookie保存到应用的SharedPreferences中&lt;/li&gt;
  &lt;li&gt;使用BaseJsonHttpResponseHandler可以搭配Jackson JSON，Gson或者其他的Json反序列化库&lt;/li&gt;
  &lt;li&gt;支持SAX解析，使用SaxAsyncHttpResponseHandler&lt;/li&gt;
  &lt;li&gt;支持多语言多种编码方式，不只是UTF-8&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;优点&quot;&gt;优点：&lt;/h2&gt;

&lt;p&gt;个人觉得优点就是&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;该框架是基于回调的，各种回调类型的处理都有&lt;/li&gt;
  &lt;li&gt;直接放到主线程使用匿名调用即可&lt;/li&gt;
  &lt;li&gt;可以扩展自己的回调处理&lt;/li&gt;
  &lt;li&gt;支持处理重定向，文档的续传&lt;/li&gt;
  &lt;li&gt;可以保存Cookie， 可以添加请求凭证保存，单次输入，多次使用&lt;/li&gt;
  &lt;li&gt;根据常用网络请求异常进行黑白名单查询，针对性重试机构&lt;/li&gt;
  &lt;li&gt;整个请求处理是使用gzip的，节省流量，速度快 用起来方便。&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;2-总体设计&quot;&gt;2. 总体设计&lt;/h1&gt;

&lt;h2 id=&quot;总体设计图&quot;&gt;总体设计图&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/5669999/9300689/490ddd0e-44fb-11e5-8381-0a438010371f.png&quot; alt=&quot;art2&quot; /&gt;
图 2-1  总体设计图&lt;/p&gt;

&lt;p&gt;以上是android-async-http 的总体设计图。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;整个请求子线程 AsyncHttpRequest处于的 参数封装是在主类AsyncHttpClient 中进行的。 处理完成之后将该请求线程 提交至 threadPool 中由线程池调度完成。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;请求的处理是在子线程AsyncHttpRequest中处理的，其中包含了ResponseHandlerInterface 的响应回调处理大接口， 也处理了由于各种原因造成的访问失败的智能重试。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;请求的响应实现ResponseHandlerInterface接口的AsyncHttpResponseHandler类，类似于BaseAdapter模式，基本上使用Handler消息处理了数据处理的各种情况。 其中AsyncHttpResponseHandler的各种子类负责了对应各种数据类型的解析与处理。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;3-流程图&quot;&gt;3. 流程图&lt;/h1&gt;

&lt;h3 id=&quot;31-总体流程图&quot;&gt;3.1. 总体流程图&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/5669999/9241562/e5bda17c-41a7-11e5-837b-2030d8866e99.png&quot; alt=&quot;art_flow&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图 3-1 总体流程图&lt;/p&gt;

&lt;h3 id=&quot;32-请求线程流程图&quot;&gt;3.2. 请求线程流程图&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/5669999/9300719/a3af1232-44fb-11e5-87cb-3022c476d863.png&quot; alt=&quot;thread&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图 3-2 请求线程流程图&lt;/p&gt;

&lt;h3 id=&quot;33-makerequestwithretries函数模块调用&quot;&gt;3.3. makeRequestWithRetries函数模块调用&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/5669999/9300718/a36420d8-44fb-11e5-96bc-de0274203d09.png&quot; alt=&quot;methodreq&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图 3-3 请求函数模块调用&lt;/p&gt;

&lt;h3 id=&quot;34-makerequest函数模块调用&quot;&gt;3.4. makeRequest函数模块调用&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/5669999/9300720/a3e84430-44fb-11e5-9d32-bc5b89e0ddb4.png&quot; alt=&quot;method2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图 3-4 请求函数模块调用&lt;/p&gt;

&lt;h3 id=&quot;35-整体回调顺序&quot;&gt;3.5 整体回调顺序&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/5669999/9326673/118d0980-45ce-11e5-869f-985ac5ad739f.png&quot; alt=&quot;callbackseq&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图 3-5 请求函数整体回调顺序&lt;/p&gt;

&lt;h1 id=&quot;4-详细设计&quot;&gt;4. 详细设计&lt;/h1&gt;

&lt;h2 id=&quot;41-总类关系图&quot;&gt;4.1 总类关系图&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/5669999/9300898/08c27fbe-44fd-11e5-8ed0-8b76a6d0bf02.png&quot; alt=&quot;art&quot; /&gt;
图 4-1&lt;/p&gt;

&lt;h2 id=&quot;42-核心类功能介绍&quot;&gt;4.2 核心类功能介绍&lt;/h2&gt;

&lt;h3 id=&quot;421-类详细介绍未完成&quot;&gt;4.2.1 类详细介绍(未完成)&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;ResponseHandlerInterface 整个框架中网络访问返回数据的处理接口，集合了包括消息发送，数据处理回调在内的共16个函数。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;AsyncHttpClient.class 整个框架的调用集合类与各个参数的组装调用类。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;AsyncHttpRequest.class 网络访问子线程类&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;AsyncHttpResponseHandler.class 网络响应处理主类&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;4.1  AsyncHttpResponseHandler 类图&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/5669999/9345981/813c2592-4649-11e5-980b-3f84a14a393e.png&quot; alt=&quot;responsehandler&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图 4.1-1 AsyncHttpResponseHandler 类图&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.2 成员变量&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   
   	private Handler handler;  /// 内部类 ResponderHandler 的赋值对象， 负责发送，处理消息。
   
    private boolean useSynchronousMode; 
   
    private boolean usePoolThread;

    private URI requestURI = null;
    
    private Header[] requestHeaders = null;  /// 请求头信息
    
    private Looper looper = null; /// 默认如果looper为null，则赋值为Looper.myLooper() 也就是获取的主线程中的Looper ;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;4.3 重要函数&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
// Methods which emulate android&apos;s Handler and Message methods
   
    protected void handleMessage(Message message) {
       
        Object[] response;

        try {
            switch (message.what) {
                case SUCCESS_MESSAGE:
                    response = (Object[]) message.obj;
                    if (response != null &amp;amp;&amp;amp; response.length &amp;gt;= 3) {
                        onSuccess((Integer) response[0], (Header[]) response[1], (byte[]) response[2]);
                    } else {
                        Log.e(LOG_TAG, &quot;SUCCESS_MESSAGE didn&apos;t got enough params&quot;);
                    }
                    break;
                case FAILURE_MESSAGE:
                    response = (Object[]) message.obj;
                    if (response != null &amp;amp;&amp;amp; response.length &amp;gt;= 4) {
                        onFailure((Integer) response[0], (Header[]) response[1], (byte[]) response[2], (Throwable) response[3]);
                    } else {
                        Log.e(LOG_TAG, &quot;FAILURE_MESSAGE didn&apos;t got enough params&quot;);
                    }
                    break;
                case START_MESSAGE:
                    onStart();
                    break;
                case FINISH_MESSAGE:
                    onFinish();
                    break;
                case PROGRESS_MESSAGE:
                    response = (Object[]) message.obj;
                    if (response != null &amp;amp;&amp;amp; response.length &amp;gt;= 2) {
                        try {
                            onProgress((Long) response[0], (Long) response[1]);
                        } catch (Throwable t) {
                            Log.e(LOG_TAG, &quot;custom onProgress contains an error&quot;, t);
                        }
                    } else {
                        Log.e(LOG_TAG, &quot;PROGRESS_MESSAGE didn&apos;t got enough params&quot;);
                    }
                    break;
                case RETRY_MESSAGE:
                    response = (Object[]) message.obj;
                    if (response != null &amp;amp;&amp;amp; response.length == 1) {
                        onRetry((Integer) response[0]);
                    } else {
                        Log.e(LOG_TAG, &quot;RETRY_MESSAGE didn&apos;t get enough params&quot;);
                    }
                    break;
                case CANCEL_MESSAGE:
                    onCancel();
                    break;
            }
        } catch(Throwable error) {
            onUserException(error);
        }
    }

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;分析：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;接收处理 请求访问以及返回过程中的所有状态， 包含  SUCCESS_MESSAGE, FAILURE_MESSAGE, START_MESSAGE, FINISH_MESSAGE, PROGRESS_MESSAGE,RETRY_MESSAGE, CANCEL_MESSAGE 在内的各种情况，以及回调。&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
    byte[] getResponseData(HttpEntity entity) throws IOException {
        byte[] responseBody = null;
        if (entity != null) {
            InputStream instream = entity.getContent();
            if (instream != null) {
                long contentLength = entity.getContentLength();
                if (contentLength &amp;gt; Integer.MAX_VALUE) {
                    throw new IllegalArgumentException(&quot;HTTP entity too large to be buffered in memory&quot;);
                }
                int buffersize = (contentLength &amp;lt;= 0) ? BUFFER_SIZE : (int) contentLength;
                try {
                    ByteArrayBuffer buffer = new ByteArrayBuffer(buffersize);
                    try {
                        byte[] tmp = new byte[BUFFER_SIZE];
                        long count = 0;
                        int l;
                        // do not send messages if request has been cancelled
                        while ((l = instream.read(tmp)) != -1 &amp;amp;&amp;amp; !Thread.currentThread().isInterrupted()) {
                            count += l;
                            buffer.append(tmp, 0, l);
                            sendProgressMessage(count, (contentLength &amp;lt;= 0 ? 1 : contentLength));
                        }
                    } finally {
                        AsyncHttpClient.silentCloseInputStream(instream);
                        AsyncHttpClient.endEntityViaReflection(entity);
                    }
                    responseBody = buffer.toByteArray();
                } catch (OutOfMemoryError e) {
                    System.gc();
                    throw new IOException(&quot;File too large to fit into available memory&quot;);
                }
            }
        }
        return responseBody;
    }

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;分析：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;sendMessage中包含了 所有 的发送消息通用函数， 调用handler的消息发送函数，send Or post 至ResponderHandler 内部， ResponderHandler内部调用AsyncHttpResponseHandler 内部 handleMessage 函数，来处理各种情况。&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
   protected void sendMessage(Message msg) ;
   
   protected void postRunnable(Runnable runnable);
   
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;接收返回HttpEntity 实体内部 content ， 并按照BufferSize来读取数据， append至 ByteArrayBuffer 内部， 同时发布进度状态sendProgressMessage ，回调onProgress 函数。 最后返回 ByteArrayBuffer 的byte[] .&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NOTE： 其中ByteArrayBuffer 属于自增长型的缓冲数据类型， code中 的默认大小为4096也就是 1KB大小 ,  而ByteArrayBuffer 过大也会导致 OutOfMemoryError ， 超过 VM heapsize 的分配内存之后就会报内存溢出异常。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; 		if (contentLength &amp;gt; Integer.MAX_VALUE) {
                    throw new IllegalArgumentException(&quot;HTTP entity too large to be buffered in memory&quot;);
        }
                
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;后边 Int 的数据类型范围：  -2147483648~2147483647 最大值 2147483647 = 2^10 也就是 1G ， 上边code写的， 也就是最大不能超1GB ， 这样的话但是后边的强转之后直接将int设置到ByteArrayBuffer中， 显然作者是将这个问题交给了系统自动去检测了。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	Runtime rt = Runtime.getRuntime();
	long maxMemory = rt.maxMemory();
	Log.v(&quot;onCreate&quot;, &quot;maxMemory:&quot; + Long.toString(maxMemory));

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;个人认为，由于每一种机型的型号不同，系统分配的 HeapSize也不同，  可以根据这个来进行匹配，检测。
 链接见： 
&lt;a href=&quot;http://stackoverflow.com/questions/2630158/detect-application-heap-size-in-android/9428660#9428660&quot;&gt;http://stackoverflow.com/questions/2630158/detect-application-heap-size-in-android/9428660#9428660&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://stackoverflow.com/questions/9818407/out-of-memory-error-in-android-due-to-heap-size-increasing&quot;&gt;http://stackoverflow.com/questions/9818407/out-of-memory-error-in-android-due-to-heap-size-increasing&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;RequestParams  请求参数的组装，实现Serializable 接口。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;RetryHandler.class 重试处理类&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;6.1 成员变量&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;黑白名单列表：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	 HashSet&amp;lt;Class&amp;lt;?&amp;gt;&amp;gt; exceptionWhitelist = new HashSet&amp;lt;Class&amp;lt;?&amp;gt;&amp;gt;();
	 
     HashSet&amp;lt;Class&amp;lt;?&amp;gt;&amp;gt; exceptionBlacklist = new HashSet&amp;lt;Class&amp;lt;?&amp;gt;&amp;gt;();

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;默认黑白名单处理策略：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; static {
 
        // Retry if the server dropped connection on us
        exceptionWhitelist.add(NoHttpResponseException.class);
        
        // retry-this, since it may happens as part of a Wi-Fi to 3G failover
        exceptionWhitelist.add(UnknownHostException.class);
        
        // retry-this, since it may happens as part of a Wi-Fi to 3G failover
        exceptionWhitelist.add(SocketException.class);
        
        // never retry timeouts
        exceptionBlacklist.add(InterruptedIOException.class);
        
        // never retry SSL handshake failures
        exceptionBlacklist.add(SSLException.class);
    }

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;6.2 重要函数&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
public boolean retryRequest(IOException exception, int executionCount, HttpContext context){

 		boolean retry = true;
 		
        Boolean b = (Boolean) context.getAttribute(ExecutionContext.HTTP_REQ_SENT);
        
        boolean sent = (b != null &amp;amp;&amp;amp; b);
        
        if (executionCount &amp;gt; maxRetries) {
        
            // Do not retry if over max retry count
            retry = false;
            
        } else if (isInList(exceptionWhitelist, exception)) {
        
            // immediately retry if error is whitelisted
            retry = true;
            
        } else if (isInList(exceptionBlacklist, exception)) {
        
            // immediately cancel retry if the error is blacklisted
            retry = false;
            
        } else if (!sent) {
        
            // for most other errors, retry only if request hasn&apos;t been fully sent yet
            retry = true;
        }
        if (retry) {
        
            // resend all idempotent requests
            HttpUriRequest currentReq = (HttpUriRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);
            
            if (currentReq == null) {
                return false;
            }
        }
        
        if (retry) {
            SystemClock.sleep(retrySleepTimeMS);
            
        } else {
        
            exception.printStackTrace();
        }
        
        return retry;
    }

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;分析：
整体异常处理策略就是，根据参数IOException 的类型，在对应的黑白名单列表中进行轮询操作，如果发现匹配项目，则进行相应的操作， 例如： 如果异常出现在白名单列表中，也就是在网络访问中常发生的，不可避免的，由用户网络环境造成的异常， 则返回可以重试标识。反之，则结束重试。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.3 其他函数&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;添加黑白名单的方法，轮询方法等。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
addClassToWhitelist(Class&amp;lt;?&amp;gt; cls);

addClassToBlacklist(Class&amp;lt;?&amp;gt; cls);

boolean isInList(HashSet&amp;lt;Class&amp;lt;?&amp;gt;&amp;gt; list, Throwable error);

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;RequestHandle 请求句柄类，例如 请求任务取消，是否完成，垃圾回收操作，  也挺重要的。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;7.1成员变量&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这个类貌似只有这么一个成员变量 就是 AsyncHttpRequest 的弱引用， 看来作者对于内存溢出考虑也挺周全。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
private final WeakReference&amp;lt;AsyncHttpRequest&amp;gt; request; 

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;7.2重要函数&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
 public boolean cancel(final boolean mayInterruptIfRunning) {
        final AsyncHttpRequest _request = request.get();
        if (_request != null) {
            if (Looper.myLooper() == Looper.getMainLooper()) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        _request.cancel(mayInterruptIfRunning);
                    }
                }).start();
            } else {
                _request.cancel(mayInterruptIfRunning);
            }
        }
        return false;
    }

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;分析：&lt;/p&gt;

&lt;p&gt;取消请求，直接将AsyncHttpRequest 内部的iscancel 置为false,   调用HttpUriRequest的abort 函数， 中断请求，发送任务取消Notification。&lt;/p&gt;

&lt;p&gt;NOTE : 这里我发现 mayInterruptIfRunning参数木有用到，我找了半天没有找到。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7.3 其他函数&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; 	 public boolean isFinished() ; // 判断是否请求结束。 
 
     public boolean isCancelled() ; // 判断任务是否已经取消过了。
     
     public boolean shouldBeGarbageCollected() // 这货好像是这个版本才出现的， 作用是 告诉 gc要准备收回该对象占用内存

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;SyncHttpClient.class  异步网络请求类&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;PersistentCookieStore 本地的cookie的存储类&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;9.1 成员变量&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    private final ConcurrentHashMap&amp;lt;String, Cookie&amp;gt; cookies; /// 运行时维护的一个cookie的Map

    private final SharedPreferences cookiePrefs; /// cookie 保存在了sharedPreference中

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;9.2 成员函数&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    public void addCookie(Cookie cookie) ;/// 添加cookie
    
    public void clear() ; /// 清除cookie
        
	public boolean clearExpired(Date date);  // 清除过期的cookie 
	
	public void deleteCookie(Cookie cookie); // 删除指定名称的cookie 从sharedPreference 

	protected Cookie decodeCookie(String cookieString); // 将cookie以16进制编码的方式通过 SerializableCookie readObject(ObjectInputStream in)函数添加至Cookie中，返回Cookie
	
	protected String encodeCookie(SerializableCookie cookie); // 将Cookie对象序列化至String中

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;DataAsyncHttpResponseHandler 数据的异步下载处理&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;FileAsyncHttpResponseHandler 文件的异步下载处理&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;14.1成员变量&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    protected final File mFile;  /// 临时文档的操作对象。
    protected final boolean append; /// 文档操作对象是否在上一次写的基础上追加数据。 

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;14.2重要函数&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
	@Override
    protected byte[] getResponseData(HttpEntity entity) throws IOException {
        if (entity != null) {
            InputStream instream = entity.getContent();
            long contentLength = entity.getContentLength();
            FileOutputStream buffer = new FileOutputStream(getTargetFile(), this.append);
            if (instream != null) {
                try {
                    byte[] tmp = new byte[BUFFER_SIZE];
                    int l, count = 0;
                    // do not send messages if request has been cancelled
                    while ((l = instream.read(tmp)) != -1 &amp;amp;&amp;amp; !Thread.currentThread().isInterrupted()) {
                        count += l;
                        buffer.write(tmp, 0, l);
                        sendProgressMessage(count, contentLength);
                    }
                } finally {
                    AsyncHttpClient.silentCloseInputStream(instream);
                    buffer.flush();
                    AsyncHttpClient.silentCloseOutputStream(buffer);
                }
            }
        }
        return null;
    }

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;分析：
该方法是处理接收的HttpEntiry 的Content 内容，使用流写至一个临时的file， 同时使用父类AsyncHttpResponseHandler的Handler 发送进度， 最后关闭流。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;14.3 其他函数&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
public boolean deleteTargetFile() ;// 删除目标文件；

protected File getTemporaryFile(Context context);// 获取临时创建的文件；

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以及其他父类 AsyncHttpResponseHandler中含有的状态回调函数。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;BinaryHttpResponseHandler  二进制数据的下载处理&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;TextHttpResponseHandler 文本数据的加载处理&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;16.1 成员变量&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    private String responseCharset = DEFAULT_CHARSET;/// 相应数据编码，默认UTF-8
    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;16.2 成员函数&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
  public static String getResponseString(byte[] stringBytes, String charset) {
        try {
            String toReturn = (stringBytes == null) ? null : new String(stringBytes, charset);
            if (toReturn != null &amp;amp;&amp;amp; toReturn.startsWith(UTF8_BOM)) {
                return toReturn.substring(1);
            }
            return toReturn;
        } catch (UnsupportedEncodingException e) {
            Log.e(LOG_TAG, &quot;Encoding response into string failed&quot;, e);
            return null;
        }
    }
    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;分析：
该方法是处理接收byte[] , 通过使用父类AsyncHttpResponseHandler的Handler 中onSuccess方法回传的byte[] ,添加指定的charset(默认编码UTF-8),返回从第一个字符开始截取的string对象。&lt;/p&gt;

&lt;p&gt;NOTE：中间有个常量 UTF8_BOM，内容是 发现BOM的作用是识别UTF-8编码的作用，具体请参考链接：&lt;a href=&quot;http://afericazebra.blog.163.com/blog/static/30050408201211199298711/&quot;&gt;utf-8与utf-8(无BOM)的区别&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;JsonHttpResponseHandler Json数据的加载解析处理&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;BaseJsonHttpResponseHandler 基础Json数据的解析，继承自TextHttpResponseHandler&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;18.1 成员变量&lt;/strong&gt;
无&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;18.2 重要函数&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;     public final void onSuccess(final int statusCode, final Header[] headers, final String responseString) {
        if (statusCode != HttpStatus.SC_NO_CONTENT) {
            Runnable parser = new Runnable() {
                @Override
                public void run() {
                    try {
                        final JSON_TYPE jsonResponse = parseResponse(responseString, false);
                        postRunnable(new Runnable() {
                            @Override
                            public void run() {
                                onSuccess(statusCode, headers, responseString, jsonResponse);
                            }
                        });
                    } catch (final Throwable t) {
                        Log.d(LOG_TAG, &quot;parseResponse thrown an problem&quot;, t);
                        postRunnable(new Runnable() {
                            @Override
                            public void run() {
                                onFailure(statusCode, headers, t, responseString, null);
                            }
                        });
                    }
                }
            };
            if (!getUseSynchronousMode() &amp;amp;&amp;amp; !getUsePoolThread()) {
                new Thread(parser).start();
            } else {
                // In synchronous mode everything should be run on one thread
                parser.run();
            }
        } else {
            onSuccess(statusCode, headers, null, null);
        }
    }

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;分析：&lt;/p&gt;

&lt;p&gt;使用TextHttpResponseHandler 中onSuccess的Text返回方法， 在返回String内容不为null的情况下， 直接解析responseString, 并调用抽象函数parseResponse ，解析的过程在子线程中运行，这取决于用户的是操作方式，是异步还是同步.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;18.3 其他函数&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
protected abstract JSON_TYPE parseResponse(String rawJsonData, boolean isFailure); // 需要子类来实现具体的解析方式.

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;JsonStreamerEntity Json流的处理，该类适合Json base 64 编码的上传操作， 节省内存，可以适合大文件。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;SaxAsyncHttpResponseHandler xml 的解析处理，继承自AsyncHttpResponseHandler 使用的sax方法解析。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;PreemptiveAuthorizationHttpRequestInterceptor  预验证拦截器类&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;MyRedirectHandler 重定向处理器类， 源码中标识该类引用自stackoverflow &lt;a href=&quot;https://stackoverflow.com/questions/3420767/httpclient-redirecting-to-url-with-spaces-throwing-exception&quot;&gt;https://stackoverflow.com/questions/3420767/httpclient-redirecting-to-url-with-spaces-throwing-exception&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Base64 二进制数据的 Base 64 的编码与解码&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Base64DataException Base 64 编码的操作异常类&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Base64OutputStream Base 64 输出流操作类&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;HttpDelete HttpDelete 操作类，继承自HttpEntityEnclosingRequestBase类&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;HttpGet HttpGet 操作类，继承自HttpEntityEnclosingRequestBase类&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;HttpPatch HttpPatch 操作类，继承自HttpEntityEnclosingRequestBase类&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;JsonValueInterface 接口，可以中App来封装完整的封装JSON的值&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;SerializableCookie 配角， 在PersistentCookieStore类中序列化Cookie的值时使用&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;SimpleMultipartEntity 简单的多部分实体，主要用于发送一个或多个文档&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Utils 工具类&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;5-杂谈总结&quot;&gt;5. 杂谈总结&lt;/h1&gt;

&lt;p&gt;第一次大胆分析大神的源码，整个分析过程一共使用了三天，包括与设计图的制作， 中间还有一些部分不太了解，有很多不对的地方，请大家批评指正 。框架整个设计框架巧妙的使用了基于Apache HttpClient框架的回调。充分使用了Httpclient 中的各种 handler ， interceptor 将整个数据响应过程通过接口使其可扩展化，过程化，结构化。 而框架整体看起来又浑然一体，的确很牛气。&lt;/p&gt;

</description>
      </item>
    
      <item>
        <title>Android_JNI_ABI兼容问题</title>
        <link>http://www.samuelnotes.cn/2015/08/01/android-jni-application-abi-config-research.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/08/01/android-jni-application-abi-config-research.html</guid>
        <pubDate>Sat, 01 Aug 2015 00:00:00 +0000</pubDate>
        <description>&lt;blockquote&gt;
  &lt;p&gt;出了错，改正，记录下来，保证下次不再犯。 但是事情发生了，发生的原因，如何适配兼容这些问题还是要搞清楚的。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;先粘一个官方链接：
&lt;a href=&quot;http://developer.android.com/ndk/guides/application_mk.html&quot;&gt;http://developer.android.com/ndk/guides/application_mk.html&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;科普&quot;&gt;科普&lt;/h3&gt;

&lt;p&gt;ABI=Application Binary Interface&lt;/p&gt;

&lt;p&gt;在我们android APK的根目录有一个 libs文件夹，此文件夹下基本包含了armeabi 和armeabi-v7a， 甚至还有两个 mips ， x86文件夹，  我们的c代码编译成的本地库（各种.so）就会放在这几个文件夹其中的一个。
那armeabi-v7a 与 armeabi ，x86 ， mips  有什么区别，都是什么意思呢？&lt;/p&gt;

&lt;p&gt;armeabi和armeabi-v7a是表示cpu的类型，我们知道一般的手机或平板都是用arm的cpu（mips的市场份额少，基本没有地位了），不同的cpu的特性不一样，&lt;/p&gt;

&lt;p&gt;armeabi 就是针对普通的或旧的arm v5 cpu，&lt;/p&gt;

&lt;p&gt;armeabi-v7a是针对有浮点运算或高级扩展功能的arm v7 cpu。&lt;/p&gt;

&lt;p&gt;在android.mk里可配置以下宏：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;TARGET_CPU_API := armeabi
APP_ABI := armeabi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在application.mk里可配置以下宏：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;APP_ABI := armeabi armeabi-v7a mips x86
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者使用all 代替。&lt;/p&gt;

&lt;p&gt;看一下官方文档 Application.md 中 table 1 中参数， 左侧是指令集，右侧是需要配置的value：&lt;/p&gt;

&lt;p&gt;armeabi 、armeabi-v7a 和x86是编译 NDK 库时，可以使用三种支持的应用二进制接口(ABI)：&lt;/p&gt;

&lt;p&gt;armeabi&lt;/p&gt;

&lt;p&gt;默认选项，
支持基于 ARM* v5TE 的设备
支持软浮点运算（不支持硬件辅助的浮点计算）
支持所有 ARM* 设备&lt;/p&gt;

&lt;p&gt;armeabi-v7a 
支持基于 ARM* v7 的设备&lt;br /&gt;
支持硬件 FPU 指令
支持硬件浮点运算&lt;/p&gt;

&lt;p&gt;x86 支持基于硬件的浮点运算的 IA-32 指令集&lt;/p&gt;

&lt;p&gt;mips  支持二进制接口&lt;/p&gt;

&lt;h3 id=&quot;如何配置&quot;&gt;如何配置&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;既然armeabi通用，为什么要使用其它指令集呢？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;虽然armeabi通用性强，但速度慢，而v7a能充分发挥v7a CPU的能力。
armeabi就是针对普通的或旧的armcpu，
armeabi-v7a是针对有浮点运算或高级扩展功能的arm cpu。x86、mips同理。&lt;/p&gt;

&lt;p&gt;armeabi-v7a确实是可以兼容armeabi的，而v7a的CPU支持硬件浮点运算，目前绝大对数设备已经是v7a了，所以为了性能上的更优，就不要为了兼容放到armeabi。
现在市面上很多追求高性能的手机，在应用程序编译时，会直接到libs下找armeabi-v7a 目录下so库，如果找不到就不会去找兼容包，armeabi 。
 为此我还曾出了个错误，
见ISSUE：&lt;a href=&quot;https://github.com/samuelhehe/androiddev_issues/issues/7&quot;&gt;java.lang.unsatisfiedlinkerror native method not found JNI armeabi so 包引用问题&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;实际项目运用中总结&quot;&gt;实际项目运用中总结:&lt;/h3&gt;
&lt;p&gt;如果项目只包含了 armeabi，那么在所有Android设备都可以运行；
 如果项目只包含了 armeabi-v7a，除armeabi架构的设备外都可以运行；
 如果项目只包含了 x86，那么armeabi架构和armeabi-v7a的Android设备是无法运行的； 
 如果同时包含了 armeabi， armeabi-v7a和x86，所有设备都可以运行，程序在运行的时候去加载不同平台对应的so，这是较为完美的一种解决方案，同时也会导致包变大。&lt;/p&gt;

</description>
      </item>
    
      <item>
        <title>Android-jni 引用so包种类探究</title>
        <link>http://www.samuelnotes.cn/2015/07/31/android-jni-application-cpu-coverage-research.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/07/31/android-jni-application-cpu-coverage-research.html</guid>
        <pubDate>Fri, 31 Jul 2015 00:00:00 +0000</pubDate>
        <description>&lt;blockquote&gt;
  &lt;p&gt;出了错，改正，记录下来，保证下次不再犯。 但是事情发生了，发生的原因，如何适配这些问题还是要搞清楚的。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;查了一大圈，找到了一个可靠的文档，还有文档中的一份重要的截图。
&lt;a href=&quot;http://developer.android.com/ndk/guides/application_mk.html&quot;&gt;http://developer.android.com/ndk/guides/application_mk.html&lt;/a&gt;
&lt;img src=&quot;https://cloud.githubusercontent.com/assets/5669999/9082112/55c8399c-3b94-11e5-9caf-231b9c1df030.png&quot; alt=&quot;spic&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图中圈中的部分就是目前所适用的cpu架构，下部，官方文档所推荐的四种型号。
至于64位的cpu架构，我觉得暂时没有必要考虑， 目前世界各大手机厂商没有达成一致，没有推广开。
即使有64位CPU的手机，也会兼容x86 CPU架构 的abi 配置 。
还是慢慢的等待他们达成一致再说吧。&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;总结：个人觉得如果so文件不大的话，或者apk打包大小没有限制的话，那就把armeabi armeabi-v7a ，mips ， x86 都给打包了，这是最为完美的解决方案。&lt;/p&gt;
&lt;/blockquote&gt;
</description>
      </item>
    
      <item>
        <title>2015阅读书单</title>
        <link>http://www.samuelnotes.cn/2015/07/16/book-list-of-2015.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/07/16/book-list-of-2015.html</guid>
        <pubDate>Thu, 16 Jul 2015 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;android开发艺术探索&quot;&gt;Android开发艺术探索&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;status: 部分&lt;/li&gt;
  &lt;li&gt;author: 任玉刚&lt;/li&gt;
  &lt;li&gt;publisher: 电子工业出版社&lt;/li&gt;
  &lt;li&gt;language: 中文&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;android群英传&quot;&gt;Android群英传&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;status: 已读&lt;/li&gt;
  &lt;li&gt;author: 徐宜生&lt;/li&gt;
  &lt;li&gt;publisher: 电子工业出版社&lt;/li&gt;
  &lt;li&gt;language: 中文&lt;/li&gt;
&lt;/ul&gt;

</description>
      </item>
    
      <item>
        <title>本科自考毕业</title>
        <link>http://www.samuelnotes.cn/2015/07/15/got-my-college-graduation-certificate.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/07/15/got-my-college-graduation-certificate.html</guid>
        <pubDate>Wed, 15 Jul 2015 00:00:00 +0000</pubDate>
        <description>&lt;blockquote&gt;
  &lt;p&gt;今天收到学校寄来的毕业证，激动的打开信封， 看着自己努力考过的20门课程+1项毕业设计 ，加起来一共有1000多分，哈哈哈，这也算是给我自己努力结果的肯定，我会一如既往地向前走。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;报名&quot;&gt;报名&lt;/h3&gt;

&lt;p&gt;大学第三年下半期，心中无比的纠结，当时已经拿到富士康GDSBG研发部录用通知书， 但是年轻人心中不服输的劲头，让我选择升本。&lt;/p&gt;

&lt;p&gt;问了很多人，有些人说专升本吧，好好地复习，考个本科，再上两年。  也有人说，自考吧，报个名，努力学习，一天学点，一年考四科，也是两年拿证。&lt;/p&gt;

&lt;p&gt;最终决定了，边工作，边自考的办法。 原因就是， 直接花两年时间再去学校啃课本，太浪费青春了。 半工半读经验学历双丰收也是不错的选择。&lt;/p&gt;

&lt;h3 id=&quot;考试&quot;&gt;考试&lt;/h3&gt;

&lt;p&gt;2013年 下半年到2014年下半年最后一门实践课程再到 2015年上半年的毕业答辩论文。 似乎是顺理成章的，当然中间也有不少困难，毕竟是大专学习本科课程的理论知识。还好咱专业基础扎实。&lt;/p&gt;

&lt;h3 id=&quot;毕业&quot;&gt;毕业&lt;/h3&gt;

&lt;p&gt;两年时间一晃即过，功夫不负有心人，我终于毕业了。 实现了心中多年的愿望，填补了因为没有考上本科心中的空缺。阳光下我被拉的斜长的影子，跟随着自己一动一动，原来真的是心有多大，舞台就有多大。&lt;/p&gt;

&lt;h3 id=&quot;总结&quot;&gt;总结&lt;/h3&gt;

&lt;p&gt;回想起小时候的课本，小马过河的故事，生活就是这样，不亲自尝试，奋斗一下，只会站在干岸上分析理论，这样就是理论脱离了实际，不现实，也难有所作为。&lt;/p&gt;

&lt;p&gt;本科证拿到手，心中的事情也算是了了一件，轻松了很多。&lt;/p&gt;

&lt;p&gt;忽然一声霹雳从彤云密布的天空中一闪而下，一道阳光照进了现实，清风徐来，云开雾散，  未来，我来了 ！&lt;/p&gt;
</description>
      </item>
    
      <item>
        <title>Android常用开发资源</title>
        <link>http://www.samuelnotes.cn/2015/05/13/general-android-dev-resources.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/05/13/general-android-dev-resources.html</guid>
        <pubDate>Wed, 13 May 2015 00:00:00 +0000</pubDate>
        <description>&lt;blockquote&gt;
  &lt;p&gt;不知道别的程序猿怎样，我自己的感觉是，每当开发一种新的功能，如果有现成的，改一改能用就行，我就不会去写，要不就是改过之后代码再添加一些优化内容。除非网上没有了，不过现在做的内容80%以上都是网上大堆的。  而这一切，都归功于伟大的开源网络贡献者。 于是，操刀整理，加入开源大军。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;官网&quot;&gt;官网&lt;/h3&gt;
&lt;p&gt;-&lt;a href=&quot;http://developer.android.com/index.html&quot;&gt;Android开发官网&lt;/a&gt;
Android开发者官网，不多说，开发标准，文档，都是从这里来的，多看看文档，例子挺好。&lt;/p&gt;

&lt;p&gt;-&lt;a href=&quot;http://developer.android.com/design/index.html&quot;&gt;官方设计&lt;/a&gt;
-&lt;a href=&quot;http://www.google.com/design/spec/material-design/introduction.html&quot;&gt;Material design风格指导&lt;/a&gt;
俗话说不符合官方设计的App不是好App，两个设计链接，希望以后自己多往这方面思考一下。&lt;/p&gt;

&lt;p&gt;-&lt;a href=&quot;http://android-developers.blogspot.com/&quot;&gt;Android开发官网Blog&lt;/a&gt;
Android官网博客，关注一些最新的官方动态还是不错的。&lt;/p&gt;

&lt;h3 id=&quot;高质量文章&quot;&gt;高质量文章&lt;/h3&gt;
&lt;p&gt;-&lt;a href=&quot;http://www.ibm.com/developerworks/cn/opensource/theme/android/&quot;&gt;http://www.ibm.com/developerworks/cn/opensource/theme/android/&lt;/a&gt;
Android IBM 发布的高质量文章 深入学习一些Android方面的技术文章还是不错的。&lt;/p&gt;

&lt;p&gt;-&lt;a href=&quot;http://www.android-studio.org/&quot;&gt;http://www.android-studio.org/&lt;/a&gt;Android-studio中文社区，可以下载一些常用资源，如果不想翻墙的话。&lt;/p&gt;

&lt;p&gt;-&lt;a href=&quot;http://p.codekk.com/&quot;&gt;codekk&lt;/a&gt;国内的一批开源小伙伴们搞的，里边有不少开源解析，技术分享， 还有工作推荐&lt;/p&gt;

&lt;h3 id=&quot;开源&quot;&gt;开源&lt;/h3&gt;
&lt;p&gt;-&lt;a href=&quot;http://android-arsenal.com/&quot;&gt;国外Android开源汇总&lt;/a&gt;
国外整理的Android开源库汇总,没事按照分类看看，star点项目for 工作，挺好。&lt;/p&gt;

&lt;p&gt;-&lt;a href=&quot;https://github.com/Trinea/android-open-project&quot;&gt;Android开源汇总&lt;/a&gt;
这里有个叫做Trinea的大神整理的开源项目，挺全乎的。&lt;/p&gt;

&lt;h3 id=&quot;性能&quot;&gt;性能&lt;/h3&gt;

&lt;p&gt;-&lt;a href=&quot;http://www.trinea.cn/android/android-performance-demo/&quot;&gt;性能优化几种方案&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;如果在一个稍微大一点的App中，性能优化就显得非常重要。以上几个链接是性能优化的。我记得有很多的，找到随后添加。希望自己时刻考虑这方面内容。&lt;/p&gt;

&lt;h3 id=&quot;设计&quot;&gt;设计&lt;/h3&gt;

&lt;p&gt;-&lt;a href=&quot;https://www.materialpalette.com/&quot;&gt;material调色板&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;先放一个最新找到的，根据几种不同颜色，自动生成颜色适配与合适界面的material 风格。&lt;/p&gt;

&lt;h3 id=&quot;常用接口&quot;&gt;常用接口&lt;/h3&gt;
&lt;p&gt;-&lt;a href=&quot;http://apistore.baidu.com/&quot;&gt;百度Api商店&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;开发中碰到过很多接口，不过目前很多都已经在百度接口商店中收录了，直接根据链接点进去就OK，或者搜一下。&lt;/p&gt;

</description>
      </item>
    
      <item>
        <title>Android port ADB server didn't ACK</title>
        <link>http://www.samuelnotes.cn/2015/05/06/android-adb-server-no-ack.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/05/06/android-adb-server-no-ack.html</guid>
        <pubDate>Wed, 06 May 2015 00:00:00 +0000</pubDate>
        <description>&lt;blockquote&gt;
  &lt;p&gt;在AndroidStudio 中的run 时碰到的问题 ，  说让检查一下 ADB 没有响应 ， 让手动启动adb server   问题已解决，此刻记录一下，备忘&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;android-adb-server--didnt-ack--端口被占用快速解决&quot;&gt;Android ADB server  didn’t ACK  端口被占用快速解决&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;问题描述：在AndroidStudio 中的run 时碰到的问题 ，  说让检查一下 ADB 没有响应 ， 让手动启动adb server   问题已解决，此刻记录一下，备忘&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;找到 出现错误&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[2014-01-08 14:00:07 - adb] ADB server didn&apos;t ACK
[2014-01-08 14:00:07 - adb] * failed to start daemon *
[2014-01-08 14:07:24 - adb] ADB server didn&apos;t ACK
[2014-01-08 14:07:24 - adb] * failed to start daemon *
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上问题，总体来说， ADB Server没有响应 无法启动 daemon  应该是守护进程 。&lt;/p&gt;

&lt;p&gt;网上搜索之后找到的办法 ， http://www.cnblogs.com/fengzhblog/p/3511434.html&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;解决方法：先找到adb服务是否绑定端口出现问题，如果真的是绑定端口出现问题，找出该端口被哪个进程占据，最后杀掉这个进程。&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;首先通过CMD启动adb服务。这个时候会提示启动失败。&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C://user/superuser&amp;gt;adbstart-server
adbserver is out of date. killing...ADBserver didn&apos;t ACK* failed to start daemon *

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;服务启动失败的原因有很多，但一般是端口绑定失败。我们来查看一下端口绑定信息。如图所示，真的是端口绑定出了问题。&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; C://user/superuser&amp;gt;adb nodaemon servercannot bind &apos;tcp:5037&apos;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;我们来看看哪个服务占用了这个端口。这里面有2个进程占用了这个端口。
C://user/superuser&amp;gt;netstat -ano | findstr “5037”
  TCP    127.0.0.1:5037         0.0.0.0:0              LISTENING    6196
在控制台下杀死在5037端口监听的进程 ，进程ID 为 6196 的进程&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C://user/superuser&amp;gt;taskkill /pid 6196
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;错误: 无法终止 PID 为 6196 的进程。原因: 只能强行终止这个进程(带 /F 选项)。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C://user/superuser&amp;gt;taskkill /pid 6196/f
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;成功: 已终止 PID 为 6196的进程。
杀死造成问题的进程，再执行前面的命令 非暴力不合作啊 ，加个f 弄死它， 再次查询占用该端口的进程信息， 发现没有，  重新启动。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C://user/superuser&amp;gt;netstat -ano | findstr &quot;5037&quot;
C://user/superuser&amp;gt;adb nodaemon server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;结果显示此问题已解决， run  OK ！&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;总结： 时刻注意细节， 用360 通过pid 查到，该进程就是个搜狐影音的一个adb 进程， 可能就是通过这个进程与手机进行连接的，暂时被这家伙占用了。 事后果断把搜狐影音卸载，哈哈哈~~  没事学习学习 cmd 挺好，这玩意儿不用不知道，用时才发现重要，还有taskill 命令 ， 哇咔咔&lt;/li&gt;
&lt;/ul&gt;

</description>
      </item>
    
      <item>
        <title>曾经的马老师</title>
        <link>http://www.samuelnotes.cn/2015/04/16/sn-msb-myteacher-forever.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/04/16/sn-msb-myteacher-forever.html</guid>
        <pubDate>Thu, 16 Apr 2015 00:00:00 +0000</pubDate>
        <description>&lt;blockquote&gt;
  &lt;p&gt;今天网上找到了一段文字，觉得是马老师写的， 就粘过来， 作为自己的训导吧。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;展望未来，总结过去10年的程序员生涯，给程序员小弟弟小妹妹们的一些总结性忠告&lt;/p&gt;

&lt;p&gt;走过的路，回忆起来是那么曲折，把自己的一些心得体会分享给程序员兄弟姐妹们，虽然时代在变化，但是很可能你也会走我已经做过的10年的路程，有些心得体会你可以借鉴一下，觉得说得有道理的你就接纳，觉得说得没道理的，你就抛弃，以下是我发自内心的，给大家的忠告，特别是针对那些小弟弟妹妹们。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;自己的户口档案、养老保险、医疗保险、住房公积金一定要保管好。
由于程序员行业每年跳槽一次，我不隐瞒大家，我至少换过5个以上的单位，这期间跳来跳去，甚至是城市都换过3个。还好户口没丢掉，其他都已经是乱了，好几个城市里，都有交过三金，甚至是一个程序的2个区里交的都有，那些东西，10年后，会变得很重要。你买房子若有公积金，可以取出来，贷款利率也会比较低一些，有孩子了，还需要上学，生病了还需要医疗保险。
特别是买房子时，你要商业贷款与公积金贷款的利率差别还是很大，有可能会有10万的差距。你平时都注意这些，会给你带来的损失会最小，例如每个月缴纳300元的公积金，公司也缴纳300元，你一个月能存下来600元，一年就是7200元，10年就是72000元。我以前都忽视了这些，到我需要买房子时，公积金里可能只有几千元，10年很快就过去了，结果我没能存下来多少公积金，医疗保险，养老金等更别提了，都已经稀里糊涂了，这些损失10年累加起来，是很庞大的数字，大家要注意，跳槽换工作时也要保护好自身的利益，现在房价很贵，你可能是跟我一样，大山里出来打拼的娃子，家里也没有丰厚的积蓄，只有靠自己拼搏，买房子是人生的一件大事，等你到了10年，才想到这个事情，已经晚了，特别是孩子要上学，上幼儿园等，需要户口啥的都要齐全。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;不要轻易换笔记本电脑，不要跟潮流，不要买过多的电子产品，不要过于频繁的更换手机。
这方面我的经验教训也是惨痛的。我大概前后购买过5-6个笔记本，以前的都是1万多元一台，最近买的是一台是1万多给女朋友的，自己买了一台是7500元左右，手机大概换过接近10个了，这些钱加起来也足够有10万以上了，你可能一不小心就购买了这些电子产品，但是时间长了，你一回过头来想想，你为什么赚得也不少，但是为什么还是那么穷，是因为你购买这些电子产品花费了过多的金钱了，平时笔记本啥的贵重物品要保护好，我一个同事不小心丢了2台笔记本电脑，接近2万的损失啊，你净赚2万，不是那么容易的，这个窟窿不是开玩笑的，我曾经也被人偷了一个崭新的笔记本，损失1.5万左右，更糟糕的是最新的代码也丢被偷了。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;这年代外语、学历、职称、驾驶证还是蛮重要的。
想找高薪，外资企业是正确的选择，在同样的打工里，外资企业的收入普遍是高的，我就想不明白，我们的赚钱能力怎么就比不过人家了，社会不断发展，将来可能去外国就像串门一样了，也说不定的，外语好将来的就业机会也会更多更广一些。
学历并不代表啥，但是学历也是敲门砖，例如有300个应聘者，那至少重点本科以下的，统统不看了，因为实在是来不及看了，你再厉害也被挡在机会的门外了，同样有时候你想改行什么的，职称也很重要，最起码评个中级职称，说不定还有机会能进入大学或者政府部门还是有可能性。
若有充裕的时间，应该把驾驶证考了，因为你越到后面越忙与工作家庭，没机会学车了也说不定的，平时也别光顾拼命工作，工作10年后你才发现，原来身边的人都至少硕士学历了，你被社会自动淘汰了，我现在就有这个感觉，虽然我带过很多硕士，他们的就业机会比我还好，经常能进入名牌企业，我也一直进不去。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;不要谈过多的女朋友，谈女朋友要看准，下手要稳准狠。
我谈过2个女朋友，平均每个女朋友身上的开支前后大概会有10万左右，还好我不用谈第3个女朋友了，若投资失误，那也是很残忍的，谈女朋友也会消耗很多时间精力、还会消耗很多金钱，实话的讲的确是这样的，人家女孩子也值钱啊，凭什么就那么轻易的跟你啊，我跟第一个朋友分手时，我的生活至少是倒退了3-4年，一切从零开始，一切从头开始，我劝大家谈女朋友是人生最大的一笔买卖，投资失误会有惨痛的后果，不仅仅是金钱上的损失，更会有精神、心灵上的沉重打击，大家要学会珍惜女朋友，要学会哄好女朋友，让老婆开心每一天，虽然鱼儿上钩了，不用再下鱼饵了，偶尔也别忘记放点米，这个鱼要是脱钩了，那不是开玩笑的。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;工作不要更换得太过于频繁，选好了行业方向最好别更换太频繁。
换工作，换行业方向，就像熊掰苞米一样的道理，有时候是丢了芝麻捡西瓜，有时候是丢了西瓜捡芝麻，这个道理我就不多讲了，大家都应该能明白的。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;要对身边的人好，要得到老板的信任、同事的认可及支持、珍惜良好的工作环境。
有个朋友的QQ名字很有意思，“只爱陌生人”，陌生人是很有意思，但是最关键时刻，还是需要靠非陌生人，你每天跟同事一起生活，要维系好身边的人。你的成功与失败，往往是你身边的30-40个人决定的。你就是世界首富，他身边也是那么不超过100个人的在左右着他的生活，当你工作10年了，没一个老板信任你，没几个要好的同事朋友，那你惨了，你在这个世界上已经是很孤单了，你的收入，其实大多是来自这些身边的朋友给你介绍的生意，不大会网上掉几个馅饼的。
现在你身边的人有可能在不久的将来，给你提供很多好机会。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;钱很重要，但是生活质量比钱还重要，工作是很重要，但是家人比工作还重要。
钱不是万能的，但是没钱是万万不能的。钱赚了，身体夸了，全送给医院了，钱赚了，身心疲惫了，人活着为了啥？不就为了开开心心生活嘛？工作重要，但是失去了家人的爱，失去了女朋友，失去了老婆孩子，那这个工作有啥用了？工作很容易就换了，家人是换不了的，老婆不是想换就换的，孩子不是想换就换的，连自己的家庭都不负责的人，怎么可能对公司负责呢？我一直是这个观念，来面试时觉得工作更重要的，我们一般不录取的，那太假了，或者太不懂事了。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;工作累了，也别太贪玩，有时候还是需要多想想如何才能赚钱。
时间一晃就过去了，工作累了是可以适当放松，但是别太贪玩，10年很容易就过去了，10年后你要买房子，要娶老婆，要买车子，要生娃娃，身体也会变得脆弱一些，需要良好的生活习惯，也经不起通宵了，通宵一次，你要低迷好几天才能缓过劲儿来，跟20刚出头完全不一样了，用钱的地方多了去了，父母也会变得更老一些，可能也需要你的照顾，整个家子都指望你赚钱，别到了这个时候，你才意识到赚钱是那么的重要，更何况现在城市的房价，动不动就是100万，加上按揭的利息，你很可能需要支付150万。还可能需要装修，买车子。可能你身上的压力是200万。别觉得谈钱就俗，你要学会赚钱，要有个需要赚钱的良好意识，当然你出身富裕家庭，就不用考虑这些因素了。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;每天一点点进步，每月一点点积累，要敬业要爱业，我们给别人提供的也是服务。
总有一天，你也会有累的时候，你也会有老的时候，这时候，你要靠啥呢？就要靠你平时的积累，你10年的积累，可以打倒很多竞争对手，他们再厉害，再怎么样，也很难抵得过你10年的积累，特别是后面5-10年的积累，成果会很明显，前面的1-5年，算是做软件的入门吧，除非你有高人指点，那可能2-3年就可以修成正果，软件在将来还是会值钱的，以为生活会越来越智能化，越来越数字化，软件的需求还是很有前途，最起码未来的10-20年里不用太担心失业问题了。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;对程序员来讲，开发思想、架构、代码就是财富，别老丢弃你的劳动成果，要学会保护你的劳动成果。
我大概7-8年前的代码都在手上，经常改进来改进去，维护来维护去，在一定的程度上，让我生活轻松了不少，因为我不用什么都从头来过，我只要痛苦一次，以后就要反复重复利用，软件的价值在于重复利用，而不是每个东西，都从头开发，那永远也是辛苦的程序员，这个生活质量就别提了，不管自己的代码丑还是拿不出手，要学会精心维护，每天改进一点点，每个月一个小进步，每年一个大进步，多年的积累是宝贵的，这个早晚也会给你带来丰厚的收益。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;当程序员要防止原地踏步，不是工作年限长了，经验就丰富了，能力就强了，年纪越大工作越难找。
我有一个朋友跟我开玩笑，工作5年的人，可能能力差距会很大，为什么呢？因为第一年他们干的事情都是一样的，都写程序了，2个人可能由于价值观不一样，5年后差距会很大，甚至是大到无法追赶的程度，为啥？因为还有机会的因素在里面，有的人干了5年，还是在原地踏步，天天只会写那些添加、删除、修改的代码。那你得注意了，需要不断的提高自己，才是硬道理。例如你会SQLServer，那要试着学习Oracle， 你是做C/S的，那得需要提高到B/S的，你是做单机软件的，那得需要提高到网络软件，你只关注自己的工作的，需要学会管理，关心他人的工作。你是当程序员的，要试着提高当项目经理、部门经理，公司的总监等等，人有野心有目标才会不断进步，最俗的为了多赚钱，提高工作职位工作岗位，工作单位，也是可以理解的。
年纪越大工作越难找，例如3-4千的工作是随便找找，玩一样，但是你30过后，最起码要找月薪上1万的工作，这样的工作是机会也少，一般小公司也给不起，还得找个好公司才可以，好公司又不是天天招聘人，天天缺好的工作岗位，说不好听点儿，小公司的老板才赚多少啊？他来钱也很不容易的，小池塘就不好容得下大鲨鱼了。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;12.当创业的收入比打工还少时，那就别创业，要找比自己能力强的人创业，你不会吃亏。
创业的收入，比打工还少，那就是瞎扯蛋，恶搞。创业的真正意思并不是要你去吃苦没钱赚，那是忽悠无知的人的。当你创业时的收入，比打工还多，那你可以考虑创业，没有工资什么的，股份啥的，都是瞎扯蛋。不要跟自己能力还弱的人一起创业，那损失最大的，很可能就是你，要创业，也要找比自己强的人一起创业，最起码赚不到钱，还能学到不少。不会有过多的损失。别热血一沸腾就创业了，创业了，也别烧自己的钱，家人的钱，那是很不抗烧的，没几下几十万就烧干了。
其实打工，也是创业的开始，每个月都能拿到钱，还可以学到知识，什么公司的股份都是空话，没几个小公司能成功，开起来了也走不了3年就分家了，都忽悠小孩子玩的，除非真的有科技含量或者是客户资源的，否则股份是一文钱不值的，就算创业每个月也按时拿工资才是硬道理。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;未来的生活节奏会更快，生活压力会更大，竞争会更激烈，社会服务体系会更完善。 
在未来，我们享受良好的服务的同时，也会为别人提供更良好的服务，需要在技能上还是服务质量上的要求会更高更严格。平时要注意提高自己，不要被时代淘汰掉，我从小的朋友，一波又一波被社会无情的淘汰了，很小的时候，我出生在大草原与大山的交界处，我小时候的玩伴，还在大山里，我跟着家人杀出来了，我小学、中学、大学、工作上的、这10年，我一直很坚强的拼搏下来，很不容易的在杭州立住脚了，说实话，参加工作后的十年，也是不断拼搏，不断提高的十年。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;真诚地谢谢马老师-&quot;&gt;真诚地谢谢马老师 ！&lt;/h3&gt;
</description>
      </item>
    
      <item>
        <title>Android sqlite 数据库 使用Like语句 参数不匹配造成的错误</title>
        <link>http://www.samuelnotes.cn/2015/03/02/android-sqlite-like-statement.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/03/02/android-sqlite-like-statement.html</guid>
        <pubDate>Mon, 02 Mar 2015 00:00:00 +0000</pubDate>
        <description>&lt;blockquote&gt;
  &lt;p&gt;Android sqlite 数据库 使用Like语句 参数不匹配造成的错误 解决办法&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;android-sqlite-数据库-使用like语句-参数不匹配造成的错误-解决办法&quot;&gt;Android sqlite 数据库 使用Like语句 参数不匹配造成的错误 解决办法&lt;/h1&gt;

&lt;p&gt;这段做项目,自己提议让把应用中使用到的离线数据放置到Sqlite中,最后根据使用类别来获取使用,结果在开发中碰到了一个问题, 折腾了半个小时,记录下来以作备忘.&lt;/p&gt;

&lt;p&gt;问题描述:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;String factoryId= &quot;F00001&quot;;
String lmtid = &quot;Q1001&quot;;
String sql = &quot;select * from TleaveMaintype where FID like ?% and LMTID = ?&quot;;
Cursor cursor = db.rawQuery(sql, new String[]{factoryId, lmtid};
java.lang.IllegalArgumentException: Cannot bind argument at index 1 because the index is out of range.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;提示参数绑定错误 ,定位错误为参数%号附近错误, 就干脆把整个SQL语句放置到SQlite工具中运行,没有问题. 无奈就干脆把%号去除,发现like 语句的效果达不到, 没办法把?号去除,结果还是不识别,最后找到了一个办法,比较凑&lt;/p&gt;

&lt;p&gt;解决办法:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;String factoryId= &quot;F00001&quot;;
String lmtid = &quot;Q1001&quot;;
String sql = &quot;select * from TleaveMaintype where FID like ? and LMTID = ?&quot;;
Cursor cursor = db.rawQuery(sql, new String[]{factoryId, lmtid+&quot;%&quot;};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;总结: 问题总算解决了, 做应用得灵活变通啊,下面是一些类似的做法.&lt;/p&gt;

&lt;p&gt;小菜:&lt;/p&gt;

&lt;p&gt;//1.使用这种query方法%号前不能加’ ;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Cursor c_test = db.query(tab_name, new String[]{tab_field02}, tab_field02+&quot;  LIKE ? &quot;,
new String[] { &quot;%&quot; + str[0] + &quot;%&quot; }, null, null, null);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;//2.使用这种query方法%号前必须加’单引号  ;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//  Cursor  c_test=db.query(tab_name, new String[]{tab_field02},tab_field02+&quot;  like &apos;%&quot; + str[0] + &quot;%&apos;&quot;, null, null, null, null);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.使用这种方式必须在%号前加’ 单引号 ;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;String current_sql_sel = &quot;SELECT  * FROM &quot;+tab_name +&quot; where &quot;+tab_field02+&quot; like &apos;%&quot;+str[0]+&quot;%&apos;&quot;;
//Cursor c_test = db.rawQuery(current_sql_sel, null);
Log.e(&quot;tag&quot;, &quot;查询完成...&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;sql模糊查询语句&quot;&gt;SQL模糊查询语句&lt;/h4&gt;
&lt;p&gt;　　SQL模糊查询，使用like比较字，加上SQL里的通配符，请参考以下：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1、LIKE’Q001%’ 将搜索以字母 Q001 开头的所有字符串（如 Q0014466）。&lt;/li&gt;
  &lt;li&gt;2、LIKE’%inger’ 将搜索以字母 inger 结尾的所有字符串（如 Ringer、Stringer）。&lt;/li&gt;
  &lt;li&gt;3、LIKE’%en%’ 将搜索在任何位置包含字母 en 的所有字符串（如 Bennet、Green、McBadden）。&lt;/li&gt;
  &lt;li&gt;4、LIKE’_heryl’ 将搜索以字母 heryl 结尾的所有六个字母的名称（如 Cheryl、Sheryl）。&lt;/li&gt;
  &lt;li&gt;5、LIKE’[CK]ars[eo]n’ 将搜索下列字符串：Carsen、Karsen、Carson 和 Karson（如 Carson）。&lt;/li&gt;
  &lt;li&gt;6、LIKE’[M-Z]inger’ 将搜索以字符串 inger 结尾、以从 M 到 Z 的任何单个字母开头的所有名称（如 Ringer）。&lt;/li&gt;
  &lt;li&gt;7、LIKE’M[^c]%’ 将搜索以字母 M 开头，并且第二个字母不是 c 的所有名称（如MacFeather）。
　　下面这句查询字符串，根据变量 zipcode_key 在邮政编码表 zipcode 中查询对应的数据，这句是判断变量 zipcode_key 为非数字时的查询语句，用 % 来匹配任意长度的字符串，从表中地址、市、省三列中查询包含关键字的所有数据项，并按省、市、地址排序。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这个例子比较简单，只要你理解了方法就可以写出更复杂的查询语句。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;　　sql = &quot;select * from zipcode where (address like&apos;%&quot; &amp;amp; zipcode_key &amp;amp; &quot;%&apos;) or (city like&apos;%&quot; &amp;amp; zipcode_key &amp;amp; &quot;%&apos;) or (province like&apos;%&quot; &amp;amp; zipcode_key &amp;amp; &quot;%&apos;) order by province,city,address
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
      </item>
    
      <item>
        <title>写在人生边上——要青菜还是要大蒜</title>
        <link>http://www.samuelnotes.cn/2015/03/02/my-choice2groupup.html</link>
        <guid isPermaLink="true">http://www.samuelnotes.cn/2015/03/02/my-choice2groupup.html</guid>
        <pubDate>Mon, 02 Mar 2015 00:00:00 +0000</pubDate>
        <description>&lt;h1 id=&quot;写在人生边上要青菜还是要大蒜&quot;&gt;写在人生边上——要青菜还是要大蒜&lt;/h1&gt;

&lt;p&gt;最近思考了很多问题，这些问题之前也思考过。理顺了记下来，算是人生的一笔财富吧。
引用俞敏洪老师的一段话：俞敏洪人生感悟在2009年8月18日&lt;/p&gt;

&lt;p&gt;论草与树的人生&lt;/p&gt;

&lt;p&gt;这是俞敏洪老师在《赢在中国》栏目的演讲，在这里贴出推荐阅读。&lt;/p&gt;
&lt;p&gt;
人的生活方式有两种， 第一种方式是像草一样活着； 你尽管活着，每年还在成长， 但你毕竟是一颗草。 你吸收雨露阳光， 但是长不大。 人们可以踩过你， 但是人们不会因为你的痛苦，而产生痛苦； 人们不会因为你的痛苦，而产生痛苦； 人们不会因为你被踩了，而来怜悯你， 因为人们本身就没有见到你。 所以，我们每一个人， 都应该像树一样成长。 即使我们现在什么都不是， 但是只要你有树的种子， 即使你被踩到泥土中间， 你依然能够吸收泥土的养分， 自己成长起来。 当你长成参天大树之后， 遥远的地方，人们都能看到你，走近你， 你能给人一片绿色。 活着是美丽的风景， 死了依然是栋梁之才， 活着死了都有用。
&lt;p&gt;
俗话说萝卜白菜各有所爱，自己总有自己喜欢的蔬菜。思考很多，人生很多时候面临的选择就和日常生活中吃菜的爱好与选择一样。

要青菜还是大蒜

&lt;p&gt;
青菜，大蒜祖国大江南北，随处可见，无人不晓。青菜不用说了，平时常见，有了也行，没了也行。 大蒜，做饭烹饪，可做配料，可做腌蒜，有些甚至直接剥了皮生吃。但是大家都知道，吃起来辣的不得了，吃过之后口中一腔蒜臭，半天不能干净，如在社交场合，更让人尴尬。有些人恨吃大蒜，在吃菜的时候，如果碰到大蒜就直接不吃，或挑出来扔掉。

选择性成材

&lt;p&gt;大蒜有自己的特性，有自己独特的味道，爱自己的人爱的心发狂，顿顿离不开，不爱自己的人，闻声色变。而青菜，却是在日常生活中可替代性很大的菜品。意思就是有的话也行，没有的话找个土豆替代一下，一顿饭就过去了。 如何成为在社会上成为一个有自己的特性，让一些企业或工作单位爱自己爱的疯狂，不可替代性，成为了关键。体现在技术层面就是你要具有其他菜都有的共同特性，也要具有自己独特的特性，辣出自己的独特，辣出自己的不可替代性，辣出自己的的风采，让爱你的人爱的心发狂，成就自己的大蒜之路。
&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;&lt;/p&gt;
</description>
      </item>
    
  </channel>
</rss>