<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Ruilin&apos;s Notes</title>
    <description>$ sudo rm -rf /*</description>
    <link>https://ruilin.github.io/blog/</link>
    <atom:link href="https://ruilin.github.io/blog/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Tue, 20 Oct 2026 07:48:17 +0000</pubDate>
    <lastBuildDate>Tue, 20 Oct 2026 07:48:17 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>Frida入门教程</title>
        <description>&lt;p&gt;Frida是逆向工程常用的Hook工具，是一款非常方便且易用的跨平台工具包。Frida分客户端环境和服务端环境，客户端即控制端，而服务端则是被控制端。&lt;/p&gt;

&lt;!--more--&gt;

&lt;h1 id=&quot;frida-hook&quot;&gt;Frida Hook&lt;/h1&gt;
&lt;p&gt;Frida是逆向工程常用的Hook工具，是一款非常方便且易用的跨平台工具包。Frida分客户端环境和服务端环境，客户端即控制端，而服务端则是被控制端。&lt;/p&gt;

&lt;p&gt;客户端可以编写Python代码来连接服务端，注入脚本到服务端。在服务端可以编写Javascript代码注入到目标进程进行Hook操作。&lt;/p&gt;

&lt;p&gt;本文指导新手如何使用Frida&lt;/p&gt;

&lt;h2 id=&quot;一frida安装&quot;&gt;一、Frida安装&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;安装
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo pip3 uninstall frida
sudo pip3 uninstall frida-tools
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;安装完查看版本
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;frida --version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;下载安装包&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;对应的Frida &lt;a href=&quot;https://github.com/frida/frida/releases&quot;&gt;客户端包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;下载对应的Frida &lt;a href=&quot;https://github.com/frida/frida/releases/download/16.4.7/frida-server-16.4.7-android-arm64.xz&quot;&gt;服务端安卓包&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;.xz为压缩文件，需要进行解压&lt;/li&gt;
  &lt;li&gt;版本号必须保持一致，否则无法兼容&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;推送到手机系统目录，手机必须Root
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;adb push ./frida-server-15.1.17-android-arm64.xz /data/local/tmp/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;授予权限
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;adb shell
su
chmod 777 /data/local/tmp/frida-server-15.1.17-android-arm64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;关闭系统SELinux
在打开服务端之前，需要先关闭SELinux
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setenforce 0&lt;/code&gt;
之后，运行getenforce命令，如果打印“Permissive”，说明关闭成功&lt;/li&gt;
  &lt;li&gt;运行服务
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;su
./data/local/tmp/frida-server-15.1.17-android-arm64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;二查看目标app进程&quot;&gt;二、查看目标APP进程&lt;/h2&gt;

&lt;p&gt;查询所有进程ID
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;frida-ps -U&lt;/code&gt;
可以添加grep 应用名找到目标进程id&lt;/p&gt;

&lt;h2 id=&quot;三注入hook脚本&quot;&gt;三、注入Hook脚本&lt;/h2&gt;

&lt;p&gt;参考 https://www.cnblogs.com/luoyesiqiu/p/10718997.html&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;frida -U -l myhook.js [进程ID]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;参数解释：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;-U 指定对USB设备操作&lt;/li&gt;
  &lt;li&gt;-l 指定加载一个Javascript脚本&lt;/li&gt;
  &lt;li&gt;最后指定一个进程名，如果想指定进程pid,用-p选项。正在运行的进程可以用frida-ps -U命令查看
这种注入脚本的方法，常用于hook在App就启动期就执行的函数。
frida运行过程中，执行%resume重新注入，执行%reload来重新加载脚本；执行exit结束脚本注入&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;hook目标方法，打印它的返回值：&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Java&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;available&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Java&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;perform&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 获取目标类&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetClass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Java&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d10&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    
        &lt;span class=&quot;c1&quot;&gt;// Hook 目标方法&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;targetClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;implementation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 调用原始方法并获取返回值&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;returnValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 打印返回值&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xxxxxxxx&lt;/span&gt;  &lt;span class=&quot;nc&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;value:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;returnValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 返回原始返回值&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;returnValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;注意：defpackage下的类不需要写包路径，直接写类名即可&lt;/p&gt;

&lt;h1 id=&quot;frida-dexdump脱壳&quot;&gt;Frida-dexDump脱壳&lt;/h1&gt;

&lt;p&gt;参考：https://blog.csdn.net/qq_37888591/article/details/124963201&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Windows环境安装Frida-Dexdump
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip3 install Frida-Dexdump
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;模拟器或者Root手机安装frida-sever&lt;/p&gt;

    &lt;p&gt;进入模拟器或者手机放置frida-sever的目录&lt;/p&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; cd /data/local/tmp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;查看&lt;/p&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; ls
 ./frida-server-15.1.22-android-x86
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;可能有权限问题，可以先给权限 755
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;adb shell chmod 755 /data/local/tmp/frida-server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;然后新开一个cmd窗口，执行：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;frida-dexdump -FU
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;会在frida-dexdump安装目录下输出模拟器当前运行的APP的dex文件，&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;查看frida-dexdump安装目录：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;F:\YY&amp;gt;where frida-dexdump
E:\Program Files\python36\Scripts\frida-dexdump.exe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;部分脱壳命令：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;指定App的应用名称：frida-dexdump -U -n 一点资讯&lt;/li&gt;
  &lt;li&gt;指定App的应用进程ID：frida-dexdump -U -p 3302&lt;/li&gt;
  &lt;li&gt;指定App的应用包名：frida-dexdump -U -f com.hipu.yidian&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 01 Aug 2024 00:00:00 +0000</pubDate>
        <link>https://ruilin.github.io/blog/2024/08/01/Frida.html</link>
        <guid isPermaLink="true">https://ruilin.github.io/blog/2024/08/01/Frida.html</guid>
        
        <category>Frida</category>
        
        <category>Hook</category>
        
        <category>脱壳</category>
        
        
      </item>
    
      <item>
        <title>基于Flutter实现的JS脚本引擎</title>
        <description>&lt;p&gt;JsEngine是基于webview_flutter插件内置的JS Runtime实现的JS扩展，能够实现动态运行JavaScript脚本，相比flutter_js等三方库更轻量级，不需要增加额外的size，能够支持JS HTTP网络请求、SP数据持久化以及对promise的支持。
由于本引擎是基于WebView内核实现，能够通过Google和AppStore审核。&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;接入方式：&lt;a href=&quot;https://pub.dev/packages/flutter_js_engine&quot;&gt;https://pub.dev/packages/flutter_js_engine&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;源码：&lt;a href=&quot;https://github.com/ruilin/flutter_js_engine&quot;&gt;https://github.com/ruilin/flutter_js_engine&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;http请求&quot;&gt;Http请求&lt;/h2&gt;
&lt;p&gt;JsEngine内置了网络请求接口，支持promise语法。&lt;/p&gt;

&lt;p&gt;JS发起Http请求：&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;HttpSender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sendRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;https://www.baidu.com/s&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;wd&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%E6%97%A5%E5%8E%86&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;sendRequest ok: &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;run_callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 返回数据到dart层&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;数据存储&quot;&gt;数据存储&lt;/h2&gt;
&lt;p&gt;JsEngine内置支持数据以&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key-value&lt;/code&gt;的形式持久化存储，同时也支持promise语法。&lt;/p&gt;

&lt;p&gt;JS存储和读取数据：&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;SpStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ijk&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;oh yep&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;SpStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ijk&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;SpStorage got ijk promise value:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;传参&quot;&gt;传参&lt;/h2&gt;
&lt;p&gt;⚠️ JsEngine约定以‘run_’前缀的变量名作为引擎内置的全局变量。&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;接口通过params进行传参，在JS脚本中可通过内置的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run_params&lt;/code&gt;获取传入的运行参数:
    &lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;JsEngine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;runCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;console.log(&quot;run_params:&quot;, run_params);&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;params:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;dadada&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;在JS中获取参数
    &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;run_params:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;run_params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;运行环境信息&quot;&gt;运行环境信息&lt;/h2&gt;
&lt;p&gt;获取JS运行环境的信息：&lt;/p&gt;
&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;JsEngine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;runCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;console.log(&quot;run_info:&quot;, run_info[&apos;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;]);&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;目前支持的信息包括：
|  字段名   | 定义  |
|  —-  | —-  |
| system  | 系统 |
| system_ver  | 系统版本 |
| locale  | 语言码 |
| engine_ver  | 引擎版本 |&lt;/p&gt;

&lt;h2 id=&quot;预加载全局lib&quot;&gt;预加载全局lib&lt;/h2&gt;
&lt;p&gt;默认情况下，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JsEngine.runCode()&lt;/code&gt;是在隔离环境中运行，但JsEngine同时也支持全局模式：&lt;/p&gt;
&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;JsEngine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;loadLib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;代理日志输出&quot;&gt;代理日志输出&lt;/h2&gt;
&lt;p&gt;代理日志能够让JS输出日志到dart层：&lt;/p&gt;
&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;JsEngine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setLogProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$tag&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$log&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;JS调用&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;run_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;hello world!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;完整示例&quot;&gt;完整示例&lt;/h2&gt;
&lt;div class=&quot;language-dart highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;JsEngine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setDebugMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;JsEngine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setLogProxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;JsEngine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;runCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&apos;&apos;
          console.log(&quot;run_params:&quot;, run_params);
          console.log(&quot;run_info:&quot;, run_info[&apos;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;]);
          HttpSender.sendRequest(&quot;https://www.baidu.com/s&quot;, null, null, {
                  &quot;wd&quot;: &quot;%E6%97%A5%E5%8E%86&quot;
              }, &quot;GET&quot;).then((value) =&amp;gt;{
                console.log(&quot;sendRequest ok: &quot;, value);
                run_callback(value);
              });
          SpStorage.set(&quot;abc&quot;, &quot;என்ன Yogi இப்படி சொல்லிட்ட |&quot;);
          SpStorage.set(&quot;ijk&quot;, &quot;oh yep&quot;);
          SpStorage.get(&quot;abc&quot;).then((value) =&amp;gt;{
            console.log(&quot;SpStorage got abc promise value:&quot;, value);
          });
          SpStorage.get(&quot;ijk&quot;).then((value) =&amp;gt;{
            console.log(&quot;SpStorage got ijk promise value:&quot;, value);
          });
        &apos;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;params:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;dadada&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;callback:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;logI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;JsEngine-console runJsCode res: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$json&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;logI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;JsEngine-console future runJsCode res: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$json&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sun, 07 Apr 2024 00:00:00 +0000</pubDate>
        <link>https://ruilin.github.io/blog/2024/04/07/JsEngine.html</link>
        <guid isPermaLink="true">https://ruilin.github.io/blog/2024/04/07/JsEngine.html</guid>
        
        <category>Flutter</category>
        
        <category>Javascript</category>
        
        
      </item>
    
      <item>
        <title>Android搭建React-Native框架</title>
        <description>&lt;p&gt;由于官方文档有些内容没更新，搭建环境过程中可能会遇到各种问题，这里把搭建过程中踩过的坑记录一下，作为对官方指引的一个补充。&lt;/p&gt;

&lt;p&gt;官方文档：&lt;a href=&quot;https://reactnative.cn/docs/integration-with-existing-apps&quot;&gt;https://reactnative.cn/docs/integration-with-existing-apps&lt;/a&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;一关于-rn-版本&quot;&gt;一、关于 RN 版本&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;目前RN最新版本是0.71.4，因0.71.0后依赖的文件结构有变化，但文档是有些地方没更新，比较多坑。建议使用0.71.0以下。&lt;/li&gt;
  &lt;li&gt;从 RN 0.64 开始，要求Android minSdkVersion&amp;gt;=21，即5.0以上、gradle7.3.3，如果要降到 19，只能把RN版本降低至0.63.x&lt;/li&gt;
  &lt;li&gt;0.64开始支持Hermes JS 引擎，需要手动配置开启，0.70以上默认开启。Hermes对启动时间，包体积、内存占用都有较大的优化：&lt;/li&gt;
&lt;/ul&gt;

&lt;div&gt;    
	&lt;img src=&quot;https://raw.githubusercontent.com/ruilin/blog/master/assets/img/f001.png&quot; style=&quot;width: 800px&quot; /&gt;
&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;同时考虑到0.69以上最低支持到IOS13，目前业务需要支持IOS11，综合评估使用0.68版本&lt;/li&gt;
  &lt;li&gt;版本差异对比工具：&lt;a href=&quot;https://react-native-community.github.io/upgrade-helper/?from=0.63.4&amp;amp;to=0.64.0&quot;&gt;https://react-native-community.github.io/upgrade-helper/?from=0.63.4&amp;amp;to=0.64.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;二rn-运行环境搭建&quot;&gt;二、RN 运行环境搭建&lt;/h2&gt;

&lt;h4 id=&quot;1-配置项目目录结构&quot;&gt;1. 配置项目目录结构&lt;/h4&gt;
&lt;p&gt;首先创建一个空目录用于存放 React Native 项目，然后在其中创建一个/android子目录，把你现有的 Android 项目拷贝到/android子目录中。&lt;/p&gt;
&lt;h4 id=&quot;2-安装-javascript-依赖包&quot;&gt;2. 安装 JavaScript 依赖包&lt;/h4&gt;
&lt;p&gt;在项目根目录下创建一个名为package.json的空文本文件，然后填入以下内容：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;MyReactNativeApp&quot;,
  &quot;version&quot;: &quot;0.0.1&quot;,
  &quot;private&quot;: true,
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;yarn react-native start&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;使用 yarn 安装 React 和 React Native 模块。请打开一个终端/命令提示行，进入到项目目录中（即包含有 package.json 文件的目录），然后运行下列命令来安装：
这里指定版本0.68.0，默认是最新版本0.71.4&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ yarn add react-native@0.68.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;安装完成后往回拉，可能会出现如下警告：&lt;/p&gt;
&lt;div&gt;    
	&lt;img src=&quot;https://raw.githubusercontent.com/ruilin/blog/master/assets/img/f002.png&quot; style=&quot;width: 800px&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;重点关注“has unmet peer dependency…”的警告，并运行以下命令，按要求安装对应的依赖：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ yarn add react@17.0.2 
$ yarn add @babel/core@
$ yarn add @babel/preset-env@7.1.6
$ yarn add uglify-js@3.13.0
$ yarn add typescript@3.6.0-beta
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所有 JavaScript 依赖模块都会被安装到项目根目录下的node_modules/目录中（这个目录我们原则上不复制、不移动、不修改、不上传，随用随装）。
把node_modules/目录记录到.gitignore文件中（即不上传到版本控制系统，只保留在本地）。&lt;/p&gt;

&lt;h3 id=&quot;集成-rn-到已有项目中&quot;&gt;集成 RN 到已有项目中&lt;/h3&gt;

&lt;h4 id=&quot;1-配置-maven&quot;&gt;1. 配置 maven&lt;/h4&gt;

&lt;p&gt;在你的 app 中 build.gradle 文件中添加 React Native 和 JSC 引擎依赖:
dependencies {
    …
    implementation “com.facebook.react:react-native:+” // From node_modules
    implementation “org.webkit:android-jsc:+”
}
在项目的 build.gradle 文件中为 React Native 和 JSC 引擎添加 maven 源的路径，必须写在 “allprojects” 代码块中&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;allprojects {
    repositories {
        maven {
            // All of React Native (JS, Android binaries) is installed from npm
            url &quot;$rootDir/../node_modules/react-native/android&quot;
        }
        maven {
            // Android JSC is installed from npm
            url(&quot;$rootDir/../node_modules/jsc-android/dist&quot;)
        }
        ...
    }
    ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果在gradle7.x 的settings.gradle有如下配置，需要去掉：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven {
            // All of React Native (JS, Android binaries) is installed from npm
            url &quot;$rootDir/../node_modules/react-native/android&quot;
        }
        maven {
            // Android JSC is installed from npm
            url(&quot;$rootDir/../node_modules/jsc-android/dist&quot;)
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;2-启用原生模块的自动链接&quot;&gt;2. 启用原生模块的自动链接&lt;/h4&gt;

&lt;p&gt;要使用自动链接的功能，我们必须将其应用于几个地方。首先，将以下内容添加到settings.gradle:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apply from: file(&quot;../node_modules/@react-native-community/cli-platform-android/native_modules.gradle&quot;); applyNativeModulesSettingsGradle(settings)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面这一步需要确保AndroidManifest.xml配置了package，否则Gradle sycn会失败
参考：&lt;a href=&quot;https://stackoverflow.com/questions/58952564/error-unable-to-determine-the-current-character-it-is-not-a-string-number-ar&quot;&gt;https://stackoverflow.com/questions/58952564/error-unable-to-determine-the-current-character-it-is-not-a-string-number-ar&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    package=&quot;com.example.rntest&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;接下来，在app/build.gradle的最底部添加以下内容:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apply from: file(&quot;../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle&quot;); applyNativeModulesAppBuildGradle(project)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;3-配置权限&quot;&gt;3. 配置权限&lt;/h4&gt;
&lt;p&gt;接着，在 AndroidManifest.xml 清单文件中声明网络权限:&lt;/p&gt;

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

&lt;p&gt;如果需要访问 DevSettingsActivity 界面（即开发者菜单），则还需要在 AndroidManifest.xml 中声明:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;activity android:name=&quot;com.facebook.react.devsupport.DevSettingsActivity&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;开发者菜单一般仅用于在开发时从 Packager 服务器刷新 JavaScript 代码，所以在正式发布时你可以去掉这一权限。&lt;/p&gt;

&lt;h4 id=&quot;4-允许明文传输http-接口-api-level-28&quot;&gt;4. 允许明文传输（http 接口） (API level 28+)&lt;/h4&gt;

&lt;p&gt;从 Android 9 (API level 28)开始，默认情况下明文传输（http 接口）是禁用的，只能访问 https 接口。这将阻止应用程序连接到Metro bundler。下面的更改允许调试版本中的明文通信。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;为 debug 版本启用 usesCleartextTraffic选项
在src/debug/AndroidManifest.xml中添加usesCleartextTraffic选项：&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;!-- ... --&amp;gt;
&amp;lt;application
  android:usesCleartextTraffic=&quot;true&quot; tools:targetApi=&quot;28&quot; &amp;gt;
  &amp;lt;!-- ... --&amp;gt;
&amp;lt;/application&amp;gt;
&amp;lt;!-- ... --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果希望在正式打包后也能继续访问 http 接口，则需要在src/main/AndroidManifest.xml中也添加这一选项。
要了解有关网络安全配置和明文通信策略的更多信息，请参阅此链接。&lt;/p&gt;

&lt;h4 id=&quot;5-测试集成结果&quot;&gt;5. 测试集成结果&lt;/h4&gt;
&lt;p&gt;你已经完成了将 React Native 与当前应用程序集成的所有基本步骤。现在我们将启动Metro bundler来构建index.bundle包，并通过本地主机提供服务。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;运行 Metro 服务
运行应用首先需要启动开发服务器（Metro）。你只需在项目根目录中执行以下命令即可：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ yarn start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;运行你的应用
保持 Metro 的窗口运行不要关闭，然后像往常一样编译运行你的 Android 应用(在命令行中执行./gradlew installDebug或是在 Android Studio 中编译运行)。
或者合并上面两步直接运行：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ yarn android
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;h4 id=&quot;6-打离线js包&quot;&gt;6. 打离线js包&lt;/h4&gt;
    &lt;p&gt;通过以上步骤智能在模拟器成功连接Metro运行，真机运行不了，但可以把 js bundle 打成离线包，放到assets文件夹成功运行，打离线包命令：&lt;/p&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ react-native bundle --entry-file index.js --bundle-output ./android/app/src/main/assets/index.android.bundle --platform android --assets-dest ./android/app/src/main/res --dev false
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;参考：&lt;a href=&quot;https://cloud.tencent.com/developer/article/1953650&quot;&gt;https://cloud.tencent.com/developer/article/1953650&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;7-输出log&quot;&gt;7. 输出Log&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;react-native log-android
react-native log-ios
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;8-常见问题&quot;&gt;8. 常见问题&lt;/h4&gt;
&lt;p&gt;如果在Metro reload出现异常，可以通过以下命令react-native info查看环境是否有问题，有可能是环境变量没配等问题。&lt;/p&gt;

&lt;h2 id=&quot;三热更新方案&quot;&gt;三、热更新方案&lt;/h2&gt;
&lt;p&gt;业内使用最多的是微软的CodePush框架，优势包括：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;微软出品，大厂保证&lt;/li&gt;
  &lt;li&gt;良好的多环境支持（Testing，Staging, Production）&lt;/li&gt;
  &lt;li&gt;灰度发布、自动回滚等等特性&lt;/li&gt;
  &lt;li&gt;良好的数据统计支持：下载、安装、出错一目了然&lt;/li&gt;
  &lt;li&gt;强大的 CLI 工具，一个终端搞定全部流程
同时微软也提供了AppCenter托管平台，在调研阶段我们先使用它来测试，正式接入时需要私有化部署我们自己的CodePush服务。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;参考：https://github.com/microsoft/react-native-code-push/blob/master/docs/setup-android.md#plugin-installation-and-configuration-for-react-native-060-version-and-above-android&lt;/p&gt;

&lt;p&gt;https://github.com/microsoft/react-native-code-push/blob/master/docs/setup-android.md#plugin-installation-and-configuration-for-react-native-060-version-and-above-android&lt;/p&gt;
&lt;h4 id=&quot;1-安装环境&quot;&gt;1. 安装环境&lt;/h4&gt;
&lt;p&gt;安装Appcenter命令工具&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ npm install -g appcenter-cli
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;其他依赖&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ yarn add superagent@7.0.2
$ yarn add react-native-code-push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;2-android-项目添加依赖&quot;&gt;2. Android 项目添加依赖&lt;/h4&gt;
&lt;p&gt;在 android/settings.gradle 文件末尾添加&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;include &apos;:app&apos;, &apos;:react-native-code-push&apos;
project(&apos;:react-native-code-push&apos;).projectDir = new File(rootProject.projectDir, &apos;../node_modules/react-native-code-push/android/app&apos;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在android/app/build.gradle添加&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apply from: &quot;../../node_modules/react-native-code-push/android/codepush.gradle&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Application.java添加&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
// 1. Import the plugin class.import com.microsoft.codepush.react.CodePush;

public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {

        // 2. Override the getJSBundleFile method in order to let// the CodePush runtime determine where to get the JS// bundle location from on each app start@Overrideprotected String getJSBundleFile() {
            return CodePush.getJSBundleFile();
        }
    };
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;3-在appcenter创建应用&quot;&gt;3. 在Appcenter创建应用&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;appcenter apps create -d MyApp-Android -o Android -p React-Native
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;创建部署&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;appcenter codepush deployment add -a &amp;lt;ownerName&amp;gt;/&amp;lt;appName&amp;gt; Staging 
appcenter codepush deployment add -a &amp;lt;ownerName&amp;gt;/&amp;lt;appName&amp;gt; Production
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;查看appcenter DeploymentKey&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;appcenter codepush deployment list -a zruilin/RNTest --displayKeys
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;4-配置deploymentkey&quot;&gt;4. 配置DeploymentKey&lt;/h4&gt;
&lt;p&gt;把DeploymentKey添加到项目strings.xml&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &amp;lt;resources&amp;gt;
     &amp;lt;string name=&quot;app_name&quot;&amp;gt;AppName&amp;lt;/string&amp;gt;
     &amp;lt;string moduleConfig=&quot;true&quot; name=&quot;CodePushDeploymentKey&quot;&amp;gt;DeploymentKey&amp;lt;/string&amp;gt;
 &amp;lt;/resources&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;或者在android/app/build.gradle中配置&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;android {
    ...
    buildTypes {
        debug {
            ...
            // Note: CodePush updates shouldn&apos;t be tested in Debug mode as they&apos;re overriden by the RN packager. However, because CodePush checks for updates in all modes, we must supply a key.
            resValue &quot;string&quot;, &quot;CodePushDeploymentKey&quot;, &apos;&quot;&quot;&apos;
            ...
        }
        releaseStaging {
            ...
            resValue &quot;string&quot;, &quot;CodePushDeploymentKey&quot;, &apos;&quot;&amp;lt;INSERT_STAGING_KEY&amp;gt;&quot;&apos;
            // Note: It&apos;s a good idea to provide matchingFallbacks for the new buildType you create to prevent build issues
            // Add the following line if not already there
            matchingFallbacks = [&apos;release&apos;]
            ...
        }
        release {
            ...
            resValue &quot;string&quot;, &quot;CodePushDeploymentKey&quot;, &apos;&quot;&amp;lt;INSERT_PRODUCTION_KEY&amp;gt;&quot;&apos;
            ...
        }
    }
    ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;5-js代码也需要引入codepush&quot;&gt;5. JS代码也需要引入CodePush&lt;/h4&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import codePush from &apos;react-native-code-push&apos;

let codePushOptions = { checkFrequency: codePush.CheckFrequency.ON_APP_RESUME }
export default codePush(codePushOptions)(App);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;6-发布到appcenter&quot;&gt;6. 发布到appcenter&lt;/h4&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ appcenter codepush release-react -a zruilin/Awesomeproject -d Staging -t 1.0.0 -m --development false --description hello...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;查看创建的应用：appcenter apps list&lt;/p&gt;
&lt;h4 id=&quot;7-codepush私有部署版&quot;&gt;7. CodePush私有部署版&lt;/h4&gt;
&lt;p&gt;如果已经部署了codepush私有服务，则安装&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ npm install -g code-push-cli@2.1.9
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;登录&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;code-push login http://172.26.130.47:3000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;查看app key&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;code-push deployment ls xh-android --k
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;四问题记录&quot;&gt;四、问题记录&lt;/h2&gt;
&lt;h4 id=&quot;1-报错non-whitespace-before-first-tag&quot;&gt;1. 报错：Non-whitespace before first tag.&lt;/h4&gt;
&lt;p&gt;原因：不要在工程目录下放打包的.apk（参考：&lt;a href=&quot;https://github.com/invertase/react-native-firebase/issues/2179&quot;&gt;https://github.com/invertase/react-native-firebase/issues/2179&lt;/a&gt;）&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/Users/[UserName]/.gradle/caches/transforms-3/650da0710ee913bbca78c9b54dabf437/transformed/jetified-react-native-0.71.0-rc.0-debug-api.jar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Mon, 06 Mar 2023 00:00:00 +0000</pubDate>
        <link>https://ruilin.github.io/blog/2023/03/06/rn.html</link>
        <guid isPermaLink="true">https://ruilin.github.io/blog/2023/03/06/rn.html</guid>
        
        <category>React-Native</category>
        
        <category>RN</category>
        
        
      </item>
    
      <item>
        <title>Android生成设备唯一ID方案</title>
        <description>&lt;p&gt;设备唯一ID对业务有非常重要的作用，比如新用户注册归因和业务风控，都对设备ID的稳定性有着很高的要求。稳定性主要表现在两个方面:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;同一台设备，卸载重装APP，生成ID不变；&lt;/li&gt;
  &lt;li&gt;同一台设备，不同的APP生成的ID是一致的。
要保持这种稳定性，主要由两种策略来实现，一种是在设备上持久化保存ID。一种是通过设备较为稳定的属性作为因子生成ID。&lt;/li&gt;
&lt;/ol&gt;

&lt;!--more--&gt;

&lt;h3 id=&quot;android持久化分析&quot;&gt;Android持久化分析&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;SD卡公共目录保存，即使卸载APP也能还原hdid，但依赖业务动态申请SD卡访问权限才能生效。
SDCard访问权限演变：&lt;/li&gt;
  &lt;li&gt;Android 6（API 23）以下只需要在清单文件申请权限。&lt;/li&gt;
  &lt;li&gt;Android 6（API 23）以上需要APP向业务动态申请访问权限。&lt;/li&gt;
  &lt;li&gt;Android 10（API 29）以上需要清单文件中application节点加上。android:requestLegacyExternalStorage=”true”属性。&lt;/li&gt;
  &lt;li&gt;Android 11 开始APP默认只能访问沙盒目录，无法访问公共SDCard目录，如果以Android10以下为目标平台仍可以兼容访问（参考&lt;a href=&quot;https://developer.android.com/about/versions/11/privacy/storage?hl=zh-cn&quot;&gt;《Android 11 中的存储机制更新》&lt;/a&gt;）。&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;总结&quot;&gt;总结：&lt;/h4&gt;
&lt;p&gt;随着系统权限的逐步收紧，SDCard公共目录保存也将逐渐被淘汰，要在卸载App后依然保存数据在手机上基本没有完全可靠的方案。&lt;/p&gt;

&lt;h3 id=&quot;生成因子分析&quot;&gt;生成因子分析&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Android_id：
    &lt;ul&gt;
      &lt;li&gt;唯一决定于应用签名、用户和设备三者的组合&lt;/li&gt;
      &lt;li&gt;AndroidID较为稳定，但无法跨业务，如果业务APP签名不同则产生的设备ID不一样。&lt;/li&gt;
      &lt;li&gt;参考：&lt;a href=&quot;https://developer.android.com/about/versions/oreo/android-8.0-changes.html&quot;&gt;https://developer.android.com/about/versions/oreo/android-8.0-changes.html&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;参考：&lt;a href=&quot;https://www.cnblogs.com/kekec/p/12544674.html&quot;&gt;https://www.cnblogs.com/kekec/p/12544674.html&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Mac地址：
    &lt;ul&gt;
      &lt;li&gt;需要申明ACCESS_WIFI_STATE，但Android 10.0后获取到的是随机生成的MAC（参考《Android随机分配 MAC 地址》）。&lt;/li&gt;
      &lt;li&gt;实测在Android10设备上，切换wifi就会发生改变。&lt;/li&gt;
      &lt;li&gt;Mac在Android8.0以后获取的值不可靠。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;IMEI：
    &lt;ul&gt;
      &lt;li&gt;Android 10以下系统，需要动态申请电话权限(READ_PHONE_STATE)，&lt;/li&gt;
      &lt;li&gt;Android 10以上无法获取，需要READ_PRIVILEGED_PHONE_STATE权限，但该权限需要系统特许的或者官方应用才能获取&lt;/li&gt;
      &lt;li&gt;参考：设备标识符官方说明&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;OAID
    &lt;ul&gt;
      &lt;li&gt;在高版本系统增加作为因子，国内厂商普遍在Android10之后开始支持OAID：&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;    
	&lt;img src=&quot;https://raw.githubusercontent.com/ruilin/blog/master/assets/img/d002.png&quot; style=&quot;width: 600px&quot; /&gt;
&lt;/div&gt;

&lt;h3 id=&quot;最终方案&quot;&gt;最终方案&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;优先使用OAID+Host，可以确保设备ID的稳定性，并且实现跨APP。&lt;/li&gt;
  &lt;li&gt;低版本系统多数设备无法获取OAID，采用Mac + Build.SERIAL 作为因子实现。&lt;/li&gt;
  &lt;li&gt;一旦前面都获取失败，则采用兜底方案：Android_id + Build.Host。&lt;/li&gt;
&lt;/ul&gt;

&lt;div&gt;    
	&lt;img src=&quot;https://raw.githubusercontent.com/ruilin/blog/master/assets/img/d003.jpg&quot; style=&quot;width: 600px&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;需要注意的是获取OAID结果回调才生成设备ID，由于OAID在有些设备上获取比较慢，甚至没有回调（内部是通过AIDL跨进程调系统服务获取），因此需要设置一个超时时间（比如1秒），超时则放弃使用，改为兜底方案。&lt;/p&gt;

&lt;h3 id=&quot;验证结果&quot;&gt;验证结果&lt;/h3&gt;
&lt;div&gt;    
	&lt;img src=&quot;https://raw.githubusercontent.com/ruilin/blog/master/assets/img/d005.jpg&quot; /&gt;
&lt;/div&gt;

</description>
        <pubDate>Wed, 21 Sep 2022 00:00:00 +0000</pubDate>
        <link>https://ruilin.github.io/blog/2022/09/21/deviceid.html</link>
        <guid isPermaLink="true">https://ruilin.github.io/blog/2022/09/21/deviceid.html</guid>
        
        <category>Android</category>
        
        <category>DeviceId</category>
        
        <category>隐私权限</category>
        
        
      </item>
    
      <item>
        <title>编译最新版FFmpeg for Android NDK(r21)</title>
        <description>&lt;p&gt;这里介绍在Mac下使用最新版NDK(r21)编译最新版的FFmpeg(4.3.1)，在NDK r17之后弃用了gcc，改用clang进行编译，因此最新版本NDK主要解决用clang配置编译ffmpeg。&lt;/p&gt;

&lt;!--more--&gt;

&lt;h3 id=&quot;准备&quot;&gt;准备&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;NDK
下载NDK(r21)
解压NDK
（这一步网上资料较多不再赘述）&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;FFmpeg：
下载官网最新版本的FFmpeg(4.3.1)：&lt;a href=&quot;https://www.ffmpeg.org/download.html#releases&quot;&gt;https://www.ffmpeg.org/download.html#releases&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/ruilin/blog/master/assets/img/e001.webp&quot; alt=&quot;FFmpeg文件目录&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;修改编译配置&quot;&gt;修改编译配置&lt;/h3&gt;
&lt;blockquote&gt;
  &lt;p&gt;默认配置是编译生成Linux环境下的库，需要修改为生成Android环境下的so&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;用文本编辑器（比如Sublime text）打开ffmpeg文件夹下的configure文件，找到以下配置进行替换：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# 注释掉默认configure 文件中的配置：
# SLIBNAME_WITH_MAJOR=&apos;$(SLIBNAME).$(LIBMAJOR)&apos;
# LIB_INSTALL_EXTRA_CMD=&apos;$$(RANLIB) &quot;$(LIBDIR)/$(LIBNAME)&quot;&apos;
# SLIB_INSTALL_NAME=&apos;$(SLIBNAME_WITH_VERSION)&apos;
# SLIB_INSTALL_LINKS=&apos;$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)&apos;

# 替换为：
SLIBNAME_WITH_MAJOR=&apos;$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)&apos;
LIB_INSTALL_EXTRA_CMD=&apos;?(RANLIB) &quot;$(LIBDIR)/$(LIBNAME)&quot;&apos;
SLIB_INSTALL_NAME=&apos;$(SLIBNAME_WITH_MAJOR)&apos;
SLIB_INSTALL_LINKS=&apos;$(SLIBNAME)&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;创建编译脚本&quot;&gt;创建编译脚本&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;打开终端，cd到ffmpeg目录下，执行以下命令创建脚本文件：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;touch build_android.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;用vim打开文件：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vim build_android.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;复制以下shell脚本到 build_android.sh：
（注意：NDK需要修改成自己的路径）&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/bin/bash
# 以下路径需要修改成自己的NDK目录
TOOLCHAIN=/Users/xxx/Library/Android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/
# 最低支持的android sdk版本
API=29

function build_android
{
# 打印
echo &quot;Compiling FFmpeg for $CPU&quot;
# 调用同级目录下的configure文件
./configure \
# 指定输出目录
    --prefix=$PREFIX \
# 各种配置项，想详细了解的可以打开configure文件找到Help options:查看
    --disable-neon \
    --disable-hwaccels \
    --disable-gpl \
    --disable-postproc \
    --enable-shared \
    --enable-jni \
    --disable-mediacodec \
    --disable-decoder=h264_mediacodec \
    --disable-static \
    --disable-doc \
    --disable-ffmpeg \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-avdevice \
    --disable-doc \
    --disable-symver \
    --cross-prefix=$CROSS_PREFIX \
    --target-os=android \
    --arch=$ARCH \
    --cpu=$CPU \
    --cc=$CC
    --cxx=$CXX
    --enable-cross-compile \
    --sysroot=$SYSROOT \
    --extra-cflags=&quot;-Os -fpic $OPTIMIZE_CFLAGS&quot; \
    --extra-ldflags=&quot;$ADDI_LDFLAGS&quot; \
    $ADDITIONAL_CONFIGURE_FLAG
make clean
make
make install
echo &quot;The Compilation of FFmpeg for $CPU is completed&quot;
}

#armv8-a
ARCH=arm64
CPU=armv8-a
# r21版本的ndk中所有的编译器都在/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/目录下（clang）
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
# NDK头文件环境
SYSROOT=$TOOLCHAIN/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
# so输出路径
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS=&quot;-march=$CPU&quot;
build_android

# 交叉编译工具目录,对应关系如下
# armv8a -&amp;gt; arm64 -&amp;gt; aarch64-linux-android-
# armv7a -&amp;gt; arm -&amp;gt; arm-linux-androideabi-
# x86 -&amp;gt; x86 -&amp;gt; i686-linux-android-
# x86_64 -&amp;gt; x86_64 -&amp;gt; x86_64-linux-android-

# CPU架构
#armv7-a
ARCH=arm
CPU=armv7-a
CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
SYSROOT=$TOOLCHAIN/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS=&quot;-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU &quot;
build_android

#x86
ARCH=x86
CPU=x86
CC=$TOOLCHAIN/bin/i686-linux-android$API-clang
CXX=$TOOLCHAIN/bin/i686-linux-android$API-clang++
SYSROOT=$TOOLCHAIN/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/i686-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS=&quot;-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32&quot;
build_android

#x86_64
ARCH=x86_64
CPU=x86-64
CC=$TOOLCHAIN/bin/x86_64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/x86_64-linux-android$API-clang++
SYSROOT=$TOOLCHAIN/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/x86_64-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS=&quot;-march=$CPU -msse4.2 -mpopcnt -m64 -mtune=intel&quot;
# 方法调用
build_android
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;输入&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:wq&lt;/code&gt;保存&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;开始编译&quot;&gt;开始编译&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;先给脚本添加权限：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;chmod 777 ./build_android.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;然后就可以运行脚本编译了：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./build_android.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;编译完成&quot;&gt;编译完成&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/ruilin/blog/master/assets/img/e002.webp&quot; alt=&quot;编译完成&quot; /&gt;&lt;/p&gt;

&lt;p&gt;编译完成后将输出文件&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/ruilin/blog/master/assets/img/e003.webp&quot; alt=&quot;so输出&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;常见问题&quot;&gt;常见问题&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;x86版本编译问题
如果编译完后发现没有输出x86 so，检查是否有输出以下错误信息：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;nasm/yasm not found or too old. Use --disable-x86asm for a crippled build.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;原因是编译x86依赖汇编器nasm或者yasm没有安装。
因此需要先安装好汇编器，这里我们选择其一进行安装
安装方法：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install yasm&lt;/code&gt;（通过homebrew 进行安装）
安装成功再重新编译即可。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Android 加载x86 so问题
在运行时加载so报错：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;java.lang.UnsatisfiedLinkError: dlopen failed: /data/app/xxx.xxx.xxx-1/lib/x86/libavcodec.so: has text relocations
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;原因是android 6.0之后，系统做了限制。&lt;a href=&quot;https://developer.android.com/about/versions/marshmallow/android-6.0-changes.html&quot;&gt;Android 6.0 changes&lt;/a&gt;&lt;/p&gt;
    &lt;blockquote&gt;
      &lt;p&gt;On previous versions of Android, if your app requested the system to load a shared library with text relocations, the system displayed a warning but still allowed the library to be loaded. Beginning in this release, the system rejects this library if your app’s target SDK version is 23 or higher. To help you detect if a library failed to load, your app should log the dlopen(3) failure, and include the problem description text that the dlerror(3) call returns. To learn more about handling text relocations, see this guide.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;解决方法：需要在编译选项中增加参数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--disable-asm&lt;/code&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/ruilin/blog/master/assets/img/e004.webp&quot; alt=&quot;增加参数&quot; /&gt;
如果出现以下错误&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dlopen failed: cannot locate symbol &quot;iconv_open&quot;，”iconv_close“
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;只能降低API版本，修改build_android.sh：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;API=26&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;[参考]&lt;/p&gt;

&lt;p&gt;[1] https://yesimroy.gitbooks.io/android-note/content/compile_ffmpeg_for_android.html&lt;/p&gt;

&lt;p&gt;[2] https://stackoverflow.com/questions/57681336/build-ffmpeg-4-2-with-android-ndk-r20&lt;/p&gt;

&lt;p&gt;[3] https://www.jianshu.com/p/feab970fd74c&lt;/p&gt;

&lt;p&gt;[4] https://juejin.im/post/6844903945496690696&lt;/p&gt;

&lt;p&gt;[5] https://blog.csdn.net/marco_0631/article/details/73292199&lt;/p&gt;

&lt;p&gt;[6] https://www.jianshu.com/p/fd938a51b01f&lt;/p&gt;

&lt;p&gt;[7] https://github.com/tianshaokai/ffmpegbuild&lt;/p&gt;
</description>
        <pubDate>Tue, 13 Oct 2020 00:00:00 +0000</pubDate>
        <link>https://ruilin.github.io/blog/2020/10/13/build_ffmpeg.html</link>
        <guid isPermaLink="true">https://ruilin.github.io/blog/2020/10/13/build_ffmpeg.html</guid>
        
        <category>FFmpeg</category>
        
        <category>ndk</category>
        
        
      </item>
    
      <item>
        <title>项目管理心得</title>
        <description>&lt;p&gt;项目管理是一个团队工作组织必不可少的一个环节，项目管理是为团队目标服务的，是以达成目标为导向的工作。&lt;/p&gt;

&lt;p&gt;以下是我在工作中的经验和个人对项目管理的理解的总结。&lt;/p&gt;

&lt;p&gt;随着经验的积累，后续也将继续整理和完善。&lt;/p&gt;

&lt;!--more--&gt;

&lt;div&gt;    
	&lt;img src=&quot;https://raw.githubusercontent.com/ruilin/blog/master/assets/img/c001.jpg&quot; style=&quot;width: 1000px&quot; /&gt;
&lt;/div&gt;
</description>
        <pubDate>Wed, 14 Aug 2019 00:00:00 +0000</pubDate>
        <link>https://ruilin.github.io/blog/2019/08/14/manage.html</link>
        <guid isPermaLink="true">https://ruilin.github.io/blog/2019/08/14/manage.html</guid>
        
        <category>PM</category>
        
        <category>project</category>
        
        
      </item>
    
      <item>
        <title>地理坐标系</title>
        <description>&lt;p&gt;地理坐标系一般是指由经度、纬度和相对高度组成的坐标系，能够标示地球上的任何一个位置。经度和纬度常合称为经纬度，把球面上的经纬度显示在平面地图上需要采用某种地图投影。&lt;/p&gt;

&lt;p&gt;为了与只选用经纬度的在扁球面或球面上的坐标系相区别，采用大地纬度的带有相对高度的三维地理坐标系也称为大地坐标系。采用的地心纬度定义的三维地理坐标系是一种球极坐标系。地理坐标系统还有采用三维笛卡尔坐标系的地心地固坐标系和本地切面坐标系。
&lt;!--more--&gt;&lt;/p&gt;

&lt;h4 id=&quot;经纬度的表示&quot;&gt;经纬度的表示&lt;/h4&gt;
&lt;hr /&gt;
&lt;p&gt;经纬度以度数表示，一般可直接以小数点表示，但亦可把度数的小数点分为角分（1角分等于六十分之一度），和秒（一秒等于六十分之一分）&lt;/p&gt;

&lt;p&gt;度分秒表示（度:分:秒）－49°30’00”-123d30m00s&lt;/p&gt;

&lt;p&gt;度分表示（度:分）－49°30.0’-123d30.0m&lt;/p&gt;

&lt;p&gt;度数表示－49.5000°-123.5000d（一般会有四位小数）&lt;/p&gt;

&lt;h4 id=&quot;经纬度的范围&quot;&gt;经纬度的范围&lt;/h4&gt;
&lt;hr /&gt;
&lt;p&gt;全球经纬度的取值范围为：纬度-90~90，经度-180~180&lt;/p&gt;

&lt;p&gt;中国的经纬度范围大约为：纬度3.86~53.55，经度73.66~135.05&lt;/p&gt;

&lt;p&gt;越北面的地方纬度数值越大，越东面的地方经度数值越大&lt;/p&gt;

&lt;h4 id=&quot;度分转换&quot;&gt;度分转换&lt;/h4&gt;
&lt;hr /&gt;
&lt;p&gt;将度分单位数据转换为度单位数据，公式：度=度+分/60&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;例如： 经度 = 116°20.12&apos;，纬度 = 39°12.34&apos;

经度 = 116 + 20.12 / 60 = 116.33533°

纬度 = 39 + 12.34 / 60 = 39.20567°

分秒转换： 将度分秒单位数据转换为度单位数据，公式：度 = 度 + 分 / 60 + 秒 / 60 / 60
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;例如： 经度 = 116°20&apos;43&quot;，纬度 = 39°12&apos;37&quot;

经度 = 116 + 20 / 60 + 43 / 60 / 60 = 116.34528°

纬度 = 39 + 12 / 60 + 37 / 60 / 60 = 39.21028°
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;参考&lt;/p&gt;

&lt;p&gt;[1]&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%9C%B0%E7%90%86%E5%9D%90%E6%A0%87%E7%B3%BB&quot;&gt;地理坐标系&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[2]&lt;a href=&quot;https://www.cnblogs.com/inteliot/archive/2012/09/14/2684471.html&quot;&gt;中国经纬度范围&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Wed, 14 Aug 2019 00:00:00 +0000</pubDate>
        <link>https://ruilin.github.io/blog/2019/08/14/latlng.html</link>
        <guid isPermaLink="true">https://ruilin.github.io/blog/2019/08/14/latlng.html</guid>
        
        <category>latitude</category>
        
        <category>longitude</category>
        
        <category>map</category>
        
        
      </item>
    
      <item>
        <title>Linux定时任务crontab配置</title>
        <description>&lt;p&gt;crontab命令常见于Unix和类Unix的操作系统之中，用于设置周期性被执行的指令。该命令从标准输入设备读取指令，并将其存放于“crontab”文件中，以供之后读取和执行。该词来源于希腊语chronos（χρόνος），原意是时间。&lt;/p&gt;

&lt;p&gt;通常，crontab储存的指令被守护进程激活，crond常常在后台运行，每一分钟检查是否有预定的作业需要执行。这类作业一般称为cron jobs。&lt;/p&gt;

&lt;!--more--&gt;

&lt;h4 id=&quot;添加任务流程&quot;&gt;添加任务流程&lt;/h4&gt;
&lt;hr /&gt;

&lt;p&gt;编辑定时任务:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;crontab -e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;编辑完需要重启cron服务:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Linux
service cron restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Macos
sudo /usr/sbin/cron start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;任务格式&quot;&gt;任务格式&lt;/h4&gt;
&lt;hr /&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[root@xiaoluo ~]# cat /etc/crontab 
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在以上各个字段中，还可以使用以下特殊字符：&lt;/p&gt;

&lt;p&gt;星号（*）：代表所有可能的值，例如month字段如果是星号，则表示在满足其它字段的制约条件后每月都执行该命令操作。&lt;/p&gt;

&lt;p&gt;逗号（,）：可以用逗号隔开的值指定一个列表范围，例如，“1,2,5,7,8,9”&lt;/p&gt;

&lt;p&gt;中杠（-）：可以用整数之间的中杠表示一个整数范围，例如“2-6”表示“2,3,4,5,6”&lt;/p&gt;

&lt;p&gt;正斜线（/）：可以用正斜线指定时间的间隔频率，例如“0-23/2”表示每两小时执行一次。同时正斜线可以和星号一起使用，例如*/10，如果用在minute字段，表示每十分钟执行一次。&lt;/p&gt;

