<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Ruby China 社区 Android 节点</title>
    <link>https://ruby-china.org/</link>
    <description>Ruby China 社区 Android 节点最新发帖。</description>
    <item>
      <title>用七牛云手机 App 开发怎么做防盗链？</title>
      <description>&lt;p&gt;看了一下相关文档，好像没有涉及这方面的。白名单加了也只能对 web 方面才有效果。不知道手机 App 的话，应该怎么做防盗链呢？
我看有一些文章说是自己修改 header，但是类似加载图片之类的操作，没有修改 header 的方法。&lt;/p&gt;

&lt;p&gt;不知道一般大家都如何处理这个问题呢？&lt;/p&gt;</description>
      <author>QueXuQ</author>
      <pubDate>Wed, 18 Mar 2020 18:17:16 +0800</pubDate>
      <link>https://ruby-china.org/topics/39625</link>
      <guid>https://ruby-china.org/topics/39625</guid>
    </item>
    <item>
      <title>教你如何用 AST 语法树对代码 “动手脚”</title>
      <description>&lt;p&gt;作者 | 刘斌
个推安卓工程师，负责公司移动端项目的架构和开发，主导移动端日志管理平台系统架构和开发工作，熟悉前后端的技术线，参与个推 SDK 主要业务研发工作，善于解决项目中遇到的痛点问题。&lt;/p&gt;

&lt;p&gt;作为程序猿，每天都在写代码，但是有没有想过通过代码对写好的代码”动点手脚”呢？今天就与大家分享——如何通过用 AST 语法树改写 Java 代码。&lt;/p&gt;

&lt;p&gt;先抛一个问题：如何将图一代码改写为图二？&lt;/p&gt;

&lt;p&gt;void someMethod(){
    String rst=callAnotherMethod();
    LogUtil.log(TAG,”这里是一条非常非常长，比唐僧还啰嗦的日志信息描述，但是我短一点还不方便进行错误日志分析，调用 callSomeMethod 返回的结果是:”+rst);
……
}
图一&lt;/p&gt;

&lt;p&gt;void someMethod(){
    String rst=callAnotherMethod();
    LogUtil.log(TAG,”&amp;lt;-(1)-&amp;gt;”+rst);
……
}
图二&lt;/p&gt;

&lt;p&gt;此题需要把代码中和程序逻辑无关的字符串提取出来，替换为 id。比如个推日志输出类，缩短日志描述信息后，输出的日志就随之变短，根据映射表可以恢复真实原始日志。&lt;/p&gt;

&lt;p&gt;通过何种方案改写？&lt;/p&gt;

&lt;p&gt;你可能会想通过万能的“正则表达式”匹配替换，但当代码较为复杂时（如下图所示），使用“正则表达法”则会将问题复杂化，难以确保所有代码的完美覆盖并匹配。若通过 AST 语法树，可以很好地解决此问题。&lt;/p&gt;

&lt;p&gt;import static Log.log;
…
log(“i am also the log”);
String aa=“i am variable string”;
log(“i am the part of log”+ aa +String.format(“current time is %d”,System.currentTimeMillis()));&lt;/p&gt;

&lt;p&gt;什么是 AST 语法树？&lt;/p&gt;

&lt;p&gt;AST（Abstract syntax tree）即为“抽象语法树”，简称语法树，指代码在计算机内存的一种树状数据结构，便于计算机理解和阅读。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2017/cdde4fe2-b699-488f-ab13-e837acca9239.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;一般只有语言的编译器开发人员或者从事语言设计的人员才涉及到语法树的提取和处理，所以很多人会对这个概念比较陌生。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2017/bbd70be1-a22d-40e7-85c7-7664670801a4.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;上图即为语法树，左边树的节点对应右边相同颜色覆盖的代码块。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2017/c9195549-4e96-49ab-8c14-ce6b232009f1.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;众所周知，Java 编译流程（上图）中也有对 AST 语法树的提取处理，那是否可以在此环节操作语法树呢？由于编译链代码栈太深，鲜有对外的接口和文档，使得其可操作性不强。不过，如果采用迂回战术如下图所示，可以对其进行操作。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2017/d6a0069c-cda2-4953-94b8-3aaa33e4e4dc.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;个推 log-rewrite 项目改写日志，就是用 AST 语法树进行的，流程图如下图所示。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2017/4e879139-d57b-4128-abd9-96d49a3cbea6.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;先把所有源码解析为 AST 语法树，遍历每一个编译单元与单元的类声明，在类声明里根据日志方法的签名找到所有的方法调用，然后遍历每个方法调用，将方法调用的第二个参数表达式放入递归方法，对字符串字面值进行改写。&lt;/p&gt;

&lt;p&gt;对应的代码较为简短，使用 github 的 Netflix-Skunkworks/rewrite开源库与kotlin语言，能读懂Java的你也一定能读明白。&lt;/p&gt;

&lt;p&gt;val JavaSources:List //Java source file path list
OracleJdkParser().parse(JavaSources)
 .forEach { unit -&amp;gt;
   unit.refactor(Consumer { tx -&amp;gt;
       unit.classes.forEach { clazz -&amp;gt;
           clazz.findMethodCalls("demo.LogUtillog(String,String)").forEach{ mc -&amp;gt;
               val args = mc.args.args
               val expression = args[1]
               logMapping.refactor(clazz, expression, tx)
            }
       }
        val fix = tx.fix()
        val newFile = ...//dist Source File ...
       newFile.writeText(fix.print())
    })
}
fun refactor(clazz: Tr.ClassDecl, target: Expression, refactor: Refactor, originSb: StringBuilder): Unit {
        when(target) {
           is Tr.Literal -&amp;gt; {
               refactor.changeLiteral(target) { t -&amp;gt;
                        val id = pushMapping(clazz, t) //pushLiteral to mapping and return id
                        originSb.append("$PREFIX$t$POSTFIX")
                        return@changeLiteral rewriteNormal(id)
                    }
               }
           }
           is Tr.Binary -&amp;gt; {
               refactor(clazz, target.left, refactor, originSb)
               refactor(clazz, target.right, refactor, originSb)
            }
       }
}&lt;/p&gt;

&lt;p&gt;如果想将日志恢复原样，可根据前缀、后缀定制正则表达式，逐行匹配替换。如下图所示。&lt;/p&gt;

