LayFz's Blog技术分享、编程心得和开发实践 - LayFz的个人博客https://layfz.com/zh-CN蓝绿部署:零停机时间发布的最佳实践https://layfz.com/posts/blue-green-deployment-guide/https://layfz.com/posts/blue-green-deployment-guide/🔁 《DevOps必备技能:掌握蓝绿部署,实现无缝版本切换》 - 现代DevOps工程师必须了解的部署策略,让用户无感知切换版本。Fri, 07 Mar 2025 16:37:00 GMT<h1 id="蓝绿部署实践">蓝绿部署实践 🚀</h1> <h2 id="-问题背景">📌 问题背景</h2> <p>在传统的Next.js项目部署中,我们经常会遇到以下问题:</p> <ul> <li>🔴 部署过程中用户访问出现样式加载失败</li> <li>🔴 静态资源无法正确加载</li> <li>🔴 Next.js的后台任务被意外中断</li> <li>🔴 用户体验断层,刷新页面出现短暂服务不可用</li> </ul> <p>这些问题主要是因为在常规部署流程中,我们通常会直接在运行的服务上进行代码更新和重启,导致服务临时不可用。</p> <h2 id="-蓝绿部署解决方案">💡 蓝绿部署解决方案</h2> <p>蓝绿部署(Blue-Green Deployment)是一种零停机部署策略,通过准备两套相同的生产环境,在不影响用户体验的情况下完成系统升级。</p> <h3 id="-方案原理">🔄 方案原理</h3> <ol> <li> <p>准备两个完全相同的Next.js服务环境:</p> <ul> <li>🔵 蓝环境:端口3019</li> <li>🟢 绿环境:端口5019</li> </ul> </li> <li> <p>使用宝塔面板的Nginx配置管理流量分发</p> <ul> <li>在任一时刻,只有一个环境接收真实用户流量</li> <li>另一个环境作为待命状态,用于准备新版本部署</li> </ul> </li> </ol> <h3 id="-具体实施流程">🔄 具体实施流程</h3> <p>假设当前蓝环境(3019端口)正在提供服务:</p> <ol> <li> <p><strong>准备绿环境</strong> 📦</p> <ul> <li> <p>将最新代码同步到绿环境(5019端口)目录</p> </li> <li> <p>在绿环境执行构建过程:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">npm</span><span style="color:#E6DB74"> install</span></span> <span class="line"><span style="color:#A6E22E">npm</span><span style="color:#E6DB74"> run</span><span style="color:#E6DB74"> build</span></span></code></pre> </li> <li> <p>此时用户依然访问的是蓝环境(3019端口),不受影响</p> </li> </ul> </li> <li> <p><strong>启动绿环境服务</strong> ▶️</p> <ul> <li> <p>使用PM2启动或重启绿环境服务:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">pm2</span><span style="color:#E6DB74"> restart</span><span style="color:#E6DB74"> next-app-green</span></span></code></pre> </li> <li> <p>确认绿环境服务正常运行</p> </li> </ul> </li> <li> <p><strong>切换流量</strong> 🔀</p> <ul> <li> <p>修改Nginx配置,将用户流量从蓝环境切换到绿环境:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"># 原配置</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic">upstream</span><span style="color:#F8F8F2"> nextjs_upstream {</span></span> <span class="line"><span style="color:#F92672"> server</span><span style="color:#F8F8F2"> 127.0.0.1:3019;</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 新配置</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic">upstream</span><span style="color:#F8F8F2"> nextjs_upstream {</span></span> <span class="line"><span style="color:#F92672"> server</span><span style="color:#F8F8F2"> 127.0.0.1:5019;</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> </li> <li> <p>重载Nginx配置:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">nginx</span><span style="color:#AE81FF"> -s</span><span style="color:#E6DB74"> reload</span></span></code></pre> </li> </ul> </li> <li> <p><strong>验证与回滚机制</strong> ✅</p> <ul> <li>验证绿环境(5019端口)是否正常提供服务</li> <li>如发现问题,可立即切换回蓝环境(3019端口)</li> </ul> </li> </ol> <h2 id="-核心优势">✨ 核心优势</h2> <p>这种蓝绿部署方案具有以下显著优势:</p> <ol> <li><strong>零停机时间</strong> ⏱️:用户在整个部署过程中不会感知到服务中断</li> <li><strong>保持任务连续性</strong> 🔄:不活跃的环境不会被关闭,其中运行的任务和进程继续执行</li> <li><strong>快速回滚能力</strong> ⏪:如果新版本出现问题,只需修改Nginx配置即可立即回滚</li> <li><strong>资源利用率高</strong> 📈:两个环境可以轮流部署,充分利用服务器资源</li> </ol> <h2 id="️-宝塔nginx配置示例">🛠️ 宝塔Nginx配置示例</h2> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#66D9EF;font-style:italic">server</span><span style="color:#F8F8F2"> {</span></span> <span class="line"><span style="color:#F92672"> listen </span><span style="color:#F8F8F2">80;</span></span> <span class="line"><span style="color:#F92672"> server_name </span><span style="color:#F8F8F2">your-domain.com;</span></span> <span class="line"><span style="color:#F8F8F2"> </span></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> location</span><span style="color:#F8F8F2"> / {</span></span> <span class="line"><span style="color:#F92672"> proxy_pass </span><span style="color:#F8F8F2">http://nextjs_upstream;</span></span> <span class="line"><span style="color:#F92672"> proxy_set_header </span><span style="color:#F8F8F2">Host $host;</span></span> <span class="line"><span style="color:#F92672"> proxy_set_header </span><span style="color:#F8F8F2">X-Real-IP $remote_addr;</span></span> <span class="line"><span style="color:#F92672"> proxy_set_header </span><span style="color:#F8F8F2">X-Forwarded-For $proxy_add_x_forwarded_for;</span></span> <span class="line"><span style="color:#F92672"> proxy_set_header </span><span style="color:#F8F8F2">X-Forwarded-Proto $scheme;</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span> <span class="line"></span> <span class="line"><span style="color:#66D9EF;font-style:italic">upstream</span><span style="color:#F8F8F2"> nextjs_upstream {</span></span> <span class="line"><span style="color:#F92672"> server</span><span style="color:#F8F8F2"> 127.0.0.1:5019; </span><span style="color:#88846F"># 当前活跃环境</span></span> <span class="line"><span style="color:#88846F"> # server 127.0.0.1:3019; # 注释掉非活跃环境</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <h2 id="-部署流程示意">📊 部署流程示意</h2> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>🔵 3019运行中 → 用户访问3019 → 代码同步到5019 → 5019构建完成</span></span> <span class="line"><span> ↓</span></span> <span class="line"><span>🔄 流量切换 ← Nginx配置修改 ← 5019服务启动 ← 5019构建验证</span></span> <span class="line"><span> ↓</span></span> <span class="line"><span>🟢 5019运行中 → 用户无感知迁移 → 3019保持运行但无流量</span></span></code></pre> <h2 id="-总结">🎯 总结</h2> <p>通过蓝绿部署策略,我们成功解决了Next.js项目在传统部署方式下面临的问题。这种方案不仅保证了用户体验的连续性,还为后端任务提供了稳定的运行环境。</p> <p>在实际应用中,这种部署模式特别适合以下场景:</p> <ul> <li>🚀 高流量Next.js应用</li> <li>⏱️ 包含重要后台任务的Next.js项目</li> <li>👨‍💻 对用户体验和服务连续性要求较高的应用</li> </ul> <p>通过宝塔面板和Nginx的配合,这种部署策略的实施变得简单高效,大大提升了Next.js应用的可靠性和用户满意度。</p>Cocos 接入穿山甲广告 SDKhttps://layfz.com/posts/cocos-chuanshanjia-insert/https://layfz.com/posts/cocos-chuanshanjia-insert/Cocos 接入穿山甲广告 SDK实现广告变现Tue, 06 Aug 2024 00:00:00 GMT<h1 id="cocos-接入穿山甲广告-sdk">Cocos 接入穿山甲广告 SDK</h1> <p>在这个教程中,我将详细介绍如何在 Cocos 引擎中接入穿山甲广告 SDK。我们将分步介绍如何在 Cocos 项目中集成穿山甲Android SDK,包括环境配置、代码实现和测试等内容。</p> <h2 id="目录">目录</h2> <ol> <li><a href="#%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6">前提条件</a></li> <li><a href="#%E8%8E%B7%E5%8F%96%E7%A9%BF%E5%B1%B1%E7%94%B2-sdk">获取穿山甲 SDK</a></li> <li><a href="#%E9%9B%86%E6%88%90-sdk">集成 SDK</a></li> <li><a href="#%E9%85%8D%E7%BD%AE%E7%A9%BF%E5%B1%B1%E7%94%B2%E5%B9%BF%E5%91%8A">配置穿山甲广告</a></li> </ol> <h2 id="前提条件">前提条件</h2> <p>在开始之前,请确保你具备以下条件:</p> <ul> <li>已安装 Cocos Creator 或 Cocos2d-x。</li> <li>已创建一个 Cocos 项目。</li> </ul> <h2 id="注意事项">注意事项:</h2> <ul> <li> <p>由于Android平台更新迭代非常快,开发者很容易陷入版本冲突以及兼容问题,若要使用该教程,请使用<strong>推荐的的版本配置</strong>:</p> <ul> <li>Gradle:8.0.2</li> <li>Gradle Plugin:8.0.2</li> <li>Android NDK: 21.3.6528147</li> <li>Android SDK: 29</li> <li>Cocos Creator: 2.4.13</li> </ul> </li> </ul> <h2 id="获取穿山甲-sdk">获取穿山甲 SDK</h2> <ol> <li><strong>访问穿山甲官网</strong>:前往 <a href="https://www.csjplatform.com/union/media/union/download/pangle">穿山甲</a> 并登录你的账户。</li> <li><strong>下载 SDK</strong>:在开发者中心找到Android SDK,下载并解压缩。 <img src="/images/csj/csj.png"></li> </ol> <h2 id="集成-sdk">集成 SDK</h2> <h3 id="1-集成-android-sdk">1. 集成 Android SDK</h3> <ol> <li> <p><strong>导入 SDK 文件</strong>:</p> <ul> <li>将下载的 Android SDK 文件(<code>.aar</code> 或 <code>.jar</code> 文件)放入 Cocos 项目的 <code>assets</code> 目录下的 <code>Plugins/Android</code> 文件夹中。</li> </ul> </li> <li> <p><strong>配置 Gradle 文件</strong>:</p> <ul> <li>打开 Cocos 项目的 <code>proj.android-studio/app/build.gradle</code> 文件。</li> <li>在 <code>dependencies</code> 部分添加穿山甲 SDK 的依赖:</li> </ul> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>implementation 'com.android.support:appcompat-v7:28.0.0'</span></span> <span class="line"><span>//融合基础包,必须引入</span></span> <span class="line"><span>implementation "com.pangle.cn:mediation-sdk:6.2.1.7"</span></span> <span class="line"><span>implementation "com.alibaba:fastjson:1.2.83"</span></span></code></pre> </li> <li> <p><strong>更新 AndroidManifest.xml</strong>:</p> <ul> <li> <p>在 <code>proj.android-studio/app/src/main/AndroidManifest.xml</code> 中添加穿山甲所需的权限和服务: <code>xml &#x3C;uses-permission android:name="android.permission.INTERNET"/> &#x3C;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> &#x3C;uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> &#x3C;!-- 所需权限 --> &#x3C;uses-permission android:name="android.permission.INTERNET" /> &#x3C;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> &#x3C;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> &#x3C;uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> &#x3C;uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> &#x3C;uses-permission android:name="android.permission.READ_PHONE_STATE" /></code> application部分: ```xml <application android:allowbackup="true" android:label="@string/app_name" android:usescleartexttraffic="true" android:icon="@mipmap/ic_launcher"> <!-- Tell Cocos2dxActivity the name of our .so --> <meta-data android:name="android.app.lib_name" android:value="cocos2djs"></meta-data></application></p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span> &#x3C;activity</span></span> <span class="line"><span> android:name="org.cocos2dx.javascript.AppActivity"</span></span> <span class="line"><span> android:screenOrientation="portrait"</span></span> <span class="line"><span> android:configChanges="orientation|keyboardHidden|screenSize|screenLayout|uiMode"</span></span> <span class="line"><span> android:label="@string/app_name"</span></span> <span class="line"><span> android:theme="@android:style/Theme.NoTitleBar.Fullscreen"</span></span> <span class="line"><span> android:launchMode="singleTask"</span></span> <span class="line"><span> android:exported="true"></span></span> <span class="line"><span> &#x3C;intent-filter></span></span> <span class="line"><span> &#x3C;action android:name="android.intent.action.MAIN" /></span></span> <span class="line"><span></span></span> <span class="line"><span> &#x3C;category android:name="android.intent.category.LAUNCHER" /></span></span> <span class="line"><span> &#x3C;/intent-filter></span></span> <span class="line"><span> &#x3C;/activity></span></span> <span class="line"><span> &#x3C;!-- 穿山甲 start================== --></span></span> <span class="line"><span> &#x3C;provider</span></span> <span class="line"><span> android:name="com.bytedance.sdk.openadsdk.TTFileProvider"</span></span> <span class="line"><span> android:authorities="${applicationId}.TTFileProvider"</span></span> <span class="line"><span> android:exported="false"</span></span> <span class="line"><span> android:grantUriPermissions="true"></span></span> <span class="line"><span> &#x3C;meta-data</span></span> <span class="line"><span> android:name="android.support.FILE_PROVIDER_PATHS"</span></span> <span class="line"><span> android:resource="@xml/file_paths" /></span></span> <span class="line"><span> &#x3C;/provider></span></span> <span class="line"><span></span></span> <span class="line"><span> &#x3C;provider</span></span> <span class="line"><span> android:name="com.bytedance.sdk.openadsdk.multipro.TTMultiProvider"</span></span> <span class="line"><span> android:authorities="${applicationId}.TTMultiProvider"</span></span> <span class="line"><span> android:exported="false" /></span></span> <span class="line"><span> &#x3C;!-- 穿山甲 end================== --></span></span> <span class="line"><span> &#x3C;/application></span></span> <span class="line"><span> ```</span></span></code></pre> <p>4.<strong>添加xml文件</strong>: 在res目录下添加文件file_paths.xml <img src="/images/csj/paths.png"></p> </li> </ul> </li> </ol> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>&#x3C;?xml version="1.0" encoding="utf-8"?></span></span> <span class="line"><span>&#x3C;paths></span></span> <span class="line"><span> &#x3C;external-path name="tt_external_root" path="." /></span></span> <span class="line"><span> &#x3C;external-path name="tt_external_download" path="Download" /></span></span> <span class="line"><span> &#x3C;external-files-path name="tt_external_files_download" path="Download" /></span></span> <span class="line"><span> &#x3C;files-path name="tt_internal_file_download" path="Download" /></span></span> <span class="line"><span> &#x3C;cache-path name="tt_internal_cache_download" path="Download" /></span></span> <span class="line"><span>&#x3C;/paths></span></span></code></pre> <ol> <li><strong>创建广告处理类</strong> 结构如下,<mark>创建并放到任意包下</mark>: <img src="/images/csj/classes.png"></li> </ol> <p>Const.java:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class Const {</span></span> <span class="line"><span> public static String TAG = "TTSdkComponent";</span></span> <span class="line"><span>}</span></span></code></pre> <p>TTAdManagerHolder.java:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class TTAdManagerHolder {</span></span> <span class="line"><span></span></span> <span class="line"><span> // 公共方法,封装了对私有方法的调用</span></span> <span class="line"><span> public void initialize(Context context, String appId) {</span></span> <span class="line"><span> initMediationAdSdk(context, appId);</span></span> <span class="line"><span> // 可以添加其他初始化逻辑或检查</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> //初始化聚合sdk</span></span> <span class="line"><span> private void initMediationAdSdk(Context context, String appId) {</span></span> <span class="line"><span> TTAdSdk.init(context, buildConfig(context, appId));</span></span> <span class="line"><span> TTAdSdk.start(new TTAdSdk.Callback() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void success() {</span></span> <span class="line"><span> System.out.println("初始化成功1");</span></span> <span class="line"><span> //初始化成功</span></span> <span class="line"><span> //在初始化成功回调之后进行广告加载"</span></span> <span class="line"><span>// JsbBridge.sendToScript("onInit", "true");</span></span> <span class="line"><span> // 一定要在 GL 线程中执行</span></span> <span class="line"><span> runOnGLThread(new Runnable() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void run() {</span></span> <span class="line"><span> Cocos2dxJavascriptJavaBridge.evalString("Ad.onNative(\"onInit\", \"true\")");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void fail(int i, String s) {</span></span> <span class="line"><span> //初始化失败</span></span> <span class="line"><span> System.out.println("初始化失败");</span></span> <span class="line"><span>// JsbBridge.sendToScript("onInit", "false");</span></span> <span class="line"><span> // 一定要在 GL 线程中执行</span></span> <span class="line"><span> runOnGLThread(new Runnable() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void run() {</span></span> <span class="line"><span> Cocos2dxJavascriptJavaBridge.evalString("Ad.onNative(\"onInit\", \"false\")");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> private static TTAdConfig buildConfig(Context context, String appId) {</span></span> <span class="line"><span></span></span> <span class="line"><span> return new TTAdConfig.Builder()</span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 注:需要替换成在媒体平台申请的appID ,切勿直接复制</span></span> <span class="line"><span> */</span></span> <span class="line"><span> .appId(appId)</span></span> <span class="line"><span> .appName("APP测试媒体")</span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 上线前需要关闭debug开关,否则会影响性能</span></span> <span class="line"><span> */</span></span> <span class="line"><span> .debug(false)</span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 使用聚合功能此开关必须设置为true,默认为false</span></span> <span class="line"><span> */</span></span> <span class="line"><span> .useMediation(true)</span></span> <span class="line"><span>// .customController(getTTCustomController()) //如果您需要设置隐私策略请参考该api</span></span> <span class="line"><span>// .setMediationConfig(new MediationConfig.Builder() //可设置聚合特有参数详细设置请参考该api</span></span> <span class="line"><span>// .setMediationConfigUserInfoForSegment(getUserInfoForSegment())//如果您需要配置流量分组信息请参考该api</span></span> <span class="line"><span>// .build())</span></span> <span class="line"><span> .build();</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <p>TTSdkBannerAd.java:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class TTSdkBannerAd {</span></span> <span class="line"><span> TTAdNative adNativeLoader;</span></span> <span class="line"><span> AdSlot adSlot;</span></span> <span class="line"><span> TTNativeExpressAd mBannerAd;</span></span> <span class="line"><span> FrameLayout mBannerContainer;</span></span> <span class="line"><span> Activity activity;</span></span> <span class="line"><span> float left, top;</span></span> <span class="line"><span></span></span> <span class="line"><span> public TTSdkBannerAd(Activity activity, String codeId, float left, float top, int width, int height) {</span></span> <span class="line"><span> activity = activity;</span></span> <span class="line"><span> this.left = left;</span></span> <span class="line"><span> this.top = top;</span></span> <span class="line"><span> adNativeLoader = TTAdSdk.getAdManager().createAdNative(activity);</span></span> <span class="line"><span> adSlot = new AdSlot.Builder()</span></span> <span class="line"><span> .setCodeId(codeId)</span></span> <span class="line"><span> .setImageAcceptedSize(width, height) // 单位px</span></span> <span class="line"><span> .build();</span></span> <span class="line"><span> mBannerContainer = (FrameLayout) activity.findViewById(android.R.id.content).getRootView();</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> private void loadBannerExpressAd() {</span></span> <span class="line"><span> adNativeLoader.loadBannerExpressAd(adSlot, new TTAdNative.NativeExpressAdListener() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onError(int i, String s) {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner load fail: errCode: " + i + ", errMsg: " + s);</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onNativeExpressAdLoad(List&#x3C;TTNativeExpressAd> list) {</span></span> <span class="line"><span> if (list != null &#x26;&#x26; list.size() > 0) {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner load success");</span></span> <span class="line"><span> destroyBannerAd();</span></span> <span class="line"><span> mBannerAd = list.get(0);</span></span> <span class="line"><span> mBannerAd.setSlideIntervalTime(30 * 1000);</span></span> <span class="line"><span> if (mBannerAd != null) {</span></span> <span class="line"><span> showBannerAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> } else {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner load success, but list is null");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void showBannerAd() {</span></span> <span class="line"><span> if (mBannerAd != null) {</span></span> <span class="line"><span> mBannerAd.setExpressInteractionListener(new</span></span> <span class="line"><span> TTNativeExpressAd.ExpressAdInteractionListener() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onAdClicked(View view, int i) {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner clicked");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onAdShow(View view, int i) {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner showed");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onRenderFail(View view, String s, int i) {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner renderFail, errCode" + i + ", errMsg: " + s);</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onRenderSuccess(View view, float v, float v1) {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner render success");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> mBannerAd.setDislikeCallback(activity, new</span></span> <span class="line"><span> TTAdDislike.DislikeInteractionCallback() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onShow() {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSelected(int i, String s, boolean b) {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner closed");</span></span> <span class="line"><span> destroyBannerAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onCancel() {</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> View bannerView = mBannerAd.getExpressAdView();</span></span> <span class="line"><span> if (bannerView != null &#x26;&#x26; mBannerContainer != null) {</span></span> <span class="line"><span> mBannerContainer.addView(bannerView);</span></span> <span class="line"><span> bannerView.setX(left);</span></span> <span class="line"><span> bannerView.setY(top);</span></span> <span class="line"><span> }</span></span> <span class="line"><span> } else {</span></span> <span class="line"><span>// Log.d(Const.TAG, "请先加载广告或等待广告加载完毕后再展示广告");</span></span> <span class="line"><span> loadBannerExpressAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void destroyBannerAd() {</span></span> <span class="line"><span> if (mBannerAd != null) {</span></span> <span class="line"><span> mBannerAd.destroy();</span></span> <span class="line"><span> mBannerAd = null;</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <p>TTSdkComponent.java:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class TTSdkComponent {</span></span> <span class="line"><span></span></span> <span class="line"><span> TTSdkBannerAd mTTBannerAd = null;</span></span> <span class="line"><span> TTSdkRewardedVideoAd mTTRewardedVideoAd = null;</span></span> <span class="line"><span> TTSdkFullScreenVideoAd mTTSdkFullScreenVideoAd = null;</span></span> <span class="line"><span> TTSdkSplashAd mTTSdkSplashAd = null;</span></span> <span class="line"><span></span></span> <span class="line"><span> Activity activity;</span></span> <span class="line"><span> </span></span> <span class="line"><span> public static TTSdkComponent mTTSdkComponent;</span></span> <span class="line"><span></span></span> <span class="line"><span></span></span> <span class="line"><span> public TTSdkComponent(Activity activity) {</span></span> <span class="line"><span> this.activity = activity;</span></span> <span class="line"><span> TTSdkComponent.mTTSdkComponent = this;</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public static void sendToNative(String arg0, String arg1){</span></span> <span class="line"><span> Log.d(Const.TAG, "banner load success, but list is null" + arg0 +"?" +arg1);</span></span> <span class="line"><span> TTSdkComponent.mTTSdkComponent.onScript(arg0,arg1);</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onScript(String arg0, String arg1){</span></span> <span class="line"><span> JSONObject jsonObject = JSON.parseObject(arg1);</span></span> <span class="line"><span> Activity activity = this.activity;</span></span> <span class="line"><span> if (arg0.equals("showToast")) {</span></span> <span class="line"><span> String content = jsonObject.getString("content");</span></span> <span class="line"><span> activity.runOnUiThread(new Runnable() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void run() {</span></span> <span class="line"><span> Toast.makeText(activity, content,</span></span> <span class="line"><span> Toast.LENGTH_SHORT).show();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> } else if (arg0.equals("init")) {</span></span> <span class="line"><span> TTAdManagerHolder ttAdManagerHolder = new TTAdManagerHolder();</span></span> <span class="line"><span> String appId = jsonObject.getString("appId");</span></span> <span class="line"><span> ttAdManagerHolder.initialize(activity, appId);</span></span> <span class="line"><span> } else if (arg0.equals("showBannerAd")) {</span></span> <span class="line"><span> if (mTTBannerAd == null) {</span></span> <span class="line"><span> String codeId = jsonObject.getString("codeId");</span></span> <span class="line"><span> float left = jsonObject.getFloatValue("left");</span></span> <span class="line"><span> float top = jsonObject.getFloatValue("top");</span></span> <span class="line"><span> int width = jsonObject.getIntValue("width");</span></span> <span class="line"><span> int height = jsonObject.getIntValue("height");</span></span> <span class="line"><span> mTTBannerAd = new TTSdkBannerAd(activity, codeId, left, top, width, height);</span></span> <span class="line"><span> }</span></span> <span class="line"><span> if (mTTBannerAd != null) {</span></span> <span class="line"><span> boolean isShow = jsonObject.getBooleanValue("isShow");</span></span> <span class="line"><span> if (isShow) {</span></span> <span class="line"><span> mTTBannerAd.showBannerAd();</span></span> <span class="line"><span> } else {</span></span> <span class="line"><span> mTTBannerAd.destroyBannerAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> } else if (arg0.equals("showRewardVideoAd")) {</span></span> <span class="line"><span> if (mTTRewardedVideoAd == null) {</span></span> <span class="line"><span> String codeId = jsonObject.getString("codeId");</span></span> <span class="line"><span> mTTRewardedVideoAd = new TTSdkRewardedVideoAd(activity, codeId);</span></span> <span class="line"><span> }</span></span> <span class="line"><span> if (mTTRewardedVideoAd != null) {</span></span> <span class="line"><span> mTTRewardedVideoAd.showRewardVideoAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> } else if (arg0.equals("showSplashAd")) {</span></span> <span class="line"><span> if (mTTSdkSplashAd == null) {</span></span> <span class="line"><span> String codeId = jsonObject.getString("codeId");</span></span> <span class="line"><span> int width = jsonObject.getIntValue("width");</span></span> <span class="line"><span> int height = jsonObject.getIntValue("height");</span></span> <span class="line"><span> mTTSdkSplashAd = new TTSdkSplashAd(activity, codeId, width, height);</span></span> <span class="line"><span> }</span></span> <span class="line"><span> mTTSdkSplashAd.showSplashAd(null);</span></span> <span class="line"><span> } else if (arg0.equals("showFullScreenVideoAd")) {</span></span> <span class="line"><span> if (mTTSdkFullScreenVideoAd == null) {</span></span> <span class="line"><span> String codeId = jsonObject.getString("codeId");</span></span> <span class="line"><span> mTTSdkFullScreenVideoAd = new TTSdkFullScreenVideoAd(activity, codeId);</span></span> <span class="line"><span> }</span></span> <span class="line"><span> mTTSdkFullScreenVideoAd.showFullScreenVideoAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <p>TTSdkFullScreenVideoAd.java:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class TTSdkFullScreenVideoAd {</span></span> <span class="line"><span></span></span> <span class="line"><span> TTAdNative adNativeLoader;</span></span> <span class="line"><span> AdSlot adSlot;</span></span> <span class="line"><span> TTFullScreenVideoAd mTTFullScreenVideoAd;</span></span> <span class="line"><span> Activity activity;</span></span> <span class="line"><span></span></span> <span class="line"><span> public TTSdkFullScreenVideoAd(Activity activity, String codeId) {</span></span> <span class="line"><span> this.activity = activity;</span></span> <span class="line"><span> adNativeLoader = TTAdSdk.getAdManager().createAdNative(activity);</span></span> <span class="line"><span> adSlot = new AdSlot.Builder()</span></span> <span class="line"><span> .setCodeId(codeId)</span></span> <span class="line"><span> .setOrientation(TTAdConstant.ORIENTATION_VERTICAL)//设置横竖屏方向</span></span> <span class="line"><span>// .setMediationAdSlot(new MediationAdSlot.Builder()</span></span> <span class="line"><span>// .setMuted(true)//是否静音</span></span> <span class="line"><span>// .setVolume(0.7f)//设置音量</span></span> <span class="line"><span>// .setBidNotify(true)//竞价结果通知</span></span> <span class="line"><span>// .build())</span></span> <span class="line"><span> .build();</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void loadFullScreenVideoAd(boolean isShow) {</span></span> <span class="line"><span> adNativeLoader.loadFullScreenVideoAd(adSlot, new TTAdNative.FullScreenVideoAdListener() {</span></span> <span class="line"><span> public void onError(int code, String message) {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onError code = " + code + " msg = " + message);</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onFullScreenVideoAdLoad(TTFullScreenVideoAd ad) {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onFullScreenVideoLoaded");</span></span> <span class="line"><span> mTTFullScreenVideoAd = ad;</span></span> <span class="line"><span>// Log.d("sada",""+isShow);</span></span> <span class="line"><span> if (isShow) {</span></span> <span class="line"><span> showFullScreenVideoAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onFullScreenVideoCached() {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onFullScreenVideoCached");</span></span> <span class="line"><span> Log.d("5785785727","57587575875");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onFullScreenVideoCached(TTFullScreenVideoAd ad) {</span></span> <span class="line"><span> Log.d("sasasdasd","sadasda");</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onFullScreenVideoCached");</span></span> <span class="line"><span> mTTFullScreenVideoAd = ad;</span></span> <span class="line"><span> if (isShow) {</span></span> <span class="line"><span> showFullScreenVideoAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void showFullScreenVideoAd() {</span></span> <span class="line"><span>// Log.d("mm", ""+(mTTFullScreenVideoAd==null));</span></span> <span class="line"><span> if (mTTFullScreenVideoAd == null) {</span></span> <span class="line"><span> loadFullScreenVideoAd(true);</span></span> <span class="line"><span> return;</span></span> <span class="line"><span> }</span></span> <span class="line"><span> // 展示广告</span></span> <span class="line"><span> this.mTTFullScreenVideoAd.setFullScreenVideoAdInteractionListener(new TTFullScreenVideoAd.FullScreenVideoAdInteractionListener() {</span></span> <span class="line"><span> public void onAdShow() {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onAdShow");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onAdVideoBarClick() {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onAdVideoBarClick");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onAdClose() {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onAdClose");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onVideoComplete() {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onVideoComplete");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onSkippedVideo() {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onSkippedVideo");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> this.mTTFullScreenVideoAd.showFullScreenVideoAd(activity);</span></span> <span class="line"><span> this.mTTFullScreenVideoAd = null;</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <p>TTSdkRewardedVideoAd.java:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class TTSdkRewardedVideoAd {</span></span> <span class="line"><span> TTAdNative adNativeLoader;</span></span> <span class="line"><span> AdSlot adSlot;</span></span> <span class="line"><span> TTRewardVideoAd mTTRewardVideoAd;</span></span> <span class="line"><span> Activity activity;</span></span> <span class="line"><span> public boolean mIsRewardValid;</span></span> <span class="line"><span></span></span> <span class="line"><span> public TTSdkRewardedVideoAd(Activity activity, String codeId) {</span></span> <span class="line"><span> activity = activity;</span></span> <span class="line"><span> adNativeLoader = TTAdSdk.getAdManager().createAdNative(activity);</span></span> <span class="line"><span> adSlot = new AdSlot.Builder()</span></span> <span class="line"><span> .setCodeId(codeId)</span></span> <span class="line"><span> .setOrientation(TTAdConstant.VERTICAL)//横竖屏设置</span></span> <span class="line"><span>// .setMediationAdSlot(new MediationAdSlot</span></span> <span class="line"><span>// .Builder()</span></span> <span class="line"><span>// .setExtraObject(MediationConstant.ADN_PANGLE, "pangleRewardCustomData")//服务端奖励验证透传参数</span></span> <span class="line"><span>// .setExtraObject(MediationConstant.ADN_GDT, "gdtRewardCustomData")</span></span> <span class="line"><span>// .setExtraObject(MediationConstant.ADN_BAIDU, "baiduRewardCustomData")</span></span> <span class="line"><span>// .build())</span></span> <span class="line"><span> .build();</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> private void loadRewardVideoAd(boolean isShow) {</span></span> <span class="line"><span> adNativeLoader.loadRewardVideoAd(adSlot, new TTAdNative.RewardVideoAdListener() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onError(int i, String s) {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onRewardVideoAdLoad(TTRewardVideoAd ttRewardVideoAd) {</span></span> <span class="line"><span> mTTRewardVideoAd = ttRewardVideoAd;</span></span> <span class="line"><span> if (isShow) {</span></span> <span class="line"><span> showRewardVideoAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onRewardVideoCached() {</span></span> <span class="line"><span></span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onRewardVideoCached(TTRewardVideoAd ttRewardVideoAd) {</span></span> <span class="line"><span> mTTRewardVideoAd = ttRewardVideoAd;</span></span> <span class="line"><span> if (isShow) {</span></span> <span class="line"><span> showRewardVideoAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void showRewardVideoAd() {</span></span> <span class="line"><span> mIsRewardValid = false;</span></span> <span class="line"><span> if (mTTRewardVideoAd == null) {</span></span> <span class="line"><span> loadRewardVideoAd(true);</span></span> <span class="line"><span> return;</span></span> <span class="line"><span> }</span></span> <span class="line"><span> mTTRewardVideoAd.setRewardAdInteractionListener(new TTRewardVideoAd.RewardAdInteractionListener() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onAdShow() {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onAdVideoBarClick() {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onAdClose() {</span></span> <span class="line"><span>// JsbBridge.sendToScript("onRewardAdCallback", mIsRewardValid ? "true" : "false");</span></span> <span class="line"><span> // 一定要在 GL 线程中执行</span></span> <span class="line"><span> runOnGLThread(new Runnable() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void run() {</span></span> <span class="line"><span> String success = mIsRewardValid ? "true" : "false";</span></span> <span class="line"><span> Cocos2dxJavascriptJavaBridge.evalString("Ad.onNative(\"onRewardAdCallback\", \""+success+"\")");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onVideoComplete() {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onVideoError() {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> //已废弃,请使用onRewardArrived方法</span></span> <span class="line"><span> public void onRewardVerify(boolean rewardVerify, int rewardAmount, String rewardName, int errorCode, String errorMsg) {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onRewardArrived(boolean isRewardValid, int rewardType, Bundle extraInfo) {//奖励是否发放请依据isRewardValid</span></span> <span class="line"><span> // 当用户的观看行为满足了奖励条件</span></span> <span class="line"><span> mIsRewardValid = isRewardValid;</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSkippedVideo() {</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> mTTRewardVideoAd.showRewardVideoAd(activity);</span></span> <span class="line"><span> mTTRewardVideoAd = null;</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <p>TTSdkSplashAd.java:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class TTSdkSplashAd {</span></span> <span class="line"><span></span></span> <span class="line"><span> TTAdNative adNativeLoader;</span></span> <span class="line"><span> AdSlot adSlot;</span></span> <span class="line"><span> Activity activity;</span></span> <span class="line"><span> FrameLayout mSplashContainer;</span></span> <span class="line"><span> CSJSplashAd mCsjSplashAd;</span></span> <span class="line"><span></span></span> <span class="line"><span> public TTSdkSplashAd(Activity activity, String codeId, int width, int height) {</span></span> <span class="line"><span> activity = activity;</span></span> <span class="line"><span> adNativeLoader = TTAdSdk.getAdManager().createAdNative(activity);</span></span> <span class="line"><span> adSlot = new AdSlot.Builder()</span></span> <span class="line"><span> .setCodeId(codeId)</span></span> <span class="line"><span> .setImageAcceptedSize(width, height)//单位px</span></span> <span class="line"><span> .build();</span></span> <span class="line"><span> mSplashContainer = (FrameLayout) activity.findViewById(android.R.id.content).getRootView();</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void loadSplashAd(boolean isShow) {</span></span> <span class="line"><span> adNativeLoader.loadSplashAd(adSlot, new TTAdNative.CSJSplashAdListener() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSplashRenderSuccess(CSJSplashAd csjSplashAd) {</span></span> <span class="line"><span> Log.d("SplashAd","onSplashRenderSuccess");</span></span> <span class="line"><span> /** 渲染成功后,展示广告 */</span></span> <span class="line"><span> if (isShow)</span></span> <span class="line"><span> showSplashAd(csjSplashAd);</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSplashLoadSuccess(CSJSplashAd csjSplashAd) {</span></span> <span class="line"><span> Log.d("SplashAd","onSplashLoadSuccess");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSplashLoadFail(CSJAdError csjAdError) {</span></span> <span class="line"><span> Log.d("SplashAd","onSplashLoadFail:"+csjAdError.getMsg());</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSplashRenderFail(CSJSplashAd csjSplashAd, CSJAdError csjAdError) {</span></span> <span class="line"><span> Log.d("SplashAd","onSplashRenderFail:"+csjAdError.getMsg());</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }, 3500);</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void showSplashAd(CSJSplashAd csjSplashAd) {</span></span> <span class="line"><span> if (csjSplashAd == null) {</span></span> <span class="line"><span> loadSplashAd(true);</span></span> <span class="line"><span> return;</span></span> <span class="line"><span> }</span></span> <span class="line"><span> csjSplashAd.setSplashAdListener(new CSJSplashAd.SplashAdListener() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSplashAdShow(CSJSplashAd csjSplashAd) {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSplashAdClick(CSJSplashAd csjSplashAd) {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSplashAdClose(CSJSplashAd csjSplashAd, int i) {</span></span> <span class="line"><span> // 广告关闭后,销毁广告页面</span></span> <span class="line"><span> finish();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> mCsjSplashAd = csjSplashAd;</span></span> <span class="line"><span> View splashView = csjSplashAd.getSplashView();</span></span> <span class="line"><span>// UIUtils.removeFromParent(splashView);</span></span> <span class="line"><span>// mSplashContainer.removeAllViews();</span></span> <span class="line"><span> mSplashContainer.addView(splashView);</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void finish() {</span></span> <span class="line"><span> if (mCsjSplashAd != null &#x26;&#x26; mCsjSplashAd.getMediationManager() != null) {</span></span> <span class="line"><span> mSplashContainer.removeView(mCsjSplashAd.getSplashView());</span></span> <span class="line"><span> mCsjSplashAd.getMediationManager().destroy();</span></span> <span class="line"><span> mCsjSplashAd = null;</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <ol start="6"> <li><strong>初始化组件</strong> 在AppActivity初始化组件</li> </ol> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class AppActivity extends Cocos2dxActivity {</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> protected void onCreate(Bundle savedInstanceState) {</span></span> <span class="line"><span> super.onCreate(savedInstanceState);</span></span> <span class="line"><span> // DO OTHER INITIALIZATION BELOW</span></span> <span class="line"><span> SDKWrapper.getInstance().init(this);</span></span> <span class="line"><span> // 初始化组件</span></span> <span class="line"><span> new TTSdkComponent(this);</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <p><strong>至此安卓的初始化就完成了,穿山甲不愧是大平台,接入就是如此丝滑</strong></p> <h2 id="配置穿山甲广告">配置穿山甲广告</h2> <h3 id="1-初始化-sdk">1. 初始化 SDK</h3> <p>在 Cocos 项目的主脚本中,初始化穿山甲 SDK: 首先创建如下TS脚本,如果采用js请自行转换。 <img src="/images/csj/cocosscript.png"> Ad.ts:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>const { ccclass, property } = cc._decorator;</span></span> <span class="line"><span> </span></span> <span class="line"><span>@ccclass("AdConfigBase")</span></span> <span class="line"><span>class AdConfigBase {</span></span> <span class="line"><span> @property({ displayName: "广告Id" })</span></span> <span class="line"><span> codeId: string = "";</span></span> <span class="line"><span>};</span></span> <span class="line"><span> </span></span> <span class="line"><span>@ccclass("RewardedVideoAdConfig")</span></span> <span class="line"><span>class RewardedVideoAdConfig extends AdConfigBase {</span></span> <span class="line"><span>};</span></span> <span class="line"><span> </span></span> <span class="line"><span>@ccclass("BannerAdConfig")</span></span> <span class="line"><span>class BannerAdConfig extends AdConfigBase {</span></span> <span class="line"><span> @property({ displayName: "宽度" })</span></span> <span class="line"><span> width: number = 500;</span></span> <span class="line"><span> @property({ displayName: "高度" })</span></span> <span class="line"><span> height: number = 300;</span></span> <span class="line"><span>};</span></span> <span class="line"><span> </span></span> <span class="line"><span>@ccclass("InterstitialAdConfig")</span></span> <span class="line"><span>class InterstitialAdConfig extends AdConfigBase {</span></span> <span class="line"><span>};</span></span> <span class="line"><span> </span></span> <span class="line"><span>@ccclass("SplashAdConfig")</span></span> <span class="line"><span>class SplashAdConfig extends AdConfigBase {</span></span> <span class="line"><span>};</span></span> <span class="line"><span> </span></span> <span class="line"><span>@ccclass("Ad")</span></span> <span class="line"><span>export class Ad {</span></span> <span class="line"><span> @property({ displayName: "应用appId" })</span></span> <span class="line"><span> appId: string = "";</span></span> <span class="line"><span> @property({ type: BannerAdConfig, displayName: "Banner广告" })</span></span> <span class="line"><span> bannerAdConfig: BannerAdConfig = new BannerAdConfig();</span></span> <span class="line"><span> @property({ type: RewardedVideoAdConfig, displayName: "激励视频广告" })</span></span> <span class="line"><span> rewardedVideoAdConfig: RewardedVideoAdConfig = new RewardedVideoAdConfig();</span></span> <span class="line"><span> @property({ type: SplashAdConfig, displayName: "开屏广告" })</span></span> <span class="line"><span> splashAdConfig: SplashAdConfig = new SplashAdConfig();</span></span> <span class="line"><span> @property({ type: InterstitialAdConfig, displayName: "插屏广告" })</span></span> <span class="line"><span> interstitialAdConfig: InterstitialAdConfig = new InterstitialAdConfig();</span></span> <span class="line"><span> </span></span> <span class="line"><span> private rewardAdCallback: any;</span></span> <span class="line"><span> private rewardAdCallbackObj: any;</span></span> <span class="line"><span> </span></span> <span class="line"><span> private static onNativeCallback: any;</span></span> <span class="line"><span> private static onNativeCallbackObj: any;</span></span> <span class="line"><span> </span></span> <span class="line"><span> sendToNative(arg0: string, arg1?: string) {</span></span> <span class="line"><span> // native.bridge.sendToNative(arg0, arg1);</span></span> <span class="line"><span> jsb.reflection.callStaticMethod("com/yiyuancoder/ttsdk/TTSdkComponent", "sendToNative", "(Ljava/lang/String;Ljava/lang/String;)V", arg0, arg1);</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> public static onNative(arg0: string, arg1: string) {</span></span> <span class="line"><span> if (this.onNativeCallback != null) {</span></span> <span class="line"><span> this.onNativeCallback.call(this.onNativeCallbackObj, arg0, arg1);</span></span> <span class="line"><span> this.onNativeCallback = null;</span></span> <span class="line"><span> this.onNativeCallbackObj = null;</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> async init() {</span></span> <span class="line"><span> return new Promise((resolve, reject) => {</span></span> <span class="line"><span> // native.bridge.onNative = (arg0: string, arg1: string): void => {</span></span> <span class="line"><span> // if (arg0 == 'onInit') {</span></span> <span class="line"><span> // resolve(arg1 == "true");</span></span> <span class="line"><span> // }</span></span> <span class="line"><span> // }</span></span> <span class="line"><span> Ad.onNativeCallbackObj = this;</span></span> <span class="line"><span> Ad.onNativeCallback = (arg0: string, arg1: string) => {</span></span> <span class="line"><span> if (arg0 == 'onInit') {</span></span> <span class="line"><span> resolve(arg1 == "true");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> this.sendToNative("init", JSON.stringify({ appId: this.appId }));</span></span> <span class="line"><span> })</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 显示Banner广告</span></span> <span class="line"><span> */</span></span> <span class="line"><span> showBannerAd(show: boolean): void {</span></span> <span class="line"><span> </span></span> <span class="line"><span> let width = this.bannerAdConfig.width;</span></span> <span class="line"><span> let height = this.bannerAdConfig.height;</span></span> <span class="line"><span> let left = screen.width / 2 - width / 2;</span></span> <span class="line"><span> let top = screen.height - height;</span></span> <span class="line"><span> this.sendToNative("showBannerAd", JSON.stringify({ isShow: show, codeId: this.bannerAdConfig.codeId, left: left, top: top, width: width, height: height }));</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 播放激励视频广告</span></span> <span class="line"><span> */</span></span> <span class="line"><span> showRewardedVideoAd(rewardAdCallback: Function, rewardAdCallbackObj: any): void {</span></span> <span class="line"><span> this.rewardAdCallback = rewardAdCallback;</span></span> <span class="line"><span> this.rewardAdCallbackObj = rewardAdCallbackObj;</span></span> <span class="line"><span> </span></span> <span class="line"><span> let that = this;</span></span> <span class="line"><span> // native.bridge.onNative = (arg0: string, arg1: string): void => {</span></span> <span class="line"><span> // if (arg0 == 'onRewardAdCallback') {</span></span> <span class="line"><span> // that.onRewardAdCallback(arg1 == "true");</span></span> <span class="line"><span> // }</span></span> <span class="line"><span> // }</span></span> <span class="line"><span> Ad.onNativeCallbackObj = this;</span></span> <span class="line"><span> Ad.onNativeCallback = (arg0: string, arg1: string) => {</span></span> <span class="line"><span> if (arg0 == 'onRewardAdCallback') {</span></span> <span class="line"><span> that.onRewardAdCallback(arg1 == "true");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> this.sendToNative("showRewardVideoAd", JSON.stringify({ codeId: this.rewardedVideoAdConfig.codeId }));</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 激励视频广告回调</span></span> <span class="line"><span> */</span></span> <span class="line"><span> onRewardAdCallback(success: boolean) {</span></span> <span class="line"><span> if (this.rewardAdCallback) {</span></span> <span class="line"><span> this.rewardAdCallback.call(this.rewardAdCallbackObj, success);</span></span> <span class="line"><span> this.rewardAdCallback = null;</span></span> <span class="line"><span> this.rewardAdCallbackObj = null;</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 显示开屏广告</span></span> <span class="line"><span> */</span></span> <span class="line"><span> showSplashAd(): void {</span></span> <span class="line"><span> this.sendToNative("showSplashAd", JSON.stringify({ codeId: this.splashAdConfig.codeId, width: screen.width, height: screen.height }));</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 显示插屏广告</span></span> <span class="line"><span> */</span></span> <span class="line"><span> showFullScreenVideoAd() {</span></span> <span class="line"><span> // 在适合的场景显示插屏广告</span></span> <span class="line"><span> this.sendToNative("showFullScreenVideoAd", JSON.stringify({ codeId: this.interstitialAdConfig.codeId }));</span></span> <span class="line"><span> }</span></span> <span class="line"><span>};</span></span> <span class="line"><span> </span></span> <span class="line"><span>window.Ad = Ad;</span></span></code></pre> <p>TTSdkComponent.ts:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>import { Ad } from "./Ad";</span></span> <span class="line"><span> </span></span> <span class="line"><span>const { ccclass, property } = cc._decorator;</span></span> <span class="line"><span> </span></span> <span class="line"><span>declare var wx: any;</span></span> <span class="line"><span> </span></span> <span class="line"><span>@ccclass</span></span> <span class="line"><span>export default class TTSdkComponent extends cc.Component {</span></span> <span class="line"><span> </span></span> <span class="line"><span> @property({ type: Ad, displayName: "广告模块" })</span></span> <span class="line"><span> ad: Ad = new Ad();</span></span> <span class="line"><span> </span></span> <span class="line"><span> sendToNative(arg0: string, arg1?: string) {</span></span> <span class="line"><span> jsb.reflection.callStaticMethod("com/yiyuancoder/ttsdk/TTSdkComponent", "sendToNative", "(Ljava/lang/String;Ljava/lang/String;)V", arg0, arg1);</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> showToast(content: string) {</span></span> <span class="line"><span> this.sendToNative('showToast', JSON.stringify({ content: content }));</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <p>Main.ts:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>//获取穿山甲Sdk组件</span></span> <span class="line"><span>let ttSdkComponent = cc.find("TTSdkComponent").getComponent(TTSdkComponent);</span></span> <span class="line"><span>//初始化</span></span> <span class="line"><span>let inited = await ttSdkComponent.ad.init();</span></span> <span class="line"><span>console.log(inited);</span></span> <span class="line"><span>if (!inited) {</span></span> <span class="line"><span> ttSdkComponent.showToast("广告组件初始化失败");</span></span> <span class="line"><span> return;</span></span> <span class="line"><span>}</span></span></code></pre> <p>以上Main.ts文件主要用于初始化脚本。</p> <h3 id="2-挂载脚本">2. 挂载脚本</h3> <p>在你的cocos场景中新建节点,并挂载脚本文件: <img src="/images/csj/model.png"> 如上,填上你在平台申请的信息。</p> <h3 id="3-调用广告">3. 调用广告</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672">import</span><span style="color:#F8F8F2"> TTSdkComponent </span><span style="color:#F92672">from</span><span style="color:#E6DB74"> "./TTSdkComponent"</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic">const</span><span style="color:#F8F8F2"> { ccclass, property } </span><span style="color:#F92672">=</span><span style="color:#F8F8F2"> cc._decorator;</span></span> <span class="line"></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2">async </span><span style="color:#A6E22E">start</span><span style="color:#F8F8F2">(){</span></span> <span class="line"><span style="color:#88846F"> // banner</span></span> <span class="line"><span style="color:#F8F8F2"> ttSdkComponent.ad.</span><span style="color:#A6E22E">showBannerAd</span><span style="color:#F8F8F2">(bannerAdFlag);</span></span> <span class="line"><span style="color:#88846F"> // reward</span></span> <span class="line"><span style="color:#F8F8F2"> ttSdkComponent.ad.</span><span style="color:#A6E22E">showRewardedVideoAd</span><span style="color:#F8F8F2">((</span><span style="color:#FD971F;font-style:italic">success</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> boolean</span><span style="color:#F8F8F2">) </span><span style="color:#66D9EF;font-style:italic">=></span><span style="color:#F8F8F2"> {</span></span> <span class="line"><span style="color:#F8F8F2"> ttSdkComponent.</span><span style="color:#A6E22E">showToast</span><span style="color:#F8F8F2">(success </span><span style="color:#F92672">?</span><span style="color:#E6DB74"> "恭喜获得奖励"</span><span style="color:#F92672"> :</span><span style="color:#E6DB74"> "完整观看视频才能获得奖励哦"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }, </span><span style="color:#FD971F">this</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#88846F"> // 开屏</span></span> <span class="line"><span style="color:#F8F8F2"> ttSdkComponent.ad.</span><span style="color:#A6E22E">showSplashAd</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#88846F"> //插屏</span></span> <span class="line"><span style="color:#F8F8F2"> ttSdkComponent.ad.</span><span style="color:#A6E22E">showFullScreenVideoAd</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <p>以上仅为示例代码,在你的具体业务中请一定要初始化以后再调用方法。</p> <p><strong>至此你就可以接入穿山甲实现广告变现</strong></p>Cocos接入九游 SDKhttps://layfz.com/posts/cocos-jiuyou-insert/https://layfz.com/posts/cocos-jiuyou-insert/将cocos游戏接入九游SDK实现上架。Tue, 27 Aug 2024 17:23:47 GMT<h1 id="android接入九游-sdk">Android接入九游 SDK</h1> <p>本文档提供了如何在 Android 项目中接入九游 SDK 的详细步骤。九游 SDK 是一个为游戏开发者提供各种功能的工具包,如广告、分析、用户认证等。</p> <h2 id="准备工作">准备工作</h2> <ol> <li> <p><strong>创建九游账号</strong>:</p> <ul> <li>访问 <a href="https://aligames.open.9game.cn/">九游开发者平台</a> 注册并登录账户。</li> </ul> </li> <li> <p><strong>下载九游 SDK</strong>:</p> <ul> <li>登录开发者平台,下载最新版本的九游 SDK。 <img src="/images/jiuyou/zip包.png"> 以上是SDK9.7.8.0_7.7.1.0版本的sdk文件夹。我们需要的是demo包 ,具体路径在这个位置👇 <img src="/images/jiuyou/demo包.png"></li> </ul> </li> <li> <p><strong>准备应用信息</strong>:</p> <ul> <li>获取你的应用包名、应用密钥等必要的信息,这些信息在九游开发者平台上会提供。</li> <li>主要是获取gameId,这是需要初始化的</li> </ul> </li> </ol> <h2 id="集成-sdk">集成 SDK</h2> <h3 id="1-将九游-sdk-添加到项目中">1. 将九游 SDK 添加到项目中</h3> <ol> <li> <p><strong>添加 SDK 依赖</strong>:</p> <ul> <li>将九游 SDK 的 <code>.aar</code> 文件添加到项目的 <code>aar</code> 目录下。如果没有 <code>aar</code> 目录,可以手动创建一个。 <img src="/images/jiuyou/aarpath.png"> <img src="/images/jiuyou/aarpakage.png"></li> </ul> </li> <li> <p><strong>配置 Gradle</strong>:</p> <ul> <li> <p>在 <code>app</code> 模块的 <code>build.gradle</code> 文件中,添加对九游 SDK 的依赖:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">dependencies {</span></span> <span class="line"><span style="color:#F8F8F2"> implementation fileTree(</span><span style="color:#AE81FF">dir</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">'aar'</span><span style="color:#F8F8F2">, </span><span style="color:#AE81FF">include</span><span style="color:#F8F8F2">: [</span><span style="color:#E6DB74">'*.jar'</span><span style="color:#F8F8F2">,</span><span style="color:#E6DB74">'*.aar'</span><span style="color:#F8F8F2">])</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> </li> </ul> </li> </ol> <h3 id="2-修改-androidmanifestxml">2. 修改 AndroidManifest.xml</h3> <ol> <li> <p><strong>添加权限和服务</strong>:</p> <ul> <li>打开 <code>AndroidManifest.xml</code> 文件,添加九游 SDK 所需的权限和服务:</li> </ul> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.INTERNET"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_WIFI_STATE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_NETWORK_STATE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.WRITE_EXTERNAL_STORAGE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.READ_PHONE_STATE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.SEND_SMS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.GET_TASKS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.SYSTEM_ALERT_WINDOW"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.AUTHENTICATE_ACCOUNTS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.USE_CREDENTIALS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.GET_ACCOUNTS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.RECORD_AUDIO"</span><span style="color:#F8F8F2">/></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.MODIFY_AUDIO_SETTINGS"</span><span style="color:#F8F8F2">/></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_COARSE_LOCATION"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.WRITE_SETTINGS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.READ_SETTINGS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.WRITE_CALENDAR"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.VIBRATE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.REQUEST_INSTALL_PACKAGES"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.MANAGE_ACCOUNTS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_FINE_LOCATION"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"com.android.alarm.permission.SET_ALARM"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.PACKAGE_USAGE_STATS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.READ_EXTERNAL_STORAGE"</span><span style="color:#F8F8F2"> /></span></span></code></pre> <p>以上是权限部分,依次添加activity部分:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"> &#x3C;!-- android:taskAffinity 填上游戏的包名,如游戏包名为cn.uc.gamesdk.demo,则下面填 cn.uc.gamesdk.demo.diff --></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- data android:scheme 里填上”ng+当前游戏的gameId”,如游戏ID是123456,则填上ng123456 --></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">activity</span></span> <span class="line"><span style="color:#A6E22E"> tools:replace</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android:taskAffinity"</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"cn.uc.gamesdk.activity.PullupActivity"</span></span> <span class="line"><span style="color:#A6E22E"> android:theme</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@android:style/Theme.Translucent"</span></span> <span class="line"><span style="color:#A6E22E"> android:taskAffinity</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"com.example.app.diff"</span></span> <span class="line"><span style="color:#A6E22E"> android:excludeFromRecents</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span></span> <span class="line"><span style="color:#A6E22E"> android:label</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"PullupActivity"</span></span> <span class="line"><span style="color:#A6E22E"> android:launchMode</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"singleTop"</span></span> <span class="line"><span style="color:#A6E22E"> android:exported</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span></span> <span class="line"><span style="color:#A6E22E"> tools:node</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"replace"</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">intent-filter</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">action</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.intent.action.VIEW"</span><span style="color:#F8F8F2">/></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">category</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.intent.category.DEFAULT"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">category</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.intent.category.BROWSABLE"</span><span style="color:#F8F8F2">/></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">data</span><span style="color:#A6E22E"> android:scheme</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"ng132465"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">intent-filter</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">activity</span><span style="color:#F8F8F2">></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">activity</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"cn.gundam.sdk.shell.activity.ThemeProxyActivity"</span></span> <span class="line"><span style="color:#A6E22E"> android:configChanges</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"keyboardHidden|orientation|screenSize"</span></span> <span class="line"><span style="color:#A6E22E"> android:label</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@string/app_name"</span></span> <span class="line"><span style="color:#A6E22E"> android:theme</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@android:style/Theme.NoTitleBar"</span></span> <span class="line"><span style="color:#A6E22E"> android:windowSoftInputMode</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"adjustResize"</span><span style="color:#F8F8F2"> ></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">intent-filter</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">action</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"cn.uc.gamesdk.sdkweb"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">category</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.intent.category.DEFAULT"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">intent-filter</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">activity</span><span style="color:#F8F8F2">></span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- sdk service 1.1.3新增 --></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">service</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"cn.gundam.sdk.shell.service.ProxyService"</span></span> <span class="line"><span style="color:#A6E22E"> android:process</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">":bgservice"</span></span> <span class="line"><span style="color:#A6E22E"> android:exported</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"false"</span></span> <span class="line"><span style="color:#A6E22E"> tools:replace</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android:exported"</span><span style="color:#F8F8F2">/></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- sdk service end--></span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- 以上声明是SDK内部声明,最好不要更改,直接拷贝过去 --></span></span></code></pre> </li> <li> <p><strong>处理 Manifest 合并冲突</strong>:</p> <ul> <li> <p>如果在集成过程中出现 Manifest 合并冲突,可以通过 <code>tools:replace</code> 来解决。例如:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">application</span></span> <span class="line"><span style="color:#A6E22E"> android:allowBackup</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span></span> <span class="line"><span style="color:#A6E22E"> tools:replace</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android:allowBackup"</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- 其他配置 --></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#F92672">application</span><span style="color:#F8F8F2">></span></span></code></pre> </li> <li> <p>确保在 <code>AndroidManifest.xml</code> 文件的开头添加了 <code>xmlns:tools="http://schemas.android.com/tools"</code> 以支持 <code>tools</code> 命名空间。 以上部分添加到位,基本就完成了引入,附几张图注意要修改的部分: <img src="/images/jiuyou/allowBackup.png"> <img src="/images/jiuyou/editinfo.png"></p> </li> </ul> </li> </ol> <h3 id="3-初始化九游-sdk">3. 初始化九游 SDK</h3> <ol> <li> <p><strong>在 <code>AppActivity</code> 类中初始化</strong>:</p> <ul> <li> <p>在你的 <code>AppActivity</code> 类中初始化九游 SDK:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.even.SDKEventKey</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.even.SDKEventReceiver</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.even.Subscribe</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.exception.AliLackActivityException</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.open.ParamInfo</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.open.UCOrientation</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.param.SDKParamKey</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.param.SDKParams</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.uc.gamesdk.UCGameSdk</span><span style="color:#F8F8F2">;</span></span> <span class="line"></span> <span class="line"><span style="color:#F92672"> public</span><span style="color:#F92672"> class</span><span style="color:#A6E22E;text-decoration:underline"> AppActivity</span><span style="color:#F92672"> extends</span><span style="color:#A6E22E;font-style:italic;text-decoration:underline"> Cocos2dxActivity</span><span style="color:#F8F8F2"> {</span></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Override</span></span> <span class="line"><span style="color:#F92672"> protected</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> onCreate</span><span style="color:#F8F8F2">(</span><span style="color:#66D9EF;font-style:italic">Bundle</span><span style="color:#FD971F;font-style:italic"> savedInstanceState</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#FD971F"> super</span><span style="color:#F8F8F2">.</span><span style="color:#A6E22E">onCreate</span><span style="color:#F8F8F2">(savedInstanceState);</span></span> <span class="line"><span style="color:#88846F"> // DO OTHER INITIALIZATION BELOW</span></span> <span class="line"><span style="color:#F8F8F2"> SDKWrapper.</span><span style="color:#A6E22E">getInstance</span><span style="color:#F8F8F2">().</span><span style="color:#A6E22E">init</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F">this</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> UCGameSdk.</span><span style="color:#A6E22E">defaultSdk</span><span style="color:#F8F8F2">().</span><span style="color:#A6E22E">registerSDKEventReceiver</span><span style="color:#F8F8F2">(receiver);</span></span> <span class="line"></span> <span class="line"></span> <span class="line"><span style="color:#A6E22E"> ucSdkInit</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F92672"> private</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> ucSdkInit</span><span style="color:#F8F8F2">() {</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> ParamInfo</span><span style="color:#F8F8F2"> gameParamInfo </span><span style="color:#F92672">=</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> ParamInfo</span><span style="color:#F8F8F2">();</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> gameParamInfo.</span><span style="color:#A6E22E">setGameId</span><span style="color:#F8F8F2">(YOUR_GAME_ID);</span></span> <span class="line"><span style="color:#F8F8F2"> gameParamInfo.</span><span style="color:#A6E22E">setOrientation</span><span style="color:#F8F8F2">(UCOrientation.PORTRAIT);</span></span> <span class="line"></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> SDKParams</span><span style="color:#F8F8F2"> sdkParams </span><span style="color:#F92672">=</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> SDKParams</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#F8F8F2"> sdkParams.</span><span style="color:#A6E22E">put</span><span style="color:#F8F8F2">(SDKParamKey.GAME_PARAMS, gameParamInfo);</span></span> <span class="line"><span style="color:#88846F"> // 如果游戏已经申请了权限,不想sdk主动请求权限,要通过SDKParamKey.GAME_HAD_REQUEST_PERMISSION参数告知九游sdk</span></span> <span class="line"><span style="color:#88846F"> // true 游戏已经弹了,SDK不需要弹出权限申请窗</span></span> <span class="line"><span style="color:#88846F"> // false 游戏没有弹,SDK可以按需弹出权限申请窗</span></span> <span class="line"><span style="color:#F8F8F2"> sdkParams.</span><span style="color:#A6E22E">put</span><span style="color:#F8F8F2">(SDKParamKey.GAME_HAD_REQUEST_PERMISSION, </span><span style="color:#AE81FF">false</span><span style="color:#F8F8F2">);</span></span> <span class="line"></span> <span class="line"></span> <span class="line"><span style="color:#F92672"> try</span><span style="color:#F8F8F2"> {</span></span> <span class="line"><span style="color:#88846F"> //初始化SDK</span></span> <span class="line"><span style="color:#F8F8F2"> UCGameSdk.</span><span style="color:#A6E22E">defaultSdk</span><span style="color:#F8F8F2">().</span><span style="color:#A6E22E">initSdk</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F">this</span><span style="color:#F8F8F2">, sdkParams);</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">catch</span><span style="color:#F8F8F2"> (</span><span style="color:#66D9EF;font-style:italic">AliLackActivityException</span><span style="color:#FD971F;font-style:italic"> e</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F8F8F2"> e.</span><span style="color:#A6E22E">printStackTrace</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> /**</span></span> <span class="line"><span style="color:#88846F"> * 退出游戏前,请调用本方法</span></span> <span class="line"><span style="color:#88846F"> * </span><span style="color:#F92672">@param</span><span style="color:#FD971F;font-style:italic"> view</span></span> <span class="line"><span style="color:#88846F"> */</span></span> <span class="line"><span style="color:#F92672"> public</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> exit</span><span style="color:#F8F8F2">(</span><span style="color:#66D9EF;font-style:italic">View</span><span style="color:#F8F8F2"> view) {</span></span> <span class="line"></span> <span class="line"><span style="color:#F92672"> try</span><span style="color:#F8F8F2"> {</span></span> <span class="line"><span style="color:#F8F8F2"> UCGameSdk.</span><span style="color:#A6E22E">defaultSdk</span><span style="color:#F8F8F2">().</span><span style="color:#A6E22E">exit</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F">this</span><span style="color:#F8F8F2">, </span><span style="color:#AE81FF">null</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">catch</span><span style="color:#F8F8F2"> (</span><span style="color:#66D9EF;font-style:italic">Exception</span><span style="color:#FD971F;font-style:italic"> e</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F8F8F2"> e.</span><span style="color:#A6E22E">printStackTrace</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#A6E22E"> exitApp</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#F92672"> private</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> exitApp</span><span style="color:#F8F8F2">() {</span></span> <span class="line"><span style="color:#A6E22E"> finish</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#88846F"> //退出程序</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> Intent</span><span style="color:#F8F8F2"> intent </span><span style="color:#F92672">=</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> Intent</span><span style="color:#F8F8F2">(Intent.ACTION_MAIN);</span></span> <span class="line"><span style="color:#F8F8F2"> intent.</span><span style="color:#A6E22E">addCategory</span><span style="color:#F8F8F2">(Intent.CATEGORY_HOME);</span></span> <span class="line"><span style="color:#F8F8F2"> intent.</span><span style="color:#A6E22E">setFlags</span><span style="color:#F8F8F2">(Intent.FLAG_ACTIVITY_CLEAR_TOP);</span></span> <span class="line"><span style="color:#A6E22E"> startActivity</span><span style="color:#F8F8F2">(intent);</span></span> <span class="line"><span style="color:#F8F8F2"> android.os.Process.</span><span style="color:#A6E22E">killProcess</span><span style="color:#F8F8F2">(android.os.Process.</span><span style="color:#A6E22E">myPid</span><span style="color:#F8F8F2">());</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> /**</span></span> <span class="line"><span style="color:#88846F"> *回调事件</span></span> <span class="line"><span style="color:#88846F"> */</span></span> <span class="line"><span style="color:#F92672"> private</span><span style="color:#66D9EF;font-style:italic"> SDKEventReceiver</span><span style="color:#F8F8F2"> receiver </span><span style="color:#F92672">=</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> SDKEventReceiver</span><span style="color:#F8F8F2">() {</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Subscribe</span><span style="color:#F8F8F2">(</span><span style="color:#AE81FF">event</span><span style="color:#F92672"> =</span><span style="color:#F8F8F2"> SDKEventKey.ON_EXIT_SUCC)</span></span> <span class="line"><span style="color:#F92672"> private</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> onExit</span><span style="color:#F8F8F2">(</span><span style="color:#66D9EF;font-style:italic">String</span><span style="color:#FD971F;font-style:italic"> desc</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F8F8F2"> Log.</span><span style="color:#A6E22E">d</span><span style="color:#F8F8F2">(TAG, </span><span style="color:#E6DB74">"ON_EXIT_SUCC"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#88846F"> //Toast.makeText(MainActivity.this, ">> 游戏即将退出", Toast.LENGTH_LONG).show();</span></span> <span class="line"></span> <span class="line"><span style="color:#A6E22E"> exitApp</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Subscribe</span><span style="color:#F8F8F2">(</span><span style="color:#AE81FF">event</span><span style="color:#F92672"> =</span><span style="color:#F8F8F2"> SDKEventKey.ON_EXIT_CANCELED)</span></span> <span class="line"><span style="color:#F92672"> private</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> onExitCanceled</span><span style="color:#F8F8F2">(</span><span style="color:#66D9EF;font-style:italic">String</span><span style="color:#FD971F;font-style:italic"> desc</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#A6E22E"> runOnUiThread</span><span style="color:#F8F8F2">(</span><span style="color:#F92672">new</span><span style="color:#A6E22E"> Runnable</span><span style="color:#F8F8F2">() {</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Override</span></span> <span class="line"><span style="color:#F92672"> public</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> run</span><span style="color:#F8F8F2">() {</span></span> <span class="line"><span style="color:#88846F"> //Toast.makeText(MainActivity.this, ">> 继续游戏", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> });</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> // @Subscribe(event = SDKEventKey.ON_LOGIN_SUCC)</span></span> <span class="line"><span style="color:#88846F"> // private void onLoginSucc(final String sid) {</span></span> <span class="line"><span style="color:#88846F"> // runOnUiThread(new Runnable() {</span></span> <span class="line"><span style="color:#88846F"> //</span></span> <span class="line"><span style="color:#88846F"> // @Override</span></span> <span class="line"><span style="color:#88846F"> // public void run() {</span></span> <span class="line"><span style="color:#88846F"> // if (TextUtils.isEmpty(sid)) {</span></span> <span class="line"><span style="color:#88846F"> // // 离线试玩</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(AppActivity.this, ">> 离线登录成功", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#88846F"> // } else {</span></span> <span class="line"><span style="color:#88846F"> // // 用户登录</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(AppActivity.this, ">> 用户登录成功", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"><span style="color:#88846F"> // });</span></span> <span class="line"><span style="color:#88846F"> //</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> // @Subscribe(event = SDKEventKey.ON_LOGIN_FAILED)</span></span> <span class="line"><span style="color:#88846F"> // private void onLoginFailed(String desc) {</span></span> <span class="line"><span style="color:#88846F"> // runOnUiThread(new Runnable() {</span></span> <span class="line"><span style="color:#88846F"> //</span></span> <span class="line"><span style="color:#88846F"> // @Override</span></span> <span class="line"><span style="color:#88846F"> // public void run() {</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(AppActivity.this, ">> 登录失败", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"><span style="color:#88846F"> // });</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Subscribe</span><span style="color:#F8F8F2">(</span><span style="color:#AE81FF">event</span><span style="color:#F92672"> =</span><span style="color:#F8F8F2"> SDKEventKey.ON_INIT_SUCC)</span></span> <span class="line"><span style="color:#F92672"> private</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> onInitSucc</span><span style="color:#F8F8F2">() {</span></span> <span class="line"><span style="color:#A6E22E"> runOnUiThread</span><span style="color:#F8F8F2">(</span><span style="color:#F92672">new</span><span style="color:#A6E22E"> Runnable</span><span style="color:#F8F8F2">() {</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Override</span></span> <span class="line"><span style="color:#F92672"> public</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> run</span><span style="color:#F8F8F2">() {</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(AppActivity.this, ">> 初始化成功", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> });</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Subscribe</span><span style="color:#F8F8F2">(</span><span style="color:#AE81FF">event</span><span style="color:#F92672"> =</span><span style="color:#F8F8F2"> SDKEventKey.ON_INIT_FAILED)</span></span> <span class="line"><span style="color:#F92672"> private</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> onInitFailed</span><span style="color:#F8F8F2">(</span><span style="color:#66D9EF;font-style:italic">String</span><span style="color:#FD971F;font-style:italic"> msg</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#A6E22E"> runOnUiThread</span><span style="color:#F8F8F2">(</span><span style="color:#F92672">new</span><span style="color:#A6E22E"> Runnable</span><span style="color:#F8F8F2">() {</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Override</span></span> <span class="line"><span style="color:#F92672"> public</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> run</span><span style="color:#F8F8F2">() {</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(AppActivity.this, ">> 初始化失败", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> });</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> // @Subscribe(event = SDKEventKey.ON_CREATE_ORDER_SUCC)</span></span> <span class="line"><span style="color:#88846F"> // private void onPaySucc(final Bundle data) {</span></span> <span class="line"><span style="color:#88846F"> // runOnUiThread(new Runnable() {</span></span> <span class="line"><span style="color:#88846F"> //</span></span> <span class="line"><span style="color:#88846F"> // @Override</span></span> <span class="line"><span style="color:#88846F"> // public void run() {</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(MainActivity.this, ">> 支付成功", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#88846F"> // Log.d(TAG, "此处为支付成功回调: callback data = " + data.getString("response"));</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"><span style="color:#88846F"> // });</span></span> <span class="line"><span style="color:#88846F"> // String response = data.getString("response");</span></span> <span class="line"><span style="color:#88846F"> // // 这里执行发货,如果发货成功需要设置以下值</span></span> <span class="line"><span style="color:#88846F"> // data.putString("result", Response.OPERATE_SUCCESS_MSG);</span></span> <span class="line"><span style="color:#88846F"> // // 如果发货失败需要设置以下值</span></span> <span class="line"><span style="color:#88846F"> // //data.putString("result", Response.OPERATE_FAIL_MSG);</span></span> <span class="line"><span style="color:#88846F"> // Log.d(TAG, "pay succ" + data);</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"><span style="color:#88846F"> //</span></span> <span class="line"><span style="color:#88846F"> // @Subscribe(event = SDKEventKey.ON_PAY_USER_EXIT)</span></span> <span class="line"><span style="color:#88846F"> // private void onPayFail(String data) {</span></span> <span class="line"><span style="color:#88846F"> // runOnUiThread( new Runnable() {</span></span> <span class="line"><span style="color:#88846F"> //</span></span> <span class="line"><span style="color:#88846F"> // @Override</span></span> <span class="line"><span style="color:#88846F"> // public void run() {</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(MainActivity.this, ">> 支付失败", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"><span style="color:#88846F"> // });</span></span> <span class="line"><span style="color:#88846F"> // Log.d(TAG, "pay exit");</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> // SDK侧发起的账号切换请求</span></span> <span class="line"><span style="color:#88846F"> // 步骤1,侦听SDK切换账号指令</span></span> <span class="line"><span style="color:#88846F"> // @Subscribe(event = SDKEventKey.ON_ACCOUNT_SWITCH_REQUEST)</span></span> <span class="line"><span style="color:#88846F"> // private void onAccountSwitchRequest(final String sid) {</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(MainActivity.this, "侦听到ON_ACCOUNT_SWITCH_REQUEST指令", Toast.LENGTH_SHORT).show();</span></span> <span class="line"><span style="color:#88846F"> // //步骤2,CP接入组先退出当前游戏角色</span></span> <span class="line"><span style="color:#88846F"> // logoutGameRole();</span></span> <span class="line"><span style="color:#88846F"> // //步骤3,和正常登录一样,调用sdk做登录请求</span></span> <span class="line"><span style="color:#88846F"> // //这里需要注意:</span></span> <span class="line"><span style="color:#88846F"> // // 1. 如果游戏的退出和重新登录在native层是异步的,</span></span> <span class="line"><span style="color:#88846F"> // // 那requestLogin()应该由cp自行决定在合适的节点调用,</span></span> <span class="line"><span style="color:#88846F"> // // 不要求必须放在onAccountSwitchRequest()方法中;</span></span> <span class="line"><span style="color:#88846F"> // // 2. requestLogin()只需要保证在角色已退出的情况下调用即可;</span></span> <span class="line"><span style="color:#88846F"> // login();</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> };</span></span></code></pre> </li> <li> <p>注意将代码中的 <code>YOUR_GAME_ID</code> 替换为从九游开发者平台获取的实际值。</p> </li> </ul> </li> </ol> <h2 id="配置项目">配置项目</h2> <ol> <li> <p><strong>配置 ProGuard</strong>:</p> <ul> <li>如果你的项目使用 ProGuard 混淆代码,确保在 <code>proguard-rules.pro</code> 文件中添加以下规则以防止混淆:</li> </ul> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>-dontwarn</span></span> <span class="line"><span>-ignorewarnings</span></span> <span class="line"><span>#------------------------- 联运 start ---------------------------------</span></span> <span class="line"><span></span></span> <span class="line"><span>-dontskipnonpubliclibraryclassmembers</span></span> <span class="line"><span>-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod</span></span> <span class="line"><span></span></span> <span class="line"><span>-keep public class * extends cn.gundam.sdk.shell.even.SDKEventReceiver</span></span> <span class="line"><span></span></span> <span class="line"><span>-keep class android.**{</span></span> <span class="line"><span> &#x3C;methods>;</span></span> <span class="line"><span> &#x3C;fields>;</span></span> <span class="line"><span>}</span></span> <span class="line"><span>-keep class cn.uc.**{</span></span> <span class="line"><span> &#x3C;methods>;</span></span> <span class="line"><span> &#x3C;fields>;</span></span> <span class="line"><span>}</span></span> <span class="line"><span>-keep class cn.gundam.**{</span></span> <span class="line"><span> &#x3C;methods>;</span></span> <span class="line"><span> &#x3C;fields>;</span></span> <span class="line"><span>}</span></span> <span class="line"><span></span></span> <span class="line"><span>#------------------------- 联运 end ---------------------------------</span></span></code></pre> </li> </ol> <h2 id="测试和调试">测试和调试</h2> <ol> <li> <p><strong>运行应用</strong>:</p> <ul> <li>启动你的应用并验证九游 SDK 是否正常工作。检查 SDK 的日志输出以确认初始化是否成功。</li> </ul> </li> <li> <p><strong>查看日志</strong>:</p> <ul> <li>使用 Logcat 查看九游 SDK 的调试信息,确保没有出现错误或警告。</li> </ul> </li> <li> <p><strong>进行功能测试</strong>:</p> <ul> <li>测试九游 SDK 提供的各种功能(如广告展示、用户登录等),确保它们在应用中正常工作。</li> </ul> </li> </ol> <h2 id="常见问题">常见问题</h2> <ol> <li> <p><strong>Manifest 合并冲突</strong>:</p> <ul> <li>如果遇到 Manifest 合并冲突,请参阅 <a href="https://developer.android.com/studio/build/manifest-merge">处理 Manifest 合并冲突</a> 文档以获取更多信息。</li> </ul> </li> <li> <p><strong>SDK 初始化失败</strong>:</p> <ul> <li>确保应用 ID 和密钥正确无误,并且网络连接正常。</li> </ul> </li> <li> <p><strong>日志查看</strong>:</p> <ul> <li>使用 Logcat 查看 SDK 的调试信息,帮助排查问题。</li> </ul> </li> </ol> <h2 id="参考资料">参考资料</h2> <ul> <li><a href="https://developer.joyme.com/">九游开发者平台</a></li> <li><a href="https://developer.android.com/studio/build/manifest-merge">Android Manifest 合并</a></li> </ul>Cocos接入优量汇https://layfz.com/posts/cocos-youlianghui-insert/https://layfz.com/posts/cocos-youlianghui-insert/将cocos接入安卓优量汇实现广告变现。Thu, 08 Aug 2024 10:55:28 GMT<h1 id="cocos-接入优量汇广告-sdk">Cocos 接入优量汇广告 SDK</h1> <p>在这个教程中,我将详细介绍如何在 Cocos 引擎中接入优量汇广告 SDK。优量汇是一款强大的广告平台,为开发者提供了丰富的广告资源和优质的广告体验。我们将分步介绍如何在 Cocos 项目中集成优量汇 SDK,包括环境配置、代码实现和测试等内容。</p> <p><mark>本教程使用的是腾讯聚合平台,聚合平台在2024-06-05作出修订,将不再维护聚合SDK</mark> <img src="/images/youlianghui/unsuggest.png"></p> <h2 id="目录">目录</h2> <ol> <li><a href="#%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6">前提条件</a></li> <li><a href="#%E8%8E%B7%E5%8F%96%E4%BC%98%E9%87%8F%E6%B1%87-sdk">获取优量汇 SDK</a></li> <li><a href="#%E9%9B%86%E6%88%90-sdk">集成 SDK</a></li> <li><a href="#%E9%85%8D%E7%BD%AE%E4%BC%98%E9%87%8F%E6%B1%87%E5%B9%BF%E5%91%8A">配置优量汇广告</a></li> </ol> <h2 id="前提条件">前提条件</h2> <p>在开始之前,请确保你具备以下条件:</p> <ul> <li>已安装 Cocos Creator 或 Cocos2d-x。</li> <li>已创建一个 Cocos 项目。</li> </ul> <h2 id="注意事项">注意事项:</h2> <ul> <li> <p>由于Android平台更新迭代非常快,开发者很容易陷入版本冲突以及兼容问题,若要使用该教程,请使用<strong>推荐的的版本配置</strong>:</p> <ul> <li>Gradle:8.0.2</li> <li>Gradle Plugin:8.0.2</li> <li>Android NDK: 21.3.6528147</li> <li>Android SDK: 29</li> <li>Cocos Creator: 2.4.13</li> </ul> </li> </ul> <h2 id="获取优量汇-sdk">获取优量汇 SDK</h2> <ol> <li><strong>访问优量汇官网</strong>:前往 <a href="https://adnet.qq.com/resource/sdk">优量汇</a> 并登录你的账户。</li> <li><strong>下载 SDK</strong>:在开发者中心找到Android SDK,下载并解压缩。 <mark>聚合SDK已不提供下载,请联系作者获取,若有备份,忽略此条消息</mark></li> </ol> <h2 id="集成-sdk">集成 SDK</h2> <h3 id="1-集成-android-sdk">1. 集成 Android SDK</h3> <ol> <li> <p><strong>导入 SDK 文件</strong>:</p> <ul> <li>将下载的 Android SDK 文件(<code>.aar</code> 或 <code>.jar</code> 文件)放入 Cocos 项目的 <code>assets</code> 目录下的 <code>Plugins/Android</code> 文件夹中。</li> </ul> </li> <li> <p><strong>配置 Gradle 文件</strong>:</p> <ul> <li> <p>打开 Cocos 项目的 <code>proj.android-studio/app/build.gradle</code> 文件。</p> </li> <li> <p>在 <code>dependencies</code> 部分添加优量汇 SDK 的依赖:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>implementation files('libs/your_sdk_file.aar')</span></span></code></pre> </li> </ul> </li> <li> <p><strong>更新 AndroidManifest.xml</strong>:</p> <ul> <li> <p>在 <code>proj.android-studio/app/src/main/AndroidManifest.xml</code> 中添加优量汇所需的权限和服务:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"> &#x3C;!--相关权限声明 : SDK不强制校验下列权限(即:无下面权限sdk也可正常工作),但建议开发者申请下面权限,尤其是READ_PHONE_STATE权限--></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!--READ_PHONE_STATE权限用于允许SDK获取用户标识,针对单媒体的用户,允许获取权限的,投放定向广告;不允许获取权限的用户,投放通投广告,媒体可以选择是否把用户标识数据提供给优量汇,并承担相应广告填充和eCPM单价下降损失的结果。--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.READ_PHONE_STATE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_COARSE_LOCATION"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- 高于Android 7的系统上,如果应用的 targetSdkVersion >= 26 ,推荐增加权限声明(SDK将通过此权限触发App安装动作)--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.REQUEST_INSTALL_PACKAGES"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- 高于Android 11的系统上,如果应用的 targetSdkVersion >= 30 ,推荐增加以下权限声明(SDK将通过此权限正常触发广告行为,并保证广告的正确投放。此权限需要在用户隐私文档中声明。)--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.QUERY_ALL_PACKAGES"</span></span> <span class="line"><span style="color:#A6E22E"> tools:ignore</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"QueryAllPackagesPermission"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> &#x3C;!--检测当前⽹络状态是2G、3G、4G还是WiFi--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_NETWORK_STATE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!--联⽹权限--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.INTERNET"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!--读写存储权限--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.READ_EXTERNAL_STORAGE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.WRITE_EXTERNAL_STORAGE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!--获取MAC地址,⽤于标识⽤户--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_WIFI_STATE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!--定位权限,不强制要求--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_FINE_LOCATION"</span><span style="color:#F8F8F2"> /></span></span></code></pre> <p>application部分:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">application</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"org.cocos2dx.javascript.GDT.GDTApplication"</span></span> <span class="line"><span style="color:#A6E22E"> android:allowBackup</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span></span> <span class="line"><span style="color:#A6E22E"> android:label</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@string/app_name"</span></span> <span class="line"><span style="color:#A6E22E"> android:usesCleartextTraffic</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span></span> <span class="line"><span style="color:#A6E22E"> tools:replace</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android:allowBackup"</span></span> <span class="line"><span style="color:#A6E22E"> android:icon</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@mipmap/ic_launcher"</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- Tell Cocos2dxActivity the name of our .so --></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">meta-data</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.app.lib_name"</span></span> <span class="line"><span style="color:#A6E22E"> android:value</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"cocos2djs"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">activity</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"org.cocos2dx.javascript.AppActivity"</span></span> <span class="line"><span style="color:#A6E22E"> android:screenOrientation</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"portrait"</span></span> <span class="line"><span style="color:#A6E22E"> android:configChanges</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"orientation|keyboardHidden|screenSize|screenLayout|uiMode"</span></span> <span class="line"><span style="color:#A6E22E"> android:label</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@string/app_name"</span></span> <span class="line"><span style="color:#A6E22E"> android:theme</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@android:style/Theme.NoTitleBar.Fullscreen"</span></span> <span class="line"><span style="color:#A6E22E"> android:launchMode</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"singleTask"</span></span> <span class="line"><span style="color:#A6E22E"> android:exported</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">intent-filter</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">action</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.intent.action.MAIN"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">category</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.intent.category.LAUNCHER"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">intent-filter</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">activity</span><span style="color:#F8F8F2">></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">activity</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"org.cocos2dx.javascript.GDT.SplashActivity"</span></span> <span class="line"><span style="color:#A6E22E"> android:configChanges</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"orientation|keyboardHidden|screenSize|screenLayout|uiMode"</span></span> <span class="line"><span style="color:#A6E22E"> android:screenOrientation</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"portrait"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- targetSDKVersion >= 24时才需要添加这个provider。provider的authorities属性的值为${applicationId}.fileprovider,请开发者根据自己的${applicationId}来设置这个值,例如本例中applicationId为"com.qq.e.union.demo"。 --></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">provider</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"com.qq.e.comm.GDTFileProvider"</span></span> <span class="line"><span style="color:#A6E22E"> android:authorities</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"${applicationId}.gdt.fileprovider"</span></span> <span class="line"><span style="color:#A6E22E"> android:exported</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"false"</span></span> <span class="line"><span style="color:#A6E22E"> android:grantUriPermissions</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">meta-data</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.support.FILE_PROVIDER_PATHS"</span></span> <span class="line"><span style="color:#A6E22E"> android:resource</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@xml/gdt_file_path"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">provider</span><span style="color:#F8F8F2">></span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- 如果使用激励视频/全屏视频功能,需要主动在AndroidManifest.xml里面声明MobRewardVideoActivity --></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">activity</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"com.baidu.mobads.sdk.api.MobRewardVideoActivity"</span></span> <span class="line"><span style="color:#A6E22E"> android:configChanges</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"screenSize|orientation|keyboardHidden"</span></span> <span class="line"><span style="color:#A6E22E"> android:launchMode</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"singleTask"</span></span> <span class="line"><span style="color:#A6E22E"> android:theme</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@android:style/Theme.Translucent.NoTitleBar"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">provider</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"com.baidu.mobads.sdk.api.BdFileProvider"</span></span> <span class="line"><span style="color:#A6E22E"> android:authorities</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"${applicationId}.bd.provider"</span></span> <span class="line"><span style="color:#A6E22E"> android:exported</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"false"</span></span> <span class="line"><span style="color:#A6E22E"> android:grantUriPermissions</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">meta-data</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.support.FILE_PROVIDER_PATHS"</span></span> <span class="line"><span style="color:#A6E22E"> android:resource</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@xml/bd_file_paths"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">provider</span><span style="color:#F8F8F2">></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">provider</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"com.bytedance.sdk.openadsdk.TTFileProvider"</span></span> <span class="line"><span style="color:#A6E22E"> android:authorities</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"${applicationId}.TTFileProvider"</span></span> <span class="line"><span style="color:#A6E22E"> android:exported</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"false"</span></span> <span class="line"><span style="color:#A6E22E"> android:grantUriPermissions</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">meta-data</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.support.FILE_PROVIDER_PATHS"</span></span> <span class="line"><span style="color:#A6E22E"> android:resource</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@xml/tt_file_paths"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">provider</span><span style="color:#F8F8F2">></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">provider</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"com.bytedance.sdk.openadsdk.multipro.TTMultiProvider"</span></span> <span class="line"><span style="color:#A6E22E"> android:authorities</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"${applicationId}.TTMultiProvider"</span></span> <span class="line"><span style="color:#A6E22E"> android:exported</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"false"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">application</span><span style="color:#F8F8F2">></span></span></code></pre> </li> </ul> </li> <li> <p><strong>目录结构</strong>: <img src="/images/youlianghui/mulujiegou.png"></p> <p><strong>将代码依次复制到自己的项目当中,并在AppConfig类中配置自己的参数</strong></p> </li> </ol> <img src="/images/youlianghui/appconfig.png"> <p><mark>添加依赖</mark></p> <p>gradle文件依赖部分:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span> implementation fileTree(dir: 'arrs', include: ['*.aar'])</span></span> <span class="line"><span> implementation 'com.qq.e.union:union:4.570.1440'</span></span> <span class="line"><span> implementation 'androidx.legacy:legacy-support-v4:1.0.0'</span></span> <span class="line"><span> implementation 'androidx.recyclerview:recyclerview:1.0.0'</span></span> <span class="line"><span> implementation "com.google.android.material:material:1.0.0"</span></span> <span class="line"><span> implementation 'androidx.cardview:cardview:1.0.0'</span></span> <span class="line"><span> implementation 'androidx.appcompat:appcompat:1.0.0'</span></span> <span class="line"><span> implementation 'androidx.constraintlayout:constraintlayout:1.1.3'</span></span> <span class="line"><span> implementation 'com.tencent.bugly:crashreport:latest.release'</span></span> <span class="line"><span> debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'</span></span> <span class="line"><span> releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1'</span></span> <span class="line"><span> implementation 'androidx.multidex:multidex:2.0.0'</span></span> <span class="line"><span> implementation 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+'</span></span></code></pre> <p>在app目录下添加arrs文件夹,把聚合平台的包放进去即可</p> <h2 id="配置优量汇广告">配置优量汇广告</h2> <h3 id="1-初始化-sdk">1. 初始化 SDK</h3> <p>在 Cocos 项目的主脚本中,初始化优量汇 SDK:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F">// JavaScript 示例</span></span> <span class="line"><span style="color:#A6E22E">onAD</span><span style="color:#F8F8F2">(sender, str) {</span></span> <span class="line"><span style="color:#F8F8F2"> console.</span><span style="color:#A6E22E">log</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"显示"</span><span style="color:#F92672"> +</span><span style="color:#F8F8F2"> str </span><span style="color:#F92672">+</span><span style="color:#E6DB74"> "广告"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (str </span><span style="color:#F92672">==</span><span style="color:#E6DB74"> "开屏"</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (cc.sys.platform </span><span style="color:#F92672">==</span><span style="color:#F8F8F2"> cc.sys.ANDROID) {</span></span> <span class="line"><span style="color:#F8F8F2"> jsb.reflection.</span><span style="color:#A6E22E">callStaticMethod</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"org/cocos2dx/javascript/AppActivity"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"showSplashAd"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"()V"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">else</span><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (str </span><span style="color:#F92672">==</span><span style="color:#E6DB74"> "激励"</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (cc.sys.platform </span><span style="color:#F92672">==</span><span style="color:#F8F8F2"> cc.sys.ANDROID) {</span></span> <span class="line"><span style="color:#F8F8F2"> jsb.reflection.</span><span style="color:#A6E22E">callStaticMethod</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"org/cocos2dx/javascript/AppActivity"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"showRewardedVideoAd"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"()V"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">else</span><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (str </span><span style="color:#F92672">==</span><span style="color:#E6DB74"> "插屏"</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (cc.sys.platform </span><span style="color:#F92672">==</span><span style="color:#F8F8F2"> cc.sys.ANDROID) {</span></span> <span class="line"><span style="color:#F8F8F2"> jsb.reflection.</span><span style="color:#A6E22E">callStaticMethod</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"org/cocos2dx/javascript/AppActivity"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"showInterstitial"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"()V"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">else</span><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (str </span><span style="color:#F92672">==</span><span style="color:#E6DB74"> "信息"</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (cc.sys.platform </span><span style="color:#F92672">==</span><span style="color:#F8F8F2"> cc.sys.ANDROID) {</span></span> <span class="line"><span style="color:#F8F8F2"> jsb.reflection.</span><span style="color:#A6E22E">callStaticMethod</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"org/cocos2dx/javascript/AppActivity"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"showDrawAd"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"()V"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">else</span><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (str </span><span style="color:#F92672">==</span><span style="color:#E6DB74"> "横幅"</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (cc.sys.platform </span><span style="color:#F92672">==</span><span style="color:#F8F8F2"> cc.sys.ANDROID) {</span></span> <span class="line"><span style="color:#F8F8F2"> jsb.reflection.</span><span style="color:#A6E22E">callStaticMethod</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"org/cocos2dx/javascript/AppActivity"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"showBanner"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"()V"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">else</span><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (str </span><span style="color:#F92672">==</span><span style="color:#E6DB74"> "关闭信息"</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (cc.sys.platform </span><span style="color:#F92672">==</span><span style="color:#F8F8F2"> cc.sys.ANDROID) {</span></span> <span class="line"><span style="color:#F8F8F2"> jsb.reflection.</span><span style="color:#A6E22E">callStaticMethod</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"org/cocos2dx/javascript/AppActivity"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"hideDrawAd"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"()V"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">else</span><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (str </span><span style="color:#F92672">==</span><span style="color:#E6DB74"> "关闭横幅"</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (cc.sys.platform </span><span style="color:#F92672">==</span><span style="color:#F8F8F2"> cc.sys.ANDROID) {</span></span> <span class="line"><span style="color:#F8F8F2"> jsb.reflection.</span><span style="color:#A6E22E">callStaticMethod</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"org/cocos2dx/javascript/AppActivity"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"hideBanner"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"()V"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span></code></pre> <p>在你需要的地方直接调取就可以实现弹出广告了,以上就是cocos脚本在需要的地方调用方法就可以实现安卓平台的广告调用。</p>git同时提交到github和gitee(双平台同步)https://layfz.com/posts/git-multipul-platform/https://layfz.com/posts/git-multipul-platform/帮助git提交到github和gitee平台Tue, 02 Jul 2024 10:35:22 GMT<h1 id="git-同时提交到-gitee-和-github">Git 同时提交到 Gitee 和 GitHub</h1> <h2 id="前提条件">前提条件</h2> <ol> <li>你已经在本地初始化了 Git 仓库。</li> <li>你已经安装了 Git 并配置了用户名和邮箱。</li> <li>你已经有了一个仓库,你想要进行仓库的同步。</li> </ol> <h2 id="配置远程仓库">配置远程仓库</h2> <h3 id="添加远程仓库">添加远程仓库</h3> <p>首先,你需要将 Gitee 和 GitHub 的远程仓库添加到你的本地仓库中。</p> <ol> <li> <p><strong>初始化仓库</strong>: 你需要在一个空文件夹下进行git仓库的本地初始化。 请使用:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> init</span></span></code></pre> </li> <li> <p><strong>添加远程仓库</strong>:</p> <ul> <li> <p><strong>添加 Gitee 远程仓库</strong>:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> remote</span><span style="color:#E6DB74"> add</span><span style="color:#E6DB74"> gitee</span><span style="color:#E6DB74"> https://gitee.com/username/your-repository.git</span></span></code></pre> </li> <li> <p><strong>添加 GitHub 远程仓库</strong>:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> remote</span><span style="color:#E6DB74"> add</span><span style="color:#E6DB74"> github</span><span style="color:#E6DB74"> https://github.com/username/your-repository.git</span></span></code></pre> <p>这里的Github也是你实际的仓库地址。 需要注意git remote add <strong>github</strong> 以及 <strong>gitee</strong> 都被称为别名,那是你在本地的一种命名,实际是只想你后面的地址,所以不必担心这里的命名规不规范,任意就可以。</p> </li> </ul> </li> <li> <p><strong>验证远程仓库</strong>:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> remote</span><span style="color:#AE81FF"> -v</span></span></code></pre> <p>输出应显示你刚才添加的两个远程仓库的 URL。</p> </li> </ol> <h2 id="提交到远程仓库">提交到远程仓库</h2> <h3 id="1-添加和提交更改">1. <strong>添加和提交更改</strong></h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> remote</span><span style="color:#E6DB74"> add</span><span style="color:#E6DB74"> .</span></span></code></pre> <p>添加文件到本地仓库,此时用的是点,代表提交所有的文件,平时也可以使用,因为此提交只会修改与合并。</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> commit</span><span style="color:#AE81FF"> -m</span><span style="color:#E6DB74"> "commit"</span></span></code></pre> <p><strong>commit</strong>是你要提交的备注,这个是可以任意的</p> <h3 id="2-观察本地仓库的分支">2. <strong>观察本地仓库的分支</strong></h3> <img src="/images/gitpush/git_fetch.png"> <p>如上图,此时你本地的分支是master分支,这非常重要! 在本地进行代码更改后,你需要将这些更改添加到 Git 索引中并进行提交。</p> <h3 id="3-推送到仓库">3. <strong>推送到仓库</strong></h3> <p>查看你远程仓库的分支名称: <strong>Github</strong> <img src="/images/gitpush/github_main.png"> <strong>Gitee</strong> <img src="/images/gitpush/gitee_master.png"></p> <p>如上面两个图,此时你的github的分支与gitee的分支名称是不一致的,不一致的情况也没有关系,如果一致则可以直接推送,这里不再赘述。</p> <p><strong>推送到github</strong></p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> push</span><span style="color:#E6DB74"> github</span><span style="color:#E6DB74"> master:main</span></span></code></pre> <p><strong>推送到gitee</strong></p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> push</span><span style="color:#E6DB74"> gitee</span><span style="color:#E6DB74"> master:master</span></span></code></pre> <p>或者</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> push</span><span style="color:#E6DB74"> github</span><span style="color:#F8F8F2"> </span></span></code></pre> <p>由于gitee的分支和我们本地的分支名称一致,所以可以忽略推送分支的名称设置。 推送到github由于分支名称与本地不一致,所以需要指定推送的分支:<strong>master:main</strong> 前面的指定本地的分支,后面指代的远程仓库的分支。</p> <h3 id="4-设置变量名称推送到仓库">4. <strong>设置变量名称推送到仓库</strong></h3> <p>以上的推送方式是一个一个推送的方式,比较适合管理分支,如开发场景和生产场景的情况,倘若不想使用上述场景,只想一键推送,可以设置变量的形式执行命令。</p> <p><strong>1. 进入.git文件并打开config文件</strong> <img src="/images/gitpush/git_config.png"></p> <p><strong>2. 设置全局变量名称</strong> <img src="/images/gitpush/git_alis.png"></p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F8F8F2"> [alias]</span></span> <span class="line"><span style="color:#A6E22E"> pushall</span><span style="color:#E6DB74"> =</span><span style="color:#E6DB74"> "!git push gitee master &#x26;&#x26; git push github master:main"</span></span></code></pre> <p>此时只需要执行就可以实现同时推送的功能</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> pushall</span></span></code></pre> <p>后记:windows打开隐藏文件夹</p> <img src="/images/gitpush/win_hide.png"> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">$</span><span style="color:#E6DB74"> git</span><span style="color:#E6DB74"> push</span><span style="color:#E6DB74"> github</span><span style="color:#AE81FF"> --delete</span><span style="color:#E6DB74"> master</span></span></code></pre> <p>仓库名称 分支名称 => 删除某一个分支(远程分支)</p> <p>删除提交commit 但保留更改</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> reset</span><span style="color:#AE81FF"> --soft</span><span style="color:#E6DB74"> HEAD~1</span></span></code></pre> <p>删除提交commit 并不保留更改</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> reset</span><span style="color:#AE81FF"> --hard</span><span style="color:#E6DB74"> HEAD~1</span></span></code></pre> <p>删除最近的 3 个提交: 保留更改</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> reset</span><span style="color:#AE81FF"> --soft</span><span style="color:#E6DB74"> HEAD~3</span></span></code></pre> <p>设置默认分支:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> branch</span><span style="color:#AE81FF"> --set-upstream-to=github/main</span><span style="color:#E6DB74"> master</span></span></code></pre> <p>master指的是本地分支 单独拉取分支:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> pull</span><span style="color:#E6DB74"> github</span><span style="color:#E6DB74"> main</span></span></code></pre>HostDare CN2-GIA虚假宣传维权记录:一个技术人的较真之路https://layfz.com/posts/hostdare-cn2/https://layfz.com/posts/hostdare-cn2/这是一份详实的技术维权记录,记录了一位开发者在购买HostDare的"CN2-GIA"VPS服务时遭遇虚假宣传,并通过专业技术手段和法律途径进行维权的完整过程。Sat, 24 May 2025 10:59:00 GMT<h2 id="前言">前言</h2> <p>作为一名技术人员,我最近的项目需要扩展国际业务寻找合适的服务器分发节点。经过多方比较,我选择了HostDare的”CN2-GIA NVMe KVM VPS”服务,期望通过优质的CN2线路来保障中美之间的网络质量。</p> <p>然而,这次购买经历让我深刻体会到了什么叫”理想很丰满,现实很骨感”。更重要的是,我决定用技术手段和法律武器,为自己和其他消费者讨个说法。</p> <h2 id="详细沟通过程">详细沟通过程</h2> <p><img src="/images/uploads/cn2_5.png" alt=""></p> <h2 id="业务需求背景">业务需求背景</h2> <h3 id="项目需求">项目需求</h3> <p>我的项目正在开展国际业务,需要在全球多个节点部署服务器来实现:</p> <ul> <li>API服务的就近访问</li> <li>内容分发网络(CDN)</li> <li>数据同步和备份</li> <li>跨境网络加速</li> </ul> <h3 id="技术要求">技术要求</h3> <p>对于中美之间的网络节点,我们的核心要求是:</p> <ul> <li><strong>低延迟</strong>:RTT &#x3C; 150ms</li> <li><strong>高稳定性</strong>:丢包率 &#x3C; 1%</li> <li><strong>充足带宽</strong>:sustained 100Mbps+</li> <li><strong>抗干扰能力</strong>:晚高峰期间性能不能严重下降</li> </ul> <h3 id="为什么选择cn2-gia">为什么选择CN2-GIA</h3> <p>在调研过程中,我了解到:</p> <ul> <li><strong>CN2-GIA</strong>:中国电信的顶级国际网络,去程回程都走59.43.x.x优质骨干网</li> <li><strong>CN2-GT</strong>:混合路由,部分优化,价格较低</li> <li><strong>普通163</strong>:传统国际线路,晚高峰严重拥堵</li> </ul> <p>基于业务的高要求,我们决定投资CN2-GIA级别的服务器。</p> <h2 id="hostdare购买经历">HostDare购买经历</h2> <h3 id="产品选择">产品选择</h3> <p>经过对比,我选择了HostDare的CSSD0套餐:</p> <ul> <li> <p><strong>产品名称</strong>:CN2-GIA NVMe KVM VPS USA</p> </li> <li> <p><strong>配置</strong>:1 vCPU Core, 768MB RAM, 10GB NVMe SSD</p> </li> <li> <p><strong>网络</strong>:250GB月流量,30Mbps CN2 GIA线路</p> </li> <li> <p><strong>价格</strong>:$35.99 USD/年</p> </li> <li> <p><strong>位置</strong>:洛杉矶数据中心</p> <p><img src="/images/uploads/cn2_3.png" alt="HostDare_cn2"></p> </li> </ul> <p><img src="/images/uploads/cn2_2.png" alt=""></p> <p><img src="/images/uploads/cn2_1.png" alt=""></p> <h2 id="性能测试与问题发现">性能测试与问题发现</h2> <h3 id="初步测试结果">初步测试结果</h3> <p>服务器部署完成后,我立即进行了网络性能测试:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"># x-ui面板速度测试</span></span> <span class="line"><span style="color:#A6E22E">测试结果:约30Mbps</span></span> <span class="line"></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 基础延迟测试</span></span> <span class="line"><span style="color:#A6E22E">ping</span><span style="color:#AE81FF"> 114.114</span><span style="color:#E6DB74">.114.114</span></span> <span class="line"><span style="color:#A6E22E">平均延迟:180-200ms</span></span> <span class="line"></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 晚高峰测试</span></span> <span class="line"><span style="color:#A6E22E">晚上8-11点:严重卡顿,代理基本不可用</span></span></code></pre> <h3 id="问题分析">问题分析</h3> <p>这个结果让我很困惑:</p> <ol> <li><strong>速度偏低</strong>:30Mbps远低于真正CN2-GIA应有的性能</li> <li><strong>延迟偏高</strong>:180-200ms超出了CN2-GIA的标准范围</li> <li><strong>晚高峰拥堵</strong>:这是典型的163线路特征</li> </ol> <h3 id="路由追踪测试">路由追踪测试</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"># 去程路由测试</span></span> <span class="line"><span style="color:#A6E22E">root@root:~#</span><span style="color:#E6DB74"> traceroute</span><span style="color:#AE81FF"> 114.114</span><span style="color:#E6DB74">.114.114</span></span> <span class="line"><span style="color:#A6E22E">traceroute</span><span style="color:#E6DB74"> to</span><span style="color:#AE81FF"> 114.114</span><span style="color:#E6DB74">.114.114</span><span style="color:#F8F8F2"> (114.114.114.114), 30 hops max, 60 byte packets</span></span> <span class="line"><span style="color:#A6E22E"> 1</span><span style="color:#AE81FF"> 193.22</span><span style="color:#E6DB74">.152.1</span><span style="color:#F8F8F2"> (193.22.152.1) 0.560 ms 0.643 ms 0.735 ms</span></span> <span class="line"><span style="color:#A6E22E"> 2</span><span style="color:#AE81FF"> 172.16</span><span style="color:#E6DB74">.0.1</span><span style="color:#F8F8F2"> (172.16.0.1) 0.502 ms </span></span> <span class="line"><span style="color:#A6E22E"> 3</span><span style="color:#FD971F"> *</span><span style="color:#FD971F"> **</span></span> <span class="line"><span style="color:#A6E22E"> 4</span><span style="color:#FD971F"> *</span><span style="color:#E6DB74"> ce-2-3-1.a04.lsanca07.us.bb.gin.ntt.net</span><span style="color:#F8F8F2"> (128.241.2.173) 0.654 ms </span><span style="color:#F92672">*</span></span> <span class="line"><span style="color:#A6E22E"> 5</span><span style="color:#E6DB74"> be3360.ccr42.lax01.atlas.cogentco.com</span><span style="color:#F8F8F2"> (154.54.25.149) 1.387 ms </span></span> <span class="line"><span style="color:#A6E22E"> 6</span><span style="color:#FD971F"> *</span><span style="color:#E6DB74"> be5992.ccr82.sjc13.atlas.cogentco.com</span><span style="color:#F8F8F2"> (154.54.169.1) 11.072 ms 10.367 ms</span></span> <span class="line"><span style="color:#A6E22E"> 7</span><span style="color:#E6DB74"> mx97-237.oafishthroat.com</span><span style="color:#F8F8F2"> (23.236.97.237) 6.881 ms </span><span style="color:#F92672">*</span><span style="color:#F8F8F2"> be6009.ccr41.sjc03.atlas.cogentco.com (</span><span style="color:#A6E22E">154.54.169.38</span><span style="color:#F8F8F2">) 10.617 ms</span></span> <span class="line"><span style="color:#A6E22E"> 8</span><span style="color:#AE81FF"> 38.104</span><span style="color:#E6DB74">.138.106</span><span style="color:#F8F8F2"> (38.104.138.106) 18.862 ms 18.897 ms 19.677 ms</span></span> <span class="line"><span style="color:#A6E22E"> 9</span><span style="color:#E6DB74"> ae-1.zenlayer-c3-networks.sngpsi07.sg.bb.gin.ntt.net</span><span style="color:#F8F8F2"> (116.51.26.90) 197.796 ms 202.97.6.1 (</span><span style="color:#A6E22E">202.97.6.1</span><span style="color:#F8F8F2">) 141.232 ms ae-1.zenlayer-c3-networks.sngpsi07.sg.bb.gin.ntt.net (</span><span style="color:#A6E22E">116.51.26.90</span><span style="color:#F8F8F2">) 199.889 ms</span></span> <span class="line"><span style="color:#A6E22E">10</span><span style="color:#AE81FF"> 98.98</span><span style="color:#E6DB74">.126.243</span><span style="color:#F8F8F2"> (98.98.126.243) 200.827 ms 98.98.126.245 (</span><span style="color:#A6E22E">98.98.126.245</span><span style="color:#F8F8F2">) 182.115 ms </span><span style="color:#F92672">*</span></span> <span class="line"><span style="color:#A6E22E">11</span><span style="color:#FD971F"> *</span><span style="color:#FD971F"> **</span></span> <span class="line"><span style="color:#A6E22E">12</span><span style="color:#AE81FF"> 202.97</span><span style="color:#E6DB74">.72.94</span><span style="color:#F8F8F2"> (202.97.72.94) 141.466 ms </span></span> <span class="line"><span style="color:#A6E22E">13</span><span style="color:#FD971F"> *</span><span style="color:#AE81FF"> 202.102</span><span style="color:#E6DB74">.73.14</span><span style="color:#F8F8F2"> (202.102.73.14) 164.746 ms 202.102.69.98 (</span><span style="color:#A6E22E">202.102.69.98</span><span style="color:#F8F8F2">) 151.943 ms</span></span></code></pre> <h3 id="as信息查询">AS信息查询</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">root@root:~#</span><span style="color:#E6DB74"> curl</span><span style="color:#E6DB74"> ipinfo.io</span></span> <span class="line"><span style="color:#F8F8F2">{</span></span> <span class="line"><span style="color:#A6E22E"> "ip"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "2.59.151.75",</span></span> <span class="line"><span style="color:#A6E22E"> "city"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "Los Angeles",</span></span> <span class="line"><span style="color:#A6E22E"> "region"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "California",</span></span> <span class="line"><span style="color:#A6E22E"> "country"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "US",</span></span> <span class="line"><span style="color:#A6E22E"> "loc"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "34.0559,-118.2666",</span></span> <span class="line"><span style="color:#A6E22E"> "org"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "AS40065 CNSERVERS LLC",</span></span> <span class="line"><span style="color:#A6E22E"> "postal"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "90017",</span></span> <span class="line"><span style="color:#A6E22E"> "timezone"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "America/Los_Angeles"</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <h3 id="回程路由验证">回程路由验证</h3> <p>使用第三方工具(tools.ipip.net)测试回程路由,结果显示:</p> <ul> <li>全程走202.97.x.x网段(163骨干网)</li> <li>AS4134(中国电信163)而非AS4809(CN2)</li> <li>延迟180-220ms,明显超标</li> </ul> <h3 id="技术结论">技术结论</h3> <p><strong>这根本不是CN2-GIA线路!</strong></p> <p><strong>证据确凿:</strong></p> <ul> <li>❌ 去程走NTT→Cogent→163,没有CN2标识</li> <li>❌ 回程全程202.97.x.x,典型163线路</li> <li>❌ AS40065 CNSERVERS LLC,不是中国电信</li> <li>❌ 无任何59.43.x.x网段出现</li> <li>❌ 晚高峰严重拥堵,符合163特征</li> </ul> <h2 id="客服沟通官方承认虚假宣传">客服沟通:官方承认虚假宣传</h2> <h3 id="初次投诉">初次投诉</h3> <p>我向HostDare客服详细说明了技术测试结果,要求解释为什么实际线路与广告不符。</p> <h3 id="客服回复原文">客服回复(原文)</h3> <blockquote> <p>“Currently, we provide one-way GIA as otherwise DDoS attacks make the network unstable. We only optimize to home ISP users in China. From your VPS to your home ISP network IP, it is GIA uplink. From your home ISP to your VPS, it is non-GIA uplink like GTT etc. Also note that we do not optimize uplinks to servers in China. So if you test from a testing website such as ipip.net which uses servers network, it will show non-GIA uplinks.”</p> </blockquote> <h3 id="回复分析">回复分析</h3> <p>这个回复实际上是<strong>自我定罪的证据</strong>:</p> <ol> <li><strong>承认只提供”单向GIA”</strong> - 这不是真正的CN2-GIA</li> <li><strong>承认回程”非GIA上行链路如GTT”</strong> - 证明回程走普通线路</li> <li><strong>承认有显著限制</strong> - 但网站广告从未提及</li> </ol> <p><strong>问题核心:</strong></p> <ul> <li><strong>广告宣传</strong>:CN2-GIA(暗示双向优化)</li> <li><strong>实际提供</strong>:单向部分优化(等同于CN2-GT)</li> <li><strong>价格收费</strong>:按CN2-GIA标准收费</li> <li><strong>实际体验</strong>:163线路水平</li> </ul> <p>这就是典型的<strong>虚假宣传</strong>!</p> <h2 id="维权策略制定">维权策略制定</h2> <p>作为一名理性的技术人员,我决定通过合法途径维护自己的权益,并为其他消费者发声。</p> <h3 id="维权目标">维权目标</h3> <ol> <li><strong>获得全额退款</strong> $35.99</li> <li><strong>曝光虚假宣传</strong> 警示其他用户</li> <li><strong>推动行业规范</strong> 促进诚信经营</li> </ol> <h3 id="证据收集">证据收集</h3> <p>我系统性地收集了以下证据:</p> <ul> <li>✅ 产品页面截图(CN2-GIA宣传)</li> <li>✅ 客服邮件往来(承认服务不符)</li> <li>✅ 技术测试结果(路由、AS、性能)</li> <li>✅ 支付凭证(支付宝交易记录)</li> <li>✅ 第三方验证(多个测试平台)</li> </ul> <h2 id="维权行动实施方案国际业务的服务商最害怕支付系统被冻结一般第一步就可以解决">维权行动实施方案(国际业务的服务商最害怕支付系统被冻结,一般第一步就可以解决)</h2> <h3 id="第一阶段友好协商">第一阶段:友好协商</h3> <p>发送正式投诉邮件,详细说明问题并要求退款:</p> <p><strong>邮件要点:</strong></p> <ul> <li>明确指出虚假宣传事实</li> <li>提供技术证据支撑</li> <li>给出合理解决方案</li> <li>设定回复时间期限</li> </ul> <h3 id="第二阶段官方投诉">第二阶段:官方投诉</h3> <p>如果协商无效,将启动以下官方投诉:</p> <p><strong>国内渠道:</strong></p> <ol> <li><strong>12315消费者投诉平台</strong> - 权威性最强</li> <li><strong>12377网络举报中心</strong> - 针对虚假广告</li> <li><strong>支付宝交易争议</strong> - 商品与描述不符</li> <li><strong>地方消协投诉</strong> - 本地消费者保护</li> </ol> <p><strong>国际渠道:</strong></p> <ol> <li><strong>FTC举报</strong> - 美国联邦贸易委员会</li> <li><strong>BBB投诉</strong> - 美国商业改进局</li> <li><strong>ICANN投诉</strong> - 针对域名相关欺诈</li> </ol> <h3 id="第三阶段公开曝光">第三阶段:公开曝光</h3> <p>通过合法渠道进行公开曝光:</p> <p><strong>技术社区:</strong></p> <ul> <li>WebHostingTalk (WHT) 论坛</li> <li>HostLoc 中文主机论坛</li> <li>Reddit r/webhosting 版块</li> <li>V2EX 技术讨论区</li> </ul> <p><strong>评价平台:</strong></p> <ul> <li>Google Reviews</li> <li>Trustpilot</li> <li>Yelp Business</li> </ul> <p><strong>自媒体平台:</strong></p> <ul> <li>技术博客详细分析</li> <li>YouTube 视频教程</li> <li>知乎专栏文章</li> </ul> <h3 id="第四阶段法律行动">第四阶段:法律行动</h3> <p>如果前三阶段无效,考虑法律途径:</p> <ol> <li><strong>小额诉讼</strong> - 针对$35.99金额</li> <li><strong>集体诉讼调查</strong> - 联合其他受害者</li> <li><strong>国际仲裁</strong> - WIPO等机构</li> </ol> <h2 id="最后通牒">最后通牒</h2> <p>基于以上准备,我向HostDare发送了最后通牒:</p> <blockquote> <p>“Dear HostDare Management,</p> </blockquote> <p>This is my FINAL ULTIMATUM regarding the false advertising of your CN2-GIA service.</p> <p>SUMMARY OF FACTS:</p> <ol> <li>I purchased “CN2-GIA NVMe KVM VPS USA” for $35.99 USD</li> <li>Your product page clearly advertised “CN2-GIA” premium network routing</li> <li>Your technical team admitted via email that you only provide “one-way GIA” and “non-GIA uplink like GTT”</li> <li>This constitutes false advertising - you charged CN2-GIA prices for a CN2-GT level service</li> </ol> <p>YOUR WRITTEN ADMISSION: Your email stated: “we provide one-way GIA” and “non-GIA uplink like GTT” - this is NOT the CN2-GIA service advertised and purchased.</p> <p>TECHNICAL EVIDENCE:</p> <ul> <li>AS40065 CNSERVERS instead of AS4809 China Telecom CN2</li> <li>Return routing via 202.97.x.x (163 backbone) instead of 59.43.x.x (CN2)</li> <li>Performance significantly below CN2-GIA standards</li> <li>Third-party traceroute confirms non-CN2 routing</li> </ul> <p>FINAL DEMAND: Process a full refund of $35.99 USD within 24 HOURS of this email.</p> <p>ACTIONS TO BE TAKEN IF NO REFUND:</p> <p>Immediate Actions (within 48 hours):</p> <ol> <li>Payment Dispute Filed - Alipay transaction dispute for “goods not as described”</li> <li>Regulatory Complaints - Filed with China’s 12315 Consumer Protection Agency</li> <li>Internet Fraud Report - Submitted to 12377.cn for false advertising</li> </ol> <p>Public Exposure (within 7 days): 4. WebHostingTalk Forum - Detailed false advertising exposure post 5. HostLoc Forum - Warning post to Chinese VPS community 6. Google Reviews - 1-star review with complete evidence 7. Trustpilot - Detailed negative review with documentation 8. Reddit - Posts in r/webhosting and r/VPS communities 9. YouTube Video - Complete technical analysis and scam exposure</p> <p>Legal/Regulatory Actions (within 14 days): 10. FTC Complaint - False advertising report to US Federal Trade Commission 11. BBB Complaint - Better Business Bureau formal complaint 12. State AG Report - Consumer fraud report to relevant state attorney general 13. Class Action Investigation - Contact other affected customers for group action</p> <p>International Arbitration: 14. WIPO Complaint - International commercial dispute resolution 15. Small Claims Court - Filing in appropriate US jurisdiction</p> <p>YOUR REPUTATION AT STAKE: This false advertising scandal will permanently damage HostDare’s reputation in the hosting community. The evidence is overwhelming and your own admission makes this case unwinnable for you.</p> <p>COST-BENEFIT ANALYSIS:</p> <ul> <li>Refund cost: $35.99</li> <li>Reputation damage cost: Potentially hundreds of thousands in lost business</li> <li>Legal defense cost:Thousands of dollars minimum</li> <li>Time cost: Months of dealing with complaints and disputes</li> </ul> <p>LAST CHANCE: This is your final opportunity to resolve this matter quietly. After 24 hours, I will proceed with ALL planned actions simultaneously.</p> <p>REFUND PROCESSING: Reply to this email confirming refund approval within 24 hours. Any delays or excuses will trigger immediate escalation.</p> <p>EVIDENCE PACKAGE: I have compiled a complete evidence package including:</p> <ul> <li>Original product advertisements</li> <li>Your email admissions</li> <li>Technical test results</li> <li>Payment receipts</li> <li>All communications</li> </ul> <p>This evidence package will be shared with all regulatory bodies, review platforms, and legal entities.</p> <p>NO FURTHER NEGOTIATIONS: I am not interested in:</p> <ul> <li>Technical explanations</li> <li>Service credits</li> <li>Account upgrades</li> <li>Partial refunds</li> </ul> <p>Only a FULL $35.99 refund is acceptable.</p> <p>Failure to respond or refund will result in immediate implementation of all threatened actions.</p> <p>Your move, HostDare.</p> <h2 id="best-regardsane-kobyaccount-anekoby95gmailcomorder-cssd0---3599-usdserver-ip-25915175">Best regards, Ane Koby Account: <a href="mailto:anekoby95@gmail.com">anekoby95@gmail.com</a> Order: CSSD0 - $35.99 USD Server IP: 2.59.151.75</h2> <p>NOTICE:This email and all related communications are being recorded and will be used as evidence in any legal or regulatory proceedings.”</p> <p>在这样的施压下我们很快就得到了回复,并收到退款,这也很容易理解,毕竟国际业务,最重要的就是收款,国际业务提供商也不想因为这样的原因导致风控,从而失去更多用户。</p> <h3 id="核心内容">核心内容</h3> <ul> <li><strong>24小时期限</strong> 退款最后机会</li> <li><strong>14项具体行动</strong> 详细的后续措施</li> <li><strong>成本效益分析</strong> 让对方权衡利弊</li> <li><strong>证据包威胁</strong> 完整的法律证据链</li> </ul> <h2 id="技术人的思考">技术人的思考</h2> <h3 id="为什么要较真">为什么要较真?</h3> <p>很多人可能觉得为了$35.99这么折腾不值得,但我认为:</p> <ol> <li><strong>原则问题</strong> - 虚假宣传必须受到惩罚</li> <li><strong>行业净化</strong> - 推动诚信经营风气</li> <li><strong>技术责任</strong> - 用专业知识保护消费者</li> <li><strong>成本考虑</strong> - 时间成本vs社会价值</li> </ol> <h3 id="技术验证的重要性">技术验证的重要性</h3> <p>普通用户可能只是感觉”网络慢”,但技术人员可以:</p> <ul> <li>通过路由追踪发现问题根源</li> <li>用AS信息验证网络归属</li> <li>对比技术标准识别虚假宣传</li> <li>提供无可辩驳的技术证据</li> </ul> <h3 id="维权策略的技术思维">维权策略的技术思维</h3> <ol> <li><strong>系统性收集证据</strong> - 如同debug程序</li> <li><strong>多路径并行执行</strong> - 分布式维权策略</li> <li><strong>持续监控反馈</strong> - 根据响应调整策略</li> <li><strong>文档化整个过程</strong> - 便于复现和分享</li> </ol> <h2 id="给其他技术人的建议">给其他技术人的建议</h2> <h3 id="购买vps前的技术验证">购买VPS前的技术验证</h3> <ol> <li><strong>查看真实用户评测</strong> - 不要只看官方宣传</li> <li><strong>要求试用或退款保证</strong> - 技术产品需要实测</li> <li><strong>了解网络基础知识</strong> - 区分CN2-GIA/GT/163</li> <li><strong>使用第三方测试工具</strong> - 验证商家宣传</li> </ol> <h3 id="遇到问题时的处理原则">遇到问题时的处理原则</h3> <ol> <li><strong>技术证据先行</strong> - 用数据说话</li> <li><strong>保存所有证据</strong> - 邮件、截图、日志</li> <li><strong>理性分析成本</strong> - 时间vs收益</li> <li><strong>坚持合法维权</strong> - 不要情绪化</li> </ol> <h3 id="推荐的测试工具">推荐的测试工具</h3> <ul> <li><strong>路由追踪</strong>: traceroute, mtr, besttrace</li> <li><strong>AS查询</strong>: ipinfo.io, whois</li> <li><strong>第三方测试</strong>: tools.ipip.net, 17ce.com</li> <li><strong>性能测试</strong>: speedtest-cli, iperf3</li> </ul> <h2 id="行业呼吁">行业呼吁</h2> <h3 id="对vps商家的建议">对VPS商家的建议</h3> <ol> <li><strong>诚实宣传</strong> - 如实描述技术指标</li> <li><strong>透明定价</strong> - 清楚说明服务级别差异</li> <li><strong>技术支持</strong> - 能够解答专业问题</li> <li><strong>退款保证</strong> - 对技术产品提供试用期</li> </ol> <h3 id="对监管部门的建议">对监管部门的建议</h3> <ol> <li><strong>制定技术标准</strong> - 明确CN2等概念定义</li> <li><strong>加强跨境监管</strong> - 海外商家的广告审查</li> <li><strong>支持技术维权</strong> - 重视专业人士的举报</li> <li><strong>行业规范化</strong> - 推动诚信经营体系</li> </ol> <h2 id="结语">结语</h2> <p>作为技术人员,我们有责任用专业知识来维护行业的健康发展。虚假宣传不仅损害了消费者利益,也破坏了整个行业的信任基础。</p> <p>这次维权行动不仅仅是为了$35.99,更是为了:</p> <ul> <li>让技术诚信成为行业标准</li> <li>保护其他消费者免受欺骗</li> <li>推动VPS行业的规范发展</li> </ul> <p>我会持续更新这个维权过程,希望能为遇到类似问题的朋友提供参考。同时,也欢迎其他技术人员分享自己的经历,让我们一起净化这个行业。</p> <h2 id="写到最后">写到最后</h2> <p>最后也是成功追回</p> <p><img src="/images/uploads/cn2_4.png" alt=""></p> <p><strong>技术改变世界,诚信铸就未来。</strong></p> <hr> <p><strong>声明:本文所有内容基于真实经历,技术数据真实有效。文章仅代表个人观点,不构成法律建议。如需专业法律意见,请咨询执业律师。</strong></p>🚀 内网项目踩坑实录:从 HTTP 到 HTTPS 的血泪史https://layfz.com/posts/intranet-http-to-https/https://layfz.com/posts/intranet-http-to-https/一个关于 Cookie 安全策略和 Deno KV 的技术冒险故事Thu, 03 Jul 2025 16:35:00 GMT<h2 id="-tldrtoo-long-didnt-read-概述">📋 TL;DR(Too Long; Didn’t Read)-概述</h2> <ul> <li><strong>问题</strong>: 微信公众号爬虫在内网部署时无法登录</li> <li><strong>原因</strong>: HTTP 环境下无法设置 Secure Cookie + Deno KV 生产环境依赖</li> <li><strong>解决</strong>: 部署自签名 HTTPS + 删除用户存储功能</li> <li><strong>结果</strong>: 功能完美运行 ✨</li> </ul> <hr> <h2 id="-项目背景">🎯 项目背景</h2> <h3 id="需求简述">需求简述</h3> <p>开发一个微信公众号文章爬虫工具,供公司内部使用。由于<strong>数据安全</strong>要求,必须部署在内网环境。</p> <h3 id="技术栈">技术栈</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672">Framework</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">Nuxt.js 3</span></span> <span class="line"><span style="color:#F92672">Runtime</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">Node.js</span></span> <span class="line"><span style="color:#F92672">Proxy</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">Nginx</span></span> <span class="line"><span style="color:#F92672">Panel</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">宝塔面板</span></span> <span class="line"><span style="color:#F92672">Environment</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">Ubuntu 22.04 + 内网 172.19.23.27</span></span></code></pre> <h3 id="预期-vs-现实">预期 vs 现实</h3> <table><thead><tr><th>环境</th><th>预期</th><th>现实</th></tr></thead><tbody><tr><td>公网测试</td><td>✅ 完美运行</td><td>✅ 确实完美</td></tr><tr><td>内网部署</td><td>🤔 应该没问题吧</td><td>💥 完全不能用</td></tr></tbody></table> <hr> <h2 id="-问题一cookie-安全策略地狱">🔥 问题一:Cookie 安全策略地狱</h2> <h3 id="-症状描述">💀 症状描述</h3> <p>部署到内网后,用户扫码登录时:</p> <ul> <li>二维码可以显示 ✅</li> <li>扫码成功 ✅</li> <li>点击登录… <strong>毫无反应</strong> ❌</li> </ul> <p>宝塔配置如下:</p> <p><img src="/images/uploads/baota_20250703.png" alt=""></p> <p>通过反向代理打到本地3000端口,实现内网访问</p> <h3 id="-错误现场">🔍 错误现场</h3> <p>打开 F12,没有任何报错,甚至无法定位问题,此时的我非常焦虑,无论是多级反向代理还是跨域配置我都尝试了,并且长达两个小时都无法解决,此时我在本机ubantu的firefox才看到具体的报错信息,如图所示:</p> <p><img src="/images/uploads/fullsizerender.jpg" alt=""></p> <p>这真的让我非常难顶!!!排查了这么久,firefox直接就帮我定位了问题。(:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">🚫</span><span style="color:#E6DB74"> Cookie</span><span style="color:#E6DB74"> "slave_user"</span><span style="color:#E6DB74"> has</span><span style="color:#E6DB74"> been</span><span style="color:#E6DB74"> rejected</span><span style="color:#E6DB74"> because</span><span style="color:#E6DB74"> a</span><span style="color:#E6DB74"> non-HTTPS</span><span style="color:#E6DB74"> cookie</span><span style="color:#E6DB74"> can't be set as "secure"</span></span> <span class="line"><span style="color:#E6DB74">🚫 Cookie "master_sid" has been rejected because a non-HTTPS cookie can't</span><span style="color:#E6DB74"> be</span><span style="color:#E6DB74"> set</span><span style="color:#E6DB74"> as</span><span style="color:#E6DB74"> "secure"</span><span style="color:#F8F8F2"> </span></span> <span class="line"><span style="color:#A6E22E">🚫</span><span style="color:#E6DB74"> Cookie</span><span style="color:#E6DB74"> "bizuin"</span><span style="color:#E6DB74"> has</span><span style="color:#E6DB74"> been</span><span style="color:#E6DB74"> rejected</span><span style="color:#E6DB74"> because</span><span style="color:#E6DB74"> a</span><span style="color:#E6DB74"> non-HTTPS</span><span style="color:#E6DB74"> cookie</span><span style="color:#E6DB74"> can't be set as "secure"</span></span> <span class="line"><span style="color:#E6DB74">🚫 Cookie "data_ticket" has been rejected because a non-HTTPS cookie can't</span><span style="color:#E6DB74"> be</span><span style="color:#E6DB74"> set</span><span style="color:#E6DB74"> as</span><span style="color:#E6DB74"> "secure"</span></span> <span class="line"><span style="color:#66D9EF">...</span><span style="color:#F8F8F2"> (重复 </span><span style="color:#E6DB74">N</span><span style="color:#E6DB74"> 遍</span><span style="color:#F8F8F2">)</span></span></code></pre> <blockquote> <p>💡 <strong>第一反应</strong>: 这什么鬼?为什么公网好好的,内网就不行?</p> </blockquote> <h3 id="️-深入调查">🕵️ 深入调查</h3> <h4 id="问题根源">问题根源</h4> <p>微信公众平台返回的 Cookie 长这样:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672">Set-Cookie:</span><span style="color:#E6DB74"> slave_user=xxx; Path=/; Secure; SameSite=None; HttpOnly</span></span> <span class="line"><span style="color:#F92672">Set-Cookie:</span><span style="color:#E6DB74"> master_sid=yyy; Path=/; Secure; SameSite=None; HttpOnly</span></span></code></pre> <p>关键在于 <code>Secure</code> 标志!</p> <h4 id="浏览器安全策略">浏览器安全策略</h4> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672">if</span><span style="color:#F8F8F2"> (cookie.hasSecureFlag </span><span style="color:#F92672">&#x26;&#x26;</span><span style="color:#F8F8F2"> location.protocol </span><span style="color:#F92672">!==</span><span style="color:#E6DB74"> 'https:'</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F8F8F2"> console.</span><span style="color:#A6E22E">error</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">'🚫 Cookie rejected: non-HTTPS environment'</span><span style="color:#F8F8F2">)</span></span> <span class="line"><span style="color:#F92672"> return</span><span style="color:#AE81FF"> false</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <h4 id="环境对比">环境对比</h4> <table><thead><tr><th>环境</th><th>协议</th><th>Cookie 设置</th><th>结果</th></tr></thead><tbody><tr><td>公网</td><td><code>https://</code></td><td>✅ 允许 Secure Cookie</td><td>🎉 登录成功</td></tr><tr><td>内网</td><td><code>http://</code></td><td>❌ 拒绝 Secure Cookie</td><td>💥 登录失败</td></tr></tbody></table> <h3 id="-解决思路">💡 解决思路</h3> <p>既然浏览器严格要求 HTTPS 才能设置 Secure Cookie,而微信登录又必须依赖这些 Cookie,那只有一个办法:</p> <p><strong>给内网环境配置 HTTPS!</strong></p> <hr> <h2 id="️-https-部署实战">🛠️ HTTPS 部署实战</h2> <h3 id="step-1-生成自签名证书">Step 1: 生成自签名证书</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"># 一键生成支持多地址的证书</span></span> <span class="line"><span style="color:#A6E22E">openssl</span><span style="color:#E6DB74"> req</span><span style="color:#AE81FF"> -x509</span><span style="color:#AE81FF"> -newkey</span><span style="color:#E6DB74"> rsa:2048</span><span style="color:#AE81FF"> \</span></span> <span class="line"><span style="color:#AE81FF"> -keyout</span><span style="color:#E6DB74"> server.key</span><span style="color:#AE81FF"> -out</span><span style="color:#E6DB74"> server.pem</span><span style="color:#AE81FF"> \</span></span> <span class="line"><span style="color:#AE81FF"> -days</span><span style="color:#AE81FF"> 365</span><span style="color:#AE81FF"> -nodes</span><span style="color:#AE81FF"> \</span></span> <span class="line"><span style="color:#AE81FF"> -config</span><span style="color:#E6DB74"> &#x3C;(</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> '[dn]'</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> CN=</span><span style="color:#AE81FF">172.19</span><span style="color:#E6DB74">.23.27</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> '[req]'</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> distinguished_name = dn</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> '[EXT]'</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> subjectAltName=@alt_names</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> '[alt_names]'</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> DNS.1=localhost</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> DNS.2=wechatccc.com</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> IP.1=</span><span style="color:#AE81FF">172.19</span><span style="color:#E6DB74">.23.27</span></span> <span class="line"><span style="color:#E6DB74"> )</span><span style="color:#AE81FF"> -extensions</span><span style="color:#E6DB74"> EXT</span></span></code></pre> <h3 id="step-2-宝塔面板配置">Step 2: 宝塔面板配置</h3> <ol> <li>网站设置 → SSL 选项</li> <li>选择”其他证书”</li> <li>将 <code>server.pem</code> 内容粘贴到”证书(PEM格式)”</li> <li>将 <code>server.key</code> 内容粘贴到”密钥(KEY)”</li> <li>保存并启用 HTTPS</li> </ol> <h3 id="step-3-nginx-配置">Step 3: Nginx 配置</h3> <p>宝塔会自动生成配置,核心部分:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#66D9EF;font-style:italic">server</span><span style="color:#F8F8F2"> {</span></span> <span class="line"><span style="color:#F92672"> listen </span><span style="color:#F8F8F2">80;</span></span> <span class="line"><span style="color:#F92672"> listen </span><span style="color:#F8F8F2">443 ssl http2;</span></span> <span class="line"><span style="color:#F92672"> server_name </span><span style="color:#F8F8F2">172.19.23.27 wechatccc.com;</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> # 🔒 SSL 证书配置</span></span> <span class="line"><span style="color:#F92672"> ssl_certificate </span><span style="color:#F8F8F2">/www/server/panel/vhost/cert/domain/fullchain.pem;</span></span> <span class="line"><span style="color:#F92672"> ssl_certificate_key </span><span style="color:#F8F8F2">/www/server/panel/vhost/cert/domain/privkey.pem;</span></span> <span class="line"><span style="color:#F92672"> ssl_protocols </span><span style="color:#F8F8F2">TLSv1.2 TLSv1.3;</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> # 🔄 反向代理到 Nuxt 应用</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> location</span><span style="color:#F8F8F2"> / {</span></span> <span class="line"><span style="color:#F92672"> proxy_pass </span><span style="color:#F8F8F2">http://127.0.0.1:3000;</span></span> <span class="line"><span style="color:#F92672"> proxy_set_header </span><span style="color:#F8F8F2">Host $http_host;</span></span> <span class="line"><span style="color:#F92672"> proxy_set_header </span><span style="color:#F8F8F2">X-Forwarded-Proto $scheme;</span></span> <span class="line"><span style="color:#88846F"> # ... 其他标准代理配置</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <h3 id="-问题一解决">🎯 问题一解决</h3> <p>访问 <code>https://172.19.23.27</code>:</p> <ul> <li>⚠️ 浏览器显示”不安全”警告(自签名证书)</li> <li>✅ 点击”高级” → “继续访问”</li> <li>✅ Cookie 设置成功,登录流程恢复正常</li> </ul> <hr> <h2 id="-问题二deno-kv-生产环境炸弹">🔥 问题二:Deno KV 生产环境炸弹</h2> <h3 id="-新的爆炸">💣 新的爆炸</h3> <p>刚解决 Cookie 问题,结果点击登录后又炸了:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> "statusCode"</span><span style="color:#F8F8F2">: </span><span style="color:#AE81FF">500</span><span style="color:#F8F8F2">,</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> "message"</span><span style="color:#F8F8F2">: </span><span style="color:#CFCFC2">"Could not find a Deno KV for production, make sure to deploy on Deno Deploy."</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <blockquote> <p>🤔 <strong>心理活动</strong>: 什么?我明明用的是 Node.js,怎么扯到 Deno 了?</p> </blockquote> <h3 id="-代码考古">🔍 代码考古</h3> <p>发现用户存储模块有这样的逻辑:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672">export</span><span style="color:#F92672"> async</span><span style="color:#66D9EF;font-style:italic"> function</span><span style="color:#A6E22E"> createUser</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F;font-style:italic">user</span><span style="color:#F92672">:</span><span style="color:#A6E22E;text-decoration:underline"> UserEntry</span><span style="color:#F8F8F2">)</span><span style="color:#F92672">:</span><span style="color:#A6E22E;text-decoration:underline"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#66D9EF;font-style:italic">boolean</span><span style="color:#F8F8F2">> {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (process.dev) { </span><span style="color:#88846F">// 🟢 开发环境:直接跳过</span></span> <span class="line"><span style="color:#F92672"> return</span><span style="color:#66D9EF;font-style:italic"> Promise</span><span style="color:#F8F8F2">.</span><span style="color:#A6E22E">resolve</span><span style="color:#F8F8F2">(</span><span style="color:#AE81FF">true</span><span style="color:#F8F8F2">)</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> // 🔴 生产环境:尝试连接 Deno KV</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> kv </span><span style="color:#F92672">=</span><span style="color:#F92672"> await</span><span style="color:#A6E22E"> useKv</span><span style="color:#F8F8F2">() </span><span style="color:#88846F">// 💥 爆炸点</span></span> <span class="line"><span style="color:#88846F"> // ... 复杂的用户存储逻辑</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <h3 id="-环境差异分析">📊 环境差异分析</h3> <table><thead><tr><th>环境类型</th><th>NODE_ENV</th><th>process.dev</th><th>执行路径</th><th>结果</th></tr></thead><tbody><tr><td>开发环境</td><td><code>development</code></td><td><code>true</code></td><td>跳过 KV</td><td>✅ 正常</td></tr><tr><td>生产环境</td><td><code>production</code></td><td><code>false</code></td><td>连接 KV</td><td>💥 报错</td></tr></tbody></table> <p><strong>原来如此!</strong> 开发环境下代码会跳过用户存储,所以没问题。但生产环境会尝试连接 Deno KV 数据库,而我的部署环境是 Node.js,当然找不到!</p> <h3 id="-功能必要性评估">🤔 功能必要性评估</h3> <p>仔细思考用户存储功能:</p> <ul> <li><strong>作用</strong>: 用户信息持久化、多账号管理、登录状态保持</li> <li><strong>对爬虫的必要性</strong>: 🤷‍♂️ 不是核心功能</li> <li><strong>复杂度</strong>: 🔴 增加部署复杂性</li> </ul> <p><strong>决策</strong>: 直接删除用户存储功能,专注核心爬虫能力!</p> <h3 id="-简单粗暴的解决方案">💡 简单粗暴的解决方案</h3> <p><strong>直接让所有用户存储函数返回成功/空值:</strong></p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672">export</span><span style="color:#F92672"> async</span><span style="color:#66D9EF;font-style:italic"> function</span><span style="color:#A6E22E"> createUser</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F;font-style:italic">user</span><span style="color:#F92672">:</span><span style="color:#A6E22E;text-decoration:underline"> UserEntry</span><span style="color:#F8F8F2">)</span><span style="color:#F92672">:</span><span style="color:#A6E22E;text-decoration:underline"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#66D9EF;font-style:italic">boolean</span><span style="color:#F8F8F2">> {</span></span> <span class="line"><span style="color:#88846F"> // 🚀 直接返回成功,不存储任何用户信息</span></span> <span class="line"><span style="color:#F8F8F2"> console.</span><span style="color:#A6E22E">log</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">`📝 跳过用户创建: </span><span style="color:#F92672">${</span><span style="color:#F8F8F2">user.nickname</span><span style="color:#F92672">}</span><span style="color:#E6DB74">`</span><span style="color:#F8F8F2">)</span></span> <span class="line"><span style="color:#F92672"> return</span><span style="color:#66D9EF;font-style:italic"> Promise</span><span style="color:#F8F8F2">.</span><span style="color:#A6E22E">resolve</span><span style="color:#F8F8F2">(</span><span style="color:#AE81FF">true</span><span style="color:#F8F8F2">)</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span> <span class="line"></span> <span class="line"><span style="color:#F92672">export</span><span style="color:#F92672"> async</span><span style="color:#66D9EF;font-style:italic"> function</span><span style="color:#A6E22E"> getUser</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F;font-style:italic">originalID</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#F92672">:</span><span style="color:#A6E22E;text-decoration:underline"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#A6E22E;text-decoration:underline">UserEntry</span><span style="color:#F92672"> |</span><span style="color:#66D9EF;font-style:italic"> null</span><span style="color:#F8F8F2">> {</span></span> <span class="line"><span style="color:#88846F"> // 🚀 直接返回 null,表示"新用户"</span></span> <span class="line"><span style="color:#F92672"> return</span><span style="color:#AE81FF"> null</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span> <span class="line"></span> <span class="line"><span style="color:#F92672">export</span><span style="color:#F92672"> async</span><span style="color:#66D9EF;font-style:italic"> function</span><span style="color:#A6E22E"> getUserByUUID</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F;font-style:italic">uuid</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#F92672">:</span><span style="color:#A6E22E;text-decoration:underline"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#A6E22E;text-decoration:underline">UserEntry</span><span style="color:#F92672"> |</span><span style="color:#66D9EF;font-style:italic"> null</span><span style="color:#F8F8F2">> {</span></span> <span class="line"><span style="color:#F92672"> return</span><span style="color:#AE81FF"> null</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span> <span class="line"></span> <span class="line"><span style="color:#F92672">export</span><span style="color:#F92672"> async</span><span style="color:#66D9EF;font-style:italic"> function</span><span style="color:#A6E22E"> getUserByFakeID</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F;font-style:italic">fakeid</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#F92672">:</span><span style="color:#A6E22E;text-decoration:underline"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#A6E22E;text-decoration:underline">UserEntry</span><span style="color:#F92672"> |</span><span style="color:#66D9EF;font-style:italic"> null</span><span style="color:#F8F8F2">> {</span></span> <span class="line"><span style="color:#F92672"> return</span><span style="color:#AE81FF"> null</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <hr> <h2 id="-最终效果">🎉 最终效果</h2> <h3 id="-功能验证">✅ 功能验证</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"># 🔗 HTTPS 访问</span></span> <span class="line"><span style="color:#A6E22E">curl</span><span style="color:#AE81FF"> -I</span><span style="color:#E6DB74"> https://172.19.23.27</span></span> <span class="line"><span style="color:#88846F"># HTTP/2 200 ✅</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 🍪 Cookie 设置</span></span> <span class="line"><span style="color:#88846F"># F12 → Application → Cookies → 看到完整的微信认证 Cookie ✅</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 🔐 登录流程</span></span> <span class="line"><span style="color:#88846F"># 扫码 → 点击登录 → 跳转成功 ✅</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 🕷️ 爬虫功能</span></span> <span class="line"><span style="color:#88846F"># 数据爬取完全正常 ✅</span></span></code></pre> <h3 id="-性能表现">📈 性能表现</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672">响应时间</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">200-500ms</span></span> <span class="line"><span style="color:#F92672">稳定性</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">24h 无异常</span></span> <span class="line"><span style="color:#F92672">资源占用</span><span style="color:#F8F8F2">: </span></span> <span class="line"><span style="color:#F92672"> CPU</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">&#x3C; 5%</span></span> <span class="line"><span style="color:#F92672"> Memory</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">&#x3C; 200MB</span></span> <span class="line"><span style="color:#F92672">用户体验</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">🌟🌟🌟🌟🌟</span></span></code></pre> <hr> <h2 id="-经验总结">🎓 经验总结</h2> <h3 id="-核心洞察">💎 核心洞察</h3> <h4 id="1-安全策略进化论">1. 安全策略进化论</h4> <p>现代浏览器对 Cookie 安全要求越来越严格:</p> <ul> <li><code>Secure</code> 标志强制要求 HTTPS</li> <li><code>SameSite=None</code> 必须配合 HTTPS 使用</li> <li>即使内网环境也无法例外</li> </ul> <h4 id="2-环境一致性的重要性">2. 环境一致性的重要性</h4> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672"><span style="user-select: none;">-</span> 开发环境: HTTP + 跳过复杂逻辑</span></span> <span class="line"><span style="color:#A6E22E"><span style="user-select: none;">+</span> 生产环境: HTTPS + 完整功能实现</span></span> <span class="line"><span style="color:#F8F8F2">= 隐藏问题,部署时爆炸 💥</span></span></code></pre> <h4 id="3-keep-it-simple-stupid">3. Keep It Simple, Stupid!</h4> <p>当遇到复杂的依赖问题时,问问自己:</p> <ul> <li>这个功能真的必要吗?</li> <li>能不能用更简单的方式解决?</li> <li>核心业务需求是什么?</li> </ul> <h3 id="️-避坑指南">🛡️ 避坑指南</h3> <h4 id="cookie-问题排查">Cookie 问题排查</h4> <ul> <li>✅ 优先检查浏览器控制台 Cookie 错误</li> <li>✅ 确认协议匹配(HTTP vs HTTPS)</li> <li>✅ 理解 Secure/SameSite 属性含义</li> </ul> <h4 id="数据库依赖管理">数据库依赖管理</h4> <ul> <li>✅ 区分开发和生产环境的差异</li> <li>✅ 评估功能的实际必要性</li> <li>✅ 优先解决核心问题,再考虑完善功能</li> </ul> <h4 id="内网部署特殊性">内网部署特殊性</h4> <ul> <li>✅ 不要以为内网就可以降低安全标准</li> <li>✅ 自签名证书是内网 HTTPS 的好选择</li> <li>✅ 简化架构,减少外部依赖</li> </ul> <hr> <h2 id="-总结">🚀 总结</h2> <p>这次踩坑经历的核心收获:</p> <h3 id="-技术层面">🔧 技术层面</h3> <ol> <li><strong>HTTPS 不是可选项</strong>:即使内网环境,现代 Web 应用也需要 HTTPS</li> <li><strong>自签名证书很实用</strong>:快速、简单、满足功能需求</li> <li><strong>功能删减有时比优化更有效</strong>:不必要的复杂性就是技术债务</li> </ol> <h3 id="-工程思维">🎯 工程思维</h3> <ol> <li><strong>问题导向</strong>:专注解决实际问题,而不是实现所有功能</li> <li><strong>渐进式开发</strong>:先让核心功能工作,再考虑完善</li> <li><strong>简化优于复杂</strong>:能删就删,能简化就简化</li> </ol> <h3 id="-部署经验">💡 部署经验</h3> <ol> <li><strong>环境一致性很重要</strong>:开发和生产环境的差异会隐藏问题</li> <li><strong>错误信息是最好的老师</strong>:仔细阅读错误信息,往往直接指向解决方案</li> <li><strong>内网不等于简单</strong>:现代 Web 标准不会因为内网而降低要求</li> </ol> <hr> <h2 id="-结语">🎬 结语</h2> <p>从 HTTP 到 HTTPS,从复杂的用户管理到简化的核心功能,这次部署经历让我深刻理解了:</p> <blockquote> <p>🌟 <strong>工程不是艺术,而是在约束条件下找到最优解的过程。</strong></p> </blockquote> <p>有时候,<strong>删除代码比写代码更有价值</strong>。有时候,<strong>简单粗暴的方案比精巧复杂的设计更实用</strong>。</p> <p><strong>技术服务于业务,而不是相反。</strong></p>投资思考与育儿智慧:透过现象看本质https://layfz.com/posts/investing-and-raising-children-wisdom/https://layfz.com/posts/investing-and-raising-children-wisdom/从股市分析到育儿哲学,从美国政治制度到媒体现象,一篇深度思考文章引发的感悟。探讨如何在信息爆炸时代保持理性思考,以及现代育儿的智慧启示。Fri, 30 May 2025 10:46:00 GMT<h2 id="文章摘要">文章摘要</h2> <p>这是一篇融合了股市分析、政治解读和育儿哲学的深度思考文章。作者从标普500和纳指的技术分析开始,延伸到对特朗普关税政策的法理解读,最后分享了关于育儿和人生的深刻思考。文章体现了作者”透过现象看本质”的思维方式,强调了解规则、理性分析的重要性。</p> <h2 id="核心观点">核心观点</h2> <h3 id="投资理念">投资理念</h3> <ul> <li>市场永远是对的,抱着自己的对错进市场的人都活不下来</li> <li>英伟达遇到143-145阻力位后回落,需要大利好才能突破</li> <li>生物医药板块联合健康在300左右震荡筑底</li> </ul> <h3 id="政治分析">政治分析</h3> <ul> <li>特朗普关税被法院叫停,但可通过《国家紧急状态法》继续执行</li> <li>马斯克退出政府效率部是因为法律限制,作为”特殊政府雇员”只能工作130天</li> <li>美国三权分立制度的运作机制与中国不同</li> </ul> <h3 id="育儿智慧">育儿智慧</h3> <ul> <li>“鸡娃不如鸡自己” - 卷自己比卷孩子效果更好</li> <li>最好的学区房是家庭,父母是原件,孩子是复印件</li> <li>成年人最好的学区房是病房 - 健康比什么都重要</li> </ul> <h3 id="人生感悟">人生感悟</h3> <ul> <li>小孩子的快乐很简单,要让他们有快乐的童年</li> <li>《西游记》中孙悟空的智慧:懂得维系关系,理解规则</li> <li>做投资的人杠精少,因为市场会教育所有人</li> </ul> <h2 id="原文内容">原文内容</h2> <p>” 标题: 成为另外一种人~ 标普500和纳指分别微涨0.4%、0.39%,高开回落。</p> <p>标普500的上方阻力位是6000点,纳指是20000点,这两个阻力位是多空双方争夺的密集区。</p> <p>持仓的个股中,生物医药板块全部微涨,消费股大部分微跌,伯克希尔和VISA微涨,科技股板块大多微涨。</p> <p>生物医药板块的联合健康短期内会在300左右震荡一段时间,筑底后再往上修复,最糟糕的阶段开始慢慢过去了。</p> <p>英伟达和预期的一致,在遇到143-145这个阻力位后回落,最终收盘价139.19美元,涨幅3.25%;后续要突破143-145这个阻力位,需要有大的利好,突破后则奔着153这个阻力位去了。</p> <p>没啥可以操作的,接下去的时间,以等待为主。</p> <p>川普的关税被联邦外贸法院叫停,关税战上半场进入休息时间,下半场再过段时间开始。</p> <p>法院的结果一出来,5分钟内川普政府立马提起上诉。</p> <p>老美是三权分立体系,按照宪法规定,征税权属于国会。</p> <p>但1976年颁布的《国家紧急状态法》,国家处于紧急状态时,总统有权力发布和采取尽力措施,其中包含临时征收税款和改动税率。</p> <p>国家是否处于紧急状态,只能由总统负责认定,法院无权干预总统的决定。即司法可以制衡行政,但不能代替行政。</p> <p>也就是说,总统有权单方面认定紧急状态是否成立,不需要为是否真的处于紧急状态这件事提供证据和证明。</p> <p>老美历任总统行使紧急状态的次数都很多,最近几个总统中,川普目前是行使次数最少的,4次,分别是:加强边境管理、2018年中美贸易战、应对新冠疫情,以及本次贸易战。</p> <p>作为对此,拜登行使9次,奥巴马12次,小布什13次,克林顿17次。</p> <p>后续川普只要以“美国财政即将破产”这一理由,就能行使紧急状态,将关税战继续打下去,并且不需要证明美国的财政是否真的即将破产。</p> <p>另外,即便最高法院以防卫措施过大、范围过大等为由,裁定川普的关税战违法,联邦政府也可以拒绝执行最高法院的裁决。典型案例,是拜登政府“拒绝”执行最高院关于豁免学生贷款的裁决,绕了个弯把豁免贷款的目的实现了。</p> <p>老美的三权分立是个很有意思的游戏,和咱们很不一样,不要用咱们的套用在老美身上,不适用。</p> <p>比如马斯克将退出政府效率部这事。</p> <p>按照老美的法律,去政府任职要避嫌,所以川普把自己的公司股权等商业资产装入到信托,由其他亲属打理。不走完这些程序,是不能去当总统的。</p> <p>但老美的官分两种:</p> <p>一种是由总统提名,由参议院确认的那些国防部长、财政部长等官员;另一种是总统根据自己的需要任免,不需要通过国会,类似于“家臣”,白宫幕僚长、总统法律顾问等,都是这一类。</p> <p>这两种马斯克都做不了,有利益冲突。因此,只能以特殊政府雇员来参与。</p> <p>根据美国司法部的定义,特殊政府雇员是指在365天内为政府工作不超过130天的临时雇员,通常以专家或顾问身份提供专业支持。</p> <p>这一身份允许马斯克在不公开披露其财务状况的情况下参与政府工作,同时可以继续管理其商业和公司。</p> <p>2025年2月3日,白宫官方确认马斯克被任命为“特殊政府雇员”。按照130天的限制,马斯克就是必须得在5月底结束任期的。</p> <p>嗯,就这么简单,明白了内在规则,就知道事情接下去的走势,不容易被带节奏。</p> <p>我喜欢跟做投资的人聊天,杠精少。搞钱的永远不会有什么杠精,因为去了市场你就知道,市场永远是对的;抱着自己的对错进市场的人,都活不下来。</p> <p>那些动不动就爱抬杠的人,往往要么穷,要么是学生或刚入社会的小白。</p> <p>昨晚睡得早,今天5点50分就醒来。起来洗漱了一番,陪孩子们吃个早饭。</p> <p>老二老三很开心,因为今天学校看表演,可以带玩具到学校玩。</p> <p>有时觉得,小孩子的快乐真的很简单。我尽量让他们有个快乐的童年。</p> <p>卷他们,还不如卷我自己,鸡娃不如鸡自己,自己都卷不出来,还想卷娃,想啥呢;自己卷出来了,说明卷自己效果更好,那就更没必要卷娃了。</p> <p>现在的机会点和窗口期,肯定比他们以后多。还不如我自己努力点,让孩子以后有需要能拼得了爹,而不是看别人拼爹。</p> <p>当然,前提是他们知道,好爹要用在刀刃上,而不是用在朋友圈里,否则就是把爹的收益最小化,把爹的风险最大化,哈哈。</p> <p>他们这代人是要去见世界的,要走出属于自己的路,踏上属于自己的人生。</p> <p>我顶多是他们去见世界的那个物质兜底,至于路怎么走、世界怎么样、从哪个角度看等等,需要他们自己根据自身的能力和见识、认知做判断。</p> <p>而自身的能力和见识、认知,是他们需要童年、少年、青年期走过一段漫长的路,也就是“学习”。</p> <p>对于孩子来说,最好的学区房是家庭,父母是原件,孩子是复印件,因此言传身教、以身作则就是最好的榜样。</p> <p>对于成年人来说,最好的学区房是病房。无论你有多大的野心和控制欲,病房里多躺一躺,就知道人生什么最重要,别期望值太高、控制欲太强,动不动把孩子往死里卷,没必要。</p> <p>小儿子有段时间不读《西游记》和《三国演义》了,我问他怎么不喜欢了?</p> <p>他说打打杀杀没意思。</p> <p>媳妇笑着说小儿子他没看懂,打打杀杀是表面,内在都是人情世故:</p> <p>你看孙悟空多聪明,知道师傅有后台,但从不让师傅出面,每次遇到麻烦都尽可能不出全力打败妖怪,而是求助于各路神仙。</p> <p>貌似都是自己在到处欠人情,但其实都是在维系关系,各路神仙因此能在取经过程中捞到一份人情和功劳;而上级都知道是在给谁面子。</p> <p>要不以孙悟空大闹天宫的本事,怎么可能打不过那些妖怪。</p> <p>而且,孙悟空无论多难,从不质疑取经事业的正确性,有大觉悟,因此显得特别能干,特别敬业,是真正的打工人楷模。</p> <p>你看不起“孙悟空”的能力退化、溜须拍马跑关系,“孙悟空”还看不起你榆木脑袋不懂变通、自以为是。</p> <p>老大打岔,说他懂这个,几年前他问过我一个问题,为什么会有两个司机?</p> <p>我说一个是职业是司机,专门负责开车的;另一个是职务是司机,挂职的,很多难搞定的关系、难预订的餐厅、难见得了面的人…他都能搞定,只是挂司机的头衔,实质做的是一系列“有核心竞争力”的活。</p> <p>看起来同样是司机,但只有第二种最吃香,不可替代。</p> <p>回到《西游记》的话题上,孙悟空很聪明,但唐僧这领导也很聪明。</p> <p>唐僧作为取经小团队的领导,他的任务根本就不是保证取经成功,因为取经必然成功。唐僧很清楚,保证取经必须按照上级要求的方式成功,才是自己要做的事。</p> <p>说白了就是只对上面负责,“权力只对权力的来源负责”,这是唐僧聪明的地方。</p> <p>老二对这些话题明显没兴趣,专注吃她的饭。</p> <p>时间过得飞快,一眨眼就要送他们去学校了。</p> <p>自从娃们上学后,每天相处的时间变得越来越少,以后上大学、工作了,能陪伴得就更少了。</p> <p>媳妇说每次想到这个,就想多生几个孩子,哈哈,我问她年轻时咋没这个觉悟。</p> <p>她回答,那个时候正值计划生育不允许超生,且那会还不具备生多孩的条件和勇气。</p> <p>我想了想,也是。</p> <p>愿我们都拥有随时停留和休息的底气,做人做事越来越平和、平淡。</p> <p>就这样吧。 ”</p> <h2 id="精选评论区互动">精选评论区互动</h2> <ul> <li><img src="/images/uploads/jinjianceng01.png" alt=""></li> <li><img src="/images/uploads/jinjianceng02.png" alt=""></li> </ul> <h3 id="关于作者夫人的智慧">关于作者夫人的智慧</h3> <p><strong>兵(重庆)</strong>:你有时间谈下是怎么娶了个睿智婆娘的吧!</p> <p><strong>金渐成(作者)</strong>:写过的,我二次创业失败时,背负了巨额负债,女朋友也跑了,同事的她不知道我海外有收入,傻乎乎抵押了自己的房子借钱给我,经常陪我聊天散心,我报恩把自己”抵押”给她了,借钱借成老公</p> <p><strong>天亮(北京)</strong>:你这么说就是当初想赖账不还钱</p> <p><strong>金渐成(作者)</strong>:两年后,给了她200多倍的回报,你就说这回报率高不高嘛</p> <h3 id="关于美国政治制度">关于美国政治制度</h3> <p><strong>凯(加拿大)</strong>:机哥你一段话把主流媒体报道马斯克和川普反目的故事给戳破了😃!</p> <p><strong>金渐成(作者)</strong>:现在反智的多,能讲清楚内在规则的少,都是情绪,少有理性。</p> <p><strong>Friend²⁰²⁵(广东)</strong>:还得是老美,法院敢和总统抬杠</p> <p><strong>金渐成(作者)</strong>:这法院,居然没被拆了,大法官居然还能干得好好的,没有丟了饭碗;总统居然奈何不了美联储的行长,哈哈哈,很多事看起来很有趣,挺好玩的,分权制衡是个好东西,避免了权力一家独大。</p> <h3 id="关于育儿理念">关于育儿理念</h3> <p><strong>文博(中国香港)</strong>:自己卷出来了,说明卷自己效果更好,那就更没必要卷娃了——就是这么干的</p> <p><strong>金渐成(作者)</strong>:嗯,卷娃不划算,卷自己回报率才高。如果自己都卷不动,还卷娃,那就更没意义了,娃基因突变、爆种的概率太低。</p> <p><strong>🌽ㅤ(广东)</strong>:鸡娃就是压力转移大法,家长把自己无能的压力、生活上的压力转移给孩子。</p> <p><strong>金渐成(作者)</strong>:嗯,鸡娃真残忍,就像虐猫虐狗一样。</p> <h3 id="关于投资心态">关于投资心态</h3> <p><strong>Peter Edmund陈(美国)</strong>:读书读得越多,看的东西越多,反而变得大多数时候有点沉默,看到自己的无力,对于有些事情我们不能改变,连改变自己的看法都异常困难。</p> <p><strong>金渐成(作者)</strong>:嗯,我也有几年是很纠结、矛盾的状态,后来想通了,怎么都是生活,不需要在意他人的看法和期待,做自己想做的和能做的就行,其他的随他去了。</p> <p><strong>ᰔᩚ 云汐大人༊(江苏)</strong>:市场永远是对的;抱着自己的对错进市场的人,都活不下来。那些动不动就爱抬杠的人,往往要么穷,要么是学生或刚入社会的小白。我很赞同</p> <p><strong>金渐成(作者)</strong>:所以一看网上的信息,就容易知道哪些人是穷人和小白,哪些人是搞投资的。信息很好分辨,搞投资的人往往观点和做法更有说服力,因为都是真金白银砸出来的。</p> <h3 id="关于健康的重要性">关于健康的重要性</h3> <p><strong>℡.wl(重庆)</strong>:前段时间结石急性,那两天唯一的感觉就是,身体健康才是第一,其他都需要排在这个后面,病房去了一遭,想起之前机哥说的身体重要,又深深的感动了</p> <p><strong>金渐成(作者)</strong>:成年人的最好学区房,是病房。这句话我深有体会</p> <h3 id="关于西游记的深度解读">关于《西游记》的深度解读</h3> <p><strong>劲松(北京)</strong>:每读机哥的育儿经,都学到不少,像孙悟空的见解和我之前看到的完全不同,那个版本说猪八戒讨领导喜欢,孙悟空累死累活打工苦命人。今天看机哥这视角,才发觉孙悟空才是有勇有谋,有情商的那个,要不最终他成佛,猪八戒没成佛呢</p> <p><strong>PowerVito(北京)</strong>:牛魔王就是不明白,坐莲台级别领导的青牛、锦毛吼,跟他那个谁都能骑的傻冤家碧水金睛兽,完全不是一回事儿😂</p> <h3 id="关于学习和成长">关于学习和成长</h3> <p><strong>晨(上海)</strong>:再次拜服极哥。才意识到白看了那么多遍西游记了…知道猴子聪明,却从来没去思考过背后这么多的真相。果然,不能只读书,读死书啊。怎样灵活变通,汲取知识和教训才有意义。</p> <p><strong>金渐成(作者)</strong>:因为大多数人只有情绪,很容易陷入自嗨。多了解规则,多熟悉底层逻辑,就会有自己的判断。</p> <h2 id="个人感悟">个人感悟</h2> <p>这篇文章让我深刻体会到了什么叫”透过现象看本质”。在信息爆炸的时代,媒体往往更倾向于制造情绪和话题,而非帮助受众理解事情的内在逻辑。</p> <p>作者对马斯克退出政府职务的分析就是典型例子。大多数媒体报道都在渲染”反目成仇”的戏剧性,但实际上这只是美国法律制度的正常运作。了解了《特殊政府雇员》的130天限制规定,就能明白这是必然结果,而非什么政治斗争。</p> <p>在育儿方面,“鸡娃不如鸡自己”这个观点特别有启发性。很多家长把自己的焦虑和压力转移给孩子,美其名曰”为了孩子好”,实际上可能是在逃避自我提升的责任。如果连自己都无法突破,又怎么指望孩子能够超越?</p> <p>文章中对《西游记》的解读也很精彩。孙悟空的智慧不在于武力,而在于对规则的理解和对关系的维护。这种职场智慧的解读,让经典名著有了新的现实意义。</p> <p>最深刻的是”成年人最好的学区房是病房”这句话。健康面前,其他一切都是浮云。这提醒我们要时刻保持清醒,不要被外在的成功标准绑架,忘记了生活的本质。</p> <hr> <p><strong>声明</strong>:本文内容来源于公众号分享,仅供学习交流使用。投资有风险,决策需谨慎。文中观点仅代表作者个人看法,不构成投资建议。</p>CentOS 7运维常用的命令https://layfz.com/posts/linux-normal/https://layfz.com/posts/linux-normal/在java开发以及网页开发中常用到的关于部署以及配置相关的命令Fri, 30 Aug 2024 11:53:22 GMT<h1 id="centos-常用命令集">CentOS 常用命令集</h1> <p>本文适合基于CentOS 7且有基础的运维同学,一些常见的命令如“cd、rm”等命令不会列举,仅仅会给出在开发视角中常用的命令。</p> <h2 id="目录">目录</h2> <ul> <li> <p><a href="#centos-%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%E9%9B%86">CentOS 常用命令集</a></p> <ul> <li> <p><a href="#%E7%9B%AE%E5%BD%95">目录</a></p> <ul> <li><a href="#%E4%B8%80%E8%88%AC%E4%BF%A1%E6%81%AF%E5%91%BD%E4%BB%A4">一般信息命令</a></li> <li><a href="#%E9%98%B2%E7%81%AB%E5%A2%99">防火墙</a></li> <li><a href="#nginx">Nginx</a></li> <li><a href="#jar%E5%8C%85%E8%BF%90%E8%A1%8C">jar包运行</a></li> </ul> </li> </ul> </li> </ul> <h3 id="一般信息命令">一般信息命令</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">ps</span><span style="color:#E6DB74"> aux</span><span style="color:#88846F"> # 显示所有进程</span></span> <span class="line"><span style="color:#A6E22E">ps</span><span style="color:#AE81FF"> -ef</span><span style="color:#88846F"> # 显示完整格式的进程信息</span></span> <span class="line"><span style="color:#A6E22E">uname</span><span style="color:#AE81FF"> -a</span><span style="color:#88846F"> # 显示所有系统信息</span></span> <span class="line"><span style="color:#A6E22E">df</span><span style="color:#AE81FF"> -h</span><span style="color:#88846F"> # 以人类可读格式显示</span></span> <span class="line"><span style="color:#A6E22E">uptime</span><span style="color:#88846F"> # 显示系统运行时间</span></span> <span class="line"><span style="color:#A6E22E">ifconfig</span><span style="color:#88846F"> # 显示网络配置</span></span> <span class="line"><span style="color:#A6E22E">ps</span><span style="color:#AE81FF"> -ef</span><span style="color:#F92672">|</span><span style="color:#A6E22E">grep</span><span style="color:#E6DB74"> nginx</span><span style="color:#88846F"> # 显示nginx进程、过滤后。</span></span></code></pre> <h3 id="防火墙">防火墙</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">systemctl</span><span style="color:#E6DB74"> stop</span><span style="color:#E6DB74"> firewalld.service</span></span> <span class="line"><span style="color:#A6E22E">systemctl</span><span style="color:#E6DB74"> start</span><span style="color:#E6DB74"> firewalld.service</span></span> <span class="line"><span style="color:#A6E22E">firewall-cmd</span><span style="color:#AE81FF"> --zone=public</span><span style="color:#AE81FF"> --add-port=200006/tcp</span><span style="color:#AE81FF"> --permanent</span></span> <span class="line"><span style="color:#A6E22E">firewall-cmd</span><span style="color:#AE81FF"> --zone=public</span><span style="color:#AE81FF"> --remove-port=200006/tcp</span><span style="color:#AE81FF"> --permanent</span></span> <span class="line"><span style="color:#A6E22E">firewall-cmd</span><span style="color:#AE81FF"> --reload</span></span> <span class="line"><span style="color:#A6E22E">firewall-cmd</span><span style="color:#AE81FF"> --list-all</span></span></code></pre> <h3 id="nginx">Nginx</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">sudo</span><span style="color:#E6DB74"> systemctl</span><span style="color:#E6DB74"> start</span><span style="color:#E6DB74"> nginx</span></span> <span class="line"><span style="color:#A6E22E">sudo</span><span style="color:#E6DB74"> systemctl</span><span style="color:#E6DB74"> stop</span><span style="color:#E6DB74"> nginx</span></span> <span class="line"><span style="color:#A6E22E">sudo</span><span style="color:#E6DB74"> systemctl</span><span style="color:#E6DB74"> restart</span><span style="color:#E6DB74"> nginx</span><span style="color:#F8F8F2"> </span></span> <span class="line"><span style="color:#A6E22E">nginx</span><span style="color:#AE81FF"> -s</span><span style="color:#E6DB74"> reload</span></span> <span class="line"><span style="color:#A6E22E">nginx</span><span style="color:#AE81FF"> -s</span><span style="color:#E6DB74"> quit</span></span> <span class="line"><span style="color:#A6E22E">nginx</span><span style="color:#AE81FF"> -v</span></span></code></pre> <h3 id="jar包运行">jar包运行</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">nohup</span><span style="color:#E6DB74"> java</span><span style="color:#AE81FF"> -jar</span><span style="color:#E6DB74"> pakageName.jar</span><span style="color:#F8F8F2"> &#x26; </span><span style="color:#88846F"># 后台运行jar包并输出到nohup日志</span></span> <span class="line"><span style="color:#A6E22E">java</span><span style="color:#AE81FF"> -jar</span><span style="color:#E6DB74"> pakageName</span><span style="color:#F8F8F2"> &#x26; </span><span style="color:#88846F"># jar包如果有配置日志</span></span></code></pre>现代AI爬虫的进化之路:从OCR到Markdown的完美转身 🚀https://layfz.com/posts/modern-ai-crawlers-ocr-to-markdown/https://layfz.com/posts/modern-ai-crawlers-ocr-to-markdown/从传统正则表达式到AI加持的现代爬虫,我们经历了什么?当OCR遇到复杂数据结构时为何"翻车"?为什么说Markdown才是AI爬虫的最佳伴侣? 本文分享了一个实战项目中的痛点和解决方案:面对混乱的数据结构(纯文本+图片+表格),如何从冗余的"OCR+拼接"方案完美转身到"PDF转Markdown"的高效流程。Fri, 04 Jul 2025 17:10:00 GMT<h2 id="前言">前言 💭</h2> <p>最近在搞爬虫项目的时候,发现了一个很有意思的现象:<strong>现代爬虫必须要有AI加持才能玩得转</strong>!🤖 传统的正则表达式抓取方式已经被AI狠狠地”降维打击”了,效率提升不是一点半点。</p> <p>但是,理想很丰满,现实很骨感… 😅</p> <h2 id="现有方案的痛点">现有方案的痛点 😵‍💫</h2> <h4 id="-纯文本类型">📝 <strong>纯文本类型</strong></h4> <ul> <li><strong>案例</strong>:新闻文章、博客内容</li> <li><strong>处理难点</strong>:相对简单</li> <li><strong>现有方案问题</strong>:基本能处理</li> </ul> <h4 id="️-图片文字类型">🖼️ <strong>图片+文字类型</strong></h4> <ul> <li><strong>案例</strong>:产品介绍页面、技术文档</li> <li><strong>处理难点</strong>:OCR识别准确率、中文字符识别</li> <li><strong>现有方案问题</strong>:百度OCR经常识别错误,需要人工校验</li> </ul> <p><img src="/images/uploads/case_spider_1.png" alt=""></p> <h4 id="-图片表格类型">📊 <strong>图片+表格类型</strong></h4> <ul> <li><strong>案例</strong>:财务报表、数据统计页面</li> <li><strong>处理难点</strong>:表格格式丢失、行列关系混乱</li> <li><strong>现有方案问题</strong>:OCR无法保留表格结构,AI理解困难</li> </ul> <p><img src="/images/uploads/case_spider_2.png" alt=""></p> <h3 id="多模态大模型的美丽陷阱">多模态大模型的”美丽陷阱”</h3> <p>说到AI爬虫,大家第一反应可能是:<strong>“用多模态大模型啊!ChatGPT不香吗?”</strong> 🤔</p> <p>emmm… 香是香,但是有几个问题:</p> <ol> <li> <p><strong>中文理解能力有限</strong> 🈹</p> <ul> <li>毕竟是国外的大模型,中文训练还是有些欠缺</li> <li>OCR识别中文的准确率让人捉急</li> <li>我们项目不允许有错误数据,所以直接pass</li> </ul> </li> <li> <p><strong>API支持不够完善</strong> 📡</p> <ul> <li>大部分支持API的多模态模型还没完全开放</li> <li>即使有,成本也不低</li> </ul> </li> </ol> <p>所以我们选择了国产大模型DeepSeek,至少中文理解能力杠杠的!🎯</p> <h3 id="数据结构的混乱现状">数据结构的”混乱现状”</h3> <p>我们项目遇到的数据结构简直是”百花齐放”:</p> <ul> <li>📝 <strong>纯文本</strong>:这个还好处理</li> <li>🖼️ <strong>图片+文字</strong>:需要OCR配合</li> <li>📊 <strong>图片+文字+表格</strong>:这就头大了…</li> </ul> <p>更要命的是,图片里经常混杂着我们需要的信息,但也有很多无用图片,程序很难自动筛选。</p> <h3 id="当前方案的冗余困境">当前方案的”冗余困境”</h3> <p>我们现在的流程是这样的:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>获取文本和图片 → 图片丢给百度OCR → 拼接完整文章 → 投给DeepSeek提取数据</span></span></code></pre> <p>问题来了:</p> <ul> <li>❌ <strong>流程冗余</strong>:步骤太多,效率低</li> <li>❌ <strong>通用性差</strong>:每个网站都要调整</li> <li>❌ <strong>表格处理弱</strong>:OCR无法保留表格格式</li> <li>❌ <strong>噪音太多</strong>:无用图片干扰数据提取</li> </ul> <h2 id="灵感闪现markdown才是王道">灵感闪现:Markdown才是王道!💡</h2> <p>突然想到一个问题:<strong>为什么不直接用Markdown呢?</strong></p> <p>想想看,Markdown的优势:</p> <p>✅ <strong>纯文本格式</strong> - 轻量级,易处理<br> ✅ <strong>格式友好</strong> - 保留基本样式和结构<br> ✅ <strong>AI友好</strong> - 大部分模型输出都是Markdown<br> ✅ <strong>通用性强</strong> - 一套流程走天下</p> <h2 id="解决方案pdf转markdown的完美方案">解决方案:PDF转Markdown的完美方案 🎯</h2> <p>经过一番调研,发现了一个神器:<strong>MinerU</strong>!</p> <h3 id="核心思路">核心思路</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>网页 → PDF/图片 → Markdown → AI提取数据</span></span></code></pre> <h3 id="为什么选择mineru">为什么选择MinerU?</h3> <p>🔍 <strong>项目地址</strong>:<a href="https://github.com/opendatalab/MinerU/tree/master">https://github.com/opendatalab/MinerU/tree/master</a></p> <p><strong>优势清单</strong>:</p> <ul> <li>🇨🇳 <strong>中文友好</strong>:识别中文准确率极高</li> <li>📐 <strong>排版自然</strong>:保留原有格式和结构</li> <li>📊 <strong>表格支持</strong>:完美处理表格数据</li> <li>🎯 <strong>精准识别</strong>:汉字识别准确度很高</li> <li>🔄 <strong>数据完整</strong>:不丢失关键信息</li> </ul> <h3 id="实际效果">实际效果</h3> <p><img src="/images/uploads/minerui_1.png" alt=""></p> <p>使用MinerU转换后的Markdown数据投给AI:</p> <ul> <li>✅ <strong>数据完整性</strong> - 不丢失任何关键信息</li> <li>✅ <strong>格式保持</strong> - 表格、列表等结构完整</li> <li>✅ <strong>噪音减少</strong> - 无用图片被过滤</li> <li>✅ <strong>处理效率</strong> - 一步到位,省心省力</li> </ul> <h2 id="总结">总结 🎊</h2> <p>从传统的正则表达式,到OCR+AI的组合拳,再到现在的PDF转Markdown方案,AI爬虫的进化之路充满了惊喜!</p> <p><strong>核心观点</strong>:</p> <ol> <li>🤖 <strong>AI加持是必然趋势</strong> - 告别正则表达式时代</li> <li>🎯 <strong>选择合适的模型</strong> - 根据业务需求选择中文友好的模型</li> <li>📄 <strong>Markdown是最佳载体</strong> - AI友好且保留格式</li> <li>🔧 <strong>工具选择很重要</strong> - MinerU这样的工具能事半功倍</li> </ol> <p>现在的方案不仅解决了数据混乱的问题,还大大提升了提取精度和效率。如果你也在做类似的项目,强烈推荐试试这个方案!🚀</p> <hr> <p><em>怎么样,这波操作是不是很6?</em> 😎</p>🔥 服务器突然挂了?我被攻击了?一次惊心动魄的故障排查经历https://layfz.com/posts/server-down/https://layfz.com/posts/server-down/你有没有遇到过这种情况:刚才还好好的服务,突然就连不上了?这不,我就遇到了这么一次"灵异事件"...Sat, 05 Jul 2025 20:26:00 GMT<h2 id="-突如其来的灾难">🚨 突如其来的灾难</h2> <p>我服务器上一直有一个服务在跑,但是突然发现我的一个重要网络服务怎么都连不上了 😱</p> <p><strong>症状如下:</strong></p> <ul> <li>🔴 客户端连接超时</li> <li>🔴 延迟显示 <code>-1</code></li> <li>🔴 各种重试都无效</li> </ul> <p>第一反应:🤯 “完了,服务器是不是被黑了?”</p> <h2 id="-紧急抢救">🆘 紧急抢救</h2> <p>既然连不上,那就先重启试试吧(程序员的万能解决方案 😅)</p> <p>随后我到服务器提供商面板去重启</p> <p>神奇的事情发生了 ✨ —— 重启后服务立即恢复正常!</p> <p>但是问题来了:<strong>为什么重启就好了?根本原因是什么?</strong></p> <p>作为一个有追求的技术人,怎么能就这样算了呢?必须要找出真相!🕵️‍♂️</p> <h2 id="-开始福尔摩斯时间">🔍 开始福尔摩斯时间</h2> <h3 id="第一条线索可疑的登录记录">第一条线索:可疑的登录记录</h3> <p>登录服务器后,发现了第一个异常:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">Last</span><span style="color:#E6DB74"> failed</span><span style="color:#E6DB74"> login:</span><span style="color:#E6DB74"> Sat</span><span style="color:#E6DB74"> Jul</span><span style="color:#AE81FF"> 5</span><span style="color:#AE81FF"> 07</span><span style="color:#E6DB74">:54:21</span><span style="color:#E6DB74"> EDT</span><span style="color:#AE81FF"> 2025</span><span style="color:#E6DB74"> from</span><span style="color:#AE81FF"> 152.200</span><span style="color:#E6DB74">.181.42</span><span style="color:#E6DB74"> on</span><span style="color:#E6DB74"> ssh:notty</span></span> <span class="line"><span style="color:#A6E22E">There</span><span style="color:#E6DB74"> were</span><span style="color:#AE81FF"> 40</span><span style="color:#E6DB74"> failed</span><span style="color:#E6DB74"> login</span><span style="color:#E6DB74"> attempts</span><span style="color:#E6DB74"> since</span><span style="color:#E6DB74"> the</span><span style="color:#E6DB74"> last</span><span style="color:#E6DB74"> successful</span><span style="color:#E6DB74"> login.</span></span></code></pre> <p>😳 <strong>40次失败登录?</strong> 这绝对不正常!</p> <h3 id="第二条线索系统错误日志">第二条线索:系统错误日志</h3> <p>继续深挖,查看系统日志:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">journalctl</span><span style="color:#AE81FF"> --since</span><span style="color:#E6DB74"> "2025-07-05 06:00:00"</span><span style="color:#AE81FF"> --until</span><span style="color:#E6DB74"> "2025-07-05 07:13:00"</span><span style="color:#AE81FF"> -p</span><span style="color:#E6DB74"> err</span></span></code></pre> <p>发现了这些可疑的错误:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>Jul 05 06:08:13 sshd: error: kex_exchange_identification: Connection closed</span></span> <span class="line"><span>Jul 05 06:10:31 sshd: error: kex_exchange_identification: Connection closed </span></span> <span class="line"><span>Jul 05 06:46:52 sshd: fatal: userauth_finish: send failure packet: Connection</span></span></code></pre> <p>🤔 SSH连接错误?感觉离真相越来越近了…</p> <h3 id="第三条线索暴力破解攻击并且ip来自于德国香港等地">第三条线索:暴力破解攻击!并且ip来自于德国、香港等地</h3> <p>当我查看详细的登录失败记录时,真相大白了!</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">journalctl</span><span style="color:#F92672"> |</span><span style="color:#A6E22E"> grep</span><span style="color:#E6DB74"> "Failed password"</span><span style="color:#F92672"> |</span><span style="color:#A6E22E"> tail</span><span style="color:#AE81FF"> -20</span></span></code></pre> <p>映入眼帘的是一连串触目惊心的攻击记录:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>Jul 05 07:41:44 Failed password for root from 80.94.95.15</span></span> <span class="line"><span>Jul 05 07:42:26 Failed password for root from 123.58.209.224 </span></span> <span class="line"><span>Jul 05 07:42:45 Failed password for invalid user odoo15 from 161.35.72.143</span></span> <span class="line"><span>Jul 05 07:43:02 Failed password for root from 103.63.25.239</span></span> <span class="line"><span>Jul 05 07:47:41 Failed password for root from 152.200.181.42</span></span> <span class="line"><span>...</span></span></code></pre> <p>🚨 <strong>天哪!我的服务器正在遭受大规模的SSH暴力破解攻击!</strong></p> <h2 id="-真相大白">🧩 真相大白</h2> <p>现在一切都说得通了!</p> <p><strong>攻击的影响链条:</strong></p> <ol> <li> <p>🏴‍☠️ <strong>多个IP同时发起SSH暴力破解</strong></p> <ul> <li>每隔几秒就有新的攻击尝试</li> <li>来自世界各地的僵尸网络</li> </ul> </li> <li> <p>⚡ <strong>系统资源被疯狂消耗</strong></p> <ul> <li>每个SSH连接尝试都要消耗CPU和内存</li> <li>网络连接池被占满</li> <li>文件描述符被耗尽</li> </ul> </li> <li> <p>💥 <strong>其他服务受到牵连</strong></p> <ul> <li>我的网络服务无法获得足够资源</li> <li>新连接被拒绝</li> <li>表现为”服务挂了”</li> </ul> </li> <li> <p>🔄 <strong>重启为什么能解决?</strong></p> <ul> <li>清除了所有攻击连接</li> <li>释放了被占用的系统资源</li> <li>服务重新获得正常运行环境</li> </ul> </li> </ol> <p><strong>原来我的服务没坏,是被攻击”挤”挂了!</strong> 😂</p> <h2 id="️-绝地反击防御策略">🛡️ 绝地反击:防御策略</h2> <p>既然找到了根本原因,那就要彻底解决这个问题! 我们遇到这种攻击可以有多种方案,譬如修改端口,设置防火墙等</p> <h3 id="-终极武器fail2ban">🚀 终极武器:fail2ban</h3> <p>经过研究,我选择了 <code>fail2ban</code> 这个神器:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"># 安装fail2ban</span></span> <span class="line"><span style="color:#A6E22E">yum</span><span style="color:#E6DB74"> install</span><span style="color:#E6DB74"> epel-release</span><span style="color:#AE81FF"> -y</span></span> <span class="line"><span style="color:#A6E22E">yum</span><span style="color:#E6DB74"> install</span><span style="color:#E6DB74"> fail2ban</span><span style="color:#AE81FF"> -y</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 配置严格的防护策略</span></span> <span class="line"><span style="color:#A6E22E">cat</span><span style="color:#F92672"> ></span><span style="color:#E6DB74"> /etc/fail2ban/jail.local</span><span style="color:#F92672"> &#x3C;&#x3C;</span><span style="color:#F8F8F2"> '</span><span style="color:#F8F8F2">EOF</span><span style="color:#F8F8F2">'</span></span> <span class="line"><span style="color:#E6DB74">[DEFAULT]</span></span> <span class="line"><span style="color:#E6DB74"># 封禁24小时,让攻击者冷静冷静</span></span> <span class="line"><span style="color:#E6DB74">bantime = 86400</span></span> <span class="line"><span style="color:#E6DB74"># 监控10分钟内的行为</span></span> <span class="line"><span style="color:#E6DB74">findtime = 600</span></span> <span class="line"><span style="color:#E6DB74"># 最多容忍3次失败</span></span> <span class="line"><span style="color:#E6DB74">maxretry = 3</span></span> <span class="line"></span> <span class="line"><span style="color:#E6DB74">[sshd]</span></span> <span class="line"><span style="color:#E6DB74">enabled = true</span></span> <span class="line"><span style="color:#E6DB74">port = ssh</span></span> <span class="line"><span style="color:#E6DB74">filter = sshd</span></span> <span class="line"><span style="color:#E6DB74">logpath = /var/log/secure</span></span> <span class="line"><span style="color:#E6DB74"># SSH更严格:2次失败就封禁</span></span> <span class="line"><span style="color:#E6DB74">maxretry = 3 </span></span> <span class="line"><span style="color:#E6DB74"># SSH攻击者封禁7天</span></span> <span class="line"><span style="color:#E6DB74">bantime = 604800</span></span> <span class="line"><span style="color:#F8F8F2">EOF</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 启动防护</span></span> <span class="line"><span style="color:#A6E22E">systemctl</span><span style="color:#E6DB74"> enable</span><span style="color:#E6DB74"> fail2ban</span></span> <span class="line"><span style="color:#A6E22E">systemctl</span><span style="color:#E6DB74"> start</span><span style="color:#E6DB74"> fail2ban</span></span></code></pre> <h3 id="-效果验证">📊 效果验证</h3> <p>配置完成后,可以通过以下命令查看防护效果:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"># 查看防护状态</span></span> <span class="line"><span style="color:#A6E22E">fail2ban-client</span><span style="color:#E6DB74"> status</span><span style="color:#E6DB74"> sshd</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 查看被封的攻击者</span></span> <span class="line"><span style="color:#A6E22E">fail2ban-client</span><span style="color:#E6DB74"> get</span><span style="color:#E6DB74"> sshd</span><span style="color:#E6DB74"> banned</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 实时监控攻击</span></span> <span class="line"><span style="color:#A6E22E">tail</span><span style="color:#AE81FF"> -f</span><span style="color:#E6DB74"> /var/log/fail2ban.log</span></span></code></pre> <p>看着攻击者的IP一个个被加入黑名单,那感觉简直太爽了! 😎</p> <h2 id="-经验总结">💡 经验总结</h2> <p>这次故障排查让我学到了很多:</p> <h3 id="-关键收获">🎯 关键收获</h3> <ol> <li> <p><strong>🔍 日志是最好的朋友</strong></p> <ul> <li>系统日志包含了所有的线索</li> <li>学会读懂各种日志格式</li> <li>时间线分析很重要</li> </ul> </li> <li> <p><strong>🌐 网络安全不容忽视</strong></p> <ul> <li>SSH暴力破解攻击无处不在</li> <li>即使不直接攻击目标服务,也可能造成间接影响</li> <li>防护措施要尽早部署</li> </ul> </li> <li> <p><strong>⚡ 资源耗尽的隐蔽性</strong></p> <ul> <li>问题表现可能与根本原因相距甚远</li> <li>系统资源竞争会导致意想不到的故障</li> <li>重启能解决很多问题,但不能只满足于此</li> </ul> </li> </ol> <h3 id="️-实用建议">🛠️ 实用建议</h3> <ul> <li><strong>🚨 立即行动</strong>: 如果你的服务器还没有部署 fail2ban,赶紧去配置!</li> <li><strong>📈 监控重要</strong>: 定期检查系统日志,及早发现异常</li> <li><strong>🔐 安全意识</strong>: 修改默认SSH端口、禁用root登录等都是好习惯</li> </ul> <h2 id="-结语">🎉 结语</h2> <p>虽然这次”故障”让我紧张了一阵子,但最终的排查过程还是很有成就感的 💪</p> <p>从表面现象到深层原因,再到最终解决方案,整个过程就像解谜游戏一样有趣。</p> <p><strong>最重要的是:遇到问题不要慌,日志会告诉你一切!</strong> 📚</p>
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
<channel>
<title>LayFz's Blog</title>
<description>技术分享、编程心得和开发实践 - LayFz的个人博客</description>
<link>https://layfz.com/</link>
<language>zh-CN</language>
<item>
<title>蓝绿部署:零停机时间发布的最佳实践</title>
<link>https://layfz.com/posts/blue-green-deployment-guide/</link>
<guid isPermaLink="true">https://layfz.com/posts/blue-green-deployment-guide/</guid>
<description>🔁 《DevOps必备技能:掌握蓝绿部署,实现无缝版本切换》 - 现代DevOps工程师必须了解的部署策略,让用户无感知切换版本。</description>
<pubDate>Fri, 07 Mar 2025 16:37:00 GMT</pubDate>
<content:encoded><h1 id="蓝绿部署实践">蓝绿部署实践 🚀</h1> <h2 id="-问题背景">📌 问题背景</h2> <p>在传统的Next.js项目部署中,我们经常会遇到以下问题:</p> <ul> <li>🔴 部署过程中用户访问出现样式加载失败</li> <li>🔴 静态资源无法正确加载</li> <li>🔴 Next.js的后台任务被意外中断</li> <li>🔴 用户体验断层,刷新页面出现短暂服务不可用</li> </ul> <p>这些问题主要是因为在常规部署流程中,我们通常会直接在运行的服务上进行代码更新和重启,导致服务临时不可用。</p> <h2 id="-蓝绿部署解决方案">💡 蓝绿部署解决方案</h2> <p>蓝绿部署(Blue-Green Deployment)是一种零停机部署策略,通过准备两套相同的生产环境,在不影响用户体验的情况下完成系统升级。</p> <h3 id="-方案原理">🔄 方案原理</h3> <ol> <li> <p>准备两个完全相同的Next.js服务环境:</p> <ul> <li>🔵 蓝环境:端口3019</li> <li>🟢 绿环境:端口5019</li> </ul> </li> <li> <p>使用宝塔面板的Nginx配置管理流量分发</p> <ul> <li>在任一时刻,只有一个环境接收真实用户流量</li> <li>另一个环境作为待命状态,用于准备新版本部署</li> </ul> </li> </ol> <h3 id="-具体实施流程">🔄 具体实施流程</h3> <p>假设当前蓝环境(3019端口)正在提供服务:</p> <ol> <li> <p><strong>准备绿环境</strong> 📦</p> <ul> <li> <p>将最新代码同步到绿环境(5019端口)目录</p> </li> <li> <p>在绿环境执行构建过程:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">npm</span><span style="color:#E6DB74"> install</span></span> <span class="line"><span style="color:#A6E22E">npm</span><span style="color:#E6DB74"> run</span><span style="color:#E6DB74"> build</span></span></code></pre> </li> <li> <p>此时用户依然访问的是蓝环境(3019端口),不受影响</p> </li> </ul> </li> <li> <p><strong>启动绿环境服务</strong> ▶️</p> <ul> <li> <p>使用PM2启动或重启绿环境服务:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">pm2</span><span style="color:#E6DB74"> restart</span><span style="color:#E6DB74"> next-app-green</span></span></code></pre> </li> <li> <p>确认绿环境服务正常运行</p> </li> </ul> </li> <li> <p><strong>切换流量</strong> 🔀</p> <ul> <li> <p>修改Nginx配置,将用户流量从蓝环境切换到绿环境:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"># 原配置</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic">upstream</span><span style="color:#F8F8F2"> nextjs_upstream {</span></span> <span class="line"><span style="color:#F92672"> server</span><span style="color:#F8F8F2"> 127.0.0.1:3019;</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 新配置</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic">upstream</span><span style="color:#F8F8F2"> nextjs_upstream {</span></span> <span class="line"><span style="color:#F92672"> server</span><span style="color:#F8F8F2"> 127.0.0.1:5019;</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> </li> <li> <p>重载Nginx配置:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">nginx</span><span style="color:#AE81FF"> -s</span><span style="color:#E6DB74"> reload</span></span></code></pre> </li> </ul> </li> <li> <p><strong>验证与回滚机制</strong> ✅</p> <ul> <li>验证绿环境(5019端口)是否正常提供服务</li> <li>如发现问题,可立即切换回蓝环境(3019端口)</li> </ul> </li> </ol> <h2 id="-核心优势">✨ 核心优势</h2> <p>这种蓝绿部署方案具有以下显著优势:</p> <ol> <li><strong>零停机时间</strong> ⏱️:用户在整个部署过程中不会感知到服务中断</li> <li><strong>保持任务连续性</strong> 🔄:不活跃的环境不会被关闭,其中运行的任务和进程继续执行</li> <li><strong>快速回滚能力</strong> ⏪:如果新版本出现问题,只需修改Nginx配置即可立即回滚</li> <li><strong>资源利用率高</strong> 📈:两个环境可以轮流部署,充分利用服务器资源</li> </ol> <h2 id="️-宝塔nginx配置示例">🛠️ 宝塔Nginx配置示例</h2> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#66D9EF;font-style:italic">server</span><span style="color:#F8F8F2"> {</span></span> <span class="line"><span style="color:#F92672"> listen </span><span style="color:#F8F8F2">80;</span></span> <span class="line"><span style="color:#F92672"> server_name </span><span style="color:#F8F8F2">your-domain.com;</span></span> <span class="line"><span style="color:#F8F8F2"> </span></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> location</span><span style="color:#F8F8F2"> / {</span></span> <span class="line"><span style="color:#F92672"> proxy_pass </span><span style="color:#F8F8F2">http://nextjs_upstream;</span></span> <span class="line"><span style="color:#F92672"> proxy_set_header </span><span style="color:#F8F8F2">Host $host;</span></span> <span class="line"><span style="color:#F92672"> proxy_set_header </span><span style="color:#F8F8F2">X-Real-IP $remote_addr;</span></span> <span class="line"><span style="color:#F92672"> proxy_set_header </span><span style="color:#F8F8F2">X-Forwarded-For $proxy_add_x_forwarded_for;</span></span> <span class="line"><span style="color:#F92672"> proxy_set_header </span><span style="color:#F8F8F2">X-Forwarded-Proto $scheme;</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span> <span class="line"></span> <span class="line"><span style="color:#66D9EF;font-style:italic">upstream</span><span style="color:#F8F8F2"> nextjs_upstream {</span></span> <span class="line"><span style="color:#F92672"> server</span><span style="color:#F8F8F2"> 127.0.0.1:5019; </span><span style="color:#88846F"># 当前活跃环境</span></span> <span class="line"><span style="color:#88846F"> # server 127.0.0.1:3019; # 注释掉非活跃环境</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <h2 id="-部署流程示意">📊 部署流程示意</h2> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>🔵 3019运行中 → 用户访问3019 → 代码同步到5019 → 5019构建完成</span></span> <span class="line"><span> ↓</span></span> <span class="line"><span>🔄 流量切换 ← Nginx配置修改 ← 5019服务启动 ← 5019构建验证</span></span> <span class="line"><span> ↓</span></span> <span class="line"><span>🟢 5019运行中 → 用户无感知迁移 → 3019保持运行但无流量</span></span></code></pre> <h2 id="-总结">🎯 总结</h2> <p>通过蓝绿部署策略,我们成功解决了Next.js项目在传统部署方式下面临的问题。这种方案不仅保证了用户体验的连续性,还为后端任务提供了稳定的运行环境。</p> <p>在实际应用中,这种部署模式特别适合以下场景:</p> <ul> <li>🚀 高流量Next.js应用</li> <li>⏱️ 包含重要后台任务的Next.js项目</li> <li>👨‍💻 对用户体验和服务连续性要求较高的应用</li> </ul> <p>通过宝塔面板和Nginx的配合,这种部署策略的实施变得简单高效,大大提升了Next.js应用的可靠性和用户满意度。</p></content:encoded>
<enclosure url="https://layfz.com/images/uploads/blue-green-title.png" length="0" type="image/jpeg"/>
</item>
<item>
<title>Cocos 接入穿山甲广告 SDK</title>
<link>https://layfz.com/posts/cocos-chuanshanjia-insert/</link>
<guid isPermaLink="true">https://layfz.com/posts/cocos-chuanshanjia-insert/</guid>
<description>Cocos 接入穿山甲广告 SDK实现广告变现</description>
<pubDate>Tue, 06 Aug 2024 00:00:00 GMT</pubDate>
<content:encoded><h1 id="cocos-接入穿山甲广告-sdk">Cocos 接入穿山甲广告 SDK</h1> <p>在这个教程中,我将详细介绍如何在 Cocos 引擎中接入穿山甲广告 SDK。我们将分步介绍如何在 Cocos 项目中集成穿山甲Android SDK,包括环境配置、代码实现和测试等内容。</p> <h2 id="目录">目录</h2> <ol> <li><a href="#%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6">前提条件</a></li> <li><a href="#%E8%8E%B7%E5%8F%96%E7%A9%BF%E5%B1%B1%E7%94%B2-sdk">获取穿山甲 SDK</a></li> <li><a href="#%E9%9B%86%E6%88%90-sdk">集成 SDK</a></li> <li><a href="#%E9%85%8D%E7%BD%AE%E7%A9%BF%E5%B1%B1%E7%94%B2%E5%B9%BF%E5%91%8A">配置穿山甲广告</a></li> </ol> <h2 id="前提条件">前提条件</h2> <p>在开始之前,请确保你具备以下条件:</p> <ul> <li>已安装 Cocos Creator 或 Cocos2d-x。</li> <li>已创建一个 Cocos 项目。</li> </ul> <h2 id="注意事项">注意事项:</h2> <ul> <li> <p>由于Android平台更新迭代非常快,开发者很容易陷入版本冲突以及兼容问题,若要使用该教程,请使用<strong>推荐的的版本配置</strong>:</p> <ul> <li>Gradle:8.0.2</li> <li>Gradle Plugin:8.0.2</li> <li>Android NDK: 21.3.6528147</li> <li>Android SDK: 29</li> <li>Cocos Creator: 2.4.13</li> </ul> </li> </ul> <h2 id="获取穿山甲-sdk">获取穿山甲 SDK</h2> <ol> <li><strong>访问穿山甲官网</strong>:前往 <a href="https://www.csjplatform.com/union/media/union/download/pangle">穿山甲</a> 并登录你的账户。</li> <li><strong>下载 SDK</strong>:在开发者中心找到Android SDK,下载并解压缩。 <img src="/images/csj/csj.png"></li> </ol> <h2 id="集成-sdk">集成 SDK</h2> <h3 id="1-集成-android-sdk">1. 集成 Android SDK</h3> <ol> <li> <p><strong>导入 SDK 文件</strong>:</p> <ul> <li>将下载的 Android SDK 文件(<code>.aar</code> 或 <code>.jar</code> 文件)放入 Cocos 项目的 <code>assets</code> 目录下的 <code>Plugins/Android</code> 文件夹中。</li> </ul> </li> <li> <p><strong>配置 Gradle 文件</strong>:</p> <ul> <li>打开 Cocos 项目的 <code>proj.android-studio/app/build.gradle</code> 文件。</li> <li>在 <code>dependencies</code> 部分添加穿山甲 SDK 的依赖:</li> </ul> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>implementation 'com.android.support:appcompat-v7:28.0.0'</span></span> <span class="line"><span>//融合基础包,必须引入</span></span> <span class="line"><span>implementation "com.pangle.cn:mediation-sdk:6.2.1.7"</span></span> <span class="line"><span>implementation "com.alibaba:fastjson:1.2.83"</span></span></code></pre> </li> <li> <p><strong>更新 AndroidManifest.xml</strong>:</p> <ul> <li> <p>在 <code>proj.android-studio/app/src/main/AndroidManifest.xml</code> 中添加穿山甲所需的权限和服务: <code>xml &#x3C;uses-permission android:name="android.permission.INTERNET"/> &#x3C;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> &#x3C;uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> &#x3C;!-- 所需权限 --> &#x3C;uses-permission android:name="android.permission.INTERNET" /> &#x3C;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> &#x3C;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> &#x3C;uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> &#x3C;uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> &#x3C;uses-permission android:name="android.permission.READ_PHONE_STATE" /></code> application部分: ```xml <application android:allowbackup="true" android:label="@string/app_name" android:usescleartexttraffic="true" android:icon="@mipmap/ic_launcher"> <!-- Tell Cocos2dxActivity the name of our .so --> <meta-data android:name="android.app.lib_name" android:value="cocos2djs"></meta-data></application></p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span> &#x3C;activity</span></span> <span class="line"><span> android:name="org.cocos2dx.javascript.AppActivity"</span></span> <span class="line"><span> android:screenOrientation="portrait"</span></span> <span class="line"><span> android:configChanges="orientation|keyboardHidden|screenSize|screenLayout|uiMode"</span></span> <span class="line"><span> android:label="@string/app_name"</span></span> <span class="line"><span> android:theme="@android:style/Theme.NoTitleBar.Fullscreen"</span></span> <span class="line"><span> android:launchMode="singleTask"</span></span> <span class="line"><span> android:exported="true"></span></span> <span class="line"><span> &#x3C;intent-filter></span></span> <span class="line"><span> &#x3C;action android:name="android.intent.action.MAIN" /></span></span> <span class="line"><span></span></span> <span class="line"><span> &#x3C;category android:name="android.intent.category.LAUNCHER" /></span></span> <span class="line"><span> &#x3C;/intent-filter></span></span> <span class="line"><span> &#x3C;/activity></span></span> <span class="line"><span> &#x3C;!-- 穿山甲 start================== --></span></span> <span class="line"><span> &#x3C;provider</span></span> <span class="line"><span> android:name="com.bytedance.sdk.openadsdk.TTFileProvider"</span></span> <span class="line"><span> android:authorities="${applicationId}.TTFileProvider"</span></span> <span class="line"><span> android:exported="false"</span></span> <span class="line"><span> android:grantUriPermissions="true"></span></span> <span class="line"><span> &#x3C;meta-data</span></span> <span class="line"><span> android:name="android.support.FILE_PROVIDER_PATHS"</span></span> <span class="line"><span> android:resource="@xml/file_paths" /></span></span> <span class="line"><span> &#x3C;/provider></span></span> <span class="line"><span></span></span> <span class="line"><span> &#x3C;provider</span></span> <span class="line"><span> android:name="com.bytedance.sdk.openadsdk.multipro.TTMultiProvider"</span></span> <span class="line"><span> android:authorities="${applicationId}.TTMultiProvider"</span></span> <span class="line"><span> android:exported="false" /></span></span> <span class="line"><span> &#x3C;!-- 穿山甲 end================== --></span></span> <span class="line"><span> &#x3C;/application></span></span> <span class="line"><span> ```</span></span></code></pre> <p>4.<strong>添加xml文件</strong>: 在res目录下添加文件file_paths.xml <img src="/images/csj/paths.png"></p> </li> </ul> </li> </ol> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>&#x3C;?xml version="1.0" encoding="utf-8"?></span></span> <span class="line"><span>&#x3C;paths></span></span> <span class="line"><span> &#x3C;external-path name="tt_external_root" path="." /></span></span> <span class="line"><span> &#x3C;external-path name="tt_external_download" path="Download" /></span></span> <span class="line"><span> &#x3C;external-files-path name="tt_external_files_download" path="Download" /></span></span> <span class="line"><span> &#x3C;files-path name="tt_internal_file_download" path="Download" /></span></span> <span class="line"><span> &#x3C;cache-path name="tt_internal_cache_download" path="Download" /></span></span> <span class="line"><span>&#x3C;/paths></span></span></code></pre> <ol> <li><strong>创建广告处理类</strong> 结构如下,<mark>创建并放到任意包下</mark>: <img src="/images/csj/classes.png"></li> </ol> <p>Const.java:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class Const {</span></span> <span class="line"><span> public static String TAG = "TTSdkComponent";</span></span> <span class="line"><span>}</span></span></code></pre> <p>TTAdManagerHolder.java:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class TTAdManagerHolder {</span></span> <span class="line"><span></span></span> <span class="line"><span> // 公共方法,封装了对私有方法的调用</span></span> <span class="line"><span> public void initialize(Context context, String appId) {</span></span> <span class="line"><span> initMediationAdSdk(context, appId);</span></span> <span class="line"><span> // 可以添加其他初始化逻辑或检查</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> //初始化聚合sdk</span></span> <span class="line"><span> private void initMediationAdSdk(Context context, String appId) {</span></span> <span class="line"><span> TTAdSdk.init(context, buildConfig(context, appId));</span></span> <span class="line"><span> TTAdSdk.start(new TTAdSdk.Callback() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void success() {</span></span> <span class="line"><span> System.out.println("初始化成功1");</span></span> <span class="line"><span> //初始化成功</span></span> <span class="line"><span> //在初始化成功回调之后进行广告加载"</span></span> <span class="line"><span>// JsbBridge.sendToScript("onInit", "true");</span></span> <span class="line"><span> // 一定要在 GL 线程中执行</span></span> <span class="line"><span> runOnGLThread(new Runnable() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void run() {</span></span> <span class="line"><span> Cocos2dxJavascriptJavaBridge.evalString("Ad.onNative(\"onInit\", \"true\")");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void fail(int i, String s) {</span></span> <span class="line"><span> //初始化失败</span></span> <span class="line"><span> System.out.println("初始化失败");</span></span> <span class="line"><span>// JsbBridge.sendToScript("onInit", "false");</span></span> <span class="line"><span> // 一定要在 GL 线程中执行</span></span> <span class="line"><span> runOnGLThread(new Runnable() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void run() {</span></span> <span class="line"><span> Cocos2dxJavascriptJavaBridge.evalString("Ad.onNative(\"onInit\", \"false\")");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> private static TTAdConfig buildConfig(Context context, String appId) {</span></span> <span class="line"><span></span></span> <span class="line"><span> return new TTAdConfig.Builder()</span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 注:需要替换成在媒体平台申请的appID ,切勿直接复制</span></span> <span class="line"><span> */</span></span> <span class="line"><span> .appId(appId)</span></span> <span class="line"><span> .appName("APP测试媒体")</span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 上线前需要关闭debug开关,否则会影响性能</span></span> <span class="line"><span> */</span></span> <span class="line"><span> .debug(false)</span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 使用聚合功能此开关必须设置为true,默认为false</span></span> <span class="line"><span> */</span></span> <span class="line"><span> .useMediation(true)</span></span> <span class="line"><span>// .customController(getTTCustomController()) //如果您需要设置隐私策略请参考该api</span></span> <span class="line"><span>// .setMediationConfig(new MediationConfig.Builder() //可设置聚合特有参数详细设置请参考该api</span></span> <span class="line"><span>// .setMediationConfigUserInfoForSegment(getUserInfoForSegment())//如果您需要配置流量分组信息请参考该api</span></span> <span class="line"><span>// .build())</span></span> <span class="line"><span> .build();</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <p>TTSdkBannerAd.java:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class TTSdkBannerAd {</span></span> <span class="line"><span> TTAdNative adNativeLoader;</span></span> <span class="line"><span> AdSlot adSlot;</span></span> <span class="line"><span> TTNativeExpressAd mBannerAd;</span></span> <span class="line"><span> FrameLayout mBannerContainer;</span></span> <span class="line"><span> Activity activity;</span></span> <span class="line"><span> float left, top;</span></span> <span class="line"><span></span></span> <span class="line"><span> public TTSdkBannerAd(Activity activity, String codeId, float left, float top, int width, int height) {</span></span> <span class="line"><span> activity = activity;</span></span> <span class="line"><span> this.left = left;</span></span> <span class="line"><span> this.top = top;</span></span> <span class="line"><span> adNativeLoader = TTAdSdk.getAdManager().createAdNative(activity);</span></span> <span class="line"><span> adSlot = new AdSlot.Builder()</span></span> <span class="line"><span> .setCodeId(codeId)</span></span> <span class="line"><span> .setImageAcceptedSize(width, height) // 单位px</span></span> <span class="line"><span> .build();</span></span> <span class="line"><span> mBannerContainer = (FrameLayout) activity.findViewById(android.R.id.content).getRootView();</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> private void loadBannerExpressAd() {</span></span> <span class="line"><span> adNativeLoader.loadBannerExpressAd(adSlot, new TTAdNative.NativeExpressAdListener() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onError(int i, String s) {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner load fail: errCode: " + i + ", errMsg: " + s);</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onNativeExpressAdLoad(List&#x3C;TTNativeExpressAd> list) {</span></span> <span class="line"><span> if (list != null &#x26;&#x26; list.size() > 0) {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner load success");</span></span> <span class="line"><span> destroyBannerAd();</span></span> <span class="line"><span> mBannerAd = list.get(0);</span></span> <span class="line"><span> mBannerAd.setSlideIntervalTime(30 * 1000);</span></span> <span class="line"><span> if (mBannerAd != null) {</span></span> <span class="line"><span> showBannerAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> } else {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner load success, but list is null");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void showBannerAd() {</span></span> <span class="line"><span> if (mBannerAd != null) {</span></span> <span class="line"><span> mBannerAd.setExpressInteractionListener(new</span></span> <span class="line"><span> TTNativeExpressAd.ExpressAdInteractionListener() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onAdClicked(View view, int i) {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner clicked");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onAdShow(View view, int i) {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner showed");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onRenderFail(View view, String s, int i) {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner renderFail, errCode" + i + ", errMsg: " + s);</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onRenderSuccess(View view, float v, float v1) {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner render success");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> mBannerAd.setDislikeCallback(activity, new</span></span> <span class="line"><span> TTAdDislike.DislikeInteractionCallback() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onShow() {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSelected(int i, String s, boolean b) {</span></span> <span class="line"><span> Log.d(Const.TAG, "banner closed");</span></span> <span class="line"><span> destroyBannerAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onCancel() {</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> View bannerView = mBannerAd.getExpressAdView();</span></span> <span class="line"><span> if (bannerView != null &#x26;&#x26; mBannerContainer != null) {</span></span> <span class="line"><span> mBannerContainer.addView(bannerView);</span></span> <span class="line"><span> bannerView.setX(left);</span></span> <span class="line"><span> bannerView.setY(top);</span></span> <span class="line"><span> }</span></span> <span class="line"><span> } else {</span></span> <span class="line"><span>// Log.d(Const.TAG, "请先加载广告或等待广告加载完毕后再展示广告");</span></span> <span class="line"><span> loadBannerExpressAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void destroyBannerAd() {</span></span> <span class="line"><span> if (mBannerAd != null) {</span></span> <span class="line"><span> mBannerAd.destroy();</span></span> <span class="line"><span> mBannerAd = null;</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <p>TTSdkComponent.java:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class TTSdkComponent {</span></span> <span class="line"><span></span></span> <span class="line"><span> TTSdkBannerAd mTTBannerAd = null;</span></span> <span class="line"><span> TTSdkRewardedVideoAd mTTRewardedVideoAd = null;</span></span> <span class="line"><span> TTSdkFullScreenVideoAd mTTSdkFullScreenVideoAd = null;</span></span> <span class="line"><span> TTSdkSplashAd mTTSdkSplashAd = null;</span></span> <span class="line"><span></span></span> <span class="line"><span> Activity activity;</span></span> <span class="line"><span> </span></span> <span class="line"><span> public static TTSdkComponent mTTSdkComponent;</span></span> <span class="line"><span></span></span> <span class="line"><span></span></span> <span class="line"><span> public TTSdkComponent(Activity activity) {</span></span> <span class="line"><span> this.activity = activity;</span></span> <span class="line"><span> TTSdkComponent.mTTSdkComponent = this;</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public static void sendToNative(String arg0, String arg1){</span></span> <span class="line"><span> Log.d(Const.TAG, "banner load success, but list is null" + arg0 +"?" +arg1);</span></span> <span class="line"><span> TTSdkComponent.mTTSdkComponent.onScript(arg0,arg1);</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onScript(String arg0, String arg1){</span></span> <span class="line"><span> JSONObject jsonObject = JSON.parseObject(arg1);</span></span> <span class="line"><span> Activity activity = this.activity;</span></span> <span class="line"><span> if (arg0.equals("showToast")) {</span></span> <span class="line"><span> String content = jsonObject.getString("content");</span></span> <span class="line"><span> activity.runOnUiThread(new Runnable() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void run() {</span></span> <span class="line"><span> Toast.makeText(activity, content,</span></span> <span class="line"><span> Toast.LENGTH_SHORT).show();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> } else if (arg0.equals("init")) {</span></span> <span class="line"><span> TTAdManagerHolder ttAdManagerHolder = new TTAdManagerHolder();</span></span> <span class="line"><span> String appId = jsonObject.getString("appId");</span></span> <span class="line"><span> ttAdManagerHolder.initialize(activity, appId);</span></span> <span class="line"><span> } else if (arg0.equals("showBannerAd")) {</span></span> <span class="line"><span> if (mTTBannerAd == null) {</span></span> <span class="line"><span> String codeId = jsonObject.getString("codeId");</span></span> <span class="line"><span> float left = jsonObject.getFloatValue("left");</span></span> <span class="line"><span> float top = jsonObject.getFloatValue("top");</span></span> <span class="line"><span> int width = jsonObject.getIntValue("width");</span></span> <span class="line"><span> int height = jsonObject.getIntValue("height");</span></span> <span class="line"><span> mTTBannerAd = new TTSdkBannerAd(activity, codeId, left, top, width, height);</span></span> <span class="line"><span> }</span></span> <span class="line"><span> if (mTTBannerAd != null) {</span></span> <span class="line"><span> boolean isShow = jsonObject.getBooleanValue("isShow");</span></span> <span class="line"><span> if (isShow) {</span></span> <span class="line"><span> mTTBannerAd.showBannerAd();</span></span> <span class="line"><span> } else {</span></span> <span class="line"><span> mTTBannerAd.destroyBannerAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> } else if (arg0.equals("showRewardVideoAd")) {</span></span> <span class="line"><span> if (mTTRewardedVideoAd == null) {</span></span> <span class="line"><span> String codeId = jsonObject.getString("codeId");</span></span> <span class="line"><span> mTTRewardedVideoAd = new TTSdkRewardedVideoAd(activity, codeId);</span></span> <span class="line"><span> }</span></span> <span class="line"><span> if (mTTRewardedVideoAd != null) {</span></span> <span class="line"><span> mTTRewardedVideoAd.showRewardVideoAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> } else if (arg0.equals("showSplashAd")) {</span></span> <span class="line"><span> if (mTTSdkSplashAd == null) {</span></span> <span class="line"><span> String codeId = jsonObject.getString("codeId");</span></span> <span class="line"><span> int width = jsonObject.getIntValue("width");</span></span> <span class="line"><span> int height = jsonObject.getIntValue("height");</span></span> <span class="line"><span> mTTSdkSplashAd = new TTSdkSplashAd(activity, codeId, width, height);</span></span> <span class="line"><span> }</span></span> <span class="line"><span> mTTSdkSplashAd.showSplashAd(null);</span></span> <span class="line"><span> } else if (arg0.equals("showFullScreenVideoAd")) {</span></span> <span class="line"><span> if (mTTSdkFullScreenVideoAd == null) {</span></span> <span class="line"><span> String codeId = jsonObject.getString("codeId");</span></span> <span class="line"><span> mTTSdkFullScreenVideoAd = new TTSdkFullScreenVideoAd(activity, codeId);</span></span> <span class="line"><span> }</span></span> <span class="line"><span> mTTSdkFullScreenVideoAd.showFullScreenVideoAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <p>TTSdkFullScreenVideoAd.java:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class TTSdkFullScreenVideoAd {</span></span> <span class="line"><span></span></span> <span class="line"><span> TTAdNative adNativeLoader;</span></span> <span class="line"><span> AdSlot adSlot;</span></span> <span class="line"><span> TTFullScreenVideoAd mTTFullScreenVideoAd;</span></span> <span class="line"><span> Activity activity;</span></span> <span class="line"><span></span></span> <span class="line"><span> public TTSdkFullScreenVideoAd(Activity activity, String codeId) {</span></span> <span class="line"><span> this.activity = activity;</span></span> <span class="line"><span> adNativeLoader = TTAdSdk.getAdManager().createAdNative(activity);</span></span> <span class="line"><span> adSlot = new AdSlot.Builder()</span></span> <span class="line"><span> .setCodeId(codeId)</span></span> <span class="line"><span> .setOrientation(TTAdConstant.ORIENTATION_VERTICAL)//设置横竖屏方向</span></span> <span class="line"><span>// .setMediationAdSlot(new MediationAdSlot.Builder()</span></span> <span class="line"><span>// .setMuted(true)//是否静音</span></span> <span class="line"><span>// .setVolume(0.7f)//设置音量</span></span> <span class="line"><span>// .setBidNotify(true)//竞价结果通知</span></span> <span class="line"><span>// .build())</span></span> <span class="line"><span> .build();</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void loadFullScreenVideoAd(boolean isShow) {</span></span> <span class="line"><span> adNativeLoader.loadFullScreenVideoAd(adSlot, new TTAdNative.FullScreenVideoAdListener() {</span></span> <span class="line"><span> public void onError(int code, String message) {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onError code = " + code + " msg = " + message);</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onFullScreenVideoAdLoad(TTFullScreenVideoAd ad) {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onFullScreenVideoLoaded");</span></span> <span class="line"><span> mTTFullScreenVideoAd = ad;</span></span> <span class="line"><span>// Log.d("sada",""+isShow);</span></span> <span class="line"><span> if (isShow) {</span></span> <span class="line"><span> showFullScreenVideoAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onFullScreenVideoCached() {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onFullScreenVideoCached");</span></span> <span class="line"><span> Log.d("5785785727","57587575875");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onFullScreenVideoCached(TTFullScreenVideoAd ad) {</span></span> <span class="line"><span> Log.d("sasasdasd","sadasda");</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onFullScreenVideoCached");</span></span> <span class="line"><span> mTTFullScreenVideoAd = ad;</span></span> <span class="line"><span> if (isShow) {</span></span> <span class="line"><span> showFullScreenVideoAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void showFullScreenVideoAd() {</span></span> <span class="line"><span>// Log.d("mm", ""+(mTTFullScreenVideoAd==null));</span></span> <span class="line"><span> if (mTTFullScreenVideoAd == null) {</span></span> <span class="line"><span> loadFullScreenVideoAd(true);</span></span> <span class="line"><span> return;</span></span> <span class="line"><span> }</span></span> <span class="line"><span> // 展示广告</span></span> <span class="line"><span> this.mTTFullScreenVideoAd.setFullScreenVideoAdInteractionListener(new TTFullScreenVideoAd.FullScreenVideoAdInteractionListener() {</span></span> <span class="line"><span> public void onAdShow() {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onAdShow");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onAdVideoBarClick() {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onAdVideoBarClick");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onAdClose() {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onAdClose");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onVideoComplete() {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onVideoComplete");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void onSkippedVideo() {</span></span> <span class="line"><span> Log.d(Const.TAG, "InterstitialFull onSkippedVideo");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> this.mTTFullScreenVideoAd.showFullScreenVideoAd(activity);</span></span> <span class="line"><span> this.mTTFullScreenVideoAd = null;</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <p>TTSdkRewardedVideoAd.java:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class TTSdkRewardedVideoAd {</span></span> <span class="line"><span> TTAdNative adNativeLoader;</span></span> <span class="line"><span> AdSlot adSlot;</span></span> <span class="line"><span> TTRewardVideoAd mTTRewardVideoAd;</span></span> <span class="line"><span> Activity activity;</span></span> <span class="line"><span> public boolean mIsRewardValid;</span></span> <span class="line"><span></span></span> <span class="line"><span> public TTSdkRewardedVideoAd(Activity activity, String codeId) {</span></span> <span class="line"><span> activity = activity;</span></span> <span class="line"><span> adNativeLoader = TTAdSdk.getAdManager().createAdNative(activity);</span></span> <span class="line"><span> adSlot = new AdSlot.Builder()</span></span> <span class="line"><span> .setCodeId(codeId)</span></span> <span class="line"><span> .setOrientation(TTAdConstant.VERTICAL)//横竖屏设置</span></span> <span class="line"><span>// .setMediationAdSlot(new MediationAdSlot</span></span> <span class="line"><span>// .Builder()</span></span> <span class="line"><span>// .setExtraObject(MediationConstant.ADN_PANGLE, "pangleRewardCustomData")//服务端奖励验证透传参数</span></span> <span class="line"><span>// .setExtraObject(MediationConstant.ADN_GDT, "gdtRewardCustomData")</span></span> <span class="line"><span>// .setExtraObject(MediationConstant.ADN_BAIDU, "baiduRewardCustomData")</span></span> <span class="line"><span>// .build())</span></span> <span class="line"><span> .build();</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> private void loadRewardVideoAd(boolean isShow) {</span></span> <span class="line"><span> adNativeLoader.loadRewardVideoAd(adSlot, new TTAdNative.RewardVideoAdListener() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onError(int i, String s) {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onRewardVideoAdLoad(TTRewardVideoAd ttRewardVideoAd) {</span></span> <span class="line"><span> mTTRewardVideoAd = ttRewardVideoAd;</span></span> <span class="line"><span> if (isShow) {</span></span> <span class="line"><span> showRewardVideoAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onRewardVideoCached() {</span></span> <span class="line"><span></span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onRewardVideoCached(TTRewardVideoAd ttRewardVideoAd) {</span></span> <span class="line"><span> mTTRewardVideoAd = ttRewardVideoAd;</span></span> <span class="line"><span> if (isShow) {</span></span> <span class="line"><span> showRewardVideoAd();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void showRewardVideoAd() {</span></span> <span class="line"><span> mIsRewardValid = false;</span></span> <span class="line"><span> if (mTTRewardVideoAd == null) {</span></span> <span class="line"><span> loadRewardVideoAd(true);</span></span> <span class="line"><span> return;</span></span> <span class="line"><span> }</span></span> <span class="line"><span> mTTRewardVideoAd.setRewardAdInteractionListener(new TTRewardVideoAd.RewardAdInteractionListener() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onAdShow() {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onAdVideoBarClick() {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onAdClose() {</span></span> <span class="line"><span>// JsbBridge.sendToScript("onRewardAdCallback", mIsRewardValid ? "true" : "false");</span></span> <span class="line"><span> // 一定要在 GL 线程中执行</span></span> <span class="line"><span> runOnGLThread(new Runnable() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void run() {</span></span> <span class="line"><span> String success = mIsRewardValid ? "true" : "false";</span></span> <span class="line"><span> Cocos2dxJavascriptJavaBridge.evalString("Ad.onNative(\"onRewardAdCallback\", \""+success+"\")");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onVideoComplete() {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onVideoError() {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> //已废弃,请使用onRewardArrived方法</span></span> <span class="line"><span> public void onRewardVerify(boolean rewardVerify, int rewardAmount, String rewardName, int errorCode, String errorMsg) {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onRewardArrived(boolean isRewardValid, int rewardType, Bundle extraInfo) {//奖励是否发放请依据isRewardValid</span></span> <span class="line"><span> // 当用户的观看行为满足了奖励条件</span></span> <span class="line"><span> mIsRewardValid = isRewardValid;</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSkippedVideo() {</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> mTTRewardVideoAd.showRewardVideoAd(activity);</span></span> <span class="line"><span> mTTRewardVideoAd = null;</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <p>TTSdkSplashAd.java:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class TTSdkSplashAd {</span></span> <span class="line"><span></span></span> <span class="line"><span> TTAdNative adNativeLoader;</span></span> <span class="line"><span> AdSlot adSlot;</span></span> <span class="line"><span> Activity activity;</span></span> <span class="line"><span> FrameLayout mSplashContainer;</span></span> <span class="line"><span> CSJSplashAd mCsjSplashAd;</span></span> <span class="line"><span></span></span> <span class="line"><span> public TTSdkSplashAd(Activity activity, String codeId, int width, int height) {</span></span> <span class="line"><span> activity = activity;</span></span> <span class="line"><span> adNativeLoader = TTAdSdk.getAdManager().createAdNative(activity);</span></span> <span class="line"><span> adSlot = new AdSlot.Builder()</span></span> <span class="line"><span> .setCodeId(codeId)</span></span> <span class="line"><span> .setImageAcceptedSize(width, height)//单位px</span></span> <span class="line"><span> .build();</span></span> <span class="line"><span> mSplashContainer = (FrameLayout) activity.findViewById(android.R.id.content).getRootView();</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void loadSplashAd(boolean isShow) {</span></span> <span class="line"><span> adNativeLoader.loadSplashAd(adSlot, new TTAdNative.CSJSplashAdListener() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSplashRenderSuccess(CSJSplashAd csjSplashAd) {</span></span> <span class="line"><span> Log.d("SplashAd","onSplashRenderSuccess");</span></span> <span class="line"><span> /** 渲染成功后,展示广告 */</span></span> <span class="line"><span> if (isShow)</span></span> <span class="line"><span> showSplashAd(csjSplashAd);</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSplashLoadSuccess(CSJSplashAd csjSplashAd) {</span></span> <span class="line"><span> Log.d("SplashAd","onSplashLoadSuccess");</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSplashLoadFail(CSJAdError csjAdError) {</span></span> <span class="line"><span> Log.d("SplashAd","onSplashLoadFail:"+csjAdError.getMsg());</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSplashRenderFail(CSJSplashAd csjSplashAd, CSJAdError csjAdError) {</span></span> <span class="line"><span> Log.d("SplashAd","onSplashRenderFail:"+csjAdError.getMsg());</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }, 3500);</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void showSplashAd(CSJSplashAd csjSplashAd) {</span></span> <span class="line"><span> if (csjSplashAd == null) {</span></span> <span class="line"><span> loadSplashAd(true);</span></span> <span class="line"><span> return;</span></span> <span class="line"><span> }</span></span> <span class="line"><span> csjSplashAd.setSplashAdListener(new CSJSplashAd.SplashAdListener() {</span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSplashAdShow(CSJSplashAd csjSplashAd) {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSplashAdClick(CSJSplashAd csjSplashAd) {</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> public void onSplashAdClose(CSJSplashAd csjSplashAd, int i) {</span></span> <span class="line"><span> // 广告关闭后,销毁广告页面</span></span> <span class="line"><span> finish();</span></span> <span class="line"><span> }</span></span> <span class="line"><span> });</span></span> <span class="line"><span> mCsjSplashAd = csjSplashAd;</span></span> <span class="line"><span> View splashView = csjSplashAd.getSplashView();</span></span> <span class="line"><span>// UIUtils.removeFromParent(splashView);</span></span> <span class="line"><span>// mSplashContainer.removeAllViews();</span></span> <span class="line"><span> mSplashContainer.addView(splashView);</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> public void finish() {</span></span> <span class="line"><span> if (mCsjSplashAd != null &#x26;&#x26; mCsjSplashAd.getMediationManager() != null) {</span></span> <span class="line"><span> mSplashContainer.removeView(mCsjSplashAd.getSplashView());</span></span> <span class="line"><span> mCsjSplashAd.getMediationManager().destroy();</span></span> <span class="line"><span> mCsjSplashAd = null;</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <ol start="6"> <li><strong>初始化组件</strong> 在AppActivity初始化组件</li> </ol> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>public class AppActivity extends Cocos2dxActivity {</span></span> <span class="line"><span></span></span> <span class="line"><span> @Override</span></span> <span class="line"><span> protected void onCreate(Bundle savedInstanceState) {</span></span> <span class="line"><span> super.onCreate(savedInstanceState);</span></span> <span class="line"><span> // DO OTHER INITIALIZATION BELOW</span></span> <span class="line"><span> SDKWrapper.getInstance().init(this);</span></span> <span class="line"><span> // 初始化组件</span></span> <span class="line"><span> new TTSdkComponent(this);</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <p><strong>至此安卓的初始化就完成了,穿山甲不愧是大平台,接入就是如此丝滑</strong></p> <h2 id="配置穿山甲广告">配置穿山甲广告</h2> <h3 id="1-初始化-sdk">1. 初始化 SDK</h3> <p>在 Cocos 项目的主脚本中,初始化穿山甲 SDK: 首先创建如下TS脚本,如果采用js请自行转换。 <img src="/images/csj/cocosscript.png"> Ad.ts:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>const { ccclass, property } = cc._decorator;</span></span> <span class="line"><span> </span></span> <span class="line"><span>@ccclass("AdConfigBase")</span></span> <span class="line"><span>class AdConfigBase {</span></span> <span class="line"><span> @property({ displayName: "广告Id" })</span></span> <span class="line"><span> codeId: string = "";</span></span> <span class="line"><span>};</span></span> <span class="line"><span> </span></span> <span class="line"><span>@ccclass("RewardedVideoAdConfig")</span></span> <span class="line"><span>class RewardedVideoAdConfig extends AdConfigBase {</span></span> <span class="line"><span>};</span></span> <span class="line"><span> </span></span> <span class="line"><span>@ccclass("BannerAdConfig")</span></span> <span class="line"><span>class BannerAdConfig extends AdConfigBase {</span></span> <span class="line"><span> @property({ displayName: "宽度" })</span></span> <span class="line"><span> width: number = 500;</span></span> <span class="line"><span> @property({ displayName: "高度" })</span></span> <span class="line"><span> height: number = 300;</span></span> <span class="line"><span>};</span></span> <span class="line"><span> </span></span> <span class="line"><span>@ccclass("InterstitialAdConfig")</span></span> <span class="line"><span>class InterstitialAdConfig extends AdConfigBase {</span></span> <span class="line"><span>};</span></span> <span class="line"><span> </span></span> <span class="line"><span>@ccclass("SplashAdConfig")</span></span> <span class="line"><span>class SplashAdConfig extends AdConfigBase {</span></span> <span class="line"><span>};</span></span> <span class="line"><span> </span></span> <span class="line"><span>@ccclass("Ad")</span></span> <span class="line"><span>export class Ad {</span></span> <span class="line"><span> @property({ displayName: "应用appId" })</span></span> <span class="line"><span> appId: string = "";</span></span> <span class="line"><span> @property({ type: BannerAdConfig, displayName: "Banner广告" })</span></span> <span class="line"><span> bannerAdConfig: BannerAdConfig = new BannerAdConfig();</span></span> <span class="line"><span> @property({ type: RewardedVideoAdConfig, displayName: "激励视频广告" })</span></span> <span class="line"><span> rewardedVideoAdConfig: RewardedVideoAdConfig = new RewardedVideoAdConfig();</span></span> <span class="line"><span> @property({ type: SplashAdConfig, displayName: "开屏广告" })</span></span> <span class="line"><span> splashAdConfig: SplashAdConfig = new SplashAdConfig();</span></span> <span class="line"><span> @property({ type: InterstitialAdConfig, displayName: "插屏广告" })</span></span> <span class="line"><span> interstitialAdConfig: InterstitialAdConfig = new InterstitialAdConfig();</span></span> <span class="line"><span> </span></span> <span class="line"><span> private rewardAdCallback: any;</span></span> <span class="line"><span> private rewardAdCallbackObj: any;</span></span> <span class="line"><span> </span></span> <span class="line"><span> private static onNativeCallback: any;</span></span> <span class="line"><span> private static onNativeCallbackObj: any;</span></span> <span class="line"><span> </span></span> <span class="line"><span> sendToNative(arg0: string, arg1?: string) {</span></span> <span class="line"><span> // native.bridge.sendToNative(arg0, arg1);</span></span> <span class="line"><span> jsb.reflection.callStaticMethod("com/yiyuancoder/ttsdk/TTSdkComponent", "sendToNative", "(Ljava/lang/String;Ljava/lang/String;)V", arg0, arg1);</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> public static onNative(arg0: string, arg1: string) {</span></span> <span class="line"><span> if (this.onNativeCallback != null) {</span></span> <span class="line"><span> this.onNativeCallback.call(this.onNativeCallbackObj, arg0, arg1);</span></span> <span class="line"><span> this.onNativeCallback = null;</span></span> <span class="line"><span> this.onNativeCallbackObj = null;</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> async init() {</span></span> <span class="line"><span> return new Promise((resolve, reject) => {</span></span> <span class="line"><span> // native.bridge.onNative = (arg0: string, arg1: string): void => {</span></span> <span class="line"><span> // if (arg0 == 'onInit') {</span></span> <span class="line"><span> // resolve(arg1 == "true");</span></span> <span class="line"><span> // }</span></span> <span class="line"><span> // }</span></span> <span class="line"><span> Ad.onNativeCallbackObj = this;</span></span> <span class="line"><span> Ad.onNativeCallback = (arg0: string, arg1: string) => {</span></span> <span class="line"><span> if (arg0 == 'onInit') {</span></span> <span class="line"><span> resolve(arg1 == "true");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> this.sendToNative("init", JSON.stringify({ appId: this.appId }));</span></span> <span class="line"><span> })</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 显示Banner广告</span></span> <span class="line"><span> */</span></span> <span class="line"><span> showBannerAd(show: boolean): void {</span></span> <span class="line"><span> </span></span> <span class="line"><span> let width = this.bannerAdConfig.width;</span></span> <span class="line"><span> let height = this.bannerAdConfig.height;</span></span> <span class="line"><span> let left = screen.width / 2 - width / 2;</span></span> <span class="line"><span> let top = screen.height - height;</span></span> <span class="line"><span> this.sendToNative("showBannerAd", JSON.stringify({ isShow: show, codeId: this.bannerAdConfig.codeId, left: left, top: top, width: width, height: height }));</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 播放激励视频广告</span></span> <span class="line"><span> */</span></span> <span class="line"><span> showRewardedVideoAd(rewardAdCallback: Function, rewardAdCallbackObj: any): void {</span></span> <span class="line"><span> this.rewardAdCallback = rewardAdCallback;</span></span> <span class="line"><span> this.rewardAdCallbackObj = rewardAdCallbackObj;</span></span> <span class="line"><span> </span></span> <span class="line"><span> let that = this;</span></span> <span class="line"><span> // native.bridge.onNative = (arg0: string, arg1: string): void => {</span></span> <span class="line"><span> // if (arg0 == 'onRewardAdCallback') {</span></span> <span class="line"><span> // that.onRewardAdCallback(arg1 == "true");</span></span> <span class="line"><span> // }</span></span> <span class="line"><span> // }</span></span> <span class="line"><span> Ad.onNativeCallbackObj = this;</span></span> <span class="line"><span> Ad.onNativeCallback = (arg0: string, arg1: string) => {</span></span> <span class="line"><span> if (arg0 == 'onRewardAdCallback') {</span></span> <span class="line"><span> that.onRewardAdCallback(arg1 == "true");</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> this.sendToNative("showRewardVideoAd", JSON.stringify({ codeId: this.rewardedVideoAdConfig.codeId }));</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 激励视频广告回调</span></span> <span class="line"><span> */</span></span> <span class="line"><span> onRewardAdCallback(success: boolean) {</span></span> <span class="line"><span> if (this.rewardAdCallback) {</span></span> <span class="line"><span> this.rewardAdCallback.call(this.rewardAdCallbackObj, success);</span></span> <span class="line"><span> this.rewardAdCallback = null;</span></span> <span class="line"><span> this.rewardAdCallbackObj = null;</span></span> <span class="line"><span> }</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 显示开屏广告</span></span> <span class="line"><span> */</span></span> <span class="line"><span> showSplashAd(): void {</span></span> <span class="line"><span> this.sendToNative("showSplashAd", JSON.stringify({ codeId: this.splashAdConfig.codeId, width: screen.width, height: screen.height }));</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> /**</span></span> <span class="line"><span> * 显示插屏广告</span></span> <span class="line"><span> */</span></span> <span class="line"><span> showFullScreenVideoAd() {</span></span> <span class="line"><span> // 在适合的场景显示插屏广告</span></span> <span class="line"><span> this.sendToNative("showFullScreenVideoAd", JSON.stringify({ codeId: this.interstitialAdConfig.codeId }));</span></span> <span class="line"><span> }</span></span> <span class="line"><span>};</span></span> <span class="line"><span> </span></span> <span class="line"><span>window.Ad = Ad;</span></span></code></pre> <p>TTSdkComponent.ts:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>import { Ad } from "./Ad";</span></span> <span class="line"><span> </span></span> <span class="line"><span>const { ccclass, property } = cc._decorator;</span></span> <span class="line"><span> </span></span> <span class="line"><span>declare var wx: any;</span></span> <span class="line"><span> </span></span> <span class="line"><span>@ccclass</span></span> <span class="line"><span>export default class TTSdkComponent extends cc.Component {</span></span> <span class="line"><span> </span></span> <span class="line"><span> @property({ type: Ad, displayName: "广告模块" })</span></span> <span class="line"><span> ad: Ad = new Ad();</span></span> <span class="line"><span> </span></span> <span class="line"><span> sendToNative(arg0: string, arg1?: string) {</span></span> <span class="line"><span> jsb.reflection.callStaticMethod("com/yiyuancoder/ttsdk/TTSdkComponent", "sendToNative", "(Ljava/lang/String;Ljava/lang/String;)V", arg0, arg1);</span></span> <span class="line"><span> }</span></span> <span class="line"><span> </span></span> <span class="line"><span> showToast(content: string) {</span></span> <span class="line"><span> this.sendToNative('showToast', JSON.stringify({ content: content }));</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code></pre> <p>Main.ts:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>//获取穿山甲Sdk组件</span></span> <span class="line"><span>let ttSdkComponent = cc.find("TTSdkComponent").getComponent(TTSdkComponent);</span></span> <span class="line"><span>//初始化</span></span> <span class="line"><span>let inited = await ttSdkComponent.ad.init();</span></span> <span class="line"><span>console.log(inited);</span></span> <span class="line"><span>if (!inited) {</span></span> <span class="line"><span> ttSdkComponent.showToast("广告组件初始化失败");</span></span> <span class="line"><span> return;</span></span> <span class="line"><span>}</span></span></code></pre> <p>以上Main.ts文件主要用于初始化脚本。</p> <h3 id="2-挂载脚本">2. 挂载脚本</h3> <p>在你的cocos场景中新建节点,并挂载脚本文件: <img src="/images/csj/model.png"> 如上,填上你在平台申请的信息。</p> <h3 id="3-调用广告">3. 调用广告</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672">import</span><span style="color:#F8F8F2"> TTSdkComponent </span><span style="color:#F92672">from</span><span style="color:#E6DB74"> "./TTSdkComponent"</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic">const</span><span style="color:#F8F8F2"> { ccclass, property } </span><span style="color:#F92672">=</span><span style="color:#F8F8F2"> cc._decorator;</span></span> <span class="line"></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2">async </span><span style="color:#A6E22E">start</span><span style="color:#F8F8F2">(){</span></span> <span class="line"><span style="color:#88846F"> // banner</span></span> <span class="line"><span style="color:#F8F8F2"> ttSdkComponent.ad.</span><span style="color:#A6E22E">showBannerAd</span><span style="color:#F8F8F2">(bannerAdFlag);</span></span> <span class="line"><span style="color:#88846F"> // reward</span></span> <span class="line"><span style="color:#F8F8F2"> ttSdkComponent.ad.</span><span style="color:#A6E22E">showRewardedVideoAd</span><span style="color:#F8F8F2">((</span><span style="color:#FD971F;font-style:italic">success</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> boolean</span><span style="color:#F8F8F2">) </span><span style="color:#66D9EF;font-style:italic">=></span><span style="color:#F8F8F2"> {</span></span> <span class="line"><span style="color:#F8F8F2"> ttSdkComponent.</span><span style="color:#A6E22E">showToast</span><span style="color:#F8F8F2">(success </span><span style="color:#F92672">?</span><span style="color:#E6DB74"> "恭喜获得奖励"</span><span style="color:#F92672"> :</span><span style="color:#E6DB74"> "完整观看视频才能获得奖励哦"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }, </span><span style="color:#FD971F">this</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#88846F"> // 开屏</span></span> <span class="line"><span style="color:#F8F8F2"> ttSdkComponent.ad.</span><span style="color:#A6E22E">showSplashAd</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#88846F"> //插屏</span></span> <span class="line"><span style="color:#F8F8F2"> ttSdkComponent.ad.</span><span style="color:#A6E22E">showFullScreenVideoAd</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <p>以上仅为示例代码,在你的具体业务中请一定要初始化以后再调用方法。</p> <p><strong>至此你就可以接入穿山甲实现广告变现</strong></p></content:encoded>
<enclosure url="https://layfz.com/images/uploads/csj.png" length="0" type="image/jpeg"/>
</item>
<item>
<title>Cocos接入九游 SDK</title>
<link>https://layfz.com/posts/cocos-jiuyou-insert/</link>
<guid isPermaLink="true">https://layfz.com/posts/cocos-jiuyou-insert/</guid>
<description>将cocos游戏接入九游SDK实现上架。</description>
<pubDate>Tue, 27 Aug 2024 17:23:47 GMT</pubDate>
<content:encoded><h1 id="android接入九游-sdk">Android接入九游 SDK</h1> <p>本文档提供了如何在 Android 项目中接入九游 SDK 的详细步骤。九游 SDK 是一个为游戏开发者提供各种功能的工具包,如广告、分析、用户认证等。</p> <h2 id="准备工作">准备工作</h2> <ol> <li> <p><strong>创建九游账号</strong>:</p> <ul> <li>访问 <a href="https://aligames.open.9game.cn/">九游开发者平台</a> 注册并登录账户。</li> </ul> </li> <li> <p><strong>下载九游 SDK</strong>:</p> <ul> <li>登录开发者平台,下载最新版本的九游 SDK。 <img src="/images/jiuyou/zip包.png"> 以上是SDK9.7.8.0_7.7.1.0版本的sdk文件夹。我们需要的是demo包 ,具体路径在这个位置👇 <img src="/images/jiuyou/demo包.png"></li> </ul> </li> <li> <p><strong>准备应用信息</strong>:</p> <ul> <li>获取你的应用包名、应用密钥等必要的信息,这些信息在九游开发者平台上会提供。</li> <li>主要是获取gameId,这是需要初始化的</li> </ul> </li> </ol> <h2 id="集成-sdk">集成 SDK</h2> <h3 id="1-将九游-sdk-添加到项目中">1. 将九游 SDK 添加到项目中</h3> <ol> <li> <p><strong>添加 SDK 依赖</strong>:</p> <ul> <li>将九游 SDK 的 <code>.aar</code> 文件添加到项目的 <code>aar</code> 目录下。如果没有 <code>aar</code> 目录,可以手动创建一个。 <img src="/images/jiuyou/aarpath.png"> <img src="/images/jiuyou/aarpakage.png"></li> </ul> </li> <li> <p><strong>配置 Gradle</strong>:</p> <ul> <li> <p>在 <code>app</code> 模块的 <code>build.gradle</code> 文件中,添加对九游 SDK 的依赖:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">dependencies {</span></span> <span class="line"><span style="color:#F8F8F2"> implementation fileTree(</span><span style="color:#AE81FF">dir</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">'aar'</span><span style="color:#F8F8F2">, </span><span style="color:#AE81FF">include</span><span style="color:#F8F8F2">: [</span><span style="color:#E6DB74">'*.jar'</span><span style="color:#F8F8F2">,</span><span style="color:#E6DB74">'*.aar'</span><span style="color:#F8F8F2">])</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> </li> </ul> </li> </ol> <h3 id="2-修改-androidmanifestxml">2. 修改 AndroidManifest.xml</h3> <ol> <li> <p><strong>添加权限和服务</strong>:</p> <ul> <li>打开 <code>AndroidManifest.xml</code> 文件,添加九游 SDK 所需的权限和服务:</li> </ul> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.INTERNET"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_WIFI_STATE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_NETWORK_STATE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.WRITE_EXTERNAL_STORAGE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.READ_PHONE_STATE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.SEND_SMS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.GET_TASKS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.SYSTEM_ALERT_WINDOW"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.AUTHENTICATE_ACCOUNTS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.USE_CREDENTIALS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.GET_ACCOUNTS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.RECORD_AUDIO"</span><span style="color:#F8F8F2">/></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.MODIFY_AUDIO_SETTINGS"</span><span style="color:#F8F8F2">/></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_COARSE_LOCATION"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.WRITE_SETTINGS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.READ_SETTINGS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.WRITE_CALENDAR"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.VIBRATE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.REQUEST_INSTALL_PACKAGES"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.MANAGE_ACCOUNTS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_FINE_LOCATION"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"com.android.alarm.permission.SET_ALARM"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.PACKAGE_USAGE_STATS"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.READ_EXTERNAL_STORAGE"</span><span style="color:#F8F8F2"> /></span></span></code></pre> <p>以上是权限部分,依次添加activity部分:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"> &#x3C;!-- android:taskAffinity 填上游戏的包名,如游戏包名为cn.uc.gamesdk.demo,则下面填 cn.uc.gamesdk.demo.diff --></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- data android:scheme 里填上”ng+当前游戏的gameId”,如游戏ID是123456,则填上ng123456 --></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">activity</span></span> <span class="line"><span style="color:#A6E22E"> tools:replace</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android:taskAffinity"</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"cn.uc.gamesdk.activity.PullupActivity"</span></span> <span class="line"><span style="color:#A6E22E"> android:theme</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@android:style/Theme.Translucent"</span></span> <span class="line"><span style="color:#A6E22E"> android:taskAffinity</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"com.example.app.diff"</span></span> <span class="line"><span style="color:#A6E22E"> android:excludeFromRecents</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span></span> <span class="line"><span style="color:#A6E22E"> android:label</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"PullupActivity"</span></span> <span class="line"><span style="color:#A6E22E"> android:launchMode</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"singleTop"</span></span> <span class="line"><span style="color:#A6E22E"> android:exported</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span></span> <span class="line"><span style="color:#A6E22E"> tools:node</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"replace"</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">intent-filter</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">action</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.intent.action.VIEW"</span><span style="color:#F8F8F2">/></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">category</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.intent.category.DEFAULT"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">category</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.intent.category.BROWSABLE"</span><span style="color:#F8F8F2">/></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">data</span><span style="color:#A6E22E"> android:scheme</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"ng132465"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">intent-filter</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">activity</span><span style="color:#F8F8F2">></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">activity</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"cn.gundam.sdk.shell.activity.ThemeProxyActivity"</span></span> <span class="line"><span style="color:#A6E22E"> android:configChanges</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"keyboardHidden|orientation|screenSize"</span></span> <span class="line"><span style="color:#A6E22E"> android:label</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@string/app_name"</span></span> <span class="line"><span style="color:#A6E22E"> android:theme</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@android:style/Theme.NoTitleBar"</span></span> <span class="line"><span style="color:#A6E22E"> android:windowSoftInputMode</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"adjustResize"</span><span style="color:#F8F8F2"> ></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">intent-filter</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">action</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"cn.uc.gamesdk.sdkweb"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">category</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.intent.category.DEFAULT"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">intent-filter</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">activity</span><span style="color:#F8F8F2">></span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- sdk service 1.1.3新增 --></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">service</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"cn.gundam.sdk.shell.service.ProxyService"</span></span> <span class="line"><span style="color:#A6E22E"> android:process</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">":bgservice"</span></span> <span class="line"><span style="color:#A6E22E"> android:exported</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"false"</span></span> <span class="line"><span style="color:#A6E22E"> tools:replace</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android:exported"</span><span style="color:#F8F8F2">/></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- sdk service end--></span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- 以上声明是SDK内部声明,最好不要更改,直接拷贝过去 --></span></span></code></pre> </li> <li> <p><strong>处理 Manifest 合并冲突</strong>:</p> <ul> <li> <p>如果在集成过程中出现 Manifest 合并冲突,可以通过 <code>tools:replace</code> 来解决。例如:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#F92672">application</span></span> <span class="line"><span style="color:#A6E22E"> android:allowBackup</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span></span> <span class="line"><span style="color:#A6E22E"> tools:replace</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android:allowBackup"</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- 其他配置 --></span></span> <span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#F92672">application</span><span style="color:#F8F8F2">></span></span></code></pre> </li> <li> <p>确保在 <code>AndroidManifest.xml</code> 文件的开头添加了 <code>xmlns:tools="http://schemas.android.com/tools"</code> 以支持 <code>tools</code> 命名空间。 以上部分添加到位,基本就完成了引入,附几张图注意要修改的部分: <img src="/images/jiuyou/allowBackup.png"> <img src="/images/jiuyou/editinfo.png"></p> </li> </ul> </li> </ol> <h3 id="3-初始化九游-sdk">3. 初始化九游 SDK</h3> <ol> <li> <p><strong>在 <code>AppActivity</code> 类中初始化</strong>:</p> <ul> <li> <p>在你的 <code>AppActivity</code> 类中初始化九游 SDK:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.even.SDKEventKey</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.even.SDKEventReceiver</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.even.Subscribe</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.exception.AliLackActivityException</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.open.ParamInfo</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.open.UCOrientation</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.param.SDKParamKey</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.gundam.sdk.shell.param.SDKParams</span><span style="color:#F8F8F2">;</span></span> <span class="line"><span style="color:#F92672"> import</span><span style="color:#F92672"> cn.uc.gamesdk.UCGameSdk</span><span style="color:#F8F8F2">;</span></span> <span class="line"></span> <span class="line"><span style="color:#F92672"> public</span><span style="color:#F92672"> class</span><span style="color:#A6E22E;text-decoration:underline"> AppActivity</span><span style="color:#F92672"> extends</span><span style="color:#A6E22E;font-style:italic;text-decoration:underline"> Cocos2dxActivity</span><span style="color:#F8F8F2"> {</span></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Override</span></span> <span class="line"><span style="color:#F92672"> protected</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> onCreate</span><span style="color:#F8F8F2">(</span><span style="color:#66D9EF;font-style:italic">Bundle</span><span style="color:#FD971F;font-style:italic"> savedInstanceState</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#FD971F"> super</span><span style="color:#F8F8F2">.</span><span style="color:#A6E22E">onCreate</span><span style="color:#F8F8F2">(savedInstanceState);</span></span> <span class="line"><span style="color:#88846F"> // DO OTHER INITIALIZATION BELOW</span></span> <span class="line"><span style="color:#F8F8F2"> SDKWrapper.</span><span style="color:#A6E22E">getInstance</span><span style="color:#F8F8F2">().</span><span style="color:#A6E22E">init</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F">this</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> UCGameSdk.</span><span style="color:#A6E22E">defaultSdk</span><span style="color:#F8F8F2">().</span><span style="color:#A6E22E">registerSDKEventReceiver</span><span style="color:#F8F8F2">(receiver);</span></span> <span class="line"></span> <span class="line"></span> <span class="line"><span style="color:#A6E22E"> ucSdkInit</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F92672"> private</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> ucSdkInit</span><span style="color:#F8F8F2">() {</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> ParamInfo</span><span style="color:#F8F8F2"> gameParamInfo </span><span style="color:#F92672">=</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> ParamInfo</span><span style="color:#F8F8F2">();</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> gameParamInfo.</span><span style="color:#A6E22E">setGameId</span><span style="color:#F8F8F2">(YOUR_GAME_ID);</span></span> <span class="line"><span style="color:#F8F8F2"> gameParamInfo.</span><span style="color:#A6E22E">setOrientation</span><span style="color:#F8F8F2">(UCOrientation.PORTRAIT);</span></span> <span class="line"></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> SDKParams</span><span style="color:#F8F8F2"> sdkParams </span><span style="color:#F92672">=</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> SDKParams</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#F8F8F2"> sdkParams.</span><span style="color:#A6E22E">put</span><span style="color:#F8F8F2">(SDKParamKey.GAME_PARAMS, gameParamInfo);</span></span> <span class="line"><span style="color:#88846F"> // 如果游戏已经申请了权限,不想sdk主动请求权限,要通过SDKParamKey.GAME_HAD_REQUEST_PERMISSION参数告知九游sdk</span></span> <span class="line"><span style="color:#88846F"> // true 游戏已经弹了,SDK不需要弹出权限申请窗</span></span> <span class="line"><span style="color:#88846F"> // false 游戏没有弹,SDK可以按需弹出权限申请窗</span></span> <span class="line"><span style="color:#F8F8F2"> sdkParams.</span><span style="color:#A6E22E">put</span><span style="color:#F8F8F2">(SDKParamKey.GAME_HAD_REQUEST_PERMISSION, </span><span style="color:#AE81FF">false</span><span style="color:#F8F8F2">);</span></span> <span class="line"></span> <span class="line"></span> <span class="line"><span style="color:#F92672"> try</span><span style="color:#F8F8F2"> {</span></span> <span class="line"><span style="color:#88846F"> //初始化SDK</span></span> <span class="line"><span style="color:#F8F8F2"> UCGameSdk.</span><span style="color:#A6E22E">defaultSdk</span><span style="color:#F8F8F2">().</span><span style="color:#A6E22E">initSdk</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F">this</span><span style="color:#F8F8F2">, sdkParams);</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">catch</span><span style="color:#F8F8F2"> (</span><span style="color:#66D9EF;font-style:italic">AliLackActivityException</span><span style="color:#FD971F;font-style:italic"> e</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F8F8F2"> e.</span><span style="color:#A6E22E">printStackTrace</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> /**</span></span> <span class="line"><span style="color:#88846F"> * 退出游戏前,请调用本方法</span></span> <span class="line"><span style="color:#88846F"> * </span><span style="color:#F92672">@param</span><span style="color:#FD971F;font-style:italic"> view</span></span> <span class="line"><span style="color:#88846F"> */</span></span> <span class="line"><span style="color:#F92672"> public</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> exit</span><span style="color:#F8F8F2">(</span><span style="color:#66D9EF;font-style:italic">View</span><span style="color:#F8F8F2"> view) {</span></span> <span class="line"></span> <span class="line"><span style="color:#F92672"> try</span><span style="color:#F8F8F2"> {</span></span> <span class="line"><span style="color:#F8F8F2"> UCGameSdk.</span><span style="color:#A6E22E">defaultSdk</span><span style="color:#F8F8F2">().</span><span style="color:#A6E22E">exit</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F">this</span><span style="color:#F8F8F2">, </span><span style="color:#AE81FF">null</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">catch</span><span style="color:#F8F8F2"> (</span><span style="color:#66D9EF;font-style:italic">Exception</span><span style="color:#FD971F;font-style:italic"> e</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F8F8F2"> e.</span><span style="color:#A6E22E">printStackTrace</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#A6E22E"> exitApp</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#F92672"> private</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> exitApp</span><span style="color:#F8F8F2">() {</span></span> <span class="line"><span style="color:#A6E22E"> finish</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#88846F"> //退出程序</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> Intent</span><span style="color:#F8F8F2"> intent </span><span style="color:#F92672">=</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> Intent</span><span style="color:#F8F8F2">(Intent.ACTION_MAIN);</span></span> <span class="line"><span style="color:#F8F8F2"> intent.</span><span style="color:#A6E22E">addCategory</span><span style="color:#F8F8F2">(Intent.CATEGORY_HOME);</span></span> <span class="line"><span style="color:#F8F8F2"> intent.</span><span style="color:#A6E22E">setFlags</span><span style="color:#F8F8F2">(Intent.FLAG_ACTIVITY_CLEAR_TOP);</span></span> <span class="line"><span style="color:#A6E22E"> startActivity</span><span style="color:#F8F8F2">(intent);</span></span> <span class="line"><span style="color:#F8F8F2"> android.os.Process.</span><span style="color:#A6E22E">killProcess</span><span style="color:#F8F8F2">(android.os.Process.</span><span style="color:#A6E22E">myPid</span><span style="color:#F8F8F2">());</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> /**</span></span> <span class="line"><span style="color:#88846F"> *回调事件</span></span> <span class="line"><span style="color:#88846F"> */</span></span> <span class="line"><span style="color:#F92672"> private</span><span style="color:#66D9EF;font-style:italic"> SDKEventReceiver</span><span style="color:#F8F8F2"> receiver </span><span style="color:#F92672">=</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> SDKEventReceiver</span><span style="color:#F8F8F2">() {</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Subscribe</span><span style="color:#F8F8F2">(</span><span style="color:#AE81FF">event</span><span style="color:#F92672"> =</span><span style="color:#F8F8F2"> SDKEventKey.ON_EXIT_SUCC)</span></span> <span class="line"><span style="color:#F92672"> private</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> onExit</span><span style="color:#F8F8F2">(</span><span style="color:#66D9EF;font-style:italic">String</span><span style="color:#FD971F;font-style:italic"> desc</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F8F8F2"> Log.</span><span style="color:#A6E22E">d</span><span style="color:#F8F8F2">(TAG, </span><span style="color:#E6DB74">"ON_EXIT_SUCC"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#88846F"> //Toast.makeText(MainActivity.this, ">> 游戏即将退出", Toast.LENGTH_LONG).show();</span></span> <span class="line"></span> <span class="line"><span style="color:#A6E22E"> exitApp</span><span style="color:#F8F8F2">();</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Subscribe</span><span style="color:#F8F8F2">(</span><span style="color:#AE81FF">event</span><span style="color:#F92672"> =</span><span style="color:#F8F8F2"> SDKEventKey.ON_EXIT_CANCELED)</span></span> <span class="line"><span style="color:#F92672"> private</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> onExitCanceled</span><span style="color:#F8F8F2">(</span><span style="color:#66D9EF;font-style:italic">String</span><span style="color:#FD971F;font-style:italic"> desc</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#A6E22E"> runOnUiThread</span><span style="color:#F8F8F2">(</span><span style="color:#F92672">new</span><span style="color:#A6E22E"> Runnable</span><span style="color:#F8F8F2">() {</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Override</span></span> <span class="line"><span style="color:#F92672"> public</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> run</span><span style="color:#F8F8F2">() {</span></span> <span class="line"><span style="color:#88846F"> //Toast.makeText(MainActivity.this, ">> 继续游戏", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> });</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> // @Subscribe(event = SDKEventKey.ON_LOGIN_SUCC)</span></span> <span class="line"><span style="color:#88846F"> // private void onLoginSucc(final String sid) {</span></span> <span class="line"><span style="color:#88846F"> // runOnUiThread(new Runnable() {</span></span> <span class="line"><span style="color:#88846F"> //</span></span> <span class="line"><span style="color:#88846F"> // @Override</span></span> <span class="line"><span style="color:#88846F"> // public void run() {</span></span> <span class="line"><span style="color:#88846F"> // if (TextUtils.isEmpty(sid)) {</span></span> <span class="line"><span style="color:#88846F"> // // 离线试玩</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(AppActivity.this, ">> 离线登录成功", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#88846F"> // } else {</span></span> <span class="line"><span style="color:#88846F"> // // 用户登录</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(AppActivity.this, ">> 用户登录成功", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"><span style="color:#88846F"> // });</span></span> <span class="line"><span style="color:#88846F"> //</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> // @Subscribe(event = SDKEventKey.ON_LOGIN_FAILED)</span></span> <span class="line"><span style="color:#88846F"> // private void onLoginFailed(String desc) {</span></span> <span class="line"><span style="color:#88846F"> // runOnUiThread(new Runnable() {</span></span> <span class="line"><span style="color:#88846F"> //</span></span> <span class="line"><span style="color:#88846F"> // @Override</span></span> <span class="line"><span style="color:#88846F"> // public void run() {</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(AppActivity.this, ">> 登录失败", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"><span style="color:#88846F"> // });</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Subscribe</span><span style="color:#F8F8F2">(</span><span style="color:#AE81FF">event</span><span style="color:#F92672"> =</span><span style="color:#F8F8F2"> SDKEventKey.ON_INIT_SUCC)</span></span> <span class="line"><span style="color:#F92672"> private</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> onInitSucc</span><span style="color:#F8F8F2">() {</span></span> <span class="line"><span style="color:#A6E22E"> runOnUiThread</span><span style="color:#F8F8F2">(</span><span style="color:#F92672">new</span><span style="color:#A6E22E"> Runnable</span><span style="color:#F8F8F2">() {</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Override</span></span> <span class="line"><span style="color:#F92672"> public</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> run</span><span style="color:#F8F8F2">() {</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(AppActivity.this, ">> 初始化成功", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> });</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Subscribe</span><span style="color:#F8F8F2">(</span><span style="color:#AE81FF">event</span><span style="color:#F92672"> =</span><span style="color:#F8F8F2"> SDKEventKey.ON_INIT_FAILED)</span></span> <span class="line"><span style="color:#F92672"> private</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> onInitFailed</span><span style="color:#F8F8F2">(</span><span style="color:#66D9EF;font-style:italic">String</span><span style="color:#FD971F;font-style:italic"> msg</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#A6E22E"> runOnUiThread</span><span style="color:#F8F8F2">(</span><span style="color:#F92672">new</span><span style="color:#A6E22E"> Runnable</span><span style="color:#F8F8F2">() {</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> @</span><span style="color:#66D9EF;font-style:italic">Override</span></span> <span class="line"><span style="color:#F92672"> public</span><span style="color:#66D9EF;font-style:italic"> void</span><span style="color:#A6E22E"> run</span><span style="color:#F8F8F2">() {</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(AppActivity.this, ">> 初始化失败", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> });</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> // @Subscribe(event = SDKEventKey.ON_CREATE_ORDER_SUCC)</span></span> <span class="line"><span style="color:#88846F"> // private void onPaySucc(final Bundle data) {</span></span> <span class="line"><span style="color:#88846F"> // runOnUiThread(new Runnable() {</span></span> <span class="line"><span style="color:#88846F"> //</span></span> <span class="line"><span style="color:#88846F"> // @Override</span></span> <span class="line"><span style="color:#88846F"> // public void run() {</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(MainActivity.this, ">> 支付成功", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#88846F"> // Log.d(TAG, "此处为支付成功回调: callback data = " + data.getString("response"));</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"><span style="color:#88846F"> // });</span></span> <span class="line"><span style="color:#88846F"> // String response = data.getString("response");</span></span> <span class="line"><span style="color:#88846F"> // // 这里执行发货,如果发货成功需要设置以下值</span></span> <span class="line"><span style="color:#88846F"> // data.putString("result", Response.OPERATE_SUCCESS_MSG);</span></span> <span class="line"><span style="color:#88846F"> // // 如果发货失败需要设置以下值</span></span> <span class="line"><span style="color:#88846F"> // //data.putString("result", Response.OPERATE_FAIL_MSG);</span></span> <span class="line"><span style="color:#88846F"> // Log.d(TAG, "pay succ" + data);</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"><span style="color:#88846F"> //</span></span> <span class="line"><span style="color:#88846F"> // @Subscribe(event = SDKEventKey.ON_PAY_USER_EXIT)</span></span> <span class="line"><span style="color:#88846F"> // private void onPayFail(String data) {</span></span> <span class="line"><span style="color:#88846F"> // runOnUiThread( new Runnable() {</span></span> <span class="line"><span style="color:#88846F"> //</span></span> <span class="line"><span style="color:#88846F"> // @Override</span></span> <span class="line"><span style="color:#88846F"> // public void run() {</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(MainActivity.this, ">> 支付失败", Toast.LENGTH_LONG).show();</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"><span style="color:#88846F"> // });</span></span> <span class="line"><span style="color:#88846F"> // Log.d(TAG, "pay exit");</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> // SDK侧发起的账号切换请求</span></span> <span class="line"><span style="color:#88846F"> // 步骤1,侦听SDK切换账号指令</span></span> <span class="line"><span style="color:#88846F"> // @Subscribe(event = SDKEventKey.ON_ACCOUNT_SWITCH_REQUEST)</span></span> <span class="line"><span style="color:#88846F"> // private void onAccountSwitchRequest(final String sid) {</span></span> <span class="line"><span style="color:#88846F"> // Toast.makeText(MainActivity.this, "侦听到ON_ACCOUNT_SWITCH_REQUEST指令", Toast.LENGTH_SHORT).show();</span></span> <span class="line"><span style="color:#88846F"> // //步骤2,CP接入组先退出当前游戏角色</span></span> <span class="line"><span style="color:#88846F"> // logoutGameRole();</span></span> <span class="line"><span style="color:#88846F"> // //步骤3,和正常登录一样,调用sdk做登录请求</span></span> <span class="line"><span style="color:#88846F"> // //这里需要注意:</span></span> <span class="line"><span style="color:#88846F"> // // 1. 如果游戏的退出和重新登录在native层是异步的,</span></span> <span class="line"><span style="color:#88846F"> // // 那requestLogin()应该由cp自行决定在合适的节点调用,</span></span> <span class="line"><span style="color:#88846F"> // // 不要求必须放在onAccountSwitchRequest()方法中;</span></span> <span class="line"><span style="color:#88846F"> // // 2. requestLogin()只需要保证在角色已退出的情况下调用即可;</span></span> <span class="line"><span style="color:#88846F"> // login();</span></span> <span class="line"><span style="color:#88846F"> // }</span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> };</span></span></code></pre> </li> <li> <p>注意将代码中的 <code>YOUR_GAME_ID</code> 替换为从九游开发者平台获取的实际值。</p> </li> </ul> </li> </ol> <h2 id="配置项目">配置项目</h2> <ol> <li> <p><strong>配置 ProGuard</strong>:</p> <ul> <li>如果你的项目使用 ProGuard 混淆代码,确保在 <code>proguard-rules.pro</code> 文件中添加以下规则以防止混淆:</li> </ul> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>-dontwarn</span></span> <span class="line"><span>-ignorewarnings</span></span> <span class="line"><span>#------------------------- 联运 start ---------------------------------</span></span> <span class="line"><span></span></span> <span class="line"><span>-dontskipnonpubliclibraryclassmembers</span></span> <span class="line"><span>-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod</span></span> <span class="line"><span></span></span> <span class="line"><span>-keep public class * extends cn.gundam.sdk.shell.even.SDKEventReceiver</span></span> <span class="line"><span></span></span> <span class="line"><span>-keep class android.**{</span></span> <span class="line"><span> &#x3C;methods>;</span></span> <span class="line"><span> &#x3C;fields>;</span></span> <span class="line"><span>}</span></span> <span class="line"><span>-keep class cn.uc.**{</span></span> <span class="line"><span> &#x3C;methods>;</span></span> <span class="line"><span> &#x3C;fields>;</span></span> <span class="line"><span>}</span></span> <span class="line"><span>-keep class cn.gundam.**{</span></span> <span class="line"><span> &#x3C;methods>;</span></span> <span class="line"><span> &#x3C;fields>;</span></span> <span class="line"><span>}</span></span> <span class="line"><span></span></span> <span class="line"><span>#------------------------- 联运 end ---------------------------------</span></span></code></pre> </li> </ol> <h2 id="测试和调试">测试和调试</h2> <ol> <li> <p><strong>运行应用</strong>:</p> <ul> <li>启动你的应用并验证九游 SDK 是否正常工作。检查 SDK 的日志输出以确认初始化是否成功。</li> </ul> </li> <li> <p><strong>查看日志</strong>:</p> <ul> <li>使用 Logcat 查看九游 SDK 的调试信息,确保没有出现错误或警告。</li> </ul> </li> <li> <p><strong>进行功能测试</strong>:</p> <ul> <li>测试九游 SDK 提供的各种功能(如广告展示、用户登录等),确保它们在应用中正常工作。</li> </ul> </li> </ol> <h2 id="常见问题">常见问题</h2> <ol> <li> <p><strong>Manifest 合并冲突</strong>:</p> <ul> <li>如果遇到 Manifest 合并冲突,请参阅 <a href="https://developer.android.com/studio/build/manifest-merge">处理 Manifest 合并冲突</a> 文档以获取更多信息。</li> </ul> </li> <li> <p><strong>SDK 初始化失败</strong>:</p> <ul> <li>确保应用 ID 和密钥正确无误,并且网络连接正常。</li> </ul> </li> <li> <p><strong>日志查看</strong>:</p> <ul> <li>使用 Logcat 查看 SDK 的调试信息,帮助排查问题。</li> </ul> </li> </ol> <h2 id="参考资料">参考资料</h2> <ul> <li><a href="https://developer.joyme.com/">九游开发者平台</a></li> <li><a href="https://developer.android.com/studio/build/manifest-merge">Android Manifest 合并</a></li> </ul></content:encoded>
<enclosure url="https://layfz.com/images/uploads/jiuyou.png" length="0" type="image/jpeg"/>
</item>
<item>
<title>Cocos接入优量汇</title>
<link>https://layfz.com/posts/cocos-youlianghui-insert/</link>
<guid isPermaLink="true">https://layfz.com/posts/cocos-youlianghui-insert/</guid>
<description>将cocos接入安卓优量汇实现广告变现。</description>
<pubDate>Thu, 08 Aug 2024 10:55:28 GMT</pubDate>
<content:encoded><h1 id="cocos-接入优量汇广告-sdk">Cocos 接入优量汇广告 SDK</h1> <p>在这个教程中,我将详细介绍如何在 Cocos 引擎中接入优量汇广告 SDK。优量汇是一款强大的广告平台,为开发者提供了丰富的广告资源和优质的广告体验。我们将分步介绍如何在 Cocos 项目中集成优量汇 SDK,包括环境配置、代码实现和测试等内容。</p> <p><mark>本教程使用的是腾讯聚合平台,聚合平台在2024-06-05作出修订,将不再维护聚合SDK</mark> <img src="/images/youlianghui/unsuggest.png"></p> <h2 id="目录">目录</h2> <ol> <li><a href="#%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6">前提条件</a></li> <li><a href="#%E8%8E%B7%E5%8F%96%E4%BC%98%E9%87%8F%E6%B1%87-sdk">获取优量汇 SDK</a></li> <li><a href="#%E9%9B%86%E6%88%90-sdk">集成 SDK</a></li> <li><a href="#%E9%85%8D%E7%BD%AE%E4%BC%98%E9%87%8F%E6%B1%87%E5%B9%BF%E5%91%8A">配置优量汇广告</a></li> </ol> <h2 id="前提条件">前提条件</h2> <p>在开始之前,请确保你具备以下条件:</p> <ul> <li>已安装 Cocos Creator 或 Cocos2d-x。</li> <li>已创建一个 Cocos 项目。</li> </ul> <h2 id="注意事项">注意事项:</h2> <ul> <li> <p>由于Android平台更新迭代非常快,开发者很容易陷入版本冲突以及兼容问题,若要使用该教程,请使用<strong>推荐的的版本配置</strong>:</p> <ul> <li>Gradle:8.0.2</li> <li>Gradle Plugin:8.0.2</li> <li>Android NDK: 21.3.6528147</li> <li>Android SDK: 29</li> <li>Cocos Creator: 2.4.13</li> </ul> </li> </ul> <h2 id="获取优量汇-sdk">获取优量汇 SDK</h2> <ol> <li><strong>访问优量汇官网</strong>:前往 <a href="https://adnet.qq.com/resource/sdk">优量汇</a> 并登录你的账户。</li> <li><strong>下载 SDK</strong>:在开发者中心找到Android SDK,下载并解压缩。 <mark>聚合SDK已不提供下载,请联系作者获取,若有备份,忽略此条消息</mark></li> </ol> <h2 id="集成-sdk">集成 SDK</h2> <h3 id="1-集成-android-sdk">1. 集成 Android SDK</h3> <ol> <li> <p><strong>导入 SDK 文件</strong>:</p> <ul> <li>将下载的 Android SDK 文件(<code>.aar</code> 或 <code>.jar</code> 文件)放入 Cocos 项目的 <code>assets</code> 目录下的 <code>Plugins/Android</code> 文件夹中。</li> </ul> </li> <li> <p><strong>配置 Gradle 文件</strong>:</p> <ul> <li> <p>打开 Cocos 项目的 <code>proj.android-studio/app/build.gradle</code> 文件。</p> </li> <li> <p>在 <code>dependencies</code> 部分添加优量汇 SDK 的依赖:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>implementation files('libs/your_sdk_file.aar')</span></span></code></pre> </li> </ul> </li> <li> <p><strong>更新 AndroidManifest.xml</strong>:</p> <ul> <li> <p>在 <code>proj.android-studio/app/src/main/AndroidManifest.xml</code> 中添加优量汇所需的权限和服务:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"> &#x3C;!--相关权限声明 : SDK不强制校验下列权限(即:无下面权限sdk也可正常工作),但建议开发者申请下面权限,尤其是READ_PHONE_STATE权限--></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!--READ_PHONE_STATE权限用于允许SDK获取用户标识,针对单媒体的用户,允许获取权限的,投放定向广告;不允许获取权限的用户,投放通投广告,媒体可以选择是否把用户标识数据提供给优量汇,并承担相应广告填充和eCPM单价下降损失的结果。--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.READ_PHONE_STATE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_COARSE_LOCATION"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- 高于Android 7的系统上,如果应用的 targetSdkVersion >= 26 ,推荐增加权限声明(SDK将通过此权限触发App安装动作)--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.REQUEST_INSTALL_PACKAGES"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- 高于Android 11的系统上,如果应用的 targetSdkVersion >= 30 ,推荐增加以下权限声明(SDK将通过此权限正常触发广告行为,并保证广告的正确投放。此权限需要在用户隐私文档中声明。)--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.QUERY_ALL_PACKAGES"</span></span> <span class="line"><span style="color:#A6E22E"> tools:ignore</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"QueryAllPackagesPermission"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> &#x3C;!--检测当前⽹络状态是2G、3G、4G还是WiFi--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_NETWORK_STATE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!--联⽹权限--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.INTERNET"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!--读写存储权限--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.READ_EXTERNAL_STORAGE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.WRITE_EXTERNAL_STORAGE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!--获取MAC地址,⽤于标识⽤户--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_WIFI_STATE"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!--定位权限,不强制要求--></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">uses-permission</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.permission.ACCESS_FINE_LOCATION"</span><span style="color:#F8F8F2"> /></span></span></code></pre> <p>application部分:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">application</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"org.cocos2dx.javascript.GDT.GDTApplication"</span></span> <span class="line"><span style="color:#A6E22E"> android:allowBackup</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span></span> <span class="line"><span style="color:#A6E22E"> android:label</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@string/app_name"</span></span> <span class="line"><span style="color:#A6E22E"> android:usesCleartextTraffic</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span></span> <span class="line"><span style="color:#A6E22E"> tools:replace</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android:allowBackup"</span></span> <span class="line"><span style="color:#A6E22E"> android:icon</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@mipmap/ic_launcher"</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- Tell Cocos2dxActivity the name of our .so --></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">meta-data</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.app.lib_name"</span></span> <span class="line"><span style="color:#A6E22E"> android:value</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"cocos2djs"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">activity</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"org.cocos2dx.javascript.AppActivity"</span></span> <span class="line"><span style="color:#A6E22E"> android:screenOrientation</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"portrait"</span></span> <span class="line"><span style="color:#A6E22E"> android:configChanges</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"orientation|keyboardHidden|screenSize|screenLayout|uiMode"</span></span> <span class="line"><span style="color:#A6E22E"> android:label</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@string/app_name"</span></span> <span class="line"><span style="color:#A6E22E"> android:theme</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@android:style/Theme.NoTitleBar.Fullscreen"</span></span> <span class="line"><span style="color:#A6E22E"> android:launchMode</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"singleTask"</span></span> <span class="line"><span style="color:#A6E22E"> android:exported</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">intent-filter</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">action</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.intent.action.MAIN"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">category</span><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.intent.category.LAUNCHER"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">intent-filter</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">activity</span><span style="color:#F8F8F2">></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">activity</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"org.cocos2dx.javascript.GDT.SplashActivity"</span></span> <span class="line"><span style="color:#A6E22E"> android:configChanges</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"orientation|keyboardHidden|screenSize|screenLayout|uiMode"</span></span> <span class="line"><span style="color:#A6E22E"> android:screenOrientation</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"portrait"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- targetSDKVersion >= 24时才需要添加这个provider。provider的authorities属性的值为${applicationId}.fileprovider,请开发者根据自己的${applicationId}来设置这个值,例如本例中applicationId为"com.qq.e.union.demo"。 --></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">provider</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"com.qq.e.comm.GDTFileProvider"</span></span> <span class="line"><span style="color:#A6E22E"> android:authorities</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"${applicationId}.gdt.fileprovider"</span></span> <span class="line"><span style="color:#A6E22E"> android:exported</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"false"</span></span> <span class="line"><span style="color:#A6E22E"> android:grantUriPermissions</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">meta-data</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.support.FILE_PROVIDER_PATHS"</span></span> <span class="line"><span style="color:#A6E22E"> android:resource</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@xml/gdt_file_path"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">provider</span><span style="color:#F8F8F2">></span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> &#x3C;!-- 如果使用激励视频/全屏视频功能,需要主动在AndroidManifest.xml里面声明MobRewardVideoActivity --></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">activity</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"com.baidu.mobads.sdk.api.MobRewardVideoActivity"</span></span> <span class="line"><span style="color:#A6E22E"> android:configChanges</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"screenSize|orientation|keyboardHidden"</span></span> <span class="line"><span style="color:#A6E22E"> android:launchMode</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"singleTask"</span></span> <span class="line"><span style="color:#A6E22E"> android:theme</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@android:style/Theme.Translucent.NoTitleBar"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">provider</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"com.baidu.mobads.sdk.api.BdFileProvider"</span></span> <span class="line"><span style="color:#A6E22E"> android:authorities</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"${applicationId}.bd.provider"</span></span> <span class="line"><span style="color:#A6E22E"> android:exported</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"false"</span></span> <span class="line"><span style="color:#A6E22E"> android:grantUriPermissions</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">meta-data</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.support.FILE_PROVIDER_PATHS"</span></span> <span class="line"><span style="color:#A6E22E"> android:resource</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@xml/bd_file_paths"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">provider</span><span style="color:#F8F8F2">></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">provider</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"com.bytedance.sdk.openadsdk.TTFileProvider"</span></span> <span class="line"><span style="color:#A6E22E"> android:authorities</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"${applicationId}.TTFileProvider"</span></span> <span class="line"><span style="color:#A6E22E"> android:exported</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"false"</span></span> <span class="line"><span style="color:#A6E22E"> android:grantUriPermissions</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"true"</span><span style="color:#F8F8F2">></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">meta-data</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"android.support.FILE_PROVIDER_PATHS"</span></span> <span class="line"><span style="color:#A6E22E"> android:resource</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"@xml/tt_file_paths"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">provider</span><span style="color:#F8F8F2">></span></span> <span class="line"></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#F92672">provider</span></span> <span class="line"><span style="color:#A6E22E"> android:name</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"com.bytedance.sdk.openadsdk.multipro.TTMultiProvider"</span></span> <span class="line"><span style="color:#A6E22E"> android:authorities</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"${applicationId}.TTMultiProvider"</span></span> <span class="line"><span style="color:#A6E22E"> android:exported</span><span style="color:#F8F8F2">=</span><span style="color:#E6DB74">"false"</span><span style="color:#F8F8F2"> /></span></span> <span class="line"><span style="color:#F8F8F2"> &#x3C;/</span><span style="color:#F92672">application</span><span style="color:#F8F8F2">></span></span></code></pre> </li> </ul> </li> <li> <p><strong>目录结构</strong>: <img src="/images/youlianghui/mulujiegou.png"></p> <p><strong>将代码依次复制到自己的项目当中,并在AppConfig类中配置自己的参数</strong></p> </li> </ol> <img src="/images/youlianghui/appconfig.png"> <p><mark>添加依赖</mark></p> <p>gradle文件依赖部分:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span> implementation fileTree(dir: 'arrs', include: ['*.aar'])</span></span> <span class="line"><span> implementation 'com.qq.e.union:union:4.570.1440'</span></span> <span class="line"><span> implementation 'androidx.legacy:legacy-support-v4:1.0.0'</span></span> <span class="line"><span> implementation 'androidx.recyclerview:recyclerview:1.0.0'</span></span> <span class="line"><span> implementation "com.google.android.material:material:1.0.0"</span></span> <span class="line"><span> implementation 'androidx.cardview:cardview:1.0.0'</span></span> <span class="line"><span> implementation 'androidx.appcompat:appcompat:1.0.0'</span></span> <span class="line"><span> implementation 'androidx.constraintlayout:constraintlayout:1.1.3'</span></span> <span class="line"><span> implementation 'com.tencent.bugly:crashreport:latest.release'</span></span> <span class="line"><span> debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'</span></span> <span class="line"><span> releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1'</span></span> <span class="line"><span> implementation 'androidx.multidex:multidex:2.0.0'</span></span> <span class="line"><span> implementation 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+'</span></span></code></pre> <p>在app目录下添加arrs文件夹,把聚合平台的包放进去即可</p> <h2 id="配置优量汇广告">配置优量汇广告</h2> <h3 id="1-初始化-sdk">1. 初始化 SDK</h3> <p>在 Cocos 项目的主脚本中,初始化优量汇 SDK:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F">// JavaScript 示例</span></span> <span class="line"><span style="color:#A6E22E">onAD</span><span style="color:#F8F8F2">(sender, str) {</span></span> <span class="line"><span style="color:#F8F8F2"> console.</span><span style="color:#A6E22E">log</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"显示"</span><span style="color:#F92672"> +</span><span style="color:#F8F8F2"> str </span><span style="color:#F92672">+</span><span style="color:#E6DB74"> "广告"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (str </span><span style="color:#F92672">==</span><span style="color:#E6DB74"> "开屏"</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (cc.sys.platform </span><span style="color:#F92672">==</span><span style="color:#F8F8F2"> cc.sys.ANDROID) {</span></span> <span class="line"><span style="color:#F8F8F2"> jsb.reflection.</span><span style="color:#A6E22E">callStaticMethod</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"org/cocos2dx/javascript/AppActivity"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"showSplashAd"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"()V"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">else</span><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (str </span><span style="color:#F92672">==</span><span style="color:#E6DB74"> "激励"</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (cc.sys.platform </span><span style="color:#F92672">==</span><span style="color:#F8F8F2"> cc.sys.ANDROID) {</span></span> <span class="line"><span style="color:#F8F8F2"> jsb.reflection.</span><span style="color:#A6E22E">callStaticMethod</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"org/cocos2dx/javascript/AppActivity"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"showRewardedVideoAd"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"()V"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">else</span><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (str </span><span style="color:#F92672">==</span><span style="color:#E6DB74"> "插屏"</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (cc.sys.platform </span><span style="color:#F92672">==</span><span style="color:#F8F8F2"> cc.sys.ANDROID) {</span></span> <span class="line"><span style="color:#F8F8F2"> jsb.reflection.</span><span style="color:#A6E22E">callStaticMethod</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"org/cocos2dx/javascript/AppActivity"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"showInterstitial"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"()V"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">else</span><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (str </span><span style="color:#F92672">==</span><span style="color:#E6DB74"> "信息"</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (cc.sys.platform </span><span style="color:#F92672">==</span><span style="color:#F8F8F2"> cc.sys.ANDROID) {</span></span> <span class="line"><span style="color:#F8F8F2"> jsb.reflection.</span><span style="color:#A6E22E">callStaticMethod</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"org/cocos2dx/javascript/AppActivity"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"showDrawAd"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"()V"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">else</span><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (str </span><span style="color:#F92672">==</span><span style="color:#E6DB74"> "横幅"</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (cc.sys.platform </span><span style="color:#F92672">==</span><span style="color:#F8F8F2"> cc.sys.ANDROID) {</span></span> <span class="line"><span style="color:#F8F8F2"> jsb.reflection.</span><span style="color:#A6E22E">callStaticMethod</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"org/cocos2dx/javascript/AppActivity"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"showBanner"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"()V"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">else</span><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (str </span><span style="color:#F92672">==</span><span style="color:#E6DB74"> "关闭信息"</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (cc.sys.platform </span><span style="color:#F92672">==</span><span style="color:#F8F8F2"> cc.sys.ANDROID) {</span></span> <span class="line"><span style="color:#F8F8F2"> jsb.reflection.</span><span style="color:#A6E22E">callStaticMethod</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"org/cocos2dx/javascript/AppActivity"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"hideDrawAd"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"()V"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">else</span><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (str </span><span style="color:#F92672">==</span><span style="color:#E6DB74"> "关闭横幅"</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (cc.sys.platform </span><span style="color:#F92672">==</span><span style="color:#F8F8F2"> cc.sys.ANDROID) {</span></span> <span class="line"><span style="color:#F8F8F2"> jsb.reflection.</span><span style="color:#A6E22E">callStaticMethod</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">"org/cocos2dx/javascript/AppActivity"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"hideBanner"</span><span style="color:#F8F8F2">, </span><span style="color:#E6DB74">"()V"</span><span style="color:#F8F8F2">);</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span></code></pre> <p>在你需要的地方直接调取就可以实现弹出广告了,以上就是cocos脚本在需要的地方调用方法就可以实现安卓平台的广告调用。</p></content:encoded>
<enclosure url="https://layfz.com/images/uploads/ylh.png" length="0" type="image/jpeg"/>
</item>
<item>
<title>git同时提交到github和gitee(双平台同步)</title>
<link>https://layfz.com/posts/git-multipul-platform/</link>
<guid isPermaLink="true">https://layfz.com/posts/git-multipul-platform/</guid>
<description>帮助git提交到github和gitee平台</description>
<pubDate>Tue, 02 Jul 2024 10:35:22 GMT</pubDate>
<content:encoded><h1 id="git-同时提交到-gitee-和-github">Git 同时提交到 Gitee 和 GitHub</h1> <h2 id="前提条件">前提条件</h2> <ol> <li>你已经在本地初始化了 Git 仓库。</li> <li>你已经安装了 Git 并配置了用户名和邮箱。</li> <li>你已经有了一个仓库,你想要进行仓库的同步。</li> </ol> <h2 id="配置远程仓库">配置远程仓库</h2> <h3 id="添加远程仓库">添加远程仓库</h3> <p>首先,你需要将 Gitee 和 GitHub 的远程仓库添加到你的本地仓库中。</p> <ol> <li> <p><strong>初始化仓库</strong>: 你需要在一个空文件夹下进行git仓库的本地初始化。 请使用:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> init</span></span></code></pre> </li> <li> <p><strong>添加远程仓库</strong>:</p> <ul> <li> <p><strong>添加 Gitee 远程仓库</strong>:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> remote</span><span style="color:#E6DB74"> add</span><span style="color:#E6DB74"> gitee</span><span style="color:#E6DB74"> https://gitee.com/username/your-repository.git</span></span></code></pre> </li> <li> <p><strong>添加 GitHub 远程仓库</strong>:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> remote</span><span style="color:#E6DB74"> add</span><span style="color:#E6DB74"> github</span><span style="color:#E6DB74"> https://github.com/username/your-repository.git</span></span></code></pre> <p>这里的Github也是你实际的仓库地址。 需要注意git remote add <strong>github</strong> 以及 <strong>gitee</strong> 都被称为别名,那是你在本地的一种命名,实际是只想你后面的地址,所以不必担心这里的命名规不规范,任意就可以。</p> </li> </ul> </li> <li> <p><strong>验证远程仓库</strong>:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> remote</span><span style="color:#AE81FF"> -v</span></span></code></pre> <p>输出应显示你刚才添加的两个远程仓库的 URL。</p> </li> </ol> <h2 id="提交到远程仓库">提交到远程仓库</h2> <h3 id="1-添加和提交更改">1. <strong>添加和提交更改</strong></h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> remote</span><span style="color:#E6DB74"> add</span><span style="color:#E6DB74"> .</span></span></code></pre> <p>添加文件到本地仓库,此时用的是点,代表提交所有的文件,平时也可以使用,因为此提交只会修改与合并。</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> commit</span><span style="color:#AE81FF"> -m</span><span style="color:#E6DB74"> "commit"</span></span></code></pre> <p><strong>commit</strong>是你要提交的备注,这个是可以任意的</p> <h3 id="2-观察本地仓库的分支">2. <strong>观察本地仓库的分支</strong></h3> <img src="/images/gitpush/git_fetch.png"> <p>如上图,此时你本地的分支是master分支,这非常重要! 在本地进行代码更改后,你需要将这些更改添加到 Git 索引中并进行提交。</p> <h3 id="3-推送到仓库">3. <strong>推送到仓库</strong></h3> <p>查看你远程仓库的分支名称: <strong>Github</strong> <img src="/images/gitpush/github_main.png"> <strong>Gitee</strong> <img src="/images/gitpush/gitee_master.png"></p> <p>如上面两个图,此时你的github的分支与gitee的分支名称是不一致的,不一致的情况也没有关系,如果一致则可以直接推送,这里不再赘述。</p> <p><strong>推送到github</strong></p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> push</span><span style="color:#E6DB74"> github</span><span style="color:#E6DB74"> master:main</span></span></code></pre> <p><strong>推送到gitee</strong></p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> push</span><span style="color:#E6DB74"> gitee</span><span style="color:#E6DB74"> master:master</span></span></code></pre> <p>或者</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> push</span><span style="color:#E6DB74"> github</span><span style="color:#F8F8F2"> </span></span></code></pre> <p>由于gitee的分支和我们本地的分支名称一致,所以可以忽略推送分支的名称设置。 推送到github由于分支名称与本地不一致,所以需要指定推送的分支:<strong>master:main</strong> 前面的指定本地的分支,后面指代的远程仓库的分支。</p> <h3 id="4-设置变量名称推送到仓库">4. <strong>设置变量名称推送到仓库</strong></h3> <p>以上的推送方式是一个一个推送的方式,比较适合管理分支,如开发场景和生产场景的情况,倘若不想使用上述场景,只想一键推送,可以设置变量的形式执行命令。</p> <p><strong>1. 进入.git文件并打开config文件</strong> <img src="/images/gitpush/git_config.png"></p> <p><strong>2. 设置全局变量名称</strong> <img src="/images/gitpush/git_alis.png"></p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F8F8F2"> [alias]</span></span> <span class="line"><span style="color:#A6E22E"> pushall</span><span style="color:#E6DB74"> =</span><span style="color:#E6DB74"> "!git push gitee master &#x26;&#x26; git push github master:main"</span></span></code></pre> <p>此时只需要执行就可以实现同时推送的功能</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> pushall</span></span></code></pre> <p>后记:windows打开隐藏文件夹</p> <img src="/images/gitpush/win_hide.png"> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">$</span><span style="color:#E6DB74"> git</span><span style="color:#E6DB74"> push</span><span style="color:#E6DB74"> github</span><span style="color:#AE81FF"> --delete</span><span style="color:#E6DB74"> master</span></span></code></pre> <p>仓库名称 分支名称 => 删除某一个分支(远程分支)</p> <p>删除提交commit 但保留更改</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> reset</span><span style="color:#AE81FF"> --soft</span><span style="color:#E6DB74"> HEAD~1</span></span></code></pre> <p>删除提交commit 并不保留更改</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> reset</span><span style="color:#AE81FF"> --hard</span><span style="color:#E6DB74"> HEAD~1</span></span></code></pre> <p>删除最近的 3 个提交: 保留更改</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> reset</span><span style="color:#AE81FF"> --soft</span><span style="color:#E6DB74"> HEAD~3</span></span></code></pre> <p>设置默认分支:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> branch</span><span style="color:#AE81FF"> --set-upstream-to=github/main</span><span style="color:#E6DB74"> master</span></span></code></pre> <p>master指的是本地分支 单独拉取分支:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">git</span><span style="color:#E6DB74"> pull</span><span style="color:#E6DB74"> github</span><span style="color:#E6DB74"> main</span></span></code></pre></content:encoded>
<enclosure url="https://layfz.com/images/uploads/gitpush.png" length="0" type="image/jpeg"/>
</item>
<item>
<title>HostDare CN2-GIA虚假宣传维权记录:一个技术人的较真之路</title>
<link>https://layfz.com/posts/hostdare-cn2/</link>
<guid isPermaLink="true">https://layfz.com/posts/hostdare-cn2/</guid>
<description>这是一份详实的技术维权记录,记录了一位开发者在购买HostDare的"CN2-GIA"VPS服务时遭遇虚假宣传,并通过专业技术手段和法律途径进行维权的完整过程。</description>
<pubDate>Sat, 24 May 2025 10:59:00 GMT</pubDate>
<content:encoded><h2 id="前言">前言</h2> <p>作为一名技术人员,我最近的项目需要扩展国际业务寻找合适的服务器分发节点。经过多方比较,我选择了HostDare的”CN2-GIA NVMe KVM VPS”服务,期望通过优质的CN2线路来保障中美之间的网络质量。</p> <p>然而,这次购买经历让我深刻体会到了什么叫”理想很丰满,现实很骨感”。更重要的是,我决定用技术手段和法律武器,为自己和其他消费者讨个说法。</p> <h2 id="详细沟通过程">详细沟通过程</h2> <p><img src="/images/uploads/cn2_5.png" alt=""></p> <h2 id="业务需求背景">业务需求背景</h2> <h3 id="项目需求">项目需求</h3> <p>我的项目正在开展国际业务,需要在全球多个节点部署服务器来实现:</p> <ul> <li>API服务的就近访问</li> <li>内容分发网络(CDN)</li> <li>数据同步和备份</li> <li>跨境网络加速</li> </ul> <h3 id="技术要求">技术要求</h3> <p>对于中美之间的网络节点,我们的核心要求是:</p> <ul> <li><strong>低延迟</strong>:RTT &#x3C; 150ms</li> <li><strong>高稳定性</strong>:丢包率 &#x3C; 1%</li> <li><strong>充足带宽</strong>:sustained 100Mbps+</li> <li><strong>抗干扰能力</strong>:晚高峰期间性能不能严重下降</li> </ul> <h3 id="为什么选择cn2-gia">为什么选择CN2-GIA</h3> <p>在调研过程中,我了解到:</p> <ul> <li><strong>CN2-GIA</strong>:中国电信的顶级国际网络,去程回程都走59.43.x.x优质骨干网</li> <li><strong>CN2-GT</strong>:混合路由,部分优化,价格较低</li> <li><strong>普通163</strong>:传统国际线路,晚高峰严重拥堵</li> </ul> <p>基于业务的高要求,我们决定投资CN2-GIA级别的服务器。</p> <h2 id="hostdare购买经历">HostDare购买经历</h2> <h3 id="产品选择">产品选择</h3> <p>经过对比,我选择了HostDare的CSSD0套餐:</p> <ul> <li> <p><strong>产品名称</strong>:CN2-GIA NVMe KVM VPS USA</p> </li> <li> <p><strong>配置</strong>:1 vCPU Core, 768MB RAM, 10GB NVMe SSD</p> </li> <li> <p><strong>网络</strong>:250GB月流量,30Mbps CN2 GIA线路</p> </li> <li> <p><strong>价格</strong>:$35.99 USD/年</p> </li> <li> <p><strong>位置</strong>:洛杉矶数据中心</p> <p><img src="/images/uploads/cn2_3.png" alt="HostDare_cn2"></p> </li> </ul> <p><img src="/images/uploads/cn2_2.png" alt=""></p> <p><img src="/images/uploads/cn2_1.png" alt=""></p> <h2 id="性能测试与问题发现">性能测试与问题发现</h2> <h3 id="初步测试结果">初步测试结果</h3> <p>服务器部署完成后,我立即进行了网络性能测试:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"># x-ui面板速度测试</span></span> <span class="line"><span style="color:#A6E22E">测试结果:约30Mbps</span></span> <span class="line"></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 基础延迟测试</span></span> <span class="line"><span style="color:#A6E22E">ping</span><span style="color:#AE81FF"> 114.114</span><span style="color:#E6DB74">.114.114</span></span> <span class="line"><span style="color:#A6E22E">平均延迟:180-200ms</span></span> <span class="line"></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 晚高峰测试</span></span> <span class="line"><span style="color:#A6E22E">晚上8-11点:严重卡顿,代理基本不可用</span></span></code></pre> <h3 id="问题分析">问题分析</h3> <p>这个结果让我很困惑:</p> <ol> <li><strong>速度偏低</strong>:30Mbps远低于真正CN2-GIA应有的性能</li> <li><strong>延迟偏高</strong>:180-200ms超出了CN2-GIA的标准范围</li> <li><strong>晚高峰拥堵</strong>:这是典型的163线路特征</li> </ol> <h3 id="路由追踪测试">路由追踪测试</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"># 去程路由测试</span></span> <span class="line"><span style="color:#A6E22E">root@root:~#</span><span style="color:#E6DB74"> traceroute</span><span style="color:#AE81FF"> 114.114</span><span style="color:#E6DB74">.114.114</span></span> <span class="line"><span style="color:#A6E22E">traceroute</span><span style="color:#E6DB74"> to</span><span style="color:#AE81FF"> 114.114</span><span style="color:#E6DB74">.114.114</span><span style="color:#F8F8F2"> (114.114.114.114), 30 hops max, 60 byte packets</span></span> <span class="line"><span style="color:#A6E22E"> 1</span><span style="color:#AE81FF"> 193.22</span><span style="color:#E6DB74">.152.1</span><span style="color:#F8F8F2"> (193.22.152.1) 0.560 ms 0.643 ms 0.735 ms</span></span> <span class="line"><span style="color:#A6E22E"> 2</span><span style="color:#AE81FF"> 172.16</span><span style="color:#E6DB74">.0.1</span><span style="color:#F8F8F2"> (172.16.0.1) 0.502 ms </span></span> <span class="line"><span style="color:#A6E22E"> 3</span><span style="color:#FD971F"> *</span><span style="color:#FD971F"> **</span></span> <span class="line"><span style="color:#A6E22E"> 4</span><span style="color:#FD971F"> *</span><span style="color:#E6DB74"> ce-2-3-1.a04.lsanca07.us.bb.gin.ntt.net</span><span style="color:#F8F8F2"> (128.241.2.173) 0.654 ms </span><span style="color:#F92672">*</span></span> <span class="line"><span style="color:#A6E22E"> 5</span><span style="color:#E6DB74"> be3360.ccr42.lax01.atlas.cogentco.com</span><span style="color:#F8F8F2"> (154.54.25.149) 1.387 ms </span></span> <span class="line"><span style="color:#A6E22E"> 6</span><span style="color:#FD971F"> *</span><span style="color:#E6DB74"> be5992.ccr82.sjc13.atlas.cogentco.com</span><span style="color:#F8F8F2"> (154.54.169.1) 11.072 ms 10.367 ms</span></span> <span class="line"><span style="color:#A6E22E"> 7</span><span style="color:#E6DB74"> mx97-237.oafishthroat.com</span><span style="color:#F8F8F2"> (23.236.97.237) 6.881 ms </span><span style="color:#F92672">*</span><span style="color:#F8F8F2"> be6009.ccr41.sjc03.atlas.cogentco.com (</span><span style="color:#A6E22E">154.54.169.38</span><span style="color:#F8F8F2">) 10.617 ms</span></span> <span class="line"><span style="color:#A6E22E"> 8</span><span style="color:#AE81FF"> 38.104</span><span style="color:#E6DB74">.138.106</span><span style="color:#F8F8F2"> (38.104.138.106) 18.862 ms 18.897 ms 19.677 ms</span></span> <span class="line"><span style="color:#A6E22E"> 9</span><span style="color:#E6DB74"> ae-1.zenlayer-c3-networks.sngpsi07.sg.bb.gin.ntt.net</span><span style="color:#F8F8F2"> (116.51.26.90) 197.796 ms 202.97.6.1 (</span><span style="color:#A6E22E">202.97.6.1</span><span style="color:#F8F8F2">) 141.232 ms ae-1.zenlayer-c3-networks.sngpsi07.sg.bb.gin.ntt.net (</span><span style="color:#A6E22E">116.51.26.90</span><span style="color:#F8F8F2">) 199.889 ms</span></span> <span class="line"><span style="color:#A6E22E">10</span><span style="color:#AE81FF"> 98.98</span><span style="color:#E6DB74">.126.243</span><span style="color:#F8F8F2"> (98.98.126.243) 200.827 ms 98.98.126.245 (</span><span style="color:#A6E22E">98.98.126.245</span><span style="color:#F8F8F2">) 182.115 ms </span><span style="color:#F92672">*</span></span> <span class="line"><span style="color:#A6E22E">11</span><span style="color:#FD971F"> *</span><span style="color:#FD971F"> **</span></span> <span class="line"><span style="color:#A6E22E">12</span><span style="color:#AE81FF"> 202.97</span><span style="color:#E6DB74">.72.94</span><span style="color:#F8F8F2"> (202.97.72.94) 141.466 ms </span></span> <span class="line"><span style="color:#A6E22E">13</span><span style="color:#FD971F"> *</span><span style="color:#AE81FF"> 202.102</span><span style="color:#E6DB74">.73.14</span><span style="color:#F8F8F2"> (202.102.73.14) 164.746 ms 202.102.69.98 (</span><span style="color:#A6E22E">202.102.69.98</span><span style="color:#F8F8F2">) 151.943 ms</span></span></code></pre> <h3 id="as信息查询">AS信息查询</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">root@root:~#</span><span style="color:#E6DB74"> curl</span><span style="color:#E6DB74"> ipinfo.io</span></span> <span class="line"><span style="color:#F8F8F2">{</span></span> <span class="line"><span style="color:#A6E22E"> "ip"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "2.59.151.75",</span></span> <span class="line"><span style="color:#A6E22E"> "city"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "Los Angeles",</span></span> <span class="line"><span style="color:#A6E22E"> "region"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "California",</span></span> <span class="line"><span style="color:#A6E22E"> "country"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "US",</span></span> <span class="line"><span style="color:#A6E22E"> "loc"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "34.0559,-118.2666",</span></span> <span class="line"><span style="color:#A6E22E"> "org"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "AS40065 CNSERVERS LLC",</span></span> <span class="line"><span style="color:#A6E22E"> "postal"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "90017",</span></span> <span class="line"><span style="color:#A6E22E"> "timezone"</span><span style="color:#66D9EF">:</span><span style="color:#E6DB74"> "America/Los_Angeles"</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <h3 id="回程路由验证">回程路由验证</h3> <p>使用第三方工具(tools.ipip.net)测试回程路由,结果显示:</p> <ul> <li>全程走202.97.x.x网段(163骨干网)</li> <li>AS4134(中国电信163)而非AS4809(CN2)</li> <li>延迟180-220ms,明显超标</li> </ul> <h3 id="技术结论">技术结论</h3> <p><strong>这根本不是CN2-GIA线路!</strong></p> <p><strong>证据确凿:</strong></p> <ul> <li>❌ 去程走NTT→Cogent→163,没有CN2标识</li> <li>❌ 回程全程202.97.x.x,典型163线路</li> <li>❌ AS40065 CNSERVERS LLC,不是中国电信</li> <li>❌ 无任何59.43.x.x网段出现</li> <li>❌ 晚高峰严重拥堵,符合163特征</li> </ul> <h2 id="客服沟通官方承认虚假宣传">客服沟通:官方承认虚假宣传</h2> <h3 id="初次投诉">初次投诉</h3> <p>我向HostDare客服详细说明了技术测试结果,要求解释为什么实际线路与广告不符。</p> <h3 id="客服回复原文">客服回复(原文)</h3> <blockquote> <p>“Currently, we provide one-way GIA as otherwise DDoS attacks make the network unstable. We only optimize to home ISP users in China. From your VPS to your home ISP network IP, it is GIA uplink. From your home ISP to your VPS, it is non-GIA uplink like GTT etc. Also note that we do not optimize uplinks to servers in China. So if you test from a testing website such as ipip.net which uses servers network, it will show non-GIA uplinks.”</p> </blockquote> <h3 id="回复分析">回复分析</h3> <p>这个回复实际上是<strong>自我定罪的证据</strong>:</p> <ol> <li><strong>承认只提供”单向GIA”</strong> - 这不是真正的CN2-GIA</li> <li><strong>承认回程”非GIA上行链路如GTT”</strong> - 证明回程走普通线路</li> <li><strong>承认有显著限制</strong> - 但网站广告从未提及</li> </ol> <p><strong>问题核心:</strong></p> <ul> <li><strong>广告宣传</strong>:CN2-GIA(暗示双向优化)</li> <li><strong>实际提供</strong>:单向部分优化(等同于CN2-GT)</li> <li><strong>价格收费</strong>:按CN2-GIA标准收费</li> <li><strong>实际体验</strong>:163线路水平</li> </ul> <p>这就是典型的<strong>虚假宣传</strong>!</p> <h2 id="维权策略制定">维权策略制定</h2> <p>作为一名理性的技术人员,我决定通过合法途径维护自己的权益,并为其他消费者发声。</p> <h3 id="维权目标">维权目标</h3> <ol> <li><strong>获得全额退款</strong> $35.99</li> <li><strong>曝光虚假宣传</strong> 警示其他用户</li> <li><strong>推动行业规范</strong> 促进诚信经营</li> </ol> <h3 id="证据收集">证据收集</h3> <p>我系统性地收集了以下证据:</p> <ul> <li>✅ 产品页面截图(CN2-GIA宣传)</li> <li>✅ 客服邮件往来(承认服务不符)</li> <li>✅ 技术测试结果(路由、AS、性能)</li> <li>✅ 支付凭证(支付宝交易记录)</li> <li>✅ 第三方验证(多个测试平台)</li> </ul> <h2 id="维权行动实施方案国际业务的服务商最害怕支付系统被冻结一般第一步就可以解决">维权行动实施方案(国际业务的服务商最害怕支付系统被冻结,一般第一步就可以解决)</h2> <h3 id="第一阶段友好协商">第一阶段:友好协商</h3> <p>发送正式投诉邮件,详细说明问题并要求退款:</p> <p><strong>邮件要点:</strong></p> <ul> <li>明确指出虚假宣传事实</li> <li>提供技术证据支撑</li> <li>给出合理解决方案</li> <li>设定回复时间期限</li> </ul> <h3 id="第二阶段官方投诉">第二阶段:官方投诉</h3> <p>如果协商无效,将启动以下官方投诉:</p> <p><strong>国内渠道:</strong></p> <ol> <li><strong>12315消费者投诉平台</strong> - 权威性最强</li> <li><strong>12377网络举报中心</strong> - 针对虚假广告</li> <li><strong>支付宝交易争议</strong> - 商品与描述不符</li> <li><strong>地方消协投诉</strong> - 本地消费者保护</li> </ol> <p><strong>国际渠道:</strong></p> <ol> <li><strong>FTC举报</strong> - 美国联邦贸易委员会</li> <li><strong>BBB投诉</strong> - 美国商业改进局</li> <li><strong>ICANN投诉</strong> - 针对域名相关欺诈</li> </ol> <h3 id="第三阶段公开曝光">第三阶段:公开曝光</h3> <p>通过合法渠道进行公开曝光:</p> <p><strong>技术社区:</strong></p> <ul> <li>WebHostingTalk (WHT) 论坛</li> <li>HostLoc 中文主机论坛</li> <li>Reddit r/webhosting 版块</li> <li>V2EX 技术讨论区</li> </ul> <p><strong>评价平台:</strong></p> <ul> <li>Google Reviews</li> <li>Trustpilot</li> <li>Yelp Business</li> </ul> <p><strong>自媒体平台:</strong></p> <ul> <li>技术博客详细分析</li> <li>YouTube 视频教程</li> <li>知乎专栏文章</li> </ul> <h3 id="第四阶段法律行动">第四阶段:法律行动</h3> <p>如果前三阶段无效,考虑法律途径:</p> <ol> <li><strong>小额诉讼</strong> - 针对$35.99金额</li> <li><strong>集体诉讼调查</strong> - 联合其他受害者</li> <li><strong>国际仲裁</strong> - WIPO等机构</li> </ol> <h2 id="最后通牒">最后通牒</h2> <p>基于以上准备,我向HostDare发送了最后通牒:</p> <blockquote> <p>“Dear HostDare Management,</p> </blockquote> <p>This is my FINAL ULTIMATUM regarding the false advertising of your CN2-GIA service.</p> <p>SUMMARY OF FACTS:</p> <ol> <li>I purchased “CN2-GIA NVMe KVM VPS USA” for $35.99 USD</li> <li>Your product page clearly advertised “CN2-GIA” premium network routing</li> <li>Your technical team admitted via email that you only provide “one-way GIA” and “non-GIA uplink like GTT”</li> <li>This constitutes false advertising - you charged CN2-GIA prices for a CN2-GT level service</li> </ol> <p>YOUR WRITTEN ADMISSION: Your email stated: “we provide one-way GIA” and “non-GIA uplink like GTT” - this is NOT the CN2-GIA service advertised and purchased.</p> <p>TECHNICAL EVIDENCE:</p> <ul> <li>AS40065 CNSERVERS instead of AS4809 China Telecom CN2</li> <li>Return routing via 202.97.x.x (163 backbone) instead of 59.43.x.x (CN2)</li> <li>Performance significantly below CN2-GIA standards</li> <li>Third-party traceroute confirms non-CN2 routing</li> </ul> <p>FINAL DEMAND: Process a full refund of $35.99 USD within 24 HOURS of this email.</p> <p>ACTIONS TO BE TAKEN IF NO REFUND:</p> <p>Immediate Actions (within 48 hours):</p> <ol> <li>Payment Dispute Filed - Alipay transaction dispute for “goods not as described”</li> <li>Regulatory Complaints - Filed with China’s 12315 Consumer Protection Agency</li> <li>Internet Fraud Report - Submitted to 12377.cn for false advertising</li> </ol> <p>Public Exposure (within 7 days): 4. WebHostingTalk Forum - Detailed false advertising exposure post 5. HostLoc Forum - Warning post to Chinese VPS community 6. Google Reviews - 1-star review with complete evidence 7. Trustpilot - Detailed negative review with documentation 8. Reddit - Posts in r/webhosting and r/VPS communities 9. YouTube Video - Complete technical analysis and scam exposure</p> <p>Legal/Regulatory Actions (within 14 days): 10. FTC Complaint - False advertising report to US Federal Trade Commission 11. BBB Complaint - Better Business Bureau formal complaint 12. State AG Report - Consumer fraud report to relevant state attorney general 13. Class Action Investigation - Contact other affected customers for group action</p> <p>International Arbitration: 14. WIPO Complaint - International commercial dispute resolution 15. Small Claims Court - Filing in appropriate US jurisdiction</p> <p>YOUR REPUTATION AT STAKE: This false advertising scandal will permanently damage HostDare’s reputation in the hosting community. The evidence is overwhelming and your own admission makes this case unwinnable for you.</p> <p>COST-BENEFIT ANALYSIS:</p> <ul> <li>Refund cost: $35.99</li> <li>Reputation damage cost: Potentially hundreds of thousands in lost business</li> <li>Legal defense cost:Thousands of dollars minimum</li> <li>Time cost: Months of dealing with complaints and disputes</li> </ul> <p>LAST CHANCE: This is your final opportunity to resolve this matter quietly. After 24 hours, I will proceed with ALL planned actions simultaneously.</p> <p>REFUND PROCESSING: Reply to this email confirming refund approval within 24 hours. Any delays or excuses will trigger immediate escalation.</p> <p>EVIDENCE PACKAGE: I have compiled a complete evidence package including:</p> <ul> <li>Original product advertisements</li> <li>Your email admissions</li> <li>Technical test results</li> <li>Payment receipts</li> <li>All communications</li> </ul> <p>This evidence package will be shared with all regulatory bodies, review platforms, and legal entities.</p> <p>NO FURTHER NEGOTIATIONS: I am not interested in:</p> <ul> <li>Technical explanations</li> <li>Service credits</li> <li>Account upgrades</li> <li>Partial refunds</li> </ul> <p>Only a FULL $35.99 refund is acceptable.</p> <p>Failure to respond or refund will result in immediate implementation of all threatened actions.</p> <p>Your move, HostDare.</p> <h2 id="best-regardsane-kobyaccount-anekoby95gmailcomorder-cssd0---3599-usdserver-ip-25915175">Best regards, Ane Koby Account: <a href="mailto:anekoby95@gmail.com">anekoby95@gmail.com</a> Order: CSSD0 - $35.99 USD Server IP: 2.59.151.75</h2> <p>NOTICE:This email and all related communications are being recorded and will be used as evidence in any legal or regulatory proceedings.”</p> <p>在这样的施压下我们很快就得到了回复,并收到退款,这也很容易理解,毕竟国际业务,最重要的就是收款,国际业务提供商也不想因为这样的原因导致风控,从而失去更多用户。</p> <h3 id="核心内容">核心内容</h3> <ul> <li><strong>24小时期限</strong> 退款最后机会</li> <li><strong>14项具体行动</strong> 详细的后续措施</li> <li><strong>成本效益分析</strong> 让对方权衡利弊</li> <li><strong>证据包威胁</strong> 完整的法律证据链</li> </ul> <h2 id="技术人的思考">技术人的思考</h2> <h3 id="为什么要较真">为什么要较真?</h3> <p>很多人可能觉得为了$35.99这么折腾不值得,但我认为:</p> <ol> <li><strong>原则问题</strong> - 虚假宣传必须受到惩罚</li> <li><strong>行业净化</strong> - 推动诚信经营风气</li> <li><strong>技术责任</strong> - 用专业知识保护消费者</li> <li><strong>成本考虑</strong> - 时间成本vs社会价值</li> </ol> <h3 id="技术验证的重要性">技术验证的重要性</h3> <p>普通用户可能只是感觉”网络慢”,但技术人员可以:</p> <ul> <li>通过路由追踪发现问题根源</li> <li>用AS信息验证网络归属</li> <li>对比技术标准识别虚假宣传</li> <li>提供无可辩驳的技术证据</li> </ul> <h3 id="维权策略的技术思维">维权策略的技术思维</h3> <ol> <li><strong>系统性收集证据</strong> - 如同debug程序</li> <li><strong>多路径并行执行</strong> - 分布式维权策略</li> <li><strong>持续监控反馈</strong> - 根据响应调整策略</li> <li><strong>文档化整个过程</strong> - 便于复现和分享</li> </ol> <h2 id="给其他技术人的建议">给其他技术人的建议</h2> <h3 id="购买vps前的技术验证">购买VPS前的技术验证</h3> <ol> <li><strong>查看真实用户评测</strong> - 不要只看官方宣传</li> <li><strong>要求试用或退款保证</strong> - 技术产品需要实测</li> <li><strong>了解网络基础知识</strong> - 区分CN2-GIA/GT/163</li> <li><strong>使用第三方测试工具</strong> - 验证商家宣传</li> </ol> <h3 id="遇到问题时的处理原则">遇到问题时的处理原则</h3> <ol> <li><strong>技术证据先行</strong> - 用数据说话</li> <li><strong>保存所有证据</strong> - 邮件、截图、日志</li> <li><strong>理性分析成本</strong> - 时间vs收益</li> <li><strong>坚持合法维权</strong> - 不要情绪化</li> </ol> <h3 id="推荐的测试工具">推荐的测试工具</h3> <ul> <li><strong>路由追踪</strong>: traceroute, mtr, besttrace</li> <li><strong>AS查询</strong>: ipinfo.io, whois</li> <li><strong>第三方测试</strong>: tools.ipip.net, 17ce.com</li> <li><strong>性能测试</strong>: speedtest-cli, iperf3</li> </ul> <h2 id="行业呼吁">行业呼吁</h2> <h3 id="对vps商家的建议">对VPS商家的建议</h3> <ol> <li><strong>诚实宣传</strong> - 如实描述技术指标</li> <li><strong>透明定价</strong> - 清楚说明服务级别差异</li> <li><strong>技术支持</strong> - 能够解答专业问题</li> <li><strong>退款保证</strong> - 对技术产品提供试用期</li> </ol> <h3 id="对监管部门的建议">对监管部门的建议</h3> <ol> <li><strong>制定技术标准</strong> - 明确CN2等概念定义</li> <li><strong>加强跨境监管</strong> - 海外商家的广告审查</li> <li><strong>支持技术维权</strong> - 重视专业人士的举报</li> <li><strong>行业规范化</strong> - 推动诚信经营体系</li> </ol> <h2 id="结语">结语</h2> <p>作为技术人员,我们有责任用专业知识来维护行业的健康发展。虚假宣传不仅损害了消费者利益,也破坏了整个行业的信任基础。</p> <p>这次维权行动不仅仅是为了$35.99,更是为了:</p> <ul> <li>让技术诚信成为行业标准</li> <li>保护其他消费者免受欺骗</li> <li>推动VPS行业的规范发展</li> </ul> <p>我会持续更新这个维权过程,希望能为遇到类似问题的朋友提供参考。同时,也欢迎其他技术人员分享自己的经历,让我们一起净化这个行业。</p> <h2 id="写到最后">写到最后</h2> <p>最后也是成功追回</p> <p><img src="/images/uploads/cn2_4.png" alt=""></p> <p><strong>技术改变世界,诚信铸就未来。</strong></p> <hr> <p><strong>声明:本文所有内容基于真实经历,技术数据真实有效。文章仅代表个人观点,不构成法律建议。如需专业法律意见,请咨询执业律师。</strong></p></content:encoded>
<enclosure url="https://layfz.com/images/uploads/cn2.png" length="0" type="image/jpeg"/>
</item>
<item>
<title>🚀 内网项目踩坑实录:从 HTTP 到 HTTPS 的血泪史</title>
<link>https://layfz.com/posts/intranet-http-to-https/</link>
<guid isPermaLink="true">https://layfz.com/posts/intranet-http-to-https/</guid>
<description>一个关于 Cookie 安全策略和 Deno KV 的技术冒险故事</description>
<pubDate>Thu, 03 Jul 2025 16:35:00 GMT</pubDate>
<content:encoded><h2 id="-tldrtoo-long-didnt-read-概述">📋 TL;DR(Too Long; Didn’t Read)-概述</h2> <ul> <li><strong>问题</strong>: 微信公众号爬虫在内网部署时无法登录</li> <li><strong>原因</strong>: HTTP 环境下无法设置 Secure Cookie + Deno KV 生产环境依赖</li> <li><strong>解决</strong>: 部署自签名 HTTPS + 删除用户存储功能</li> <li><strong>结果</strong>: 功能完美运行 ✨</li> </ul> <hr> <h2 id="-项目背景">🎯 项目背景</h2> <h3 id="需求简述">需求简述</h3> <p>开发一个微信公众号文章爬虫工具,供公司内部使用。由于<strong>数据安全</strong>要求,必须部署在内网环境。</p> <h3 id="技术栈">技术栈</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672">Framework</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">Nuxt.js 3</span></span> <span class="line"><span style="color:#F92672">Runtime</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">Node.js</span></span> <span class="line"><span style="color:#F92672">Proxy</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">Nginx</span></span> <span class="line"><span style="color:#F92672">Panel</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">宝塔面板</span></span> <span class="line"><span style="color:#F92672">Environment</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">Ubuntu 22.04 + 内网 172.19.23.27</span></span></code></pre> <h3 id="预期-vs-现实">预期 vs 现实</h3> <table><thead><tr><th>环境</th><th>预期</th><th>现实</th></tr></thead><tbody><tr><td>公网测试</td><td>✅ 完美运行</td><td>✅ 确实完美</td></tr><tr><td>内网部署</td><td>🤔 应该没问题吧</td><td>💥 完全不能用</td></tr></tbody></table> <hr> <h2 id="-问题一cookie-安全策略地狱">🔥 问题一:Cookie 安全策略地狱</h2> <h3 id="-症状描述">💀 症状描述</h3> <p>部署到内网后,用户扫码登录时:</p> <ul> <li>二维码可以显示 ✅</li> <li>扫码成功 ✅</li> <li>点击登录… <strong>毫无反应</strong> ❌</li> </ul> <p>宝塔配置如下:</p> <p><img src="/images/uploads/baota_20250703.png" alt=""></p> <p>通过反向代理打到本地3000端口,实现内网访问</p> <h3 id="-错误现场">🔍 错误现场</h3> <p>打开 F12,没有任何报错,甚至无法定位问题,此时的我非常焦虑,无论是多级反向代理还是跨域配置我都尝试了,并且长达两个小时都无法解决,此时我在本机ubantu的firefox才看到具体的报错信息,如图所示:</p> <p><img src="/images/uploads/fullsizerender.jpg" alt=""></p> <p>这真的让我非常难顶!!!排查了这么久,firefox直接就帮我定位了问题。(:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">🚫</span><span style="color:#E6DB74"> Cookie</span><span style="color:#E6DB74"> "slave_user"</span><span style="color:#E6DB74"> has</span><span style="color:#E6DB74"> been</span><span style="color:#E6DB74"> rejected</span><span style="color:#E6DB74"> because</span><span style="color:#E6DB74"> a</span><span style="color:#E6DB74"> non-HTTPS</span><span style="color:#E6DB74"> cookie</span><span style="color:#E6DB74"> can't be set as "secure"</span></span> <span class="line"><span style="color:#E6DB74">🚫 Cookie "master_sid" has been rejected because a non-HTTPS cookie can't</span><span style="color:#E6DB74"> be</span><span style="color:#E6DB74"> set</span><span style="color:#E6DB74"> as</span><span style="color:#E6DB74"> "secure"</span><span style="color:#F8F8F2"> </span></span> <span class="line"><span style="color:#A6E22E">🚫</span><span style="color:#E6DB74"> Cookie</span><span style="color:#E6DB74"> "bizuin"</span><span style="color:#E6DB74"> has</span><span style="color:#E6DB74"> been</span><span style="color:#E6DB74"> rejected</span><span style="color:#E6DB74"> because</span><span style="color:#E6DB74"> a</span><span style="color:#E6DB74"> non-HTTPS</span><span style="color:#E6DB74"> cookie</span><span style="color:#E6DB74"> can't be set as "secure"</span></span> <span class="line"><span style="color:#E6DB74">🚫 Cookie "data_ticket" has been rejected because a non-HTTPS cookie can't</span><span style="color:#E6DB74"> be</span><span style="color:#E6DB74"> set</span><span style="color:#E6DB74"> as</span><span style="color:#E6DB74"> "secure"</span></span> <span class="line"><span style="color:#66D9EF">...</span><span style="color:#F8F8F2"> (重复 </span><span style="color:#E6DB74">N</span><span style="color:#E6DB74"> 遍</span><span style="color:#F8F8F2">)</span></span></code></pre> <blockquote> <p>💡 <strong>第一反应</strong>: 这什么鬼?为什么公网好好的,内网就不行?</p> </blockquote> <h3 id="️-深入调查">🕵️ 深入调查</h3> <h4 id="问题根源">问题根源</h4> <p>微信公众平台返回的 Cookie 长这样:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672">Set-Cookie:</span><span style="color:#E6DB74"> slave_user=xxx; Path=/; Secure; SameSite=None; HttpOnly</span></span> <span class="line"><span style="color:#F92672">Set-Cookie:</span><span style="color:#E6DB74"> master_sid=yyy; Path=/; Secure; SameSite=None; HttpOnly</span></span></code></pre> <p>关键在于 <code>Secure</code> 标志!</p> <h4 id="浏览器安全策略">浏览器安全策略</h4> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672">if</span><span style="color:#F8F8F2"> (cookie.hasSecureFlag </span><span style="color:#F92672">&#x26;&#x26;</span><span style="color:#F8F8F2"> location.protocol </span><span style="color:#F92672">!==</span><span style="color:#E6DB74"> 'https:'</span><span style="color:#F8F8F2">) {</span></span> <span class="line"><span style="color:#F8F8F2"> console.</span><span style="color:#A6E22E">error</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">'🚫 Cookie rejected: non-HTTPS environment'</span><span style="color:#F8F8F2">)</span></span> <span class="line"><span style="color:#F92672"> return</span><span style="color:#AE81FF"> false</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <h4 id="环境对比">环境对比</h4> <table><thead><tr><th>环境</th><th>协议</th><th>Cookie 设置</th><th>结果</th></tr></thead><tbody><tr><td>公网</td><td><code>https://</code></td><td>✅ 允许 Secure Cookie</td><td>🎉 登录成功</td></tr><tr><td>内网</td><td><code>http://</code></td><td>❌ 拒绝 Secure Cookie</td><td>💥 登录失败</td></tr></tbody></table> <h3 id="-解决思路">💡 解决思路</h3> <p>既然浏览器严格要求 HTTPS 才能设置 Secure Cookie,而微信登录又必须依赖这些 Cookie,那只有一个办法:</p> <p><strong>给内网环境配置 HTTPS!</strong></p> <hr> <h2 id="️-https-部署实战">🛠️ HTTPS 部署实战</h2> <h3 id="step-1-生成自签名证书">Step 1: 生成自签名证书</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"># 一键生成支持多地址的证书</span></span> <span class="line"><span style="color:#A6E22E">openssl</span><span style="color:#E6DB74"> req</span><span style="color:#AE81FF"> -x509</span><span style="color:#AE81FF"> -newkey</span><span style="color:#E6DB74"> rsa:2048</span><span style="color:#AE81FF"> \</span></span> <span class="line"><span style="color:#AE81FF"> -keyout</span><span style="color:#E6DB74"> server.key</span><span style="color:#AE81FF"> -out</span><span style="color:#E6DB74"> server.pem</span><span style="color:#AE81FF"> \</span></span> <span class="line"><span style="color:#AE81FF"> -days</span><span style="color:#AE81FF"> 365</span><span style="color:#AE81FF"> -nodes</span><span style="color:#AE81FF"> \</span></span> <span class="line"><span style="color:#AE81FF"> -config</span><span style="color:#E6DB74"> &#x3C;(</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> '[dn]'</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> CN=</span><span style="color:#AE81FF">172.19</span><span style="color:#E6DB74">.23.27</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> '[req]'</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> distinguished_name = dn</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> '[EXT]'</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> subjectAltName=@alt_names</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> '[alt_names]'</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> DNS.1=localhost</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> DNS.2=wechatccc.com</span></span> <span class="line"><span style="color:#66D9EF"> echo</span><span style="color:#E6DB74"> IP.1=</span><span style="color:#AE81FF">172.19</span><span style="color:#E6DB74">.23.27</span></span> <span class="line"><span style="color:#E6DB74"> )</span><span style="color:#AE81FF"> -extensions</span><span style="color:#E6DB74"> EXT</span></span></code></pre> <h3 id="step-2-宝塔面板配置">Step 2: 宝塔面板配置</h3> <ol> <li>网站设置 → SSL 选项</li> <li>选择”其他证书”</li> <li>将 <code>server.pem</code> 内容粘贴到”证书(PEM格式)”</li> <li>将 <code>server.key</code> 内容粘贴到”密钥(KEY)”</li> <li>保存并启用 HTTPS</li> </ol> <h3 id="step-3-nginx-配置">Step 3: Nginx 配置</h3> <p>宝塔会自动生成配置,核心部分:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#66D9EF;font-style:italic">server</span><span style="color:#F8F8F2"> {</span></span> <span class="line"><span style="color:#F92672"> listen </span><span style="color:#F8F8F2">80;</span></span> <span class="line"><span style="color:#F92672"> listen </span><span style="color:#F8F8F2">443 ssl http2;</span></span> <span class="line"><span style="color:#F92672"> server_name </span><span style="color:#F8F8F2">172.19.23.27 wechatccc.com;</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> # 🔒 SSL 证书配置</span></span> <span class="line"><span style="color:#F92672"> ssl_certificate </span><span style="color:#F8F8F2">/www/server/panel/vhost/cert/domain/fullchain.pem;</span></span> <span class="line"><span style="color:#F92672"> ssl_certificate_key </span><span style="color:#F8F8F2">/www/server/panel/vhost/cert/domain/privkey.pem;</span></span> <span class="line"><span style="color:#F92672"> ssl_protocols </span><span style="color:#F8F8F2">TLSv1.2 TLSv1.3;</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> # 🔄 反向代理到 Nuxt 应用</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> location</span><span style="color:#F8F8F2"> / {</span></span> <span class="line"><span style="color:#F92672"> proxy_pass </span><span style="color:#F8F8F2">http://127.0.0.1:3000;</span></span> <span class="line"><span style="color:#F92672"> proxy_set_header </span><span style="color:#F8F8F2">Host $http_host;</span></span> <span class="line"><span style="color:#F92672"> proxy_set_header </span><span style="color:#F8F8F2">X-Forwarded-Proto $scheme;</span></span> <span class="line"><span style="color:#88846F"> # ... 其他标准代理配置</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <h3 id="-问题一解决">🎯 问题一解决</h3> <p>访问 <code>https://172.19.23.27</code>:</p> <ul> <li>⚠️ 浏览器显示”不安全”警告(自签名证书)</li> <li>✅ 点击”高级” → “继续访问”</li> <li>✅ Cookie 设置成功,登录流程恢复正常</li> </ul> <hr> <h2 id="-问题二deno-kv-生产环境炸弹">🔥 问题二:Deno KV 生产环境炸弹</h2> <h3 id="-新的爆炸">💣 新的爆炸</h3> <p>刚解决 Cookie 问题,结果点击登录后又炸了:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> "statusCode"</span><span style="color:#F8F8F2">: </span><span style="color:#AE81FF">500</span><span style="color:#F8F8F2">,</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> "message"</span><span style="color:#F8F8F2">: </span><span style="color:#CFCFC2">"Could not find a Deno KV for production, make sure to deploy on Deno Deploy."</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <blockquote> <p>🤔 <strong>心理活动</strong>: 什么?我明明用的是 Node.js,怎么扯到 Deno 了?</p> </blockquote> <h3 id="-代码考古">🔍 代码考古</h3> <p>发现用户存储模块有这样的逻辑:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672">export</span><span style="color:#F92672"> async</span><span style="color:#66D9EF;font-style:italic"> function</span><span style="color:#A6E22E"> createUser</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F;font-style:italic">user</span><span style="color:#F92672">:</span><span style="color:#A6E22E;text-decoration:underline"> UserEntry</span><span style="color:#F8F8F2">)</span><span style="color:#F92672">:</span><span style="color:#A6E22E;text-decoration:underline"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#66D9EF;font-style:italic">boolean</span><span style="color:#F8F8F2">> {</span></span> <span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (process.dev) { </span><span style="color:#88846F">// 🟢 开发环境:直接跳过</span></span> <span class="line"><span style="color:#F92672"> return</span><span style="color:#66D9EF;font-style:italic"> Promise</span><span style="color:#F8F8F2">.</span><span style="color:#A6E22E">resolve</span><span style="color:#F8F8F2">(</span><span style="color:#AE81FF">true</span><span style="color:#F8F8F2">)</span></span> <span class="line"><span style="color:#F8F8F2"> }</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"> // 🔴 生产环境:尝试连接 Deno KV</span></span> <span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> kv </span><span style="color:#F92672">=</span><span style="color:#F92672"> await</span><span style="color:#A6E22E"> useKv</span><span style="color:#F8F8F2">() </span><span style="color:#88846F">// 💥 爆炸点</span></span> <span class="line"><span style="color:#88846F"> // ... 复杂的用户存储逻辑</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <h3 id="-环境差异分析">📊 环境差异分析</h3> <table><thead><tr><th>环境类型</th><th>NODE_ENV</th><th>process.dev</th><th>执行路径</th><th>结果</th></tr></thead><tbody><tr><td>开发环境</td><td><code>development</code></td><td><code>true</code></td><td>跳过 KV</td><td>✅ 正常</td></tr><tr><td>生产环境</td><td><code>production</code></td><td><code>false</code></td><td>连接 KV</td><td>💥 报错</td></tr></tbody></table> <p><strong>原来如此!</strong> 开发环境下代码会跳过用户存储,所以没问题。但生产环境会尝试连接 Deno KV 数据库,而我的部署环境是 Node.js,当然找不到!</p> <h3 id="-功能必要性评估">🤔 功能必要性评估</h3> <p>仔细思考用户存储功能:</p> <ul> <li><strong>作用</strong>: 用户信息持久化、多账号管理、登录状态保持</li> <li><strong>对爬虫的必要性</strong>: 🤷‍♂️ 不是核心功能</li> <li><strong>复杂度</strong>: 🔴 增加部署复杂性</li> </ul> <p><strong>决策</strong>: 直接删除用户存储功能,专注核心爬虫能力!</p> <h3 id="-简单粗暴的解决方案">💡 简单粗暴的解决方案</h3> <p><strong>直接让所有用户存储函数返回成功/空值:</strong></p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672">export</span><span style="color:#F92672"> async</span><span style="color:#66D9EF;font-style:italic"> function</span><span style="color:#A6E22E"> createUser</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F;font-style:italic">user</span><span style="color:#F92672">:</span><span style="color:#A6E22E;text-decoration:underline"> UserEntry</span><span style="color:#F8F8F2">)</span><span style="color:#F92672">:</span><span style="color:#A6E22E;text-decoration:underline"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#66D9EF;font-style:italic">boolean</span><span style="color:#F8F8F2">> {</span></span> <span class="line"><span style="color:#88846F"> // 🚀 直接返回成功,不存储任何用户信息</span></span> <span class="line"><span style="color:#F8F8F2"> console.</span><span style="color:#A6E22E">log</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">`📝 跳过用户创建: </span><span style="color:#F92672">${</span><span style="color:#F8F8F2">user.nickname</span><span style="color:#F92672">}</span><span style="color:#E6DB74">`</span><span style="color:#F8F8F2">)</span></span> <span class="line"><span style="color:#F92672"> return</span><span style="color:#66D9EF;font-style:italic"> Promise</span><span style="color:#F8F8F2">.</span><span style="color:#A6E22E">resolve</span><span style="color:#F8F8F2">(</span><span style="color:#AE81FF">true</span><span style="color:#F8F8F2">)</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span> <span class="line"></span> <span class="line"><span style="color:#F92672">export</span><span style="color:#F92672"> async</span><span style="color:#66D9EF;font-style:italic"> function</span><span style="color:#A6E22E"> getUser</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F;font-style:italic">originalID</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#F92672">:</span><span style="color:#A6E22E;text-decoration:underline"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#A6E22E;text-decoration:underline">UserEntry</span><span style="color:#F92672"> |</span><span style="color:#66D9EF;font-style:italic"> null</span><span style="color:#F8F8F2">> {</span></span> <span class="line"><span style="color:#88846F"> // 🚀 直接返回 null,表示"新用户"</span></span> <span class="line"><span style="color:#F92672"> return</span><span style="color:#AE81FF"> null</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span> <span class="line"></span> <span class="line"><span style="color:#F92672">export</span><span style="color:#F92672"> async</span><span style="color:#66D9EF;font-style:italic"> function</span><span style="color:#A6E22E"> getUserByUUID</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F;font-style:italic">uuid</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#F92672">:</span><span style="color:#A6E22E;text-decoration:underline"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#A6E22E;text-decoration:underline">UserEntry</span><span style="color:#F92672"> |</span><span style="color:#66D9EF;font-style:italic"> null</span><span style="color:#F8F8F2">> {</span></span> <span class="line"><span style="color:#F92672"> return</span><span style="color:#AE81FF"> null</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span> <span class="line"></span> <span class="line"><span style="color:#F92672">export</span><span style="color:#F92672"> async</span><span style="color:#66D9EF;font-style:italic"> function</span><span style="color:#A6E22E"> getUserByFakeID</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F;font-style:italic">fakeid</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#F92672">:</span><span style="color:#A6E22E;text-decoration:underline"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#A6E22E;text-decoration:underline">UserEntry</span><span style="color:#F92672"> |</span><span style="color:#66D9EF;font-style:italic"> null</span><span style="color:#F8F8F2">> {</span></span> <span class="line"><span style="color:#F92672"> return</span><span style="color:#AE81FF"> null</span></span> <span class="line"><span style="color:#F8F8F2">}</span></span></code></pre> <hr> <h2 id="-最终效果">🎉 最终效果</h2> <h3 id="-功能验证">✅ 功能验证</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"># 🔗 HTTPS 访问</span></span> <span class="line"><span style="color:#A6E22E">curl</span><span style="color:#AE81FF"> -I</span><span style="color:#E6DB74"> https://172.19.23.27</span></span> <span class="line"><span style="color:#88846F"># HTTP/2 200 ✅</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 🍪 Cookie 设置</span></span> <span class="line"><span style="color:#88846F"># F12 → Application → Cookies → 看到完整的微信认证 Cookie ✅</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 🔐 登录流程</span></span> <span class="line"><span style="color:#88846F"># 扫码 → 点击登录 → 跳转成功 ✅</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 🕷️ 爬虫功能</span></span> <span class="line"><span style="color:#88846F"># 数据爬取完全正常 ✅</span></span></code></pre> <h3 id="-性能表现">📈 性能表现</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672">响应时间</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">200-500ms</span></span> <span class="line"><span style="color:#F92672">稳定性</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">24h 无异常</span></span> <span class="line"><span style="color:#F92672">资源占用</span><span style="color:#F8F8F2">: </span></span> <span class="line"><span style="color:#F92672"> CPU</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">&#x3C; 5%</span></span> <span class="line"><span style="color:#F92672"> Memory</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">&#x3C; 200MB</span></span> <span class="line"><span style="color:#F92672">用户体验</span><span style="color:#F8F8F2">: </span><span style="color:#E6DB74">🌟🌟🌟🌟🌟</span></span></code></pre> <hr> <h2 id="-经验总结">🎓 经验总结</h2> <h3 id="-核心洞察">💎 核心洞察</h3> <h4 id="1-安全策略进化论">1. 安全策略进化论</h4> <p>现代浏览器对 Cookie 安全要求越来越严格:</p> <ul> <li><code>Secure</code> 标志强制要求 HTTPS</li> <li><code>SameSite=None</code> 必须配合 HTTPS 使用</li> <li>即使内网环境也无法例外</li> </ul> <h4 id="2-环境一致性的重要性">2. 环境一致性的重要性</h4> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#F92672"><span style="user-select: none;">-</span> 开发环境: HTTP + 跳过复杂逻辑</span></span> <span class="line"><span style="color:#A6E22E"><span style="user-select: none;">+</span> 生产环境: HTTPS + 完整功能实现</span></span> <span class="line"><span style="color:#F8F8F2">= 隐藏问题,部署时爆炸 💥</span></span></code></pre> <h4 id="3-keep-it-simple-stupid">3. Keep It Simple, Stupid!</h4> <p>当遇到复杂的依赖问题时,问问自己:</p> <ul> <li>这个功能真的必要吗?</li> <li>能不能用更简单的方式解决?</li> <li>核心业务需求是什么?</li> </ul> <h3 id="️-避坑指南">🛡️ 避坑指南</h3> <h4 id="cookie-问题排查">Cookie 问题排查</h4> <ul> <li>✅ 优先检查浏览器控制台 Cookie 错误</li> <li>✅ 确认协议匹配(HTTP vs HTTPS)</li> <li>✅ 理解 Secure/SameSite 属性含义</li> </ul> <h4 id="数据库依赖管理">数据库依赖管理</h4> <ul> <li>✅ 区分开发和生产环境的差异</li> <li>✅ 评估功能的实际必要性</li> <li>✅ 优先解决核心问题,再考虑完善功能</li> </ul> <h4 id="内网部署特殊性">内网部署特殊性</h4> <ul> <li>✅ 不要以为内网就可以降低安全标准</li> <li>✅ 自签名证书是内网 HTTPS 的好选择</li> <li>✅ 简化架构,减少外部依赖</li> </ul> <hr> <h2 id="-总结">🚀 总结</h2> <p>这次踩坑经历的核心收获:</p> <h3 id="-技术层面">🔧 技术层面</h3> <ol> <li><strong>HTTPS 不是可选项</strong>:即使内网环境,现代 Web 应用也需要 HTTPS</li> <li><strong>自签名证书很实用</strong>:快速、简单、满足功能需求</li> <li><strong>功能删减有时比优化更有效</strong>:不必要的复杂性就是技术债务</li> </ol> <h3 id="-工程思维">🎯 工程思维</h3> <ol> <li><strong>问题导向</strong>:专注解决实际问题,而不是实现所有功能</li> <li><strong>渐进式开发</strong>:先让核心功能工作,再考虑完善</li> <li><strong>简化优于复杂</strong>:能删就删,能简化就简化</li> </ol> <h3 id="-部署经验">💡 部署经验</h3> <ol> <li><strong>环境一致性很重要</strong>:开发和生产环境的差异会隐藏问题</li> <li><strong>错误信息是最好的老师</strong>:仔细阅读错误信息,往往直接指向解决方案</li> <li><strong>内网不等于简单</strong>:现代 Web 标准不会因为内网而降低要求</li> </ol> <hr> <h2 id="-结语">🎬 结语</h2> <p>从 HTTP 到 HTTPS,从复杂的用户管理到简化的核心功能,这次部署经历让我深刻理解了:</p> <blockquote> <p>🌟 <strong>工程不是艺术,而是在约束条件下找到最优解的过程。</strong></p> </blockquote> <p>有时候,<strong>删除代码比写代码更有价值</strong>。有时候,<strong>简单粗暴的方案比精巧复杂的设计更实用</strong>。</p> <p><strong>技术服务于业务,而不是相反。</strong></p></content:encoded>
<enclosure url="https://layfz.com/images/uploads/http2https_bg.png" length="0" type="image/jpeg"/>
</item>
<item>
<title>投资思考与育儿智慧:透过现象看本质</title>
<link>https://layfz.com/posts/investing-and-raising-children-wisdom/</link>
<guid isPermaLink="true">https://layfz.com/posts/investing-and-raising-children-wisdom/</guid>
<description>从股市分析到育儿哲学,从美国政治制度到媒体现象,一篇深度思考文章引发的感悟。探讨如何在信息爆炸时代保持理性思考,以及现代育儿的智慧启示。</description>
<pubDate>Fri, 30 May 2025 10:46:00 GMT</pubDate>
<content:encoded><h2 id="文章摘要">文章摘要</h2> <p>这是一篇融合了股市分析、政治解读和育儿哲学的深度思考文章。作者从标普500和纳指的技术分析开始,延伸到对特朗普关税政策的法理解读,最后分享了关于育儿和人生的深刻思考。文章体现了作者”透过现象看本质”的思维方式,强调了解规则、理性分析的重要性。</p> <h2 id="核心观点">核心观点</h2> <h3 id="投资理念">投资理念</h3> <ul> <li>市场永远是对的,抱着自己的对错进市场的人都活不下来</li> <li>英伟达遇到143-145阻力位后回落,需要大利好才能突破</li> <li>生物医药板块联合健康在300左右震荡筑底</li> </ul> <h3 id="政治分析">政治分析</h3> <ul> <li>特朗普关税被法院叫停,但可通过《国家紧急状态法》继续执行</li> <li>马斯克退出政府效率部是因为法律限制,作为”特殊政府雇员”只能工作130天</li> <li>美国三权分立制度的运作机制与中国不同</li> </ul> <h3 id="育儿智慧">育儿智慧</h3> <ul> <li>“鸡娃不如鸡自己” - 卷自己比卷孩子效果更好</li> <li>最好的学区房是家庭,父母是原件,孩子是复印件</li> <li>成年人最好的学区房是病房 - 健康比什么都重要</li> </ul> <h3 id="人生感悟">人生感悟</h3> <ul> <li>小孩子的快乐很简单,要让他们有快乐的童年</li> <li>《西游记》中孙悟空的智慧:懂得维系关系,理解规则</li> <li>做投资的人杠精少,因为市场会教育所有人</li> </ul> <h2 id="原文内容">原文内容</h2> <p>” 标题: 成为另外一种人~ 标普500和纳指分别微涨0.4%、0.39%,高开回落。</p> <p>标普500的上方阻力位是6000点,纳指是20000点,这两个阻力位是多空双方争夺的密集区。</p> <p>持仓的个股中,生物医药板块全部微涨,消费股大部分微跌,伯克希尔和VISA微涨,科技股板块大多微涨。</p> <p>生物医药板块的联合健康短期内会在300左右震荡一段时间,筑底后再往上修复,最糟糕的阶段开始慢慢过去了。</p> <p>英伟达和预期的一致,在遇到143-145这个阻力位后回落,最终收盘价139.19美元,涨幅3.25%;后续要突破143-145这个阻力位,需要有大的利好,突破后则奔着153这个阻力位去了。</p> <p>没啥可以操作的,接下去的时间,以等待为主。</p> <p>川普的关税被联邦外贸法院叫停,关税战上半场进入休息时间,下半场再过段时间开始。</p> <p>法院的结果一出来,5分钟内川普政府立马提起上诉。</p> <p>老美是三权分立体系,按照宪法规定,征税权属于国会。</p> <p>但1976年颁布的《国家紧急状态法》,国家处于紧急状态时,总统有权力发布和采取尽力措施,其中包含临时征收税款和改动税率。</p> <p>国家是否处于紧急状态,只能由总统负责认定,法院无权干预总统的决定。即司法可以制衡行政,但不能代替行政。</p> <p>也就是说,总统有权单方面认定紧急状态是否成立,不需要为是否真的处于紧急状态这件事提供证据和证明。</p> <p>老美历任总统行使紧急状态的次数都很多,最近几个总统中,川普目前是行使次数最少的,4次,分别是:加强边境管理、2018年中美贸易战、应对新冠疫情,以及本次贸易战。</p> <p>作为对此,拜登行使9次,奥巴马12次,小布什13次,克林顿17次。</p> <p>后续川普只要以“美国财政即将破产”这一理由,就能行使紧急状态,将关税战继续打下去,并且不需要证明美国的财政是否真的即将破产。</p> <p>另外,即便最高法院以防卫措施过大、范围过大等为由,裁定川普的关税战违法,联邦政府也可以拒绝执行最高法院的裁决。典型案例,是拜登政府“拒绝”执行最高院关于豁免学生贷款的裁决,绕了个弯把豁免贷款的目的实现了。</p> <p>老美的三权分立是个很有意思的游戏,和咱们很不一样,不要用咱们的套用在老美身上,不适用。</p> <p>比如马斯克将退出政府效率部这事。</p> <p>按照老美的法律,去政府任职要避嫌,所以川普把自己的公司股权等商业资产装入到信托,由其他亲属打理。不走完这些程序,是不能去当总统的。</p> <p>但老美的官分两种:</p> <p>一种是由总统提名,由参议院确认的那些国防部长、财政部长等官员;另一种是总统根据自己的需要任免,不需要通过国会,类似于“家臣”,白宫幕僚长、总统法律顾问等,都是这一类。</p> <p>这两种马斯克都做不了,有利益冲突。因此,只能以特殊政府雇员来参与。</p> <p>根据美国司法部的定义,特殊政府雇员是指在365天内为政府工作不超过130天的临时雇员,通常以专家或顾问身份提供专业支持。</p> <p>这一身份允许马斯克在不公开披露其财务状况的情况下参与政府工作,同时可以继续管理其商业和公司。</p> <p>2025年2月3日,白宫官方确认马斯克被任命为“特殊政府雇员”。按照130天的限制,马斯克就是必须得在5月底结束任期的。</p> <p>嗯,就这么简单,明白了内在规则,就知道事情接下去的走势,不容易被带节奏。</p> <p>我喜欢跟做投资的人聊天,杠精少。搞钱的永远不会有什么杠精,因为去了市场你就知道,市场永远是对的;抱着自己的对错进市场的人,都活不下来。</p> <p>那些动不动就爱抬杠的人,往往要么穷,要么是学生或刚入社会的小白。</p> <p>昨晚睡得早,今天5点50分就醒来。起来洗漱了一番,陪孩子们吃个早饭。</p> <p>老二老三很开心,因为今天学校看表演,可以带玩具到学校玩。</p> <p>有时觉得,小孩子的快乐真的很简单。我尽量让他们有个快乐的童年。</p> <p>卷他们,还不如卷我自己,鸡娃不如鸡自己,自己都卷不出来,还想卷娃,想啥呢;自己卷出来了,说明卷自己效果更好,那就更没必要卷娃了。</p> <p>现在的机会点和窗口期,肯定比他们以后多。还不如我自己努力点,让孩子以后有需要能拼得了爹,而不是看别人拼爹。</p> <p>当然,前提是他们知道,好爹要用在刀刃上,而不是用在朋友圈里,否则就是把爹的收益最小化,把爹的风险最大化,哈哈。</p> <p>他们这代人是要去见世界的,要走出属于自己的路,踏上属于自己的人生。</p> <p>我顶多是他们去见世界的那个物质兜底,至于路怎么走、世界怎么样、从哪个角度看等等,需要他们自己根据自身的能力和见识、认知做判断。</p> <p>而自身的能力和见识、认知,是他们需要童年、少年、青年期走过一段漫长的路,也就是“学习”。</p> <p>对于孩子来说,最好的学区房是家庭,父母是原件,孩子是复印件,因此言传身教、以身作则就是最好的榜样。</p> <p>对于成年人来说,最好的学区房是病房。无论你有多大的野心和控制欲,病房里多躺一躺,就知道人生什么最重要,别期望值太高、控制欲太强,动不动把孩子往死里卷,没必要。</p> <p>小儿子有段时间不读《西游记》和《三国演义》了,我问他怎么不喜欢了?</p> <p>他说打打杀杀没意思。</p> <p>媳妇笑着说小儿子他没看懂,打打杀杀是表面,内在都是人情世故:</p> <p>你看孙悟空多聪明,知道师傅有后台,但从不让师傅出面,每次遇到麻烦都尽可能不出全力打败妖怪,而是求助于各路神仙。</p> <p>貌似都是自己在到处欠人情,但其实都是在维系关系,各路神仙因此能在取经过程中捞到一份人情和功劳;而上级都知道是在给谁面子。</p> <p>要不以孙悟空大闹天宫的本事,怎么可能打不过那些妖怪。</p> <p>而且,孙悟空无论多难,从不质疑取经事业的正确性,有大觉悟,因此显得特别能干,特别敬业,是真正的打工人楷模。</p> <p>你看不起“孙悟空”的能力退化、溜须拍马跑关系,“孙悟空”还看不起你榆木脑袋不懂变通、自以为是。</p> <p>老大打岔,说他懂这个,几年前他问过我一个问题,为什么会有两个司机?</p> <p>我说一个是职业是司机,专门负责开车的;另一个是职务是司机,挂职的,很多难搞定的关系、难预订的餐厅、难见得了面的人…他都能搞定,只是挂司机的头衔,实质做的是一系列“有核心竞争力”的活。</p> <p>看起来同样是司机,但只有第二种最吃香,不可替代。</p> <p>回到《西游记》的话题上,孙悟空很聪明,但唐僧这领导也很聪明。</p> <p>唐僧作为取经小团队的领导,他的任务根本就不是保证取经成功,因为取经必然成功。唐僧很清楚,保证取经必须按照上级要求的方式成功,才是自己要做的事。</p> <p>说白了就是只对上面负责,“权力只对权力的来源负责”,这是唐僧聪明的地方。</p> <p>老二对这些话题明显没兴趣,专注吃她的饭。</p> <p>时间过得飞快,一眨眼就要送他们去学校了。</p> <p>自从娃们上学后,每天相处的时间变得越来越少,以后上大学、工作了,能陪伴得就更少了。</p> <p>媳妇说每次想到这个,就想多生几个孩子,哈哈,我问她年轻时咋没这个觉悟。</p> <p>她回答,那个时候正值计划生育不允许超生,且那会还不具备生多孩的条件和勇气。</p> <p>我想了想,也是。</p> <p>愿我们都拥有随时停留和休息的底气,做人做事越来越平和、平淡。</p> <p>就这样吧。 ”</p> <h2 id="精选评论区互动">精选评论区互动</h2> <ul> <li><img src="/images/uploads/jinjianceng01.png" alt=""></li> <li><img src="/images/uploads/jinjianceng02.png" alt=""></li> </ul> <h3 id="关于作者夫人的智慧">关于作者夫人的智慧</h3> <p><strong>兵(重庆)</strong>:你有时间谈下是怎么娶了个睿智婆娘的吧!</p> <p><strong>金渐成(作者)</strong>:写过的,我二次创业失败时,背负了巨额负债,女朋友也跑了,同事的她不知道我海外有收入,傻乎乎抵押了自己的房子借钱给我,经常陪我聊天散心,我报恩把自己”抵押”给她了,借钱借成老公</p> <p><strong>天亮(北京)</strong>:你这么说就是当初想赖账不还钱</p> <p><strong>金渐成(作者)</strong>:两年后,给了她200多倍的回报,你就说这回报率高不高嘛</p> <h3 id="关于美国政治制度">关于美国政治制度</h3> <p><strong>凯(加拿大)</strong>:机哥你一段话把主流媒体报道马斯克和川普反目的故事给戳破了😃!</p> <p><strong>金渐成(作者)</strong>:现在反智的多,能讲清楚内在规则的少,都是情绪,少有理性。</p> <p><strong>Friend²⁰²⁵(广东)</strong>:还得是老美,法院敢和总统抬杠</p> <p><strong>金渐成(作者)</strong>:这法院,居然没被拆了,大法官居然还能干得好好的,没有丟了饭碗;总统居然奈何不了美联储的行长,哈哈哈,很多事看起来很有趣,挺好玩的,分权制衡是个好东西,避免了权力一家独大。</p> <h3 id="关于育儿理念">关于育儿理念</h3> <p><strong>文博(中国香港)</strong>:自己卷出来了,说明卷自己效果更好,那就更没必要卷娃了——就是这么干的</p> <p><strong>金渐成(作者)</strong>:嗯,卷娃不划算,卷自己回报率才高。如果自己都卷不动,还卷娃,那就更没意义了,娃基因突变、爆种的概率太低。</p> <p><strong>🌽ㅤ(广东)</strong>:鸡娃就是压力转移大法,家长把自己无能的压力、生活上的压力转移给孩子。</p> <p><strong>金渐成(作者)</strong>:嗯,鸡娃真残忍,就像虐猫虐狗一样。</p> <h3 id="关于投资心态">关于投资心态</h3> <p><strong>Peter Edmund陈(美国)</strong>:读书读得越多,看的东西越多,反而变得大多数时候有点沉默,看到自己的无力,对于有些事情我们不能改变,连改变自己的看法都异常困难。</p> <p><strong>金渐成(作者)</strong>:嗯,我也有几年是很纠结、矛盾的状态,后来想通了,怎么都是生活,不需要在意他人的看法和期待,做自己想做的和能做的就行,其他的随他去了。</p> <p><strong>ᰔᩚ 云汐大人༊(江苏)</strong>:市场永远是对的;抱着自己的对错进市场的人,都活不下来。那些动不动就爱抬杠的人,往往要么穷,要么是学生或刚入社会的小白。我很赞同</p> <p><strong>金渐成(作者)</strong>:所以一看网上的信息,就容易知道哪些人是穷人和小白,哪些人是搞投资的。信息很好分辨,搞投资的人往往观点和做法更有说服力,因为都是真金白银砸出来的。</p> <h3 id="关于健康的重要性">关于健康的重要性</h3> <p><strong>℡.wl(重庆)</strong>:前段时间结石急性,那两天唯一的感觉就是,身体健康才是第一,其他都需要排在这个后面,病房去了一遭,想起之前机哥说的身体重要,又深深的感动了</p> <p><strong>金渐成(作者)</strong>:成年人的最好学区房,是病房。这句话我深有体会</p> <h3 id="关于西游记的深度解读">关于《西游记》的深度解读</h3> <p><strong>劲松(北京)</strong>:每读机哥的育儿经,都学到不少,像孙悟空的见解和我之前看到的完全不同,那个版本说猪八戒讨领导喜欢,孙悟空累死累活打工苦命人。今天看机哥这视角,才发觉孙悟空才是有勇有谋,有情商的那个,要不最终他成佛,猪八戒没成佛呢</p> <p><strong>PowerVito(北京)</strong>:牛魔王就是不明白,坐莲台级别领导的青牛、锦毛吼,跟他那个谁都能骑的傻冤家碧水金睛兽,完全不是一回事儿😂</p> <h3 id="关于学习和成长">关于学习和成长</h3> <p><strong>晨(上海)</strong>:再次拜服极哥。才意识到白看了那么多遍西游记了…知道猴子聪明,却从来没去思考过背后这么多的真相。果然,不能只读书,读死书啊。怎样灵活变通,汲取知识和教训才有意义。</p> <p><strong>金渐成(作者)</strong>:因为大多数人只有情绪,很容易陷入自嗨。多了解规则,多熟悉底层逻辑,就会有自己的判断。</p> <h2 id="个人感悟">个人感悟</h2> <p>这篇文章让我深刻体会到了什么叫”透过现象看本质”。在信息爆炸的时代,媒体往往更倾向于制造情绪和话题,而非帮助受众理解事情的内在逻辑。</p> <p>作者对马斯克退出政府职务的分析就是典型例子。大多数媒体报道都在渲染”反目成仇”的戏剧性,但实际上这只是美国法律制度的正常运作。了解了《特殊政府雇员》的130天限制规定,就能明白这是必然结果,而非什么政治斗争。</p> <p>在育儿方面,“鸡娃不如鸡自己”这个观点特别有启发性。很多家长把自己的焦虑和压力转移给孩子,美其名曰”为了孩子好”,实际上可能是在逃避自我提升的责任。如果连自己都无法突破,又怎么指望孩子能够超越?</p> <p>文章中对《西游记》的解读也很精彩。孙悟空的智慧不在于武力,而在于对规则的理解和对关系的维护。这种职场智慧的解读,让经典名著有了新的现实意义。</p> <p>最深刻的是”成年人最好的学区房是病房”这句话。健康面前,其他一切都是浮云。这提醒我们要时刻保持清醒,不要被外在的成功标准绑架,忘记了生活的本质。</p> <hr> <p><strong>声明</strong>:本文内容来源于公众号分享,仅供学习交流使用。投资有风险,决策需谨慎。文中观点仅代表作者个人看法,不构成投资建议。</p></content:encoded>
<enclosure url="https://layfz.com/images/uploads/jinjianceng03.png" length="0" type="image/jpeg"/>
</item>
<item>
<title>CentOS 7运维常用的命令</title>
<link>https://layfz.com/posts/linux-normal/</link>
<guid isPermaLink="true">https://layfz.com/posts/linux-normal/</guid>
<description>在java开发以及网页开发中常用到的关于部署以及配置相关的命令</description>
<pubDate>Fri, 30 Aug 2024 11:53:22 GMT</pubDate>
<content:encoded><h1 id="centos-常用命令集">CentOS 常用命令集</h1> <p>本文适合基于CentOS 7且有基础的运维同学,一些常见的命令如“cd、rm”等命令不会列举,仅仅会给出在开发视角中常用的命令。</p> <h2 id="目录">目录</h2> <ul> <li> <p><a href="#centos-%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%E9%9B%86">CentOS 常用命令集</a></p> <ul> <li> <p><a href="#%E7%9B%AE%E5%BD%95">目录</a></p> <ul> <li><a href="#%E4%B8%80%E8%88%AC%E4%BF%A1%E6%81%AF%E5%91%BD%E4%BB%A4">一般信息命令</a></li> <li><a href="#%E9%98%B2%E7%81%AB%E5%A2%99">防火墙</a></li> <li><a href="#nginx">Nginx</a></li> <li><a href="#jar%E5%8C%85%E8%BF%90%E8%A1%8C">jar包运行</a></li> </ul> </li> </ul> </li> </ul> <h3 id="一般信息命令">一般信息命令</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">ps</span><span style="color:#E6DB74"> aux</span><span style="color:#88846F"> # 显示所有进程</span></span> <span class="line"><span style="color:#A6E22E">ps</span><span style="color:#AE81FF"> -ef</span><span style="color:#88846F"> # 显示完整格式的进程信息</span></span> <span class="line"><span style="color:#A6E22E">uname</span><span style="color:#AE81FF"> -a</span><span style="color:#88846F"> # 显示所有系统信息</span></span> <span class="line"><span style="color:#A6E22E">df</span><span style="color:#AE81FF"> -h</span><span style="color:#88846F"> # 以人类可读格式显示</span></span> <span class="line"><span style="color:#A6E22E">uptime</span><span style="color:#88846F"> # 显示系统运行时间</span></span> <span class="line"><span style="color:#A6E22E">ifconfig</span><span style="color:#88846F"> # 显示网络配置</span></span> <span class="line"><span style="color:#A6E22E">ps</span><span style="color:#AE81FF"> -ef</span><span style="color:#F92672">|</span><span style="color:#A6E22E">grep</span><span style="color:#E6DB74"> nginx</span><span style="color:#88846F"> # 显示nginx进程、过滤后。</span></span></code></pre> <h3 id="防火墙">防火墙</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">systemctl</span><span style="color:#E6DB74"> stop</span><span style="color:#E6DB74"> firewalld.service</span></span> <span class="line"><span style="color:#A6E22E">systemctl</span><span style="color:#E6DB74"> start</span><span style="color:#E6DB74"> firewalld.service</span></span> <span class="line"><span style="color:#A6E22E">firewall-cmd</span><span style="color:#AE81FF"> --zone=public</span><span style="color:#AE81FF"> --add-port=200006/tcp</span><span style="color:#AE81FF"> --permanent</span></span> <span class="line"><span style="color:#A6E22E">firewall-cmd</span><span style="color:#AE81FF"> --zone=public</span><span style="color:#AE81FF"> --remove-port=200006/tcp</span><span style="color:#AE81FF"> --permanent</span></span> <span class="line"><span style="color:#A6E22E">firewall-cmd</span><span style="color:#AE81FF"> --reload</span></span> <span class="line"><span style="color:#A6E22E">firewall-cmd</span><span style="color:#AE81FF"> --list-all</span></span></code></pre> <h3 id="nginx">Nginx</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">sudo</span><span style="color:#E6DB74"> systemctl</span><span style="color:#E6DB74"> start</span><span style="color:#E6DB74"> nginx</span></span> <span class="line"><span style="color:#A6E22E">sudo</span><span style="color:#E6DB74"> systemctl</span><span style="color:#E6DB74"> stop</span><span style="color:#E6DB74"> nginx</span></span> <span class="line"><span style="color:#A6E22E">sudo</span><span style="color:#E6DB74"> systemctl</span><span style="color:#E6DB74"> restart</span><span style="color:#E6DB74"> nginx</span><span style="color:#F8F8F2"> </span></span> <span class="line"><span style="color:#A6E22E">nginx</span><span style="color:#AE81FF"> -s</span><span style="color:#E6DB74"> reload</span></span> <span class="line"><span style="color:#A6E22E">nginx</span><span style="color:#AE81FF"> -s</span><span style="color:#E6DB74"> quit</span></span> <span class="line"><span style="color:#A6E22E">nginx</span><span style="color:#AE81FF"> -v</span></span></code></pre> <h3 id="jar包运行">jar包运行</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">nohup</span><span style="color:#E6DB74"> java</span><span style="color:#AE81FF"> -jar</span><span style="color:#E6DB74"> pakageName.jar</span><span style="color:#F8F8F2"> &#x26; </span><span style="color:#88846F"># 后台运行jar包并输出到nohup日志</span></span> <span class="line"><span style="color:#A6E22E">java</span><span style="color:#AE81FF"> -jar</span><span style="color:#E6DB74"> pakageName</span><span style="color:#F8F8F2"> &#x26; </span><span style="color:#88846F"># jar包如果有配置日志</span></span></code></pre></content:encoded>
<enclosure url="https://layfz.com/images/uploads/centos.png" length="0" type="image/jpeg"/>
</item>
<item>
<title>现代AI爬虫的进化之路:从OCR到Markdown的完美转身 🚀</title>
<link>https://layfz.com/posts/modern-ai-crawlers-ocr-to-markdown/</link>
<guid isPermaLink="true">https://layfz.com/posts/modern-ai-crawlers-ocr-to-markdown/</guid>
<description>从传统正则表达式到AI加持的现代爬虫,我们经历了什么?当OCR遇到复杂数据结构时为何"翻车"?为什么说Markdown才是AI爬虫的最佳伴侣? 本文分享了一个实战项目中的痛点和解决方案:面对混乱的数据结构(纯文本+图片+表格),如何从冗余的"OCR+拼接"方案完美转身到"PDF转Markdown"的高效流程。</description>
<pubDate>Fri, 04 Jul 2025 17:10:00 GMT</pubDate>
<content:encoded><h2 id="前言">前言 💭</h2> <p>最近在搞爬虫项目的时候,发现了一个很有意思的现象:<strong>现代爬虫必须要有AI加持才能玩得转</strong>!🤖 传统的正则表达式抓取方式已经被AI狠狠地”降维打击”了,效率提升不是一点半点。</p> <p>但是,理想很丰满,现实很骨感… 😅</p> <h2 id="现有方案的痛点">现有方案的痛点 😵‍💫</h2> <h4 id="-纯文本类型">📝 <strong>纯文本类型</strong></h4> <ul> <li><strong>案例</strong>:新闻文章、博客内容</li> <li><strong>处理难点</strong>:相对简单</li> <li><strong>现有方案问题</strong>:基本能处理</li> </ul> <h4 id="️-图片文字类型">🖼️ <strong>图片+文字类型</strong></h4> <ul> <li><strong>案例</strong>:产品介绍页面、技术文档</li> <li><strong>处理难点</strong>:OCR识别准确率、中文字符识别</li> <li><strong>现有方案问题</strong>:百度OCR经常识别错误,需要人工校验</li> </ul> <p><img src="/images/uploads/case_spider_1.png" alt=""></p> <h4 id="-图片表格类型">📊 <strong>图片+表格类型</strong></h4> <ul> <li><strong>案例</strong>:财务报表、数据统计页面</li> <li><strong>处理难点</strong>:表格格式丢失、行列关系混乱</li> <li><strong>现有方案问题</strong>:OCR无法保留表格结构,AI理解困难</li> </ul> <p><img src="/images/uploads/case_spider_2.png" alt=""></p> <h3 id="多模态大模型的美丽陷阱">多模态大模型的”美丽陷阱”</h3> <p>说到AI爬虫,大家第一反应可能是:<strong>“用多模态大模型啊!ChatGPT不香吗?”</strong> 🤔</p> <p>emmm… 香是香,但是有几个问题:</p> <ol> <li> <p><strong>中文理解能力有限</strong> 🈹</p> <ul> <li>毕竟是国外的大模型,中文训练还是有些欠缺</li> <li>OCR识别中文的准确率让人捉急</li> <li>我们项目不允许有错误数据,所以直接pass</li> </ul> </li> <li> <p><strong>API支持不够完善</strong> 📡</p> <ul> <li>大部分支持API的多模态模型还没完全开放</li> <li>即使有,成本也不低</li> </ul> </li> </ol> <p>所以我们选择了国产大模型DeepSeek,至少中文理解能力杠杠的!🎯</p> <h3 id="数据结构的混乱现状">数据结构的”混乱现状”</h3> <p>我们项目遇到的数据结构简直是”百花齐放”:</p> <ul> <li>📝 <strong>纯文本</strong>:这个还好处理</li> <li>🖼️ <strong>图片+文字</strong>:需要OCR配合</li> <li>📊 <strong>图片+文字+表格</strong>:这就头大了…</li> </ul> <p>更要命的是,图片里经常混杂着我们需要的信息,但也有很多无用图片,程序很难自动筛选。</p> <h3 id="当前方案的冗余困境">当前方案的”冗余困境”</h3> <p>我们现在的流程是这样的:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>获取文本和图片 → 图片丢给百度OCR → 拼接完整文章 → 投给DeepSeek提取数据</span></span></code></pre> <p>问题来了:</p> <ul> <li>❌ <strong>流程冗余</strong>:步骤太多,效率低</li> <li>❌ <strong>通用性差</strong>:每个网站都要调整</li> <li>❌ <strong>表格处理弱</strong>:OCR无法保留表格格式</li> <li>❌ <strong>噪音太多</strong>:无用图片干扰数据提取</li> </ul> <h2 id="灵感闪现markdown才是王道">灵感闪现:Markdown才是王道!💡</h2> <p>突然想到一个问题:<strong>为什么不直接用Markdown呢?</strong></p> <p>想想看,Markdown的优势:</p> <p>✅ <strong>纯文本格式</strong> - 轻量级,易处理<br> ✅ <strong>格式友好</strong> - 保留基本样式和结构<br> ✅ <strong>AI友好</strong> - 大部分模型输出都是Markdown<br> ✅ <strong>通用性强</strong> - 一套流程走天下</p> <h2 id="解决方案pdf转markdown的完美方案">解决方案:PDF转Markdown的完美方案 🎯</h2> <p>经过一番调研,发现了一个神器:<strong>MinerU</strong>!</p> <h3 id="核心思路">核心思路</h3> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>网页 → PDF/图片 → Markdown → AI提取数据</span></span></code></pre> <h3 id="为什么选择mineru">为什么选择MinerU?</h3> <p>🔍 <strong>项目地址</strong>:<a href="https://github.com/opendatalab/MinerU/tree/master">https://github.com/opendatalab/MinerU/tree/master</a></p> <p><strong>优势清单</strong>:</p> <ul> <li>🇨🇳 <strong>中文友好</strong>:识别中文准确率极高</li> <li>📐 <strong>排版自然</strong>:保留原有格式和结构</li> <li>📊 <strong>表格支持</strong>:完美处理表格数据</li> <li>🎯 <strong>精准识别</strong>:汉字识别准确度很高</li> <li>🔄 <strong>数据完整</strong>:不丢失关键信息</li> </ul> <h3 id="实际效果">实际效果</h3> <p><img src="/images/uploads/minerui_1.png" alt=""></p> <p>使用MinerU转换后的Markdown数据投给AI:</p> <ul> <li>✅ <strong>数据完整性</strong> - 不丢失任何关键信息</li> <li>✅ <strong>格式保持</strong> - 表格、列表等结构完整</li> <li>✅ <strong>噪音减少</strong> - 无用图片被过滤</li> <li>✅ <strong>处理效率</strong> - 一步到位,省心省力</li> </ul> <h2 id="总结">总结 🎊</h2> <p>从传统的正则表达式,到OCR+AI的组合拳,再到现在的PDF转Markdown方案,AI爬虫的进化之路充满了惊喜!</p> <p><strong>核心观点</strong>:</p> <ol> <li>🤖 <strong>AI加持是必然趋势</strong> - 告别正则表达式时代</li> <li>🎯 <strong>选择合适的模型</strong> - 根据业务需求选择中文友好的模型</li> <li>📄 <strong>Markdown是最佳载体</strong> - AI友好且保留格式</li> <li>🔧 <strong>工具选择很重要</strong> - MinerU这样的工具能事半功倍</li> </ol> <p>现在的方案不仅解决了数据混乱的问题,还大大提升了提取精度和效率。如果你也在做类似的项目,强烈推荐试试这个方案!🚀</p> <hr> <p><em>怎么样,这波操作是不是很6?</em> 😎</p></content:encoded>
<enclosure url="https://layfz.com/images/uploads/2025_0704_bg.png" length="0" type="image/jpeg"/>
</item>
<item>
<title>🔥 服务器突然挂了?我被攻击了?一次惊心动魄的故障排查经历</title>
<link>https://layfz.com/posts/server-down/</link>
<guid isPermaLink="true">https://layfz.com/posts/server-down/</guid>
<description>你有没有遇到过这种情况:刚才还好好的服务,突然就连不上了?这不,我就遇到了这么一次"灵异事件"...</description>
<pubDate>Sat, 05 Jul 2025 20:26:00 GMT</pubDate>
<content:encoded><h2 id="-突如其来的灾难">🚨 突如其来的灾难</h2> <p>我服务器上一直有一个服务在跑,但是突然发现我的一个重要网络服务怎么都连不上了 😱</p> <p><strong>症状如下:</strong></p> <ul> <li>🔴 客户端连接超时</li> <li>🔴 延迟显示 <code>-1</code></li> <li>🔴 各种重试都无效</li> </ul> <p>第一反应:🤯 “完了,服务器是不是被黑了?”</p> <h2 id="-紧急抢救">🆘 紧急抢救</h2> <p>既然连不上,那就先重启试试吧(程序员的万能解决方案 😅)</p> <p>随后我到服务器提供商面板去重启</p> <p>神奇的事情发生了 ✨ —— 重启后服务立即恢复正常!</p> <p>但是问题来了:<strong>为什么重启就好了?根本原因是什么?</strong></p> <p>作为一个有追求的技术人,怎么能就这样算了呢?必须要找出真相!🕵️‍♂️</p> <h2 id="-开始福尔摩斯时间">🔍 开始福尔摩斯时间</h2> <h3 id="第一条线索可疑的登录记录">第一条线索:可疑的登录记录</h3> <p>登录服务器后,发现了第一个异常:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">Last</span><span style="color:#E6DB74"> failed</span><span style="color:#E6DB74"> login:</span><span style="color:#E6DB74"> Sat</span><span style="color:#E6DB74"> Jul</span><span style="color:#AE81FF"> 5</span><span style="color:#AE81FF"> 07</span><span style="color:#E6DB74">:54:21</span><span style="color:#E6DB74"> EDT</span><span style="color:#AE81FF"> 2025</span><span style="color:#E6DB74"> from</span><span style="color:#AE81FF"> 152.200</span><span style="color:#E6DB74">.181.42</span><span style="color:#E6DB74"> on</span><span style="color:#E6DB74"> ssh:notty</span></span> <span class="line"><span style="color:#A6E22E">There</span><span style="color:#E6DB74"> were</span><span style="color:#AE81FF"> 40</span><span style="color:#E6DB74"> failed</span><span style="color:#E6DB74"> login</span><span style="color:#E6DB74"> attempts</span><span style="color:#E6DB74"> since</span><span style="color:#E6DB74"> the</span><span style="color:#E6DB74"> last</span><span style="color:#E6DB74"> successful</span><span style="color:#E6DB74"> login.</span></span></code></pre> <p>😳 <strong>40次失败登录?</strong> 这绝对不正常!</p> <h3 id="第二条线索系统错误日志">第二条线索:系统错误日志</h3> <p>继续深挖,查看系统日志:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">journalctl</span><span style="color:#AE81FF"> --since</span><span style="color:#E6DB74"> "2025-07-05 06:00:00"</span><span style="color:#AE81FF"> --until</span><span style="color:#E6DB74"> "2025-07-05 07:13:00"</span><span style="color:#AE81FF"> -p</span><span style="color:#E6DB74"> err</span></span></code></pre> <p>发现了这些可疑的错误:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>Jul 05 06:08:13 sshd: error: kex_exchange_identification: Connection closed</span></span> <span class="line"><span>Jul 05 06:10:31 sshd: error: kex_exchange_identification: Connection closed </span></span> <span class="line"><span>Jul 05 06:46:52 sshd: fatal: userauth_finish: send failure packet: Connection</span></span></code></pre> <p>🤔 SSH连接错误?感觉离真相越来越近了…</p> <h3 id="第三条线索暴力破解攻击并且ip来自于德国香港等地">第三条线索:暴力破解攻击!并且ip来自于德国、香港等地</h3> <p>当我查看详细的登录失败记录时,真相大白了!</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#A6E22E">journalctl</span><span style="color:#F92672"> |</span><span style="color:#A6E22E"> grep</span><span style="color:#E6DB74"> "Failed password"</span><span style="color:#F92672"> |</span><span style="color:#A6E22E"> tail</span><span style="color:#AE81FF"> -20</span></span></code></pre> <p>映入眼帘的是一连串触目惊心的攻击记录:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span>Jul 05 07:41:44 Failed password for root from 80.94.95.15</span></span> <span class="line"><span>Jul 05 07:42:26 Failed password for root from 123.58.209.224 </span></span> <span class="line"><span>Jul 05 07:42:45 Failed password for invalid user odoo15 from 161.35.72.143</span></span> <span class="line"><span>Jul 05 07:43:02 Failed password for root from 103.63.25.239</span></span> <span class="line"><span>Jul 05 07:47:41 Failed password for root from 152.200.181.42</span></span> <span class="line"><span>...</span></span></code></pre> <p>🚨 <strong>天哪!我的服务器正在遭受大规模的SSH暴力破解攻击!</strong></p> <h2 id="-真相大白">🧩 真相大白</h2> <p>现在一切都说得通了!</p> <p><strong>攻击的影响链条:</strong></p> <ol> <li> <p>🏴‍☠️ <strong>多个IP同时发起SSH暴力破解</strong></p> <ul> <li>每隔几秒就有新的攻击尝试</li> <li>来自世界各地的僵尸网络</li> </ul> </li> <li> <p>⚡ <strong>系统资源被疯狂消耗</strong></p> <ul> <li>每个SSH连接尝试都要消耗CPU和内存</li> <li>网络连接池被占满</li> <li>文件描述符被耗尽</li> </ul> </li> <li> <p>💥 <strong>其他服务受到牵连</strong></p> <ul> <li>我的网络服务无法获得足够资源</li> <li>新连接被拒绝</li> <li>表现为”服务挂了”</li> </ul> </li> <li> <p>🔄 <strong>重启为什么能解决?</strong></p> <ul> <li>清除了所有攻击连接</li> <li>释放了被占用的系统资源</li> <li>服务重新获得正常运行环境</li> </ul> </li> </ol> <p><strong>原来我的服务没坏,是被攻击”挤”挂了!</strong> 😂</p> <h2 id="️-绝地反击防御策略">🛡️ 绝地反击:防御策略</h2> <p>既然找到了根本原因,那就要彻底解决这个问题! 我们遇到这种攻击可以有多种方案,譬如修改端口,设置防火墙等</p> <h3 id="-终极武器fail2ban">🚀 终极武器:fail2ban</h3> <p>经过研究,我选择了 <code>fail2ban</code> 这个神器:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"># 安装fail2ban</span></span> <span class="line"><span style="color:#A6E22E">yum</span><span style="color:#E6DB74"> install</span><span style="color:#E6DB74"> epel-release</span><span style="color:#AE81FF"> -y</span></span> <span class="line"><span style="color:#A6E22E">yum</span><span style="color:#E6DB74"> install</span><span style="color:#E6DB74"> fail2ban</span><span style="color:#AE81FF"> -y</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 配置严格的防护策略</span></span> <span class="line"><span style="color:#A6E22E">cat</span><span style="color:#F92672"> ></span><span style="color:#E6DB74"> /etc/fail2ban/jail.local</span><span style="color:#F92672"> &#x3C;&#x3C;</span><span style="color:#F8F8F2"> '</span><span style="color:#F8F8F2">EOF</span><span style="color:#F8F8F2">'</span></span> <span class="line"><span style="color:#E6DB74">[DEFAULT]</span></span> <span class="line"><span style="color:#E6DB74"># 封禁24小时,让攻击者冷静冷静</span></span> <span class="line"><span style="color:#E6DB74">bantime = 86400</span></span> <span class="line"><span style="color:#E6DB74"># 监控10分钟内的行为</span></span> <span class="line"><span style="color:#E6DB74">findtime = 600</span></span> <span class="line"><span style="color:#E6DB74"># 最多容忍3次失败</span></span> <span class="line"><span style="color:#E6DB74">maxretry = 3</span></span> <span class="line"></span> <span class="line"><span style="color:#E6DB74">[sshd]</span></span> <span class="line"><span style="color:#E6DB74">enabled = true</span></span> <span class="line"><span style="color:#E6DB74">port = ssh</span></span> <span class="line"><span style="color:#E6DB74">filter = sshd</span></span> <span class="line"><span style="color:#E6DB74">logpath = /var/log/secure</span></span> <span class="line"><span style="color:#E6DB74"># SSH更严格:2次失败就封禁</span></span> <span class="line"><span style="color:#E6DB74">maxretry = 3 </span></span> <span class="line"><span style="color:#E6DB74"># SSH攻击者封禁7天</span></span> <span class="line"><span style="color:#E6DB74">bantime = 604800</span></span> <span class="line"><span style="color:#F8F8F2">EOF</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 启动防护</span></span> <span class="line"><span style="color:#A6E22E">systemctl</span><span style="color:#E6DB74"> enable</span><span style="color:#E6DB74"> fail2ban</span></span> <span class="line"><span style="color:#A6E22E">systemctl</span><span style="color:#E6DB74"> start</span><span style="color:#E6DB74"> fail2ban</span></span></code></pre> <h3 id="-效果验证">📊 效果验证</h3> <p>配置完成后,可以通过以下命令查看防护效果:</p> <pre class="astro-code monokai" style="background-color:#272822;color:#F8F8F2; overflow-x: auto;" tabindex="0"><code><span class="line"><span style="color:#88846F"># 查看防护状态</span></span> <span class="line"><span style="color:#A6E22E">fail2ban-client</span><span style="color:#E6DB74"> status</span><span style="color:#E6DB74"> sshd</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 查看被封的攻击者</span></span> <span class="line"><span style="color:#A6E22E">fail2ban-client</span><span style="color:#E6DB74"> get</span><span style="color:#E6DB74"> sshd</span><span style="color:#E6DB74"> banned</span></span> <span class="line"></span> <span class="line"><span style="color:#88846F"># 实时监控攻击</span></span> <span class="line"><span style="color:#A6E22E">tail</span><span style="color:#AE81FF"> -f</span><span style="color:#E6DB74"> /var/log/fail2ban.log</span></span></code></pre> <p>看着攻击者的IP一个个被加入黑名单,那感觉简直太爽了! 😎</p> <h2 id="-经验总结">💡 经验总结</h2> <p>这次故障排查让我学到了很多:</p> <h3 id="-关键收获">🎯 关键收获</h3> <ol> <li> <p><strong>🔍 日志是最好的朋友</strong></p> <ul> <li>系统日志包含了所有的线索</li> <li>学会读懂各种日志格式</li> <li>时间线分析很重要</li> </ul> </li> <li> <p><strong>🌐 网络安全不容忽视</strong></p> <ul> <li>SSH暴力破解攻击无处不在</li> <li>即使不直接攻击目标服务,也可能造成间接影响</li> <li>防护措施要尽早部署</li> </ul> </li> <li> <p><strong>⚡ 资源耗尽的隐蔽性</strong></p> <ul> <li>问题表现可能与根本原因相距甚远</li> <li>系统资源竞争会导致意想不到的故障</li> <li>重启能解决很多问题,但不能只满足于此</li> </ul> </li> </ol> <h3 id="️-实用建议">🛠️ 实用建议</h3> <ul> <li><strong>🚨 立即行动</strong>: 如果你的服务器还没有部署 fail2ban,赶紧去配置!</li> <li><strong>📈 监控重要</strong>: 定期检查系统日志,及早发现异常</li> <li><strong>🔐 安全意识</strong>: 修改默认SSH端口、禁用root登录等都是好习惯</li> </ul> <h2 id="-结语">🎉 结语</h2> <p>虽然这次”故障”让我紧张了一阵子,但最终的排查过程还是很有成就感的 💪</p> <p>从表面现象到深层原因,再到最终解决方案,整个过程就像解谜游戏一样有趣。</p> <p><strong>最重要的是:遇到问题不要慌,日志会告诉你一切!</strong> 📚</p></content:encoded>
<enclosure url="https://layfz.com/images/uploads/server_dwon_bg.png" length="0" type="image/jpeg"/>
</item>
</channel>
</rss>