&lt;p&gt;示例：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# 每天0:30, 12:30 执行命令 command
30 0,12 * * * command
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;crontab命令&quot;&gt;crontab命令&lt;/h4&gt;
&lt;hr /&gt;

&lt;p&gt;格式：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;crontab [-e [UserName]|-l [UserName]|-r [UserName]|-v [UserName]|File ] 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;参数:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-e [UserName]: 执行文字编辑器来设定时程表，内定的文字编辑器是 vi
-r [UserName]: 删除目前的时程表
-l [UserName]: 列出目前的时程表
-v [UserName]:列出用户cron作业的状态
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;参考&lt;/p&gt;

&lt;p&gt;[1]&lt;a href=&quot;https://zh.wikipedia.org/wiki/Cron&quot;&gt;https://zh.wikipedia.org/wiki/Cron&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[2]&lt;a href=&quot;http://www.atool9.com/crontab.php&quot;&gt;http://www.atool9.com/crontab.php&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Tue, 16 Jul 2019 00:00:00 +0000</pubDate>
        <link>https://ruilin.github.io/blog/2019/07/16/crontab.html</link>
        <guid isPermaLink="true">https://ruilin.github.io/blog/2019/07/16/crontab.html</guid>
        
        <category>linux</category>
        
        <category>crontab</category>
        
        
      </item>
    
      <item>
        <title>导航系统概要</title>
        <description>&lt;p&gt;手机导航已成为人们出行必备的一种应用，它为用户提供到达任何目的地的路线以及前往该目的地的过程指导，使用户在陌生环境依然可以享受自由的出行。&lt;/p&gt;