&lt;p&gt;val normalPattern = Pattern.compile("(&amp;lt;!--\[([^|]+)\|(\d+)_(\d+):(\d+)]--&amp;gt;)")
logFiles.forEach { file -&amp;gt;
file.bufferedReader().use { reader -&amp;gt;
   File(distDir, file.name).bufferedWriter().use { writer -&amp;gt;
        var line: String
        while(true){
           line = reader.readLine()
           if (line == null) break
           val matcher = normalPattern.matcher(line)
           var newLine: String = line + ""
           while (matcher.find()) { //normal recover
               val token = matcher.group(1)
               val projectName = matcher.group(2)
               val appVersion = matcher.group(3).toInt()
               val targetVersion = matcher.group(4).toInt()
               val id = matcher.group(5).toLong()
               val replaceMent = findReplacement(projectName,appVersion, targetVersion, id)
               newLine = newLine.replace(token, replaceMent)
           }
           writer.write(newLine)
           writer.newLine()
       }
     }
 }&lt;/p&gt;

&lt;p&gt;AST 有哪些应用场景？&lt;/p&gt;

&lt;p&gt;1、编译工具从 ant 到 gradle 的切换&lt;/p&gt;

&lt;p&gt;the ant env SDK_VERSION=2.0.0.2
// #expand public static final Stringsdk_conf_version = "%SDK_VERSION%";
publicstaticfinalString sdk_conf_version = "1.0.0.1";&lt;/p&gt;

&lt;p&gt;publicstaticfinalString sdk_conf_version = “2.0.0.2";
//public static final String sdk_conf_version= "1.0.0.1";&lt;/p&gt;

&lt;p&gt;此项目起步于 ant 主流时期，随着技术日渐成熟，gradle 逐渐取代了 ant 的位置，演变成官方的编译打包方式。因为历史原因，若直接将上图类似预编译的代码切换到 gradle 较为棘手，通过 AST 语法树重写，再用 gradle 编译，就可以解决此问题。&lt;/p&gt;

&lt;p&gt;try{
    value = Boolean.parseBoolean(str);
} catch (Throwable e) {
    // #debug
    e.printStackTrace();
}&lt;/p&gt;

&lt;p&gt;try{
    value = Boolean.parseBoolean(str);
} catch (Throwable e) {&lt;/p&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;void m(){
    relaseCall();
    //#mdebug
    String info="some debug infomation";
    LogUtil.log(info);
    //#enddebug
}&lt;/p&gt;

&lt;p&gt;void m(){
    relaseCall();
}&lt;/p&gt;

&lt;p&gt;上图的#debug 和#mdebug 指令，也可以通过 AST 改写之后再进行编译。&lt;/p&gt;

&lt;p&gt;2、自动静态埋点&lt;/p&gt;

&lt;p&gt;void onClick(View v){
    doSomeThing()
}&lt;/p&gt;

&lt;p&gt;void onClick(View v){
    RUtil.recordClick(v); 
    doSomeThing();
}&lt;/p&gt;

&lt;p&gt;代码中需要运营统计、数据分析等，需要通过代码埋点进行用户行为数据收集。传统的做法是手动在代码中添加埋点代码，但此过程较为繁琐，可能会对业务代码造成干扰，倘若通过改写 AST 语法树，在编译打包期添加这种类似的埋点代码，就可减少不必要的繁琐过程，使其更加高效。&lt;/p&gt;

&lt;p&gt;最后附推荐操作 AST 类库链接&amp;amp;完整项目源码地址，希望可以帮助大家打开脑洞，设想更多的应用场景。&lt;/p&gt;

&lt;p&gt;推荐操作 AST 类库链接
&lt;a href="https://github.com/Netflix-Skunkworks/rewrite" rel="nofollow" target="_blank"&gt;https://github.com/Netflix-Skunkworks/rewrite&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/Javaparser/Javaparser" rel="nofollow" target="_blank"&gt;https://github.com/Javaparser/Javaparser&lt;/a&gt;
&lt;a href="https://github.com/antlr/antlr4" rel="nofollow" target="_blank"&gt;https://github.com/antlr/antlr4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;完整项目源码地址如下，欢迎 fork&amp;amp;start
&lt;a href="https://github.com/foxundermoon/log-rewrite" rel="nofollow" target="_blank"&gt;https://github.com/foxundermoon/log-rewrite&lt;/a&gt;&lt;/p&gt;</description>
      <author>HongJack</author>
      <pubDate>Mon, 15 May 2017 10:29:29 +0800</pubDate>
      <link>https://ruby-china.org/topics/32991</link>
      <guid>https://ruby-china.org/topics/32991</guid>
    </item>
    <item>
      <title>APIJson，类似 GraphQL，大家评估一下</title>
      <description>&lt;p&gt;据作者说比 GraphQL 简单很多，功能类似，目前支持 java。&lt;/p&gt;

&lt;p&gt;如果真能做到，后端将大幅度简化了。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://my.oschina.net/u/2437072/blog/805459" rel="nofollow" target="_blank"&gt;https://my.oschina.net/u/2437072/blog/805459&lt;/a&gt;&lt;/p&gt;</description>
      <author>chenge</author>
      <pubDate>Sun, 25 Dec 2016 12:04:12 +0800</pubDate>
      <link>https://ruby-china.org/topics/31997</link>
      <guid>https://ruby-china.org/topics/31997</guid>
    </item>
    <item>
      <title>最可靠的天气数据 API，包括雾霾指数？</title>
      <description>&lt;p&gt;最近的一个 Android 开发需要天气方面的 API，有没有这种 API 服务，或者还有什么其他方式可以得到天气雾霾数据呢？谢谢&lt;/p&gt;</description>
      <author>junzhi</author>
      <pubDate>Sun, 09 Oct 2016 15:51:00 +0800</pubDate>
      <link>https://ruby-china.org/topics/31262</link>
      <guid>https://ruby-china.org/topics/31262</guid>
    </item>
    <item>
      <title>Android App 持续集成性能测试：启动流量 (2)</title>
      <description>&lt;p&gt;在&lt;a href="https://testerhome.com/topics/4783" rel="nofollow" target="_blank" title=""&gt;上一篇文章&lt;/a&gt;中，介绍了一种测试 Android App 启动流量的方法。当时也提到了，通过读取&lt;code&gt;/proc/uid_stat/&amp;lt;UID&amp;gt;/&lt;/code&gt;目录下的&lt;code&gt;tcp_rcv&lt;/code&gt;和&lt;code&gt;tcp_snd&lt;/code&gt;文件，只能得到 App 的流量总值，无法得到更细化的数据。&lt;/p&gt;

&lt;p&gt;例如，UC 浏览器国际版在启动后，会和美国的服务器进行通讯交互，如果我们想测试浏览器在启动后与美国服务器的通讯流量，要怎么实现呢？。&lt;/p&gt;

&lt;p&gt;本文便是针对这类场景的测试需求，讲解如何采用&lt;code&gt;tcpdump&lt;/code&gt;测试得到更细化的流量数据。&lt;/p&gt;
&lt;h2 id="tcpdump"&gt;tcpdump&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;tcpdump&lt;/code&gt;，是一个在 Unix-like 系统中通用的网络抓包工具，当然，这个工具在 Android 系统中也是可以使用的。&lt;/p&gt;

&lt;p&gt;对于工具本身，本文不做过多介绍。为了防止有读者之前完全没有&lt;code&gt;tcpdump&lt;/code&gt;的使用经验，在这里我只简单地进行几点说明：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;大多 Android 系统默认未集成 tcpdump 工具，我们需要事先将专门针对 Android 系统编译好的的 tcpdump 导入到 Android 系统，例如&lt;code&gt;/data/local/tmp/tcpdump&lt;/code&gt;；当然，我们也不用自己编译，在&lt;a href="http://www.androidtcpdump.com" rel="nofollow" target="_blank" title=""&gt;&lt;code&gt;androidtcpdump&lt;/code&gt;&lt;/a&gt;网站就可以下载到编译好的 tcpdump 二进制文件。&lt;/li&gt;
&lt;li&gt;运行&lt;code&gt;tcpdump&lt;/code&gt;工具时需要 root 权限。&lt;/li&gt;
&lt;li&gt;tcpdump 命令支持许多参数，常见的有：

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-i&lt;/code&gt;指定网卡（interface），&lt;code&gt;any&lt;/code&gt;表示不限网卡；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-c&lt;/code&gt;指定接收的 packets 数量，接收完成后自动停止抓包；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-w&lt;/code&gt;指定输出文件，输出文件的格式为 pcap；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-s&lt;/code&gt;(&lt;code&gt;--snapshot-length&lt;/code&gt;) 指定在每个 packet 中最多截取的字节数，设置为 0 时表示截取上限取默认值 262144；&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-v&lt;/code&gt;/&lt;code&gt;-vv&lt;/code&gt;/&lt;code&gt;-vvv&lt;/code&gt;，指定输出的详细程度，针对流量测试，我们不需要非常详尽的输出数据，取&lt;code&gt;-v&lt;/code&gt;即可。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;基于以上对&lt;code&gt;tcpdump&lt;/code&gt;的介绍，我们要测试浏览器在启动后与美国服务器的通讯流量，就只需要先启动浏览器，然后在&lt;code&gt;adb shell&lt;/code&gt;中执行以下命令即可。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;1|shell@hammerhead:/ &lt;span class="nv"&gt;$ &lt;/span&gt;su &lt;span class="nt"&gt;-c&lt;/span&gt; /data/local/tmp/tcpdump &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; any &lt;span class="nt"&gt;-s&lt;/span&gt; 0 &lt;span class="nt"&gt;-c&lt;/span&gt; 2000 &lt;span class="nt"&gt;-w&lt;/span&gt; /sdcard/us.pcap
tcpdump: listening on any, link-type LINUX_SLL &lt;span class="o"&gt;(&lt;/span&gt;Linux cooked&lt;span class="o"&gt;)&lt;/span&gt;, capture size 262144 bytes
2000 packets captured
2024 packets received by filter
0 packets dropped by kernel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这里之所有指定接收 packets 数为 2000，是因为浏览器启动后并不是立即与美国服务器进行通讯。所以在这里设置了一个比较大的值，确保浏览器与美国服务器的异步通讯数据能包含在这 2000packets 之中。当然，这个 2000 只是一个工程实践得到的经验值，具体取多少还是要依赖于具体场景。&lt;/p&gt;

&lt;p&gt;经过一段时间的抓包后，就生产了抓包结果，即&lt;code&gt;/sdcard/us.pcap&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="人工分析pcap文件"&gt;人工分析 pcap 文件&lt;/h2&gt;
&lt;p&gt;拿到 pcap 文件只是第一步，我们必须要对这个文件进行解析才能得到我们想要的结果。&lt;/p&gt;

&lt;p&gt;那么，通过什么方法解析 pcap 文件呢？&lt;/p&gt;

&lt;p&gt;先简单介绍下 pcap。pcap，即&lt;code&gt;packet capture&lt;/code&gt;的缩写，是一种通用的网络抓包数据存储格式。既然是通用，因此它除了可以被&lt;code&gt;tcpdump&lt;/code&gt;解析外，还支持被多种网络工具解析，其中，就包括大家熟知的&lt;code&gt;wireshark&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;至于为什么有了&lt;code&gt;tcpdump&lt;/code&gt;还要用&lt;code&gt;wireshark&lt;/code&gt;来解析，这主要还是因为&lt;code&gt;wireshark&lt;/code&gt;是图形界面，操作和使用上更友好一些。&lt;/p&gt;

&lt;p&gt;在&lt;code&gt;wireshark&lt;/code&gt;中分析 pcap 文件十分简单，只需要直接打开文件，就可以看到抓包过程中捕获的所有网络通讯数据。不过，由于我们抓包获得的数据是系统层面的，除了我们关注的与美国服务器的通讯交互外，还包含了非常多的其它通讯信息。&lt;/p&gt;

&lt;p&gt;好在&lt;code&gt;wireshark&lt;/code&gt;有非常强大的筛选功能。对于本案例，我们可以先确定出美国服务器的 host 或 IP，例如 host 为&lt;code&gt;ucus.ucweb.com&lt;/code&gt;，那么我们就可以在筛选器中通过&lt;code&gt;http.host == "ucus.ucweb.com"&lt;/code&gt;语句，即可筛选出所有本地与美国服务器的通讯交互数据。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://debugtalk.com/assets/images/wireshark_host_filter.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;对于更丰富的筛选功能，大家可以根据实际需求查询&lt;code&gt;wireshark&lt;/code&gt;的帮助文档，在此就不再进行展开。&lt;/p&gt;

&lt;p&gt;从上图的筛选结果中可以看到，美国服务器的地址为&lt;code&gt;168.235.199.134&lt;/code&gt;。那接下来如何查看通讯的流量大小呢？&lt;/p&gt;

&lt;p&gt;首先，找出该次请求的&lt;code&gt;TCP Stream&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://debugtalk.com/assets/images/wireshark_tcp_stream_menu.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;在筛选出的&lt;code&gt;TCP Stream&lt;/code&gt;中，将各条记录的 Length 进行求和，即可得到总的大小。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://debugtalk.com/assets/images/wireshark_tcp_stream_data.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;例如，发送流量的总和，即&lt;code&gt;100.84.126.160&lt;/code&gt;-&amp;gt;&lt;code&gt;168.235.199.134&lt;/code&gt;的总和，加和总值为 3722bytes；接收流量的总和，即&lt;code&gt;168.235.199.134&lt;/code&gt;-&amp;gt;&lt;code&gt;100.84.126.160&lt;/code&gt;的总和，加和总值为 6300bytes。&lt;/p&gt;

&lt;p&gt;当然，这里只是为了讲解计算流量的原理，实际上，我们并不需要去进行这个计算，可以直接读取得到总值。&lt;/p&gt;

&lt;p&gt;【Statistics】-&amp;gt;【Endpoints】&lt;/p&gt;

&lt;p&gt;&lt;img src="http://debugtalk.com/assets/images/wireshark_endpoints_menu.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;在 Endpoints 界面中，选择&lt;code&gt;TCP&lt;/code&gt; tab，勾选“Limit to display filter”，即可看到通讯流量汇总数据。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://debugtalk.com/assets/images/wireshark_tcp_stream_data.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;可以看出，这个的汇总数值与前面计算得到的数值完全相同。&lt;/p&gt;
&lt;h2 id="自动化测试脚本"&gt;自动化测试脚本&lt;/h2&gt;
&lt;p&gt;通过前面的人工分析，我们已经掌握了分析特定流量的一般性方法。然而，要想将此种场景的流量测试加入持续集成自动化测试系统，采用以上方法显然是不行的。&lt;/p&gt;

&lt;p&gt;那么，要怎样才能在代码中完成对 pcap 文件的分析呢？&lt;/p&gt;

&lt;p&gt;好在已经有前辈做了相应的工作，在 GitHub 上就找到了一个开源项目&lt;a href="https://github.com/andrewf/pcap2har" rel="nofollow" target="_blank" title=""&gt;&lt;code&gt;pcap2har&lt;/code&gt;&lt;/a&gt;，可以实现对 pcap 文件的解析。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pcap2har&lt;/code&gt;项目的详细介绍请大家自行查看项目文档。&lt;/p&gt;

&lt;p&gt;针对本文中的测试场景，解析 pcap 文件的代码实现如下。&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python
#coding=utf-8
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dpkt&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pcap2har&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pcap&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pcap2har&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parsePcapFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pcap_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_host&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# parse pcap file
&lt;/span&gt;    &lt;span class="n"&gt;dispatcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pcap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EasyParsePcap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pcap_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;traffic_total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;traffic_receive_total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;traffic_send_total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;url_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="c1"&gt;# stream为tcp数据流，当为长链接时一个tcp流里面可以有多个http请求
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flows&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="c1"&gt;# fwd为请求大小，如果小于200则肯定不是正常的HTTP请求，忽略
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fwd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caplen&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="n"&gt;pointer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;pointer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fwd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;myrequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#解析请求头
&lt;/span&gt;            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;dpkt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# if the message failed
&lt;/span&gt;                &lt;span class="k"&gt;break&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt;

            &lt;span class="n"&gt;pointer&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;myrequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data_consumed&lt;/span&gt;
            &lt;span class="n"&gt;myhead&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myrequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;

            &lt;span class="c1"&gt;# 请求头大小&amp;lt;200时忽略
&lt;/span&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;myrequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data_consumed&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;myhead&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;host&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target_host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;traffic_receive_total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caplen&lt;/span&gt;
                &lt;span class="n"&gt;traffic_send_total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fwd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;caplen&lt;/span&gt;
                &lt;span class="n"&gt;traffic_total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;streamlen&lt;/span&gt;
                &lt;span class="n"&gt;url_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myrequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fullurl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;traffic_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;total&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;traffic_total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tcp_snd&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;traffic_send_total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tcp_rcv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;traffic_receive_total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;url_list&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url_list&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;traffic_data&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;pcap_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;target_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ucus.ucweb.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nf"&gt;parsePcapFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pcap_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_host&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# output: {'url_list': ['http://ucus.ucweb.com/usquery.php'], 'total': 10022, 'tcp_rcv': 6300, 'tcp_snd': 3722}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id="Read More ..."&gt;Read More ...&lt;/h2&gt;
&lt;p&gt;博客地址：&lt;a href="http://debugtalk.com" rel="nofollow" target="_blank"&gt;http://debugtalk.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;微信公众号二维码：
&lt;img src="https://l.ruby-china.com/photo/2016/642460ab13367640b6ccdc96c8561a30.jpg" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>debugtalk</author>
      <pubDate>Thu, 12 May 2016 21:07:00 +0800</pubDate>
      <link>https://ruby-china.org/topics/30003</link>
      <guid>https://ruby-china.org/topics/30003</guid>
    </item>
    <item>
      <title>Android App 持续集成性能测试：启动流量 (1)</title>
      <description>&lt;p&gt;本文对 Android App 的启动流量测试进行介绍。这里的启动流量指的是网络流量，即 App 在启动时发起网络请求和接收网络响应时传输的网络数据量。&lt;/p&gt;

&lt;p&gt;说起流量，也许大家的第一反应就是 tcpdump/wireshark 这类网络抓包工具。的确，Android 系统确实也支持&lt;code&gt;tcpdump&lt;/code&gt;工具，通过&lt;code&gt;tcpdump&lt;/code&gt;，我们可以实现非常精准的流量测试。但&lt;code&gt;tcpdump&lt;/code&gt;也有个问题，就是它捕捉到的流量是系统层面的，我们很难区分捕捉得到的流量数据是否都是当前 apk 产生的。&lt;/p&gt;

&lt;p&gt;其实，对于特定 apk 的整体流量数据，在 Android 系统中都会存储到对应文件中，我们完全可以通过读取对应文件来获得当前 apk 的流量信息。&lt;/p&gt;
&lt;h2 id="get app UID"&gt;get app UID&lt;/h2&gt;
&lt;p&gt;与流量相关的状态数据存储在&lt;code&gt;/proc/uid_stat/&amp;lt;UID&amp;gt;/&lt;/code&gt;目录下，其中，&lt;code&gt;&amp;lt;UID&amp;gt;&lt;/code&gt;表示 apk 对应的 UID。&lt;/p&gt;

&lt;p&gt;关于 UID，简单地进行下说明。在 Linux 系统中，UID 表示的是 User Identifier，主要用于表示是哪位用户运行了该程序。但在 Android 系统中，由于 Android 系统本身就为单用户系统，这时 UID 就被赋予了新的使命，主要用于实现数据共享。具体地，Android 系统为每个应用都分配了一个 UID，不同 apk 的 UID 几乎都是互不相同的，而对于不同 UID 的 apk，不能共享数据资源。之所以用“几乎”，是因为有时候同一厂家会存在多个产品，并且希望能在多个 apk 之间实现数据共享，这个时候，便可通过在 menifest 配置文件中指定相同的 sharedUserId，然后在 Android 系统中安装应用时便会分配相同的 UID。&lt;/p&gt;

&lt;p&gt;获取 app UID 的方式有多种，最简单的方式应该还是从&lt;code&gt;/data/system/packages.list&lt;/code&gt;中读取，并通过 apk 的&lt;code&gt;&amp;lt;PKGNAME&amp;gt;&lt;/code&gt;找到对应的 UID。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@hammerhead:/ &lt;span class="c"&gt;# cat /data/system/packages.list | grep com.UCMobile.trunk                   &lt;/span&gt;
com.UCMobile.trunk 10084 0 /data/data/com.UCMobile.trunk default 3003,1028,1015
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这里，10084 即是&lt;code&gt;com.UCMobile.trunk&lt;/code&gt;的 UID。&lt;/p&gt;
&lt;h2 id="获取流量数据"&gt;获取流量数据&lt;/h2&gt;
&lt;p&gt;流量数据分为接收流量（tcp_rcv）和发送流量（tcp_snd）两部分，这两个状态数值我们可以通过读取&lt;code&gt;/proc/uid_stat/&amp;lt;UID&amp;gt;&lt;/code&gt;目录下的两个文件得到。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;shell@hammerhead:/ &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/uid_stat/10084/tcp_rcv                          
3446837
shell@hammerhead:/ &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/uid_stat/10084/tcp_snd                          
134366
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过这种方式，我们就可以读取得到指定 apk 在当前时刻的累计流量数值。&lt;/p&gt;
&lt;h2 id="获得启动流量数据"&gt;获得启动流量数据&lt;/h2&gt;
&lt;p&gt;有了前面的基础，我们要测试启动流量就很好实现了。只需要在启动前采集下累计流量数值，然后启动应用，完成启动后再采集一次累计流量数值，前后两次累计数值的差值便是当次启动耗费的流量数。需要注意的是，由于很多时候 apk 在启动后，会在系统后台异步加载一些数据资源，因此为了保证我们采集到当次启动耗费的全部流量数值，我们在启动应用后最好能等待一段时间。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@hammerhead:/ &lt;span class="c"&gt;# cat /proc/uid_stat/10084/tcp_snd                           &lt;/span&gt;
15068
root@hammerhead:/ &lt;span class="c"&gt;# cat /proc/uid_stat/10084/tcp_rcv                           &lt;/span&gt;
98021

&lt;span class="c"&gt;# start app activity, sleep 10s&lt;/span&gt;

root@hammerhead:/ &lt;span class="c"&gt;# cat /proc/uid_stat/10142/tcp_snd                           &lt;/span&gt;
23268
root@hammerhead:/ &lt;span class="c"&gt;# cat /proc/uid_stat/10142/tcp_rcv                           &lt;/span&gt;
965651
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;采集到前后两次流量数值后，即可计算得到当次启动耗费的总流量。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;当次启动总流量 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;23268 + 965651&lt;span class="o"&gt;)&lt;/span&gt; - &lt;span class="o"&gt;(&lt;/span&gt;15068 + 98021&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 875830 bytes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然，这里的启动还分为好几种，包括首次安装启动、非首次安装启动、覆盖安装启动等。具体的启动方式可根据实际场景来定，但在统计流量的方法方面都是相同的。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;本文讲解了 Android App 启动流量测试的一种方法。然而，本次介绍的方法也存在一定局限性，因为&lt;code&gt;/proc/uid_stat/&amp;lt;UID&amp;gt;/&lt;/code&gt;目录下的&lt;code&gt;tcp_rcv&lt;/code&gt;和&lt;code&gt;tcp_snd&lt;/code&gt;文件中都只记录了总值，如果我们只关注总体的流量数值还好，但要是我们希望能测试得到更细化的数据，该方法就没法满足我们的测试需求了。&lt;/p&gt;

&lt;p&gt;举个例子，UC 浏览器国际版在启动后，会和美国的服务器进行通讯交互。现在，我们想测试 UC 浏览器国际版在启动后与美国服务器的通讯流量。&lt;/p&gt;

&lt;p&gt;显然，本文中介绍的方法是没法实现上述例子中的测试需求的。那例子中的场景要怎么测呢？这就还是得用到&lt;code&gt;tcpdump&lt;/code&gt;，在下一篇文章中我会再详细进行介绍。&lt;/p&gt;

&lt;hr&gt;
&lt;h2 id="Read More ..."&gt;Read More ...&lt;/h2&gt;
&lt;p&gt;博客地址：&lt;a href="http://debugtalk.com" rel="nofollow" target="_blank"&gt;http://debugtalk.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;微信公众号二维码：
&lt;img src="https://l.ruby-china.com/photo/2016/642460ab13367640b6ccdc96c8561a30.jpg" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>debugtalk</author>
      <pubDate>Tue, 03 May 2016 19:36:10 +0800</pubDate>
      <link>https://ruby-china.org/topics/29905</link>
      <guid>https://ruby-china.org/topics/29905</guid>
    </item>
    <item>
      <title>蓝灯 Android 版 出来啦！   可以科学上网</title>
      <description>&lt;p&gt;Lantern 是一款开源软件，任何人都可以查看 源代码来获知其工作机制，自己做出判断。我们非常欢迎安全专家来审计，你也可以自己编译出软件包的哦！ &lt;/p&gt;

&lt;p&gt;关于蓝灯的 Q&amp;amp;A: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;谁开发了 Lantern？ 
Lantern 由 Brave New Software Project, Inc 开发。这是一个 501(c)3 的非赢利机构，专注于用软件解决艰巨的全球性问题。Brave New Software 的创始人，也是 Lantern 的首席开发者，Adam Fisk，曾是 P2P 文件共享服务 LimeWire 和 LittleShoot 的主力程序员。在搭建 P2P 架构的过程中，他发现同样的架构也能用于提供不受限制的互联网访问。 &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;上图：
&lt;img src="https://l.ruby-china.com/photo/2016/4064ad68b3aa43dcdab7f2568580cc7a.png" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2016/d39ba6e2d6aba3712906b9872dbba32f.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;界面非常干净&lt;/p&gt;

&lt;p&gt;#&lt;a href="http://finalshares.com/read-1026?ruby-china-44908" rel="nofollow" target="_blank" title=""&gt;下载地址&lt;/a&gt;#&lt;/p&gt;</description>
      <author>finalshares</author>
      <pubDate>Wed, 24 Feb 2016 12:25:33 +0800</pubDate>
      <link>https://ruby-china.org/topics/29078</link>
      <guid>https://ruby-china.org/topics/29078</guid>
    </item>
    <item>
      <title>一个绚丽的 loading 动效分析与实现！</title>
      <description>&lt;p&gt;前两天我们这边的头儿给我说，有个 gif 动效很不错，可以考虑用来做项目里的 loading，问我能不能实现，看了下效果确实不错，也还比较有新意，复杂度也不是非常高，所以就花时间给做了，我们先一起看下原 gif 图效果： &lt;/p&gt;
&lt;h2 id="源码下载"&gt;&lt;a href="http://finalshares.com/read-6767?ruby-5426" rel="nofollow" target="_blank" title=""&gt;源码下载&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src="http://finalshares.cn/attachment/1601/thread/81_15_e6ca53883925a24.gif" title="" alt="Alt text"&gt;&lt;/p&gt;

&lt;p&gt;从效果上看，我们需要考虑以下几个问题： 
1.叶子的随机产生； 
2.叶子随着一条正余弦曲线移动； 
3.叶子在移动的时候旋转，旋转方向随机，正时针或逆时针； 
4.叶子遇到进度条，似乎是融合进入； 
5.叶子不能超出最左边的弧角； 
7.叶子飘出时的角度不是一致，走的曲线的振幅也有差别，否则太有规律性，缺乏美感； &lt;/p&gt;

&lt;p&gt;总的看起来，需要注意和麻烦的地方主要是以上几点，当然还有一些细节问题，比如最左边是圆弧等等； 
那接下来我们将效果进行分解，然后逐个击破： 
整个效果来说，我们需要的图主要是飞动的小叶子和右边旋转的风扇，其他的部分都可以用色值进行绘制，当然我们为了方便，就连底部框一起切了； 
先从 gif 图里把飞动的小叶子和右边旋转的风扇、底部框抠出来，小叶子图如下： &lt;/p&gt;

&lt;p&gt;我们需要处理的主要有两个部分： &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;随着进度往前绘制的进度条； &lt;/li&gt;
&lt;li&gt;不断飞出来的小叶片； &lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="复制代码"&gt;&lt;a href="http://finalshares.com/read-6767?ruby-5426" rel="nofollow" target="_blank" title=""&gt;复制代码&lt;/a&gt;&lt;/h2&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="n"&gt;mProgressWidth为进度条的宽度&lt;/span&gt;&lt;span class="err"&gt;，&lt;/span&gt;&lt;span class="n"&gt;根据当前进度算出进度条的位置&lt;/span&gt;  
&lt;span class="n"&gt;mCurrentProgressPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mProgressWidth&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;mProgress&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="no"&gt;TOTAL_PROGRESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="n"&gt;即当前位置在图中所示1范围内&lt;/span&gt;  
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mCurrentProgressPosition&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;mArcRadius&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="no"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;i&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"mProgress = "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;mProgress&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"---mCurrentProgressPosition = "&lt;/span&gt;  
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;mCurrentProgressPosition&lt;/span&gt;  
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"--mArcProgressWidth"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;mArcRadius&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;绘制白色ARC&lt;/span&gt;&lt;span class="err"&gt;，&lt;/span&gt;&lt;span class="n"&gt;绘制orange&lt;/span&gt; &lt;span class="no"&gt;ARC&lt;/span&gt;  
    &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;绘制白色矩形&lt;/span&gt;  

    &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;绘制白色ARC&lt;/span&gt;  
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawArc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mArcRectF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mWhitePaint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  

    &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;绘制白色矩形&lt;/span&gt;  
    &lt;span class="n"&gt;mWhiteRectF&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mArcRightLocation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mWhiteRectF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mWhitePaint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  

    &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;绘制棕色&lt;/span&gt; &lt;span class="no"&gt;ARC&lt;/span&gt;  
    &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="n"&gt;单边角度&lt;/span&gt;  
    &lt;span class="n"&gt;int&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDegrees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;acos&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;mArcRadius&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;mCurrentProgressPosition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
            &lt;span class="sr"&gt;/ (float) mArcRadius));  
    /&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;起始的位置&lt;/span&gt;  
    &lt;span class="n"&gt;int&lt;/span&gt; &lt;span class="n"&gt;startAngle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
    &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="n"&gt;扫过的角度&lt;/span&gt;  
    &lt;span class="n"&gt;int&lt;/span&gt; &lt;span class="n"&gt;sweepAngle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
    &lt;span class="no"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;i&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"startAngle = "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;startAngle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawArc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mArcRectF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;startAngle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sweepAngle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mOrangePaint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="no"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;i&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"mProgress = "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;mProgress&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"---transfer-----mCurrentProgressPosition = "&lt;/span&gt;  
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;mCurrentProgressPosition&lt;/span&gt;  
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"--mArcProgressWidth"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;mArcRadius&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;绘制white&lt;/span&gt; &lt;span class="no"&gt;RECT&lt;/span&gt;  
    &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;绘制Orange&lt;/span&gt; &lt;span class="no"&gt;ARC&lt;/span&gt;  
    &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;绘制orange&lt;/span&gt; &lt;span class="no"&gt;RECT&lt;/span&gt;  

    &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;绘制white&lt;/span&gt; &lt;span class="no"&gt;RECT&lt;/span&gt;  
    &lt;span class="n"&gt;mWhiteRectF&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mCurrentProgressPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mWhiteRectF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mWhitePaint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  

    &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;绘制Orange&lt;/span&gt; &lt;span class="no"&gt;ARC&lt;/span&gt;  
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawArc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mArcRectF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mOrangePaint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;绘制orange&lt;/span&gt; &lt;span class="no"&gt;RECT&lt;/span&gt;  
    &lt;span class="n"&gt;mOrangeRectF&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mArcRightLocation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
    &lt;span class="n"&gt;mOrangeRectF&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mCurrentProgressPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mOrangeRectF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mOrangePaint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;——————————————————————————————————————————————————————————————————————————————————————————————&lt;/p&gt;
&lt;h2 id="源码下载"&gt;&lt;a href="http://finalshares.com/read-6767?ruby-5426" rel="nofollow" target="_blank" title=""&gt;源码下载&lt;/a&gt;&lt;/h2&gt;</description>
      <author>finalshares</author>
      <pubDate>Fri, 19 Feb 2016 16:49:38 +0800</pubDate>
      <link>https://ruby-china.org/topics/29036</link>
      <guid>https://ruby-china.org/topics/29036</guid>
    </item>
    <item>
      <title>初探微信 JSBridge</title>
      <description>&lt;p&gt;最近也是心血来潮顺便看了下&lt;code&gt;WeiXin&lt;/code&gt;的，这里分享下给大家&lt;/p&gt;
&lt;h2 id="解压缩"&gt;解压缩&lt;/h2&gt;
&lt;p&gt;当然第一步肯定还是先解压缩两者，微信和支付宝钱包还是有很大不同的。
  微信解压缩之后我们能够在在&lt;code&gt;/assets/&lt;/code&gt;目录下面看到没有混淆的 JS 文件，我们多少可以通过这个文件来窥探一下。
&lt;img src="https://l.ruby-china.com/photo/2016/18c90ea09727c6647a455cd562093903.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;钱包解压缩之后同样的目录下我们并找不到对外暴露的容器相关的文件，但其实钱包也是有办法窥探的，只是不在这个位置，碍于身份我就不能多说了，有兴趣的同学自行探索。
&lt;img src="https://l.ruby-china.com/photo/2016/95e6597624401cd248a6943be41fe55e.png" title="" alt=""&gt;
  当然了，解压缩之后，&lt;code&gt;class.dex&lt;/code&gt;的反编译也是不能少的，那么首先一切就绪了。&lt;/p&gt;