&lt;p&gt;在这里简单介绍导航的技术构成，也作为我在探途地图开发导航功能的实践中的一些经验总结。&lt;/p&gt;

&lt;!--more--&gt;

&lt;h3 id=&quot;手机导航系统流程图&quot;&gt;手机导航系统流程图：&lt;/h3&gt;

&lt;div align=&quot;left&quot;&gt;    
	&lt;img src=&quot;https://raw.githubusercontent.com/ruilin/blog/master/assets/img/b000.jpg&quot; /&gt;
&lt;/div&gt;

</description>
        <pubDate>Mon, 08 Jul 2019 00:00:00 +0000</pubDate>
        <link>https://ruilin.github.io/blog/2019/07/08/nav.html</link>
        <guid isPermaLink="true">https://ruilin.github.io/blog/2019/07/08/nav.html</guid>
        
        <category>navigation</category>
        
        
      </item>
    
      <item>
        <title>C/C++调用Lua接口封装技巧</title>
        <description>&lt;p&gt;作为一个轻量级、高性能的脚本语言，Lua绝对是C/C++首选的脚本语言。但由于为了保证语言层面的灵活性，C/C++调用Lua传递参数是基于堆栈实现的，使得调用过程比较繁琐，这给C/C++开发者带来比较大的维护成本。&lt;/p&gt;