&lt;h2 id="wxjs.js"&gt;wxjs.js&lt;/h2&gt;
&lt;p&gt;这个文件将近 3000 行，当然还是搜索&lt;code&gt;bridge&lt;/code&gt;进行搜索，找到了如下的方法&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WeixinJSBridge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;和&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;__WeixinJSBridge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// public&lt;/span&gt;
    &lt;span class="na"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;_call&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;_call&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;_onfor3rd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;_env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;_log&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// private&lt;/span&gt;
    &lt;span class="c1"&gt;// _test_start:_test_start,&lt;/span&gt;
    &lt;span class="na"&gt;_fetchQueue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_fetchQueue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;_handleMessageFromWeixin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_handleMessageFromWeixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;_hasInit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;_hasPreInit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;_continueSetResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_continueSetResult&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;到了这里我们看到了首先微信在初始化的时候会判断有没有&lt;code&gt;WeixinJSBridge&lt;/code&gt;，然后我们也看到了这个&lt;code&gt;Bridge&lt;/code&gt;定义的时候包含的参数。接下来就一个一个来看了，看完不知道能不能明白微信的逻辑。&lt;/p&gt;
&lt;h2 id="_call"&gt;_call&lt;/h2&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;curFuncIdentifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;__WeixinJSBridge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;curFuncIdentifier&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;_callIdentifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;func&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;func&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;callbackID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_callback_count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;_callback_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;callbackID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;msgObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;func&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;params&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;msgObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_MESSAGE_TYPE&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;call&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        
    &lt;span class="nx"&gt;msgObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_CALLBACK_ID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;callbackID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;_sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msgObj&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的方法看到了&lt;code&gt;func&lt;/code&gt;和&lt;code&gt;params&lt;/code&gt;和&lt;code&gt;callback&lt;/code&gt;三个参数，分别&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;func&lt;/code&gt;对应调用方法的一个&lt;code&gt;tag&lt;/code&gt;，看上去像是一个标志&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;*&lt;code&gt;params&lt;/code&gt;从调用的情况来看，会传入一个对象或者一个映射表，经过&lt;code&gt;_call&lt;/code&gt;之后会被转换成一个&lt;code&gt;JSON&lt;/code&gt;放入消息队列中&lt;/p&gt;

&lt;p&gt;*&lt;code&gt;callback&lt;/code&gt;，从调用情况来看，传入回调方法之后，&lt;code&gt;callbackID&lt;/code&gt;首先先自增 1，如果传入的是一个方法的话，，那么就会把这个 callbackID 放入原本空的&lt;code&gt;_callback_map&lt;/code&gt;，经过搜索看到了之前会先定义一个空的&lt;code&gt;_callback_map&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="_handleMessageFromWeixin"&gt;_handleMessageFromWeixin&lt;/h2&gt;
&lt;p&gt;调查&lt;code&gt;_callback_map&lt;/code&gt;的时候，查到了&lt;code&gt;_handleMessageFromWeixin&lt;/code&gt;，那么就继续来看这个方法。首先微信是在&lt;code&gt;handle&lt;/code&gt;层来接受方法的，部分代码如下：&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msgWrap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_MESSAGE_TYPE&lt;/span&gt;&lt;span class="p"&gt;]){&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;callback&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;       
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;msgWrap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_CALLBACK_ID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;_callback_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msgWrap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_CALLBACK_ID&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_callback_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msgWrap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_CALLBACK_ID&lt;/span&gt;&lt;span class="p"&gt;]](&lt;/span&gt;&lt;span class="nx"&gt;msgWrap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;__params&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;_callback_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msgWrap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_CALLBACK_ID&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt; 
      &lt;span class="nf"&gt;_setResultValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SCENE_HANDLEMSGFROMWX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;_setResultValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SCENE_HANDLEMSGFROMWX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;__err_code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cb404&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;__err_code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cb404&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;          
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里参数传入的是一个&lt;code&gt;message&lt;/code&gt;，同时我们看到了在&lt;code&gt;_handleMessageFromWeixin&lt;/code&gt;里面进行了消息类型的判断。首先是&lt;code&gt;callback&lt;/code&gt;类型，不过通过代码的分析，暂时我还不知道怎么算是&lt;code&gt;callback&lt;/code&gt;类型，暂时我们先看下去。从实现来看，只要调用&lt;code&gt;_call&lt;/code&gt;这个方法的并且传入&lt;code&gt;callback&lt;/code&gt;参数的消息都算是&lt;code&gt;callback&lt;/code&gt;类型的消息，这里微信做了一次&lt;code&gt;delete&lt;/code&gt;，应该是说方法只使用一次，用完删除。如果判断失败就会给 404，这个逻辑还是很清楚的。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;msgWrap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_EVENT_ID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;_event_hook_map_for3rd&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msgWrap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_EVENT_ID&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;_isIn3rdApiList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msgWrap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_EVENT_ID&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                                                                                                                                                                                       
           &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_event_hook_map_for3rd&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msgWrap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_EVENT_ID&lt;/span&gt;&lt;span class="p"&gt;]](&lt;/span&gt;&lt;span class="nx"&gt;msgWrap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;__params&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
           &lt;span class="nf"&gt;_setResultValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SCENE_HANDLEMSGFROMWX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;_event_hook_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msgWrap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_EVENT_ID&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_event_hook_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msgWrap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_EVENT_ID&lt;/span&gt;&lt;span class="p"&gt;]](&lt;/span&gt;&lt;span class="nx"&gt;msgWrap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;__params&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
           &lt;span class="nf"&gt;_setResultValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SCENE_HANDLEMSGFROMWX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;//window.JsApi &amp;amp;&amp;amp; JsApi.keep_setReturnValue &amp;amp;&amp;amp; window.JsApi.keep_setReturnValue('SCENE_HANDLEMSGFROMWX', JSON.stringify({'__err_code':'ev404'}));&lt;/span&gt;
  &lt;span class="nf"&gt;_setResultValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SCENE_HANDLEMSGFROMWX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;__err_code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ev404&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;__err_code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ev404&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查遍了 js 文件没有看到&lt;code&gt;_EVENT_ID&lt;/code&gt;类型是啥，不过从判断来看应该是给外部第三方应用来使用的，逻辑同上。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;msgWrap&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_isUseMd5&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;realMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_JSON_MESSAGE&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;shaStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_SHA_KEY&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;realMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_xxyy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;msgSha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;shaObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CryptoJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SHA1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;msgSha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shaObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msgSha&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;shaStr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_handleMessageFromWeixin , shaStr : &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;shaStr&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; , str : &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; , msgSha : &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;msgSha&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;msgWrap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;realMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;msgWrap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段其实简单来看就是作了一个校验的过程，这段代码是在拿到&lt;code&gt;message&lt;/code&gt;最开始就做的，详细加密的算法可见&lt;code&gt;base64encode(js:1854)&lt;/code&gt;方法。&lt;/p&gt;
&lt;h2 id="_setResultValue &amp;amp;&amp;amp; _continueSetResult"&gt;_setResultValue &amp;amp;&amp;amp; _continueSetResult&lt;/h2&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_setResultQueue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_setResultQueueRunning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_setResultValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dummy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;_setResultQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;base64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;_setResultQueueRunning&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;_continueSetResult&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里可以看到扔到了一个&lt;code&gt;_setResultQueue&lt;/code&gt;队列里，队列原本为空。接下来关键就是&lt;code&gt;_continueSetResult()&lt;/code&gt;，事实证明这的确是一个很重要的方法。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_continueSetResult&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_setResultQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;_setResultQueueRunning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;_setResultQueueRunning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;_setResultIframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;weixin://private/setresult/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;虽然我不知道 js 中的&lt;code&gt;shift()&lt;/code&gt;啥意思，但大概能够明白，将一个&lt;code&gt;iframe&lt;/code&gt;的&lt;code&gt;src&lt;/code&gt;赋予以 &lt;code&gt;weixin://private/setresult/&lt;/code&gt;加上一个结果的 URI 地址，到了这里就感觉到了一些眉目了。&lt;/p&gt;
&lt;h2 id="_createQueueReadyIframe"&gt;_createQueueReadyIframe&lt;/h2&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;_setResultIframe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iframe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这个方法里我们可以清楚的看到了&lt;code&gt;_setResultIframe&lt;/code&gt;在这里被初始化了，创建了一个新的&lt;code&gt;iframe&lt;/code&gt;。返回了一个已经准备好接受消息的&lt;code&gt;iframe&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="_on"&gt;_on&lt;/h2&gt;
&lt;p&gt;继续翻阅的时候发现了这个很神奇的方法，猜想可能是给 3rd 用的，结果还真是如此。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nx"&gt;_event_hook_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;和&lt;code&gt;_call&lt;/code&gt;一样，只不过将传入的 callback 方法放入另外一个 map 表中，类型为&lt;code&gt;event&lt;/code&gt;，到这里我们大概能够明白了。的确，&lt;code&gt;_event_hook_map&lt;/code&gt;是给外部应用所存储的类型表。调用到的地方有&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nf"&gt;_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu:share:timeline&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;){...}&lt;/span&gt;
      &lt;span class="nf"&gt;_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu:share:qq&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;){...}&lt;/span&gt;
      &lt;span class="nf"&gt;_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu:share:weiboApp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;){...}&lt;/span&gt;
      &lt;span class="nf"&gt;_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu:share:QZone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;){...}&lt;/span&gt;
      &lt;span class="nf"&gt;_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu:share:appmessage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;){...}&lt;/span&gt;
      &lt;span class="nf"&gt;_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu:share:email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="结论"&gt;结论&lt;/h2&gt;
&lt;p&gt;反编译的&lt;code&gt;class.dex&lt;/code&gt;还没有看。但从上面的分析来看基本上可以理解为调用微信 webview 基本上是这样一些特点&lt;/p&gt;

&lt;p&gt;*微信很好的区分了内部调用和外部调用&lt;/p&gt;

&lt;p&gt;*同样的有自定义的 URI 规则，这应该每家公司都是一样的&lt;/p&gt;

&lt;p&gt;*js 调用 native 的时候对&lt;code&gt;IFrame&lt;/code&gt;的 src 属性的变更，增加了队列机制&lt;/p&gt;

&lt;p&gt;*&lt;code&gt;_handleMessageFromWeixin&lt;/code&gt;方法为 js 监听 native 的方法&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2016/532fd808e885fd8721cdbefb2293dda1.png" title="" alt=""&gt;
扔了&lt;code&gt;class.dex&lt;/code&gt;，不知道这个里面还能看到什么不，有空再看了&lt;/p&gt;

&lt;p&gt;原文：&lt;a href="https://testerhome.com/topics/4083" rel="nofollow" target="_blank"&gt;https://testerhome.com/topics/4083&lt;/a&gt;&lt;/p&gt;</description>
      <author>monkeytest</author>
      <pubDate>Sun, 14 Feb 2016 14:41:27 +0800</pubDate>
      <link>https://ruby-china.org/topics/28981</link>
      <guid>https://ruby-china.org/topics/28981</guid>
    </item>
    <item>
      <title>关于如何高效率开发一个 Android App</title>
      <description>&lt;h3 id="前段时间在知乎和segmentfault回答过类似的问题。在这里总结一下，希望帮到有需要的人。"&gt;前段时间在&lt;a href="https://www.zhihu.com/question/37160415" rel="nofollow" target="_blank" title=""&gt;知乎&lt;/a&gt;和&lt;a href="http://segmentfault.com/q/1010000004253063/a-1020000004256243" rel="nofollow" target="_blank" title=""&gt;segmentfault&lt;/a&gt;回答过类似的问题。在这里总结一下，希望帮到有需要的人。&lt;/h3&gt;&lt;h4 id="网络模块"&gt;网络模块&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/square/okhttp" rel="nofollow" target="_blank" title=""&gt;okhttp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/loopj/android-async-http" rel="nofollow" target="_blank" title=""&gt;android-async-http&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/intl/ja/training/volley/index.html" rel="nofollow" target="_blank" title=""&gt;volley&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="事件总线"&gt;事件总线&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/square/otto" rel="nofollow" target="_blank" title=""&gt;otto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/greenrobot/EventBus" rel="nofollow" target="_blank" title=""&gt;EventBus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="依赖注入"&gt;依赖注入&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/square/dagger" rel="nofollow" target="_blank" title=""&gt;Dagger&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/roboguice/roboguice" rel="nofollow" target="_blank" title=""&gt;RoboGuice&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/JakeWharton/butterknife" rel="nofollow" target="_blank" title=""&gt;ButterKnife&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="图片模块"&gt;图片模块&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt; &lt;a href="https://github.com/facebook/fresco" rel="nofollow" target="_blank" title=""&gt;Fresco&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://github.com/bumptech/glide" rel="nofollow" target="_blank" title=""&gt;Glide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://github.com/square/picasso" rel="nofollow" target="_blank" title=""&gt;picasso&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="数据库模块"&gt;数据库模块&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt; &lt;a href="https://github.com/greenrobot/greenDAO" rel="nofollow" target="_blank" title=""&gt;greenDao&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://github.com/j256/ormlite-android" rel="nofollow" target="_blank" title=""&gt;ormlite&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://github.com/LitePalFramework/LitePal" rel="nofollow" target="_blank" title=""&gt;LitePal&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="响应式编程"&gt;响应式编程&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt; &lt;a href="https://github.com/ReactiveX/RxJava" rel="nofollow" target="_blank" title=""&gt;RxJava&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://github.com/ReactiveX/RxAndroid" rel="nofollow" target="_blank" title=""&gt;RxAndroid&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="日志输出"&gt;日志输出&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt; &lt;a href="https://github.com/orhanobut/logger" rel="nofollow" target="_blank" title=""&gt;logger&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://github.com/ZhaoKaiQiang/KLog" rel="nofollow" target="_blank" title=""&gt;KLog&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="测试相关"&gt;测试相关&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://testerhome.com/" rel="nofollow" target="_blank" title=""&gt;testerhome&lt;/a&gt;
测试社区，里面有很多干货&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://bugtags.com/" rel="nofollow" target="_blank" title=""&gt;bugtags&lt;/a&gt;
新一代的、专为移动测试而生的缺陷发现及管理工具&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://www.testin.cn/" rel="nofollow" target="_blank" title=""&gt;testin&lt;/a&gt;
为移动测试而生&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/NetEase/Emmagee" rel="nofollow" target="_blank" title=""&gt;Emmagee&lt;/a&gt;
网易出品的，是监控指定被测应用在使用过程中占用机器的 CPU、内存、流量资源的性能测试小工具。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/hotchemi/awesome-android-testing" rel="nofollow" target="_blank" title=""&gt;awesome-android-testing&lt;/a&gt;
A curated list of awesome android testing libraries.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="崩溃统计平台"&gt;崩溃统计平台&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt; &lt;a href="http://bugly.qq.com/" rel="nofollow" target="_blank" title=""&gt;腾讯 bugly&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://app.crittercism.com/" rel="nofollow" target="_blank" title=""&gt;Crittercism&lt;/a&gt; &lt;/li&gt;
&lt;li&gt; &lt;a href="http://try.crashlytics.com/" rel="nofollow" target="_blank" title=""&gt;Crashlytics&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;
&lt;h3 id="架构设计"&gt;架构设计&lt;/h3&gt;
&lt;p&gt;这个肯定得了解下 MVC，MVP，MVVM 还有设计模式这些，这里有几个开源项目推荐下&lt;/p&gt;
&lt;h5 id="philm"&gt;philm&lt;/h5&gt;
&lt;p&gt;Movie collection and information app for Android.
Github 地址：&lt;a href="https://github.com/chrisbanes/philm" rel="nofollow" target="_blank"&gt;https://github.com/chrisbanes/philm&lt;/a&gt;&lt;/p&gt;
&lt;h5 id="SimpleNews"&gt;SimpleNews&lt;/h5&gt;
&lt;p&gt;基于 Material Design 和 MVP 的新闻客户端
Github 地址：&lt;a href="https://github.com/liuling07/SimpleNews" rel="nofollow" target="_blank"&gt;https://github.com/liuling07/SimpleNews&lt;/a&gt;&lt;/p&gt;
&lt;h5 id="GankDaily"&gt;GankDaily&lt;/h5&gt;
&lt;p&gt;A application show technical information every working days, use MVP pattern.
Github 地址：&lt;a href="https://github.com/maoruibin/GankDaily" rel="nofollow" target="_blank"&gt;https://github.com/maoruibin/GankDaily&lt;/a&gt;&lt;/p&gt;
&lt;h5 id="SimplifyReader"&gt;SimplifyReader&lt;/h5&gt;
&lt;p&gt;Github 地址：&lt;a href="https://github.com/SkillCollege/SimplifyReader" rel="nofollow" target="_blank"&gt;https://github.com/SkillCollege/SimplifyReader&lt;/a&gt;&lt;/p&gt;
&lt;h5 id="NBAPlus"&gt;NBAPlus&lt;/h5&gt;
&lt;p&gt;Github 地址：&lt;a href="https://github.com/SilenceDut/NBAPlus" rel="nofollow" target="_blank"&gt;https://github.com/SilenceDut/NBAPlus&lt;/a&gt;&lt;/p&gt;
&lt;h5 id="PhotoNoter"&gt;PhotoNoter&lt;/h5&gt;
&lt;p&gt;Github 地址：&lt;a href="https://github.com/yydcdut/PhotoNoter" rel="nofollow" target="_blank"&gt;https://github.com/yydcdut/PhotoNoter&lt;/a&gt;&lt;/p&gt;
&lt;h5 id="Meizhi"&gt;Meizhi&lt;/h5&gt;
&lt;p&gt;Github 地址：&lt;a href="https://github.com/drakeet/Meizhi" rel="nofollow" target="_blank"&gt;https://github.com/drakeet/Meizhi&lt;/a&gt;&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="其他"&gt;其他&lt;/h3&gt;&lt;h5 id="leakcanary"&gt;leakcanary&lt;/h5&gt;
&lt;p&gt;检查内存泄露 
Github 地址：&lt;a href="https://github.com/square/leakcanary" rel="nofollow" target="_blank"&gt;https://github.com/square/leakcanary&lt;/a&gt;&lt;/p&gt;
&lt;h5 id="DebugDrawer"&gt;DebugDrawer&lt;/h5&gt;
&lt;p&gt;Android Debug Drawer for faster development 
Github 地址：&lt;a href="https://github.com/palaima/DebugDrawer" rel="nofollow" target="_blank"&gt;https://github.com/palaima/DebugDrawer&lt;/a&gt;&lt;/p&gt;
&lt;h5 id="ViewServer"&gt;ViewServer&lt;/h5&gt;
&lt;p&gt;Local server for Android's HierarchyViewer 
Github 地址：&lt;a href="https://github.com/romainguy/ViewServer" rel="nofollow" target="_blank"&gt;https://github.com/romainguy/ViewServer&lt;/a&gt;&lt;/p&gt;