&lt;p&gt;本文分享一种我个人在实践中掌握的一种封装技巧——实现调用Lua函数像调用本地函数一样简单的调用方法。&lt;/p&gt;

&lt;!--more--&gt;
&lt;h3 id=&quot;常规调用流程&quot;&gt;常规调用流程&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;double test(lua_State * L, double x, double y) {
    double z;
    lua_getglobal(L, &quot;f&quot;);    // 获取lua函数f
    lua_pushnumber(L, x);    // 压入参数x和y
    lua_pushnumber(L, y);

    if(lua_pcall(L, 2, 1, 0) != 0)
        LOG(LERROR, (&quot;error running function &apos;f&apos;: &quot;, lua_tostring(L, -1)));

    if(!lua_isnumber(L, -1))
        LOG(LERROR, (&quot;function &apos;f&apos; must return a number&quot;));
    z = lua_tonumber(L, -1);
    lua_pop(L, 1);
    return z;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;正常的调用流程，每一个Lua接口都需要编写以上代码来实现与C/C++的对接，一旦接口多起来，就会难以维护。&lt;/p&gt;

&lt;h3 id=&quot;改进方案&quot;&gt;改进方案&lt;/h3&gt;
&lt;hr /&gt;

&lt;h4 id=&quot;限制返回参数类型&quot;&gt;限制返回参数类型&lt;/h4&gt;
&lt;p&gt;为使接口更加简化，我们限制Lua函数返回类型只能有一个，对不同的返回类型分别封装不同的接口：
（对于返回复杂的数据类型，建议采用Json传递）&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;string callLuaFuncStr();
double callLuaFuncNum();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;接口封装&quot;&gt;接口封装&lt;/h4&gt;
&lt;p&gt;使用C/C++可变参数，模仿print()的方式传参，保持Lua传参的灵活性。同时需要传入Lua函数名，最终的接口封装如下：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;string callLuaFuncStr(const string funcName, const string argsFormat, ...);
double callLuaFuncNum(const string funcName, const string argsFormat, ...);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;可变参数的实现&quot;&gt;可变参数的实现&lt;/h4&gt;

&lt;p&gt;通过 setLuaFuncArgs() 对传入的数据类型进行解析，并分别传递给Lua：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;uint32_t LuaHelper::setLuaFuncArgs(const string argsFormat, va_list args) {
    uint32_t count = 0;
    const char *format = argsFormat.c_str();
    while (*format != &apos;\0&apos;) {
        char type = *format;
        if (type == &apos;%&apos; &amp;amp;&amp;amp; *(format + 1) != &apos;\0&apos;) {
            type = *(format + 1);
            switch (type) {
            case &apos;s&apos;: {
                const char *arg = va_arg(args, const char *);
                lua_pushstring(L, arg);
                break;
            }
            case &apos;d&apos;: {
                int arg = va_arg(args, int);
                lua_pushinteger(L, arg);
                break;
            }
            case &apos;f&apos;: {
                float arg = va_arg(args, double);
                lua_pushnumber(L, arg);
                break;
            }
            default:
                LOG(LERROR, (&quot;type undefined!!!&quot;));
                format++;
                continue;
            }
            count++;
        }
        format++;
    }
    return count;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;通过函数名调用Lua接口：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// 返回String
string LuaHelper::callLuaFuncStr(const string funcName, const string argsFormat, ...) {
    lua_getglobal(L, funcName.c_str());

    va_list args;
    va_start(args, argsFormat);
    uint32_t count = setLuaFuncArgs(argsFormat, args);
    va_end(args);

    if(lua_pcall(L, count, 1, 0) != 0)
        LOG(LERROR, (&quot;error running function &apos;&quot;, funcName ,&quot;&apos;: &quot;, lua_tostring(L, -1)));

    if(!lua_isstring(L, -1))
        LOG(LERROR, (&quot;function &apos;&quot;, funcName ,&quot;&apos; must return a string&quot;));

    string result = lua_tostring(L, -1);
    lua_pop(L, 1);
    return result;
}

// 返回double
double LuaHelper::callLuaFuncNum(const string funcName, const string argsFormat, ...) {
    lua_getglobal(L, funcName.c_str());

    va_list args;
    va_start(args, argsFormat);
    uint32_t count = setLuaFuncArgs(argsFormat, args);
    va_end(args);

    if(lua_pcall(L, count, 1, 0) != 0)
        LOG(LERROR, (&quot;error running function &apos;&quot;, funcName ,&quot;&apos;: &quot;, lua_tostring(L, -1)));

    if(!lua_isnumber(L, -1))
        LOG(LERROR, (&quot;function &apos;&quot;, funcName ,&quot;&apos; must return a number&quot;));

    double result = lua_tonumber(L, -1);
    lua_pop(L, 1);
    return result;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;使用示例&quot;&gt;使用示例&lt;/h3&gt;
&lt;hr /&gt;
&lt;p&gt;Lua:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;function testLua(speak)
    print(speak)
    return &quot;Hello Cpp!!&quot;
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;C++:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;string result = callLuaFuncStr(&quot;testLua&quot;, &quot;%s&quot;, &quot;Hello Lua!!&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;参考&lt;/p&gt;

&lt;p&gt;[1][https://blog.csdn.net/jackystudio/article/details/17523523]&lt;/p&gt;

&lt;p&gt;[2][https://blog.csdn.net/arnozhang12/article/details/6848678]&lt;/p&gt;

</description>
        <pubDate>Wed, 16 Jan 2019 00:00:00 +0000</pubDate>
        <link>https://ruilin.github.io/blog/2019/01/16/C++%E8%B0%83%E7%94%A8Lua%E6%8E%A5%E5%8F%A3%E5%B0%81%E8%A3%85%E6%8A%80%E5%B7%A7.html</link>
        <guid isPermaLink="true">https://ruilin.github.io/blog/2019/01/16/C++%E8%B0%83%E7%94%A8Lua%E6%8E%A5%E5%8F%A3%E5%B0%81%E8%A3%85%E6%8A%80%E5%B7%A7.html</guid>
        
        <category>C/C++</category>
        
        <category>Lua</category>
        
        
      </item>
    
  </channel>
</rss>