&lt;hr&gt;
&lt;h4 id="APP内测，分发"&gt;APP 内测，分发&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt; &lt;a href="http://fir.im/" rel="nofollow" target="_blank" title=""&gt;FIR.im&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="http://www.pgyer.com/" rel="nofollow" target="_blank" title=""&gt;蒲公英&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;
&lt;h4 id="PS：开源框架虽然好用，但最好要了解框架存在的一些bug和坑，遇到问题可以给作者提issue，一般这些有名的框架，作者都会很积极的回答。"&gt;PS：开源框架虽然好用，但最好要了解框架存在的一些 bug 和坑，遇到问题可以给作者提 issue，一般这些有名的框架，作者都会很积极的回答。&lt;/h4&gt;&lt;h4 id="另外打个广告，推荐下自己的Android-Dev-Favorites，里面收集了Android开发的干货。"&gt;另外打个广告，推荐下自己的&lt;a href="https://github.com/ruijun/Android-Dev-Favorites" rel="nofollow" target="_blank" title=""&gt;Android-Dev-Favorites&lt;/a&gt;，里面收集了 Android 开发的干货。&lt;/h4&gt;</description>
      <author>ruijun</author>
      <pubDate>Thu, 14 Jan 2016 14:23:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/28729</link>
      <guid>https://ruby-china.org/topics/28729</guid>
    </item>
    <item>
      <title>Android 开源社会化登录 SDK，支持微信，微博， QQ</title>
      <description>&lt;p&gt;这半年做的项目被大老板 ban 掉了，略伤感。。。&lt;br&gt;
这几天整理了一下项目中负责的社会化登录和分享模块，SDK 集成本身是很简单的事情，但是三家平台的接口差异很大，如果只见简单的把代码垒在一起，几乎不可以复用，扩展性和可维护性为零。所以简单抽象了一下接口，提供 aar 库。&lt;br&gt;
其实，像友盟，ShareSDK 等平台也提供类似的 SDK，之所以造轮子是因为这些平台的 SDK 内部肯定会带有数据统计功能，不想给他们共享数据。   &lt;/p&gt;

&lt;p&gt;当前已经实现社会化登录功能，分享功能 ing&lt;/p&gt;

&lt;p&gt;项目地址： &lt;a href="https://github.com/ElbbbirdStudio/ESSocialSDK" rel="nofollow" target="_blank"&gt;https://github.com/ElbbbirdStudio/ESSocialSDK&lt;/a&gt;   &lt;/p&gt;
&lt;h2 id="ESSocialSDK "&gt;ESSocialSDK &lt;a href="https://travis-ci.org/ElbbbirdStudio/ESSocialSDK" rel="nofollow" target="_blank" title=""&gt;&lt;img src="https://travis-ci.org/ElbbbirdStudio/ESSocialSDK.svg?branch=master" title="" alt="Build Status"&gt;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;社交登录授权，分享 SDK&lt;br&gt;
支持微信、微博、QQ 登录授权&lt;br&gt;
微信好友、微信朋友圈、微博、QQ 好友、QQ 空间分享&lt;/p&gt;
&lt;h2 id="Gradle"&gt;Gradle&lt;/h2&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;compile&lt;/span&gt; &lt;span class="s1"&gt;'com.elbbbird.android:socialsdk:0.1.0@aar'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="使用指南"&gt;使用指南&lt;/h2&gt;&lt;h3 id="Debug 模式"&gt;Debug 模式&lt;/h3&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SocialSDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDebugMode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//默认 false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="平台 SSO 授权功能"&gt;平台 SSO 授权功能&lt;/h3&gt;&lt;h4 id="ISocialOauthCallback授权回调接口"&gt;
&lt;code&gt;ISocialOauthCallback&lt;/code&gt;授权回调接口&lt;/h4&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ISocialOauthCallback&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ISocialOauthCallback&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onGetTokenSuccess&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SocialToken&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//获取 token 成功&lt;/span&gt;
        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;i&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TAG&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"onGetTokenSuccess"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onGetUserSuccess&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SocialUser&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//获取用户信息成功&lt;/span&gt;
        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;i&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TAG&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"onGetUserSuccess# "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onFailure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//失败&lt;/span&gt;
        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;i&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TAG&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"onFailure# "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onCancel&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//取消&lt;/span&gt;
        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;i&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TAG&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"onCancel#"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="微博授权"&gt;微博授权&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;配置微博后台回调地址&lt;br&gt;
SDK 的默认回调地址为&lt;code&gt;http://www.sina.com&lt;/code&gt;，需要在微博后台配置，否则会提示回调地址错误。&lt;br&gt;
如果在&lt;code&gt;SocialSDK.initWeibo()&lt;/code&gt;方法自定义了回调地址，需要在后台配置为相应地址。&lt;/li&gt;
&lt;li&gt;oauth
&lt;code&gt;java
SocialSDK.initWeibo("app_key");
SocialSDK.oauthWeibo(context, callback);
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;onActivityResult&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SocialSDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;oauthWeiboCallback&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requestCode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultCode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;revoke&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SocialSDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;revokeWeibo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="微信授权"&gt;微信授权&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;WXEntryActivity&lt;br&gt;
创建包名：&lt;code&gt;package_name.wxapi&lt;/code&gt;&lt;br&gt;
在该包名下创建类&lt;code&gt;WXEntryActivity&lt;/code&gt;继承自&lt;code&gt;WXCallbackActivity&lt;/code&gt;&lt;br&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.encore.actionnow.wxapi&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WXEntryActivity&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;WXCallbackActivity&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;AndroidManifest.xml&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;activity&lt;/span&gt;
&lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;".wxapi.WXEntryActivity"&lt;/span&gt;
&lt;span class="na"&gt;android:configChanges=&lt;/span&gt;&lt;span class="s"&gt;"keyboardHidden|orientation|screenSize"&lt;/span&gt;
&lt;span class="na"&gt;android:exported=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
&lt;span class="na"&gt;android:screenOrientation=&lt;/span&gt;&lt;span class="s"&gt;"portrait"&lt;/span&gt;
&lt;span class="na"&gt;android:theme=&lt;/span&gt;&lt;span class="s"&gt;"@android:style/Theme.Translucent.NoTitleBar"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;oauth&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SocialSDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initWeChat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"app_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"app_secret"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;SocialSDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;oauthWeChat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;revoke&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SocialSDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;revokeWeChat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="QQ 授权"&gt;QQ 授权&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;AndroidManifest.xml&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;activity&lt;/span&gt;
&lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"com.tencent.tauth.AuthActivity"&lt;/span&gt;
&lt;span class="na"&gt;android:launchMode=&lt;/span&gt;&lt;span class="s"&gt;"singleTask"&lt;/span&gt;
&lt;span class="na"&gt;android:noHistory=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;intent-filter&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;action&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.action.VIEW"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.DEFAULT"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.BROWSABLE"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;data&lt;/span&gt; &lt;span class="na"&gt;android:scheme=&lt;/span&gt;&lt;span class="s"&gt;"tencentXXXXXXXXX"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/intent-filter&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/activity&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;以上配置中的&lt;code&gt;XXXXXXXXX&lt;/code&gt;换成 app_id.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;oauth&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SocialSDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initQQ&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;SocialSDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;oauthQQ&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;onActivityResult&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;Constants&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;REQUEST_LOGIN&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;requestCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;Constants&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;REQUEST_APPBAR&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="nc"&gt;SocialSDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;oauthQQCallback&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestCode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultCode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;revoke&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SocialSDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;revokeQQ&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="SDK 默认授权界面，展示全平台授权接口"&gt;SDK 默认授权界面，展示全平台授权接口&lt;/h4&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;配置微博后台回调地址&lt;br&gt;
SDK 的默认回调地址为&lt;code&gt;http://www.sina.com&lt;/code&gt;，需要在微博后台配置，否则会提示回调地址错误。&lt;br&gt;
如果在&lt;code&gt;SocialSDK.init()&lt;/code&gt;方法自定义了回调地址，需要在后台配置为相应地址。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WXEntryActivity&lt;br&gt;
创建包名：&lt;code&gt;package_name.wxapi&lt;/code&gt;&lt;br&gt;
在该包名下创建类&lt;code&gt;WXEntryActivity&lt;/code&gt;继承自&lt;code&gt;WXCallbackActivity&lt;/code&gt;   &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.encore.actionnow.wxapi&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WXEntryActivity&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;WXCallbackActivity&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;AndroidManifest.xml   &lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;activity&lt;/span&gt;
&lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;".wxapi.WXEntryActivity"&lt;/span&gt;
&lt;span class="na"&gt;android:configChanges=&lt;/span&gt;&lt;span class="s"&gt;"keyboardHidden|orientation|screenSize"&lt;/span&gt;
&lt;span class="na"&gt;android:exported=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
&lt;span class="na"&gt;android:screenOrientation=&lt;/span&gt;&lt;span class="s"&gt;"portrait"&lt;/span&gt;
&lt;span class="na"&gt;android:theme=&lt;/span&gt;&lt;span class="s"&gt;"@android:style/Theme.Translucent.NoTitleBar"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;activity&lt;/span&gt;
&lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"com.tencent.tauth.AuthActivity"&lt;/span&gt;
&lt;span class="na"&gt;android:launchMode=&lt;/span&gt;&lt;span class="s"&gt;"singleTask"&lt;/span&gt;
&lt;span class="na"&gt;android:noHistory=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;intent-filter&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;action&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.action.VIEW"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.DEFAULT"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.BROWSABLE"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;data&lt;/span&gt; &lt;span class="na"&gt;android:scheme=&lt;/span&gt;&lt;span class="s"&gt;"tencentXXXXXXXXX"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/intent-filter&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/activity&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;以上配置中的&lt;code&gt;XXXXXXXXX&lt;/code&gt;换成 app_id.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;oauth&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SocialSDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wechat_app_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"wechat_app_secret"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"weibo_app_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"qq_app_id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;SocialSDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;oauth&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;授权结果回调&lt;br&gt;
SDK 使用了&lt;a href="http://square.github.io/otto/" rel="nofollow" target="_blank" title=""&gt;Otto&lt;/a&gt;作为事件库，用以组件通信。（其实我是不想写 startActivityForResult ...）&lt;br&gt;
在调用&lt;code&gt;SocialSDK.oauth()&lt;/code&gt;接口&lt;code&gt;Activity&lt;/code&gt;的&lt;code&gt;onCreate()&lt;/code&gt;方法内添加   &lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;BusProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在该&lt;code&gt;Activity&lt;/code&gt;的&lt;code&gt;onDestroy()&lt;/code&gt;方法添加   &lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onDestroy&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="nc"&gt;BusProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;unregister&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onDestroy&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;添加回调接口   &lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Subscribe&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onOauthResult&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BusEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getType&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;BusEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TYPE_GET_TOKEN&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="nc"&gt;SocialToken&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getToken&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;i&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TAG&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"onOauthResult#BusEvent.TYPE_GET_TOKEN "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;BusEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TYPE_GET_USER&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="nc"&gt;SocialUser&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUser&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;i&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TAG&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"onOauthResult#BusEvent.TYPE_GET_USER "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;BusEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TYPE_FAILURE&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getException&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;i&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TAG&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"onOauthResult#BusEvent.TYPE_FAILURE "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;BusEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TYPE_CANCEL&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;i&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TAG&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"onOauthResult#BusEvent.TYPE_CANCEL"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;revoke&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SocialSDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;revoke&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="FAQ"&gt;FAQ&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;关于三个平台的账号&lt;br&gt;
微博应用程序注册完成后，需要在后台配置测试账号，包名，签名信息，然后开始测试；&lt;br&gt;
微信应用程序注册后，需要配置包名和签名，并提交审核通过，可以获得分享权限。SSO 登录权限需要开发者认证。（保护费不到位，测试都不能做）&lt;br&gt;
QQ 需要在后台配置测试账号才能 SSO 登录。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;是否需要配置权限？&lt;br&gt;
SDK 已经在 aar 中添加三个平台需要的权限，以下   &lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;uses-permission&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.permission.INTERNET"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;uses-permission&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.permission.ACCESS_NETWORK_STATE"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;uses-permission&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.permission.ACCESS_WIFI_STATE"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;uses-permission&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.permission.READ_PHONE_STATE"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;uses-permission&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.permission.WRITE_EXTERNAL_STORAGE"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>hailong0707</author>
      <pubDate>Mon, 23 Nov 2015 09:46:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/28156</link>
      <guid>https://ruby-china.org/topics/28156</guid>
    </item>
    <item>
      <title>TesterHome 写了一个 native 的 Android 客户端</title>
      <description>&lt;p&gt;我们也使用最新的接口开发了一个客户端，目前进展不错&lt;/p&gt;

&lt;p&gt;项目地址：&lt;a href="https://github.com/testerhome/A-Native-TesterHome" rel="nofollow" target="_blank"&gt;https://github.com/testerhome/A-Native-TesterHome&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这是我们的截图：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2015/de9aad0ad6d3e59ec87499e43a4de1ff.jpg" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2015/af0edd6989c3628797c7291ae8c39cfc.jpg" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2015/ff56ea952dffb2a350132c85e18a18e5.jpg" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2015/1f2b413e61f8b4390d714939f683e28e.jpg" title="" alt=""&gt;
&lt;img src="https://l.ruby-china.com/photo/2015/0956d4582cba17c3b8eba4e58f19551a.jpg" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;个人感觉不错，其实换个 endpoint 就可以做 rubychina 的客户端了。
希望各位大大指教。&lt;/p&gt;</description>
      <author>lihuazhang</author>
      <pubDate>Fri, 23 Oct 2015 21:45:36 +0800</pubDate>
      <link>https://ruby-china.org/topics/27799</link>
      <guid>https://ruby-china.org/topics/27799</guid>
    </item>
    <item>
      <title>抢门票 | UPYUN 邀你参加 2015 安卓全球开发者大会！</title>
      <description>&lt;p&gt;&lt;img src="http://lock522.b0.upaiyun.com/%E4%B8%8B%E8%BD%BD.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;UPYUN 为大伙儿奉上免费参加  2015 安卓全球开发者大会的机会—— 12 张大会门票已备好，待你来抢。&lt;/p&gt;
&lt;h2 id="安卓全球开发者大会是啥？"&gt;安卓全球开发者大会是啥？&lt;/h2&gt;
&lt;p&gt;美国国际数据集团旗下的知名展会品牌。主要针对基于 Android 系统平台的商业模式探讨、技术交流、移动应用推广的大会。大会代表着未来移动互联网产品和应用服务类型的发展趋势。&lt;/p&gt;
&lt;h2 id="参与方式"&gt;参与方式&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;关注 UPYUN 微信公众号并回复“参会”&lt;/strong&gt; ，点击弹出的自动回复中的链接，填写报名表。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://lock522.b0.upaiyun.com/%E4%BA%8C%E7%BB%B4%E7%A0%81%E7%AC%AC%E4%BA%8C%E7%89%88.png" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="获奖规则"&gt;获奖规则&lt;/h2&gt;
&lt;p&gt;我们将选取报名序号为 &lt;strong&gt;3 的整数倍&lt;/strong&gt; 的参与者，赠送门票。赠送优先顺序：按照报名序号由先到后，赠完为止！&lt;/p&gt;</description>
      <author>upyun</author>
      <pubDate>Mon, 19 Oct 2015 09:51:44 +0800</pubDate>
      <link>https://ruby-china.org/topics/27716</link>
      <guid>https://ruby-china.org/topics/27716</guid>
    </item>
    <item>
      <title>RubyMotion 真的好贵～～你们是怎么玩的</title>
      <description>&lt;p&gt;都说 rubymotion 很爽，就想学习感觉一下，可是试用都得先扣一千多块钱。不能愉快的玩耍啊～，能破吗？&lt;/p&gt;</description>
      <author>simoon</author>
      <pubDate>Tue, 23 Jun 2015 01:48:28 +0800</pubDate>
      <link>https://ruby-china.org/topics/26134</link>
      <guid>https://ruby-china.org/topics/26134</guid>
    </item>
    <item>
      <title>jimu Mirror，加速你的 Android UI 开发</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;这个节点好久没人新建帖子了，我来热一热..&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr&gt;

&lt;p&gt;用过 &lt;strong&gt;Sketch&lt;/strong&gt; 的朋友应该都知道一个叫 &lt;strong&gt;Mirror&lt;/strong&gt; 的功能，它可以将你的设计稿放到手机上预览，为设计师们提高了不少效率。而今天我要介绍的 &lt;strong&gt;jimu Mirror&lt;/strong&gt; ，和 &lt;strong&gt;Sketch Mirror&lt;/strong&gt; 类似，可以将 Android 的 xml 实时放到手机上预览。但是现代化的 IDE 都已经提供在 IDE 内实时预览 xml 的功能，&lt;strong&gt;jimu Mirror&lt;/strong&gt; 有啥用呢？&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;有用！还是相当有用！&lt;/strong&gt; 本文将以微信的界面作为参考，在不写一句 Java 代码的情况下，轻松实现微信的几个界面。&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="安装 jimu Mirror"&gt;安装 jimu Mirror&lt;/h3&gt;
&lt;p&gt;jimu Mirror 支持 Android Studio / IDEA，本文将以 Android Studio 作为开发环境，读者请自行下载对应版本。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="http://jimulabs.com/mirror-downloads/" rel="nofollow" target="_blank"&gt;http://jimulabs.com/mirror-downloads/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;将插件下载下来后，在 Android Studio 菜单进行简单操作，即可安装。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Configure -&amp;gt; Plugins -&amp;gt; install plugin from disk&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;没什么意外的话，你应该就能看见 Mirror 的小图标了。：）&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.specyci.com/uploads/picture/asset/17/736807d2-7187-4d7c-9339-dfca766a38c6.png" title="" alt="Alt text"&gt;&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="配置 jimu Mirror"&gt;配置 jimu Mirror&lt;/h3&gt;
&lt;p&gt;如果你使用的是 Android Studio，恭喜你，你什么都不用做，把手机接上电脑就可以了！如果你是其它 IDE，请查看&lt;a href="http://jimulabs.com/mirror-docs/project-configuration-in-mirror-server/" rel="nofollow" target="_blank" title=""&gt;官方文档&lt;/a&gt;。&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="基本操作"&gt;基本操作&lt;/h3&gt;
&lt;p&gt;首先，将 Android Studio 的 Project 面板从 &lt;strong&gt;Android&lt;/strong&gt; 切换到 &lt;strong&gt;Project&lt;/strong&gt;，这样我们就能观察所有文件的变化；&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.specyci.com/uploads/picture/asset/18/2.png" title="" alt="Alt text"&gt;&lt;/p&gt;

&lt;p&gt;再通过菜单，将 Mirror 的 Console 打开，这样我们就能观察 Mirror 的运行情况；&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.specyci.com/uploads/picture/asset/19/3.png" title="" alt="Alt text"&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;最后点一下「Start/Stop Mirror」的小图标，把 Mirror 跑起来，如无意外，Project 下会生成一个 app/mirror 的目录，同时你的手机应该会跑起 jimu Mirror 的主程序。&lt;/strong&gt;&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="编写「聊天」列表界面"&gt;编写「聊天」列表界面&lt;/h3&gt;
&lt;p&gt;先新建两个 layout，一个放 ListView 一个放 ListView Item。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;fragemnt_messages.xml&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;list_item_message.xml&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;往 &lt;strong&gt;fragment_messages.xml&lt;/strong&gt; 增加一个 ListView，代码大概如下：&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;LinearLayout&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ListView&lt;/span&gt;
        &lt;span class="err"&gt;...&lt;/span&gt;
        &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/listView"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/LinearLayout&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后往 &lt;strong&gt;list_item_message.xml&lt;/strong&gt; 添加所需要的控件，例如放置头像的 ImageView，放置昵称的 TextView。&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;LinearLayout&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ImageView&lt;/span&gt;
        &lt;span class="err"&gt;...&lt;/span&gt;
        &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/avatar"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TextView&lt;/span&gt;
        &lt;span class="err"&gt;...&lt;/span&gt;
        &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/name"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TextView&lt;/span&gt;
        &lt;span class="err"&gt;...&lt;/span&gt;
        &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/content"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TextView&lt;/span&gt;
        &lt;span class="err"&gt;...&lt;/span&gt;
        &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/time"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/LinearLayout&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;按下「保存」按钮，可以看到在 Mirror 的 Console 里有新的输出，它为刚才修改的文件生成了新的 mirror 数据文件。&lt;/p&gt;
&lt;h4 id="Mirror XML 简介"&gt;Mirror XML 简介&lt;/h4&gt;
&lt;p&gt;打开 &lt;strong&gt;app/mirror&lt;/strong&gt; 下的 &lt;strong&gt;list_item_message.xml&lt;/strong&gt;，可以看到如下 XML 代码：&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;screen&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;_content&lt;/span&gt; &lt;span class="na"&gt;layout=&lt;/span&gt;&lt;span class="s"&gt;"@layout/list_item_message"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- ImageView Examples:
        &amp;lt;avatar src="@drawable/image_resource" /&amp;gt;
        &amp;lt;avatar src="relative_path/image.jpg" /&amp;gt; --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;avatar&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- TextView Examples:
        &amp;lt;name text="@string/string_resource" /&amp;gt;
        &amp;lt;name text="Text literal" textSize="14sp" /&amp;gt; --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;name&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;content&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;time&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/_content&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/screen&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们在 layout 中 ID 为 avatar 的 ImageView，在 Mirror 的数据文件里被 &lt;code&gt;&amp;lt;avatar/&amp;gt;&lt;/code&gt; 闭合所定义，我们可以在闭合内设置 avatar 的属性，例如我要给它设置个 src：&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;avatar&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"@drawable/image_resource"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同理地，其它属性也可以在闭合里写属性定义。&lt;/p&gt;
&lt;h4 id="实现聊天列表界面"&gt;实现聊天列表界面&lt;/h4&gt;
&lt;p&gt;上面简单地说明了下 &lt;strong&gt;Mirror XML&lt;/strong&gt;，但是我们的目标是编写一个列表，然不是一个 Item，所以要实现聊天列表，真正要修改的是 &lt;strong&gt;app/mirror/fragment_messages.xml&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;打开 &lt;strong&gt;app/mirror/fragment_messages.xml&lt;/strong&gt;，可以看到类似结构。&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;screen&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;_content&lt;/span&gt; &lt;span class="na"&gt;layout=&lt;/span&gt;&lt;span class="s"&gt;"@layout/fragment_messages"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;listView&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/listView&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/_content&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/screen&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要往 ID 为 &lt;strong&gt;listView&lt;/strong&gt; 的 ListView 添加数据，往闭合内放 &lt;strong&gt;items&lt;/strong&gt; 即可；&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;items&lt;/span&gt; &lt;span class="na"&gt;layout=&lt;/span&gt;&lt;span class="s"&gt;"list_item_message"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;_item&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;avatar&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"@drawable/avatar_special"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;name&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Special"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;content&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"[图片]"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;time&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"晚上 11:59"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/_item&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;_item&amp;gt;&lt;/span&gt;
    ....
    &lt;span class="nt"&gt;&amp;lt;/_item&amp;gt;&lt;/span&gt;
    ...
&lt;span class="nt"&gt;&amp;lt;/items&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;小 Tips&lt;/strong&gt;: 这里的测试数据，例如 drawable，可以放到 app/mirror/res 下，这样可以实现测试数据与正式代码分离，非常优雅！&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;其中我们在最外层的 &lt;strong&gt;items&lt;/strong&gt; 声明了渲染的 layout 为 &lt;strong&gt;list_item_message&lt;/strong&gt;，子 &lt;strong&gt;_item&lt;/strong&gt; 也支持定义它自己的 layout，例如在聊天列表里，有服务号、订阅号的记录，它们的样式可能与其它不同。&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;items&lt;/span&gt; &lt;span class="na"&gt;layout=&lt;/span&gt;&lt;span class="s"&gt;"list_item_message"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;_item&lt;/span&gt; &lt;span class="na"&gt;layout=&lt;/span&gt;&lt;span class="s"&gt;"list_item_message_special"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;_item&lt;/span&gt; &lt;span class="na"&gt;count=&lt;/span&gt;&lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/items&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;另外，通过设置 &lt;strong&gt;count&lt;/strong&gt; 属性可以实现添加重复的 item。&lt;/p&gt;
&lt;h4 id="聊天列表效果"&gt;聊天列表效果&lt;/h4&gt;
&lt;p&gt;添加好各种模拟数据后，保存好所有文件，回到手机上，在 jimu Mirror 的界面列表选择 &lt;code&gt;fragment_messages&lt;/code&gt;。
&lt;img src="http://www.specyci.com/uploads/picture/asset/20/4.png" title="" alt="Alt text"&gt;&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="编写主界面"&gt;编写主界面&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;目前 jimu Mirror 暂时只支持 Actionbar Tab 去切换 Fragments，所以主界面先用 Actionbar Tab 吧。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;新建 layout &lt;strong&gt;activity_main.xml&lt;/strong&gt;，往里添加一个 ViewPager：&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;LinearLayout&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;android.support.v4.view.ViewPager&lt;/span&gt; &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/pager"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/LinearLayout&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改 mirror 下对应的 XML 文件：&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;screen&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;actionbar&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"微信"&lt;/span&gt; &lt;span class="na"&gt;showTabsFor=&lt;/span&gt;&lt;span class="s"&gt;"@id/pager"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;_content&lt;/span&gt; &lt;span class="na"&gt;layout=&lt;/span&gt;&lt;span class="s"&gt;"@layout/activity_main"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;pager&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;_page&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"聊天"&lt;/span&gt; &lt;span class="na"&gt;layout=&lt;/span&gt;&lt;span class="s"&gt;"fragment_messages"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;listView&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;items&amp;gt;&lt;/span&gt;
                    ....
                    &lt;span class="nt"&gt;&amp;lt;/items&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/listView&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/_page&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;_page&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"发现"&lt;/span&gt; &lt;span class="na"&gt;layout=&lt;/span&gt;&lt;span class="s"&gt;"fragment_discovery"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;listView&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;items&amp;gt;&lt;/span&gt;
                    ....
                    &lt;span class="nt"&gt;&amp;lt;/items&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/listView&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/_page&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;_page&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"通讯录"&lt;/span&gt; &lt;span class="na"&gt;layout=&lt;/span&gt;&lt;span class="s"&gt;"fragment_contacts"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/pager&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/_content&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/screen&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;items&amp;gt;&amp;lt;/items&amp;gt;&lt;/code&gt; 内就是把上面&lt;strong&gt;编写「聊天列表」&lt;/strong&gt;的 items 往里堆，当然这样很不 DRY。我们可以把&lt;strong&gt;items&lt;/strong&gt;抽离出一个 XML，再把它们 &lt;strong&gt;include&lt;/strong&gt; 进去。&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!--- File name: messages.xml ---&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;items&lt;/span&gt; &lt;span class="na"&gt;layout=&lt;/span&gt;&lt;span class="s"&gt;"list_item_message"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;_item&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;avatar/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;name/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;content/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;time/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/_item&amp;gt;&lt;/span&gt;
    ....
&lt;span class="nt"&gt;&amp;lt;/items&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!--- File name: activity_main.xml ---&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;_page&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"聊天"&lt;/span&gt; &lt;span class="na"&gt;layout=&lt;/span&gt;&lt;span class="s"&gt;"fragment_messages"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;listView&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;items&lt;/span&gt; &lt;span class="na"&gt;include=&lt;/span&gt;&lt;span class="s"&gt;"messages.xml"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/listView&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/_page&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="效果"&gt;效果&lt;/h4&gt;
&lt;p&gt;在 jimu Mirror 的 layout 列表里选择 &lt;strong&gt;activity_main&lt;/strong&gt;，哈哈，还挺像嘛！&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.specyci.com/uploads/picture/asset/21/5.png" title="" alt="Alt text"&gt;&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="结语"&gt;结语&lt;/h3&gt;
&lt;p&gt;这几年 Android 相关的开发工具都在飞速进步，前几年我们还在挣扎 ADT，现在一个 Android Studio 妥妥的，再配个 Genymotion，谁还想念那自带模拟器呢？
这个插件虽算不上革命之举，但从效率上，无疑提高了界面开发速度，如果团队里还有屌炸天懂 XML 规范的设计师，相当部分的前端工作可以托付给他了。&lt;/p&gt;

&lt;p&gt;最后，&lt;strong&gt;相关资源&lt;/strong&gt;附上本文项目代码。&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="相关资源"&gt;相关资源&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/SpecialCyCi/MirrorWechat" rel="nofollow" target="_blank" title=""&gt;MirrorWechat&lt;/a&gt;&lt;/strong&gt;: 本文示范代码，使用 jimu Mirror 模仿微信界面。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="http://jimulabs.com/mirror-docs/mirror-tutorial/" rel="nofollow" target="_blank" title=""&gt;MirrorTutorial&lt;/a&gt;&lt;/strong&gt;: 官方的说明文档。&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>special</author>
      <pubDate>Tue, 30 Dec 2014 13:55:43 +0800</pubDate>
      <link>https://ruby-china.org/topics/23475</link>
      <guid>https://ruby-china.org/topics/23475</guid>
    </item>
    <item>
      <title>前阵子学 Android 的时候写的一个 Ruby China 论坛 Demo，求 Android 能人加入</title>
      <description>&lt;p&gt;纯粹就是初学者 demo , 只有列表和详情页面。本来是打算做成多个论坛的客户端的，但无奈没有时间搞了，求 fork , 求 pull request.&lt;/p&gt;

&lt;p&gt;代码地址： &lt;a href="https://github.com/RobotJiang/TechForumOnAndroid" rel="nofollow" target="_blank"&gt;https://github.com/RobotJiang/TechForumOnAndroid&lt;/a&gt;   &lt;/p&gt;

&lt;p&gt;IDE 我使用的是 Android Studio，用 gradle 做的依赖和包管理，发布打包也是用得 gradle。&lt;/p&gt;</description>
      <author>outman</author>
      <pubDate>Fri, 27 Jun 2014 16:19:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/20199</link>
      <guid>https://ruby-china.org/topics/20199</guid>
    </item>
    <item>
      <title>目前成熟的即时通讯协议除了 XMPP，还有哪些？</title>
      <description>&lt;p&gt;最近在做一个手机的 app，需要用到即时通讯模块，但是自己开发协议暂时没必要（也没能力），问问大伙，目前成熟的即时通讯协议除了 XMPP，还有哪些？&lt;/p&gt;</description>
      <author>flowerwrong</author>
      <pubDate>Mon, 23 Jun 2014 02:00:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/20113</link>
      <guid>https://ruby-china.org/topics/20113</guid>
    </item>
    <item>
      <title>[分享] 一个 android 下测试 UI 和逻辑的单元测试工具，使开发效率倍增</title>
      <description>&lt;p&gt;团队内部用的，使用效果很好。最近整理发布出来了。&lt;/p&gt;

&lt;p&gt;项目地址： &lt;a href="https://github.com/examplecode/android-ui-test-runner" rel="nofollow" target="_blank"&gt;https://github.com/examplecode/android-ui-test-runner&lt;/a&gt;&lt;/p&gt;</description>
      <author>chengkai</author>
      <pubDate>Wed, 11 Jun 2014 22:01:34 +0800</pubDate>
      <link>https://ruby-china.org/topics/19883</link>
      <guid>https://ruby-china.org/topics/19883</guid>
    </item>
    <item>
      <title>请问 Ruby China 的移动客户端是用什么框架开发的？</title>
      <description>&lt;p&gt;如题。
目前看到有 Rhodes、PhoneGap 等，本人是做企业应用开发的，数据、业务流程处理比较多，音视频等娱乐要素较少。哪个框架更适合呢？请各位大牛推荐哈！&lt;/p&gt;</description>
      <author>jksharer</author>
      <pubDate>Tue, 06 May 2014 14:50:27 +0800</pubDate>
      <link>https://ruby-china.org/topics/19047</link>
      <guid>https://ruby-china.org/topics/19047</guid>
    </item>
    <item>
      <title>寻成都使用 phonegap 开发混合应用的开发团队或个人项目合作</title>
      <description>&lt;p&gt;需要有较丰富使用 phonegap 框架开发 webapp 经验。合作细节私聊。QQ:282577425&lt;/p&gt;</description>
      <author>supermars</author>
      <pubDate>Sat, 18 Jan 2014 23:22:27 +0800</pubDate>
      <link>https://ruby-china.org/topics/16865</link>
      <guid>https://ruby-china.org/topics/16865</guid>
    </item>
  </channel>
</rss>
