<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>朝闻道 - 初晴神社</title>
	<atom:link href="https://vki.cc/greattruth/feed/" rel="self" type="application/rss+xml" />
	<link>https://vki.cc/category/greattruth/</link>
	<description>白露的博客</description>
	<lastBuildDate>Fri, 30 Jul 2021 15:57:31 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1</generator>

<image>
	<url>https://cdn.vki.cc/wp-content/uploads/2018/03/cropped-icemaplen-1-32x32.png</url>
	<title>朝闻道 - 初晴神社</title>
	<link>https://vki.cc/category/greattruth/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Unity 3D 纹理压缩格式指南</title>
		<link>https://vki.cc/greattruth/experience/872/</link>
					<comments>https://vki.cc/greattruth/experience/872/#respond</comments>
		
		<dc:creator><![CDATA[白露]]></dc:creator>
		<pubDate>Fri, 30 Jul 2021 15:57:30 +0000</pubDate>
				<category><![CDATA[咸鱼心得]]></category>
		<category><![CDATA[朝闻道]]></category>
		<category><![CDATA[Unity]]></category>
		<category><![CDATA[Unity 3D]]></category>
		<category><![CDATA[贴图]]></category>
		<guid isPermaLink="false">https://vki.cc/?p=872</guid>

					<description><![CDATA[<p>压缩纹理的主要目的是为了节省空间，缩短加载时间。针对不同的需求和平台需要不同的压缩格式，有的需要高性能，有的需要高质量，Unity本身提供了多种压缩格式供选择。</p>
<p><a href="https://vki.cc/greattruth/experience/872/">Unity 3D 纹理压缩格式指南</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p class="has-black-color has-text-color wp-block-paragraph">Unity本身支持多种贴图格式，比如jpg、png、psd等。但是当这些资源导入后，Unity会统一转化为自己的格式（非直接更改源文件），以供游戏运行时使用。</p>



<p class="has-black-color has-text-color wp-block-paragraph">压缩纹理的主要目的是为了节省空间，缩短加载时间。针对不同的需求和平台需要不同的压缩格式，有的需要高性能，有的需要高质量，Unity本身提供了多种压缩格式供选择。</p>



<h3 id="etc" class="wp-block-heading">ETC</h3>



<p class="wp-block-paragraph"><em>Ericsson Texture Compression，是由 Khronos 支持的开放标准，在移动平台中广泛采用。它是一种为感知质量设计的有损算法，其依据是人眼对亮度改变的反应要高于色度改变。类似于DXT，ETC也是把4&#215;4的像素块压缩成一个64或128位的数据块，也是有损压缩。</em></p>



<p class="has-black-color has-text-color wp-block-paragraph">ETC又分为ETC1和ETC2，通常用于Android平台。其中ETC2是较新的标准，也是Unity官方推荐的格式（虽然我还不太理解）。</p>



<p class="has-black-color has-text-color wp-block-paragraph">相对于ETC1，<strong>ETC2向前兼容，不强制要求贴图大小为2的整数次幂，且对RGB的压缩质量更好，支持透明通道。</strong>唯一的缺点是需要设备支持OpenGL ES 3.0标准。不过该标准诞生于2012年，目前不支持它的设备非常少，因此不用担心。</p>



<p class="wp-block-paragraph">注意：<em>可对具有 <a href="https://docs.unity3d.com/cn/current/Manual/HOWTO-alphamaps.html">Alpha 通道</a>的纹理使用 ETC1 格式，但仅当构建目标为 Android 且纹理放置在图集上（通过指定打包标签）时才适用。要启用此功能，请勾选纹理的 <strong>Compress using ETC1</strong> 复选框。Unity 会将生成的图集拆分为两个纹理，每个纹理都没有 Alpha 通道，然后将它们组合在渲染管线的最后部分中。</em></p>



<h3 id="pvrtc" class="wp-block-heading">PVRTC</h3>



<p class="wp-block-paragraph">PowerVR Texture Compression，PVRTC格式与基于块的压缩格式，比如S3TC、ETC的不同之处是，它使用2张双线性放大的<em>低分辨率图，根据精度和每个像素的权重，融合到一起来呈现纹理，并且2-bpp和4-bpp都支持ARGB数据。PVRTC格式压缩比高，也是有损压缩。</em></p>



<p class="has-black-color has-text-color wp-block-paragraph">PVRTC格式一般用于iOS平台，可以用在所有的iOS设备上。缺点是<strong>要求纹理的高、宽必须都是2的整数次幂（POT），最小为8，并且必须是正方形</strong>。</p>



<p class="has-vivid-red-color has-text-color wp-block-paragraph">注意：使用该格式，极端情况下会让内存占用变得更大。比如原先300*300的贴图，因为要求尺寸为2的整数次幂，所以变成512*512 . 这种情况下还不如使用RGBA16 .</p>



<h3 id="rgba16" class="wp-block-heading">RGBA16</h3>



<p class="has-black-color has-text-color wp-block-paragraph">RGBA16，即RGBA4444，每个像素2个字节，RGBA通道各占4位。</p>



<p class="has-black-color has-text-color wp-block-paragraph">相比以上压缩格式，贴图质量相对较好，缺点是内存占用大，且相对于RGBA32来说会有色彩断层问题。</p>



<h3 id="rgba32" class="wp-block-heading">RGBA32</h3>



<p class="has-black-color has-text-color wp-block-paragraph"> RGBA32，即RGBA8888，每个像素4个字节，RGBA通道各占8位。</p>



<p class="has-black-color has-text-color wp-block-paragraph">原图无压缩，质量最好 ，缺点是内存占用极大。除非美术要求极高才会使用。</p>



<p class="has-black-color has-text-color wp-block-paragraph">假如贴图没有透明通道则使用RGB24格式，少占用一个字节的空间。</p>



<h3 id="astc" class="wp-block-heading">ASTC</h3>



<p class="wp-block-paragraph"><em>ASTC 是在 OpenGL ES3.0 出现后在 2012 年中产生的一种业界领先的纹理压缩格式，它的压缩分块从 4&#215;4 到 12&#215;12 最终可以压缩到每个像素占用 1bit 以下，压缩比例有多种可选。ASTC 格式支持 RGBA，且适用于 2 的幂次方长宽等比尺寸和无尺寸要求的 NPOT（非 2 的幂次方）纹理。</em></p>



<p class="has-black-color has-text-color wp-block-paragraph">目前我最推荐使用的一种压缩格式，优点是压缩质量高，显示质量好，且Android和iOS平台都可以使用，比较省心。压缩质量从4&#215;4到12&#215;12，按需选择。</p>



<figure class="wp-block-image size-full"><img  decoding="async"  width="160"  height="261"  src="https://oss.vki.cc/wp-content/uploads/2021/07/1627659914-a905a96f-bb75-4bd8-a53a-adaaf02d9b80.png"  alt=""  class="wp-image-874"  srcset="https://cdn.vki.cc/wp-content/uploads/2021/07/1627659914-a905a96f-bb75-4bd8-a53a-adaaf02d9b80.png 160w, https://cdn.vki.cc/wp-content/uploads/2021/07/1627659914-a905a96f-bb75-4bd8-a53a-adaaf02d9b80-120x196.png 120w, https://cdn.vki.cc/wp-content/uploads/2021/07/1627659914-a905a96f-bb75-4bd8-a53a-adaaf02d9b80-90x147.png 90w"  sizes="(max-width: 160px) 100vw, 160px" ></figure>



<p class="has-black-color has-text-color wp-block-paragraph">缺点和ETC2差不多，老设备不支持。其中iOS设备从iPhone 5s才开始支持，Android主流压缩格式也正在从ETC2转向ASTC（这也是我不太理解Unity还在推荐ETC2的原因）。另一个小缺点是压缩时间稍长，不过也不是问题。</p>



<p class="has-black-color has-text-color wp-block-paragraph"><strong>所以长期来看，ASTC将一统江湖！</strong></p>



<h2 id="%e7%bb%93%e8%ae%ba" class="wp-block-heading">结论</h2>



<p class="has-black-color has-text-color wp-block-paragraph">综上来看，新项目的贴图基本可以无脑使用ASTC，这里有张图，有个大佬总结出来的。</p>



<figure class="wp-block-image size-full"><img  fetchpriority="high"  decoding="async"  width="720"  height="337"  src="https://oss.vki.cc/wp-content/uploads/2021/07/1627660332-8650276a-f037-48d0-a24c-48714dc8e316.jpg"  alt=""  class="wp-image-876"  srcset="https://cdn.vki.cc/wp-content/uploads/2021/07/1627660332-8650276a-f037-48d0-a24c-48714dc8e316.jpg 720w, https://cdn.vki.cc/wp-content/uploads/2021/07/1627660332-8650276a-f037-48d0-a24c-48714dc8e316-120x56.jpg 120w, https://cdn.vki.cc/wp-content/uploads/2021/07/1627660332-8650276a-f037-48d0-a24c-48714dc8e316-90x42.jpg 90w, https://cdn.vki.cc/wp-content/uploads/2021/07/1627660332-8650276a-f037-48d0-a24c-48714dc8e316-320x150.jpg 320w, https://cdn.vki.cc/wp-content/uploads/2021/07/1627660332-8650276a-f037-48d0-a24c-48714dc8e316-560x262.jpg 560w"  sizes="(max-width: 720px) 100vw, 720px" ></figure>



<figure class="wp-block-image size-full"><img  decoding="async"  width="618"  height="100"  src="https://oss.vki.cc/wp-content/uploads/2021/07/1627660356-adfe9ed4-3dd1-42a9-8d7d-b03aa74aab11.png"  alt=""  class="wp-image-877"  srcset="https://cdn.vki.cc/wp-content/uploads/2021/07/1627660356-adfe9ed4-3dd1-42a9-8d7d-b03aa74aab11.png 618w, https://cdn.vki.cc/wp-content/uploads/2021/07/1627660356-adfe9ed4-3dd1-42a9-8d7d-b03aa74aab11-120x19.png 120w, https://cdn.vki.cc/wp-content/uploads/2021/07/1627660356-adfe9ed4-3dd1-42a9-8d7d-b03aa74aab11-90x15.png 90w, https://cdn.vki.cc/wp-content/uploads/2021/07/1627660356-adfe9ed4-3dd1-42a9-8d7d-b03aa74aab11-320x52.png 320w, https://cdn.vki.cc/wp-content/uploads/2021/07/1627660356-adfe9ed4-3dd1-42a9-8d7d-b03aa74aab11-560x91.png 560w"  sizes="(max-width: 618px) 100vw, 618px" ></figure>



<p class="has-black-color has-text-color wp-block-paragraph"><strong>不过尽管ASTC支持非二次幂纹理，但在时机开发中还是建议UI及美术尽量制作二次幂纹理。</strong></p>



<p class="wp-block-paragraph">具体可参考：<a href="https://www.zhihu.com/question/376921536/answer/1063272336" target="_blank" rel="noreferrer noopener">https://www.zhihu.com/question/376921536/answer/1063272336</a></p>
<p><a href="https://vki.cc/greattruth/experience/872/">Unity 3D 纹理压缩格式指南</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vki.cc/greattruth/experience/872/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>记一次由接入AppsFlyer导致海外iOS无法登录的问题</title>
		<link>https://vki.cc/greattruth/experience/861/</link>
					<comments>https://vki.cc/greattruth/experience/861/#respond</comments>
		
		<dc:creator><![CDATA[白露]]></dc:creator>
		<pubDate>Thu, 10 Jun 2021 06:11:42 +0000</pubDate>
				<category><![CDATA[咸鱼心得]]></category>
		<category><![CDATA[朝闻道]]></category>
		<category><![CDATA[AppsFlyer]]></category>
		<category><![CDATA[SDK]]></category>
		<guid isPermaLink="false">https://vki.cc/?p=861</guid>

					<description><![CDATA[<p>项目临近发布前测试发现的，具体表现为在iOS设备上，使用TapTap登录，输入完账号密码后点击登录游戏无反应，再次点击需要再次登录。</p>
<p><a href="https://vki.cc/greattruth/experience/861/">记一次由接入AppsFlyer导致海外iOS无法登录的问题</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<h3 class="wp-block-heading" id="id-记一次由接入AppsFlyerSDK导致海外iOS无法登录的问题-Bug表现"><span id="bug%e8%a1%a8%e7%8e%b0">Bug表现</span></h3>



<p class="wp-block-paragraph">服务器：海外服</p>



<p class="wp-block-paragraph">平台：iOS</p>



<p class="wp-block-paragraph">项目临近发布前测试发现的，具体表现为在iOS设备上，使用TapTap登录，输入完账号密码后点击登录游戏无反应，再次点击需要再次登录。</p>



<h3 class="wp-block-heading" id="id-记一次由接入AppsFlyerSDK导致海外iOS无法登录的问题-排查过程"><span id="%e6%8e%92%e6%9f%a5%e8%bf%87%e7%a8%8b">排查过程</span></h3>



<p class="wp-block-paragraph">查找过程中发现成功拉起TapTap登录没有异常，但是在TapTap上点击登录后游戏<strong>没有收到任何回调</strong>。</p>



<p class="wp-block-paragraph">正常情况下，<strong>点击登录之后SDK会调用对应的回调方法</strong>，包括：</p>



<p class="wp-block-paragraph">登录成功（OnLoginSuccess）</p>



<p class="wp-block-paragraph">登录失败（OnLoginError）</p>



<p class="wp-block-paragraph">登录取消（OnLoginCancel）</p>



<p class="wp-block-paragraph">最终在TapSDK的同学帮助下定位了问题，本应由TapSDK传给客户端的消息被AppsFlyer拦截了。。</p>



<p class="wp-block-paragraph">相关AppsFlyer代码如下：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">//  AppsFlyerAppController.mm
//  Unity-iPhone
//
//  Created by Jonathan Wesfield on 30/07/2019.
//
 
#import &lt;Foundation/Foundation.h>
#import "UnityAppController.h"
#import "AppDelegateListener.h"
#import "AppsFlyeriOSWrapper.h"
#if __has_include(&lt;AppsFlyerLib/AppsFlyerLib.h>)
#import &lt;AppsFlyerLib/AppsFlyerLib.h>
#else
#import "AppsFlyerLib.h"
#endif
#import &lt;objc/message.h>
 
/**
 Note if you would like to use method swizzeling see AppsFlyer+AppController.m
 If you are using swizzeling then comment out the method that is being swizzeled in AppsFlyerAppController.mm
 Only use swizzeling if there are conflicts with other plugins that needs to be resolved.
*/
 
 
@interface AppsFlyerAppController : UnityAppController &lt;AppDelegateListener>
{
    BOOL didEnteredBackGround;
}
@end
 
@implementation AppsFlyerAppController
 
- (instancetype)init
{
    self = [super init];
    if (self) {
         
        id swizzleFlag = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppsFlyerShouldSwizzle"];
        BOOL shouldSwizzle = swizzleFlag ? [swizzleFlag boolValue] : NO;
         
        if(!shouldSwizzle){
            UnityRegisterAppDelegateListener(self);
        }
    }
    return self;
}
 
- (void)didFinishLaunching:(NSNotification*)notification {
    NSLog(@"got didFinishLaunching = %@",notification.userInfo);
 
 
    if (_AppsFlyerdelegate == nil) {
        _AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init];
    }
    [[AppsFlyerLib shared] setDelegate:_AppsFlyerdelegate];
 
    if (notification.userInfo[@"url"]) {
        [self onOpenURL:notification];
    }
}
 
-(void)didBecomeActive:(NSNotification*)notification {
    NSLog(@"got didBecomeActive(out) = %@", notification.userInfo);
    if (didEnteredBackGround == YES &amp;&amp; AppsFlyeriOSWarpper.didCallStart == YES) {
        [[AppsFlyerLib shared] start];
        didEnteredBackGround = NO;
    }
}
 
- (void)didEnterBackground:(NSNotification*)notification {
    NSLog(@"got didEnterBackground = %@", notification.userInfo);
    didEnteredBackGround = YES;
}
 
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler {
    [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
    [[AppsFlyerAttribution shared] continueUserActivity:userActivity restorationHandler:restorationHandler];
    return YES;
}
 
-(BOOL) application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options {
    NSLog(@"got openUrl: %@",url);
    [super application:application openURL:url options:options];
    [[AppsFlyerAttribution shared] handleOpenUrl:url options:options];
    return NO;
}
 
- (void)onOpenURL:(NSNotification*)notification {
    NSLog(@"got onOpenURL = %@", notification.userInfo);
    NSURL *url = notification.userInfo[@"url"];
    NSString *sourceApplication = notification.userInfo[@"sourceApplication"];
     
    if (sourceApplication == nil) {
        sourceApplication = @"";
    }
     
    if (url != nil) {
        [[AppsFlyerAttribution shared] handleOpenUrl:url sourceApplication:sourceApplication annotation:nil];
    }
     
}
 
- (void)didReceiveRemoteNotification:(NSNotification*)notification {
    NSLog(@"got didReceiveRemoteNotification = %@", notification.userInfo);
    [[AppsFlyerLib shared] handlePushNotification:notification.userInfo];
}
 
@end
 
IMPL_APP_CONTROLLER_SUBCLASS(AppsFlyerAppController)
 
 
/**
Note if you would not like to use IMPL_APP_CONTROLLER_SUBCLASS you can replace it with the code below.
 &lt;code>
 +(void)load
 {
 [AppsFlyerAppController plugin];
 }
  
 // Singleton accessor.
 + (AppsFlyerAppController *)plugin
 {
 static AppsFlyerAppController *sharedInstance = nil;
 static dispatch_once_t onceToken;
  
 dispatch_once(&amp;onceToken, ^{
  
 sharedInstance = [[AppsFlyerAppController alloc] init];
 });
  
 return sharedInstance;
 }
&lt;/code>
 **/</pre>



<p class="wp-block-paragraph">首先TapTap的跳转登录是通过修改UnityAppController中的openURL等相关函数实现的；</p>



<p class="wp-block-paragraph">而该SDK通过&nbsp;IMPL_APP_CONTROLLER_SUBCLASS(AppsFlyerAppController) 将自己的AppController文件替换掉了原本的UnityAppController，导致原先TapTap SDK的代码没有生效，以至于无法接收到登录回调，最后无法登录。。</p>



<h3 class="wp-block-heading" id="id-记一次由接入AppsFlyerSDK导致海外iOS无法登录的问题-解决方法"><span id="%e8%a7%a3%e5%86%b3%e6%96%b9%e6%b3%95">解决方法</span></h3>



<ol class="wp-block-list"><li>删除AppsFlyer；</li><li>在AppsFlyer的AppController中调用原UnityAppController的函数；</li></ol>



<p class="wp-block-paragraph">这两种方法都可以，国内版不需要AppsFlyer所以可以删掉。但是海外版还是需要该SDK的。</p>



<p class="wp-block-paragraph">考虑到该AppController类继承自UnityAppController，最终采用了方法2，以openURL函数为例：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">-(BOOL) application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options {
NSLog(@"got openUrl: %@",url);
[super application:application openURL:url options:options];
[[AppsFlyerAttribution shared] handleOpenUrl:url options:options];
return NO;
}</pre>



<p class="wp-block-paragraph">增加&nbsp;[super application:application openURL:url options:options];&nbsp;语句，重新调用了父类UnityAppController的openURL函数，使得游戏可以正常接受来自TapSDK的信息，成功登录。</p>



<p class="wp-block-paragraph">同时，假如以后不需要该SDK了，可以直接删除而不用修改代码（删除之后会默认使用原有的UnityAppController）。</p>



<h3 class="wp-block-heading" id="id-记一次由接入AppsFlyerSDK导致海外iOS无法登录的问题-反思"><span id="%e5%8f%8d%e6%80%9d">反思</span></h3>



<p class="wp-block-paragraph">我的锅。。其实以我的计划来说，AppsFlyer已经接入游戏，但还没有经过测试，这次出版本来不及，所以我加了开关给关掉了。</p>



<p class="wp-block-paragraph">可是万万没想到AppsFlyer SDK还有替换AppController这一手，我接了那么多SDK第一次见，还是我太naive了。。。</p>



<p class="wp-block-paragraph">辛苦几位同事大半夜还在找bug，这里说声抱歉 Orz<a href="https://confluence.xindong.com/pages/viewpage.action?pageId=193701575"></a></p>
<p><a href="https://vki.cc/greattruth/experience/861/">记一次由接入AppsFlyer导致海外iOS无法登录的问题</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vki.cc/greattruth/experience/861/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>iOS 开发踩坑日记</title>
		<link>https://vki.cc/greattruth/846/</link>
					<comments>https://vki.cc/greattruth/846/#respond</comments>
		
		<dc:creator><![CDATA[白露]]></dc:creator>
		<pubDate>Wed, 14 Apr 2021 07:08:57 +0000</pubDate>
				<category><![CDATA[咸鱼心得]]></category>
		<category><![CDATA[朝闻道]]></category>
		<category><![CDATA[iOS]]></category>
		<guid isPermaLink="false">https://vki.cc/?p=846</guid>

					<description><![CDATA[<p>XXX must be used from main thread only. 问题场景：某游戏在播放视频时闪退。连接 pc 后使用 XCode 运行，最终停在代码段： 并且提示 XXX must be used from main thread only. 该错误会导致 app 闪退，如&#8230;</p>
<p><a href="https://vki.cc/greattruth/846/">iOS 开发踩坑日记</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<h4 id="xxx-must-be-used-from-main-thread-only" class="has-vivid-red-color has-text-color wp-block-heading">XXX must be used from main thread only.</h4>



<p class="has-black-color has-text-color wp-block-paragraph">问题场景：某游戏在播放视频时闪退。连接 pc 后使用 XCode 运行，最终停在代码段：</p>



<figure class="wp-block-image size-large"><img  loading="lazy"  decoding="async"  width="960"  height="192"  src="https://oss.vki.cc/wp-content/uploads/2021/04/1618383962-image.png"  alt=""  class="wp-image-847"  title="image.png"  srcset="https://cdn.vki.cc/wp-content/uploads/2021/04/1618383962-image.png 960w, https://cdn.vki.cc/wp-content/uploads/2021/04/1618383962-image-800x160.png 800w, https://cdn.vki.cc/wp-content/uploads/2021/04/1618383962-image-120x24.png 120w, https://cdn.vki.cc/wp-content/uploads/2021/04/1618383962-image-90x18.png 90w, https://cdn.vki.cc/wp-content/uploads/2021/04/1618383962-image-320x64.png 320w, https://cdn.vki.cc/wp-content/uploads/2021/04/1618383962-image-560x112.png 560w"  sizes="auto, (max-width: 960px) 100vw, 960px" ></figure>



<p class="has-black-color has-text-color wp-block-paragraph">并且提示 XXX must be used from main thread only.</p>



<p class="has-black-color has-text-color wp-block-paragraph">该错误会导致 app 闪退，如题，应该将语句放到主线程中执行。</p>



<p class="has-black-color has-text-color wp-block-paragraph">这里使用 dispatch_async 分发线程：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">dispatch_async(dispatch_get_main_queue(), ^{
        // 代码放这里。
    });</pre>



<p class="has-black-color has-text-color wp-block-paragraph">最终代码：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 自動消灯を無効にする
void UI_SetIdleTimerDisabled(bool b)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        // 主线程执行。
        [[UIApplication sharedApplication] setIdleTimerDisabled:b];
    });
}</pre>



<h4 id="%e7%bb%8f%e8%bf%87-apple-developer-enterprise-program-%e9%87%8d%e7%ad%be%e7%9a%84-app-%e9%97%aa%e9%80%80" class="has-vivid-red-color has-text-color wp-block-heading">经过 Apple Developer Enterprise Program 重签的 app 闪退</h4>



<p class="wp-block-paragraph">Apple Developer Enterprise Program 可让大型组织开发内部专属的 app 并向其员工部署。此计划仅适用于需要使用安全的内部系统或经由移动设备管理解决方案，向员工私密地直接分发的特例情况。</p>



<p class="has-black-color has-text-color wp-block-paragraph">一般游戏公司都会使用这个服务，绕过 APP Store 来给测试设备安装 app。我们公司也是，某天发现一个游戏上传到该平台重签后，在手机上出现闪退情况，但是使用 PC 连接真机安装没有问题。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 报错如下。
Bootstrapping failed for &lt;FBApplicationProcess: 0x10a253640; application&lt;com.xd.collections>:&lt;invalid>> with error: &lt;NSError: 0x28071d920; domain: RBSRequestErrorDomain; code: 5; reason: "Launch failed."> {
    underlyingError = &lt;NSError: 0x28071d140; domain: NSPOSIXErrorDomain; code: 88>;
}</pre>



<p class="has-luminous-vivid-orange-color has-text-color has-medium-font-size wp-block-paragraph"><strong>解决方法：关闭 bitcode &#8230;</strong></p>



<p class="wp-block-paragraph">说实话，我确实没往这方面想。。我以为 Apple Developer Enterprise Program 只是单纯的换个证书重签名，就像 apk 重签那样。</p>
<p><a href="https://vki.cc/greattruth/846/">iOS 开发踩坑日记</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vki.cc/greattruth/846/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>网站迁移笔记</title>
		<link>https://vki.cc/greattruth/experience/819/</link>
					<comments>https://vki.cc/greattruth/experience/819/#respond</comments>
		
		<dc:creator><![CDATA[白露]]></dc:creator>
		<pubDate>Fri, 02 Apr 2021 04:23:27 +0000</pubDate>
				<category><![CDATA[咸鱼心得]]></category>
		<category><![CDATA[朝闻道]]></category>
		<category><![CDATA[CDN]]></category>
		<category><![CDATA[Nginx]]></category>
		<category><![CDATA[WordPress]]></category>
		<guid isPermaLink="false">https://vki.cc/?p=819</guid>

					<description><![CDATA[<p>为了网站访问速度以及之后展示摄影照片，近期把服务器从香港迁移到了上海。过程中踩了不少坑，以此来记录一下。 运行环境 网站基于WordPress搭建，WordPress需要PHP，MySQL以及Apache（或者Nginx）。 旧服务&#8230;</p>
<p><a href="https://vki.cc/greattruth/experience/819/">网站迁移笔记</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<pre class="wp-block-preformatted">为了网站访问速度以及之后展示摄影照片，近期把服务器从香港迁移到了上海。过程中踩了不少坑，以此来记录一下。</pre>



<h2 class="wp-block-heading" id="5T9kc"><span id="%e8%bf%90%e8%a1%8c%e7%8e%af%e5%a2%83">运行环境</span></h2>



<p class="has-black-color has-text-color wp-block-paragraph">网站基于WordPress搭建，WordPress需要PHP，MySQL以及Apache（或者Nginx）。</p>



<p class="has-black-color has-text-color wp-block-paragraph">旧服务器使用CentOS加宝塔面板，使用简单方便，堪称傻瓜式操作。。缺点就是占用资源，还会可能出现漏洞，推荐小白或者不喜欢折腾的用户使用。</p>



<figure class="wp-block-table"><table><tbody><tr><td class="has-text-align-center" data-align="center">项目</td><td class="has-text-align-center" data-align="center">旧服务器</td><td class="has-text-align-center" data-align="center">新服务器</td></tr><tr><td class="has-text-align-center" data-align="center">配置</td><td class="has-text-align-center" data-align="center">1C1G30M</td><td class="has-text-align-center" data-align="center">1C2G5M</td></tr><tr><td class="has-text-align-center" data-align="center">系统</td><td class="has-text-align-center" data-align="center">CentOS（BT-Panel）</td><td class="has-text-align-center" data-align="center">Aliyun Linux 2.19.03</td></tr><tr><td class="has-text-align-center" data-align="center">地理位置</td><td class="has-text-align-center" data-align="center">香港</td><td class="has-text-align-center" data-align="center">上海</td></tr><tr><td class="has-text-align-center" data-align="center">WordPress</td><td class="has-text-align-center" data-align="center">5.7</td></tr><tr><td class="has-text-align-center" data-align="center">PHP</td><td class="has-text-align-center" data-align="center">7.4</td></tr><tr><td class="has-text-align-center" data-align="center">Nginx</td><td class="has-text-align-center" data-align="center">1.18</td><td class="has-text-align-center" data-align="center">1.16</td></tr><tr><td class="has-text-align-center" data-align="center">MySQL</td><td class="has-text-align-center" data-align="center">MySQL 5.6</td><td class="has-text-align-center" data-align="center">阿里云 云数据库RDS（MySQL 8.0）</td></tr><tr><td class="has-text-align-center" data-align="center">OSS</td><td class="has-text-align-center" data-align="center">阿里云OSS</td></tr><tr><td class="has-text-align-center" data-align="center">CDN</td><td class="has-text-align-center" data-align="center">无</td><td class="has-text-align-center" data-align="center">阿里云CDN/DCDN加速</td></tr></tbody></table><figcaption>对比</figcaption></figure>



<p class="has-black-color has-text-color wp-block-paragraph">相较于旧的，新服务器移除了宝塔面板，系统和服务更加精简，且使用了CDN加速，缺点就是大陆服务器需要备案，一套流程走下来费时费力。。</p>



<h2 class="wp-block-heading" id="LJiw8"><span id="%e5%ae%89%e8%a3%85nginx">安装Nginx</span></h2>



<p class="has-black-color has-text-color wp-block-paragraph">注意：以下内容基于Aliyun Linux系统，不过CenOS应该也能用。</p>



<p class="has-black-color has-text-color wp-block-paragraph">老规矩，先更新软件。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">yum update</pre>



<p class="has-black-color has-text-color wp-block-paragraph">自带的yum源就有nginx，直接安装即可。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">yum -y install nginx</pre>



<figure class="wp-block-image size-large"><img  loading="lazy"  decoding="async"  width="1024"  height="442"  src="https://oss.vki.cc/wp-content/uploads/2021/04/1617359633-20210402-11.png"  alt=""  class="wp-image-843"  title="image.png" ></figure>



<p class="has-black-color has-text-color wp-block-paragraph">安装完成之后使用以下命令添加到开机启动项。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">systemctl enable nginx</pre>



<p class="has-black-color has-text-color wp-block-paragraph">然后启动nginx服务。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">systemctl start nginx</pre>



<p class="has-black-color has-text-color wp-block-paragraph">其他命令：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 关闭Nginx
systemctl stop nginx

// 重启Nginx
systemctl restart nginx</pre>



<p class="has-black-color has-text-color wp-block-paragraph">启动Nginx之后，打开浏览器，地址栏输入服务器的IP并回车，不出意外就能看到成功安装Nginx的提示了。</p>



<figure class="wp-block-image size-large"><img  loading="lazy"  decoding="async"  width="602"  height="222"  src="https://oss.vki.cc/wp-content/uploads/2021/04/1617336538-2021-04-02-2.png"  alt=""  class="wp-image-821"  title="image.png" ></figure>



<h2 class="wp-block-heading" id="rMSMx"><span id="%e5%ae%89%e8%a3%85php">安装PHP</span></h2>



<p class="has-black-color has-text-color wp-block-paragraph">这一步稍显麻烦，首先查看是否有适合的php版本：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">yum list php</pre>



<p class="has-black-color has-text-color wp-block-paragraph">然后发现只有5.4的版本，这肯定是不行的，推荐使用php 7以上。</p>



<figure class="wp-block-image size-large"><img  loading="lazy"  decoding="async"  width="1038"  height="78"  src="https://oss.vki.cc/wp-content/uploads/2021/04/1617336539-2021-04-02-3.png"  alt=""  class="wp-image-822"  title="image.png" ></figure>



<p class="wp-block-paragraph">安装remi</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">yum install http://rpms.remirepo.net/enterprise/remi-release-7.rpm</pre>



<p class="has-black-color has-text-color wp-block-paragraph">重建yum缓存，可以提高检索速度。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">yum clean all
yum makecache</pre>



<p class="has-black-color has-text-color wp-block-paragraph">进入正题，安装php全家桶。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">yum install -y php74-php-fpm php74-php-cli php74-php-bcmath php74-php-gd php74-php-json php74-php-mbstring php74-php-mcrypt php74-php-mysqlnd php74-php-opcache php74-php-pdo php74-php-pecl-crypto php74-php-pecl-mcrypt php74-php-pecl-geoip php74-php-recode php74-php-snmp php74-php-soap php74-php-xmll</pre>



<p class="has-black-color has-text-color wp-block-paragraph">注意，这行是安装php7.4的。如果想安装7.3版本，将上述命令中的74替换成73即可。然后</p>



<p class="has-black-color has-text-color wp-block-paragraph">然后开机启动。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">systemctl enable php74-php-fpm</pre>



<p class="has-black-color has-text-color wp-block-paragraph">启动php服务。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">systemctl start php74-php-fpm</pre>



<p class="has-black-color has-text-color wp-block-paragraph">这里面最坑的就是php74-php-fpm这个服务名。。特么的试了好多次才试出来，不知道是不是系统是阿里云的原因。</p>



<p class="has-black-color has-text-color wp-block-paragraph">如果是CentOS，这个服务名应该是php-fpm.service</p>



<h2 class="wp-block-heading" id="KA9tT"><span id="%e9%85%8d%e7%bd%aenginx">配置Nginx</span></h2>



<p class="has-black-color has-text-color wp-block-paragraph">到这一部，已经装好了Nginx和PHP，但是还有一些问题。</p>



<p class="has-black-color has-text-color wp-block-paragraph">新建一个名为info.php的文件，内容如下：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&amp;lt;?php 
    phpinfo(); 
?&amp;gt;</pre>



<p class="has-black-color has-text-color wp-block-paragraph">将该文件上传到网站根目录/usr/share/nginx/html，然后使用浏览器打开 服务器IP/info.php ，正常情况下会显示php的详细信息，但是现在会发现浏览器把info.php文件下载下来了。。</p>



<p class="has-black-color has-text-color wp-block-paragraph">原因是Nginx配置错误。</p>



<p class="has-black-color has-text-color wp-block-paragraph">打开nginx的配置文件 /etc/nginx/ngin.conf，里面有如下配置：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
        }

        error_page 404 /404.html;
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
    }</pre>



<p class="has-black-color has-text-color wp-block-paragraph">其中root &nbsp; &nbsp; &nbsp; &nbsp; /usr/share/nginx/html; 就是指网站的根目录。</p>



<p class="has-black-color has-text-color wp-block-paragraph">在server { } 区块内添加如下代码：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">location ~ .*\.php$ {
  fastcgi_pass 127.0.0.1:9000;
  fastcgi_index index.php;
  fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  include fastcgi_params;
  }</pre>



<p class="has-black-color has-text-color wp-block-paragraph">这段代码意思是让php后缀的文件传入后端php服务处理，添加之后重启nginx服务。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">systemctl restart nginx</pre>



<p class="has-black-color has-text-color wp-block-paragraph">再次访问 IP/info.php，页面也正常显示。</p>



<figure class="wp-block-image size-large"><img  loading="lazy"  decoding="async"  width="1892"  height="762"  src="https://oss.vki.cc/wp-content/uploads/2021/04/1617336540-2021-04-02-4.png"  alt=""  class="wp-image-823"  title="image.png" ></figure>



<p class="has-black-color has-text-color wp-block-paragraph">其中<strong>Loaded Configuration File</strong>是php的配置信息目录，以后修改WordPress的文件上传大小限制会用得到。</p>



<h2 class="wp-block-heading" id="Mm4bu"><span id="%e9%85%8d%e7%bd%aemysql%e6%95%b0%e6%8d%ae%e5%ba%93">配置MySQL数据库</span></h2>



<p class="has-black-color has-text-color wp-block-paragraph">为了追求安全和性能，这次不再在本地搭建MySQL，转而使用阿里云的数据库服务。</p>



<p class="has-black-color has-text-color wp-block-paragraph">登录阿里云RDS，创建完用户和数据库之后登陆DMS后台，将备份的sql文件进行导入。</p>



<p class="has-black-color has-text-color wp-block-paragraph">（如果是第一次搭建网站，这一步可以略过）</p>



<figure class="wp-block-image size-large"><img  loading="lazy"  decoding="async"  width="1526"  height="1136"  src="https://oss.vki.cc/wp-content/uploads/2021/04/1617336541-2021-04-02-5.png"  alt=""  class="wp-image-824"  title="image.png" ></figure>



<p class="has-black-color has-text-color wp-block-paragraph">如果新网站的域名和旧的不一样，或者域名还未解析（比如我的还在备案，无法解析到大陆服务器），就要手动改一下数据库里存储的网站地址，不然打开新网站会自动跳转到旧网站。</p>



<p class="has-black-color has-text-color wp-block-paragraph">在DMS后台使用以下命令查询：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">SELECT * FROM `wp_options`</pre>



<figure class="wp-block-image size-large"><img  loading="lazy"  decoding="async"  width="1818"  height="690"  src="https://oss.vki.cc/wp-content/uploads/2021/04/1617336542-2021-04-02-6.png"  alt=""  class="wp-image-825"  title="image.png" ></figure>



<p class="has-black-color has-text-color wp-block-paragraph">点击开启编辑，将siteurl和home字段改为新的服务器地址。</p>



<p class="has-black-color has-text-color wp-block-paragraph">如果用的其他数据库管理a系统没有可视化界面，也可以使用命令行修改：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">UPDATE `数据库名`.`wp_options` SET `option_value`='新网站地址' WHERE `option_id`=1;
UPDATE `数据库名`.`wp_options` SET `option_value`='新网站地址' WHERE `option_id`=2;</pre>



<p class="has-black-color has-text-color wp-block-paragraph">另外记得将服务器IP地址加入白名单，否则无法访问。</p>



<figure class="wp-block-image size-large"><img  loading="lazy"  decoding="async"  width="1050"  height="1372"  src="https://oss.vki.cc/wp-content/uploads/2021/04/1617336543-2021-04-02-7.png"  alt=""  class="wp-image-826"  title="image.png" ></figure>



<p class="wp-block-paragraph">设置完毕之后可以在服务器上ping一下数据库地址，如果能ping通，代表能正常访问。</p>



<h2 class="wp-block-heading" id="kQrsJ"><span id="%e5%af%bc%e5%85%a5wordpress">导入WordPress</span></h2>



<p class="has-black-color has-text-color wp-block-paragraph">打开备份好的WordPress目录里的wp-config.php文件，修改数据库配置。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', '数据库名' );

/** MySQL database username */
define( 'DB_USER', '用户名' );

/** MySQL database password */
define( 'DB_PASSWORD', '密码' );

/** MySQL hostname */
define( 'DB_HOST', '数据库地址' );

/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );

/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );</pre>



<p class="has-black-color has-text-color wp-block-paragraph">然后将所有文件上传到服务器根目录。</p>



<p class="has-black-color has-text-color wp-block-paragraph">（如果是第一次搭建网站，直接使用WordPress官网下载的文件即可）</p>



<p class="has-black-color has-text-color wp-block-paragraph">上传完毕后使用浏览器打开网址，可能会出现403错误，这个时候需要继续修改nginx配置。</p>



<p class="has-black-color has-text-color wp-block-paragraph">打开nginx.conf，在location / {&nbsp;} 区块内添加代码如下：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">location / {
    index index.php index.html index.htm;
}</pre>



<p class="has-black-color has-text-color wp-block-paragraph">然后重启nginx，再次打开网址就正常了。</p>



<p class="has-black-color has-text-color wp-block-paragraph">如果什么都看不到，可能是数据库配置出了问题，建议删除网站根目录的wp-config.php文件，重新打开网址一步步配置。</p>



<h2 class="wp-block-heading" id="RW7LU"><span id="%e5%85%b6%e4%bb%96%e7%9a%84%e5%9d%91">其他的坑</span></h2>



<p class="has-vivid-red-color has-text-color wp-block-paragraph">WordPress-设置-固定链接只能只用朴素类型，其他其他的类型时打开文章或分类出现403，404错误。</p>



<p class="has-black-color has-text-color wp-block-paragraph">这个也是因为nginx配置问题，</p>



<p class="has-black-color has-text-color wp-block-paragraph">打开nginx.conf，在location / {&nbsp;} 区块内添加代码如下：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">location / {
    index index.php index.html index.htm;
  try_files $uri $uri/ /index.php?$args;
}</pre>



<p class="has-black-color has-text-color wp-block-paragraph">然后重启nginx就可以了。</p>



<p class="has-vivid-red-color has-text-color wp-block-paragraph">更新插件/系统或上传文件提示没有权限，要输入FTP账号。</p>



<p class="has-black-color has-text-color wp-block-paragraph">这个就更坑了。。本质上是Linux系统的权限问题，WordPress没有访问自身文件夹的权限，解决方法有两个：</p>



<p class="has-black-color has-text-color wp-block-paragraph">1.将网站根目录权限改为777；</p>



<p class="has-black-color has-text-color wp-block-paragraph">网上很多教程都是这么建议的，讲道理，但凡有点安全方面的尝试，就不会这么做。</p>



<p class="has-black-color has-text-color wp-block-paragraph">2.创建FTP服务和账号；</p>



<p class="has-black-color has-text-color wp-block-paragraph">这个可以用，但是太麻烦了。。</p>



<p class="has-black-color has-text-color wp-block-paragraph">2.将网站根目录所有者改为WordPress</p>



<p class="has-black-color has-text-color wp-block-paragraph">简单好用，就它了。蛋疼的事情来了，我需要知道WordPress的user名是什么，但这个user名既不是WordPress又不是root，更不是nginx，也不是网上说的www，www-data。</p>



<p class="has-black-color has-text-color wp-block-paragraph">最终试了一圈，嘿！居然是 apache</p>



<p class="has-black-color has-text-color wp-block-paragraph">apache？？？我不是用的nginx吗？？</p>



<p class="has-black-color has-text-color wp-block-paragraph">结果就是，使用chown命令赋予权限。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">chown -R apache:apache 网站根目录</pre>



<p class="wp-block-paragraph">设置完成后可使用ll命令查看：</p>



<figure class="wp-block-image size-large"><img  loading="lazy"  decoding="async"  width="1032"  height="534"  src="https://oss.vki.cc/wp-content/uploads/2021/04/1617336544-2021-04-02-8.png"  alt=""  class="wp-image-827"  title="image.png" ></figure>



<p class="has-vivid-red-color has-text-color wp-block-paragraph">​使用www.域名方式访问网站报403错误</p>



<p class="has-black-color has-text-color wp-block-paragraph">比如我的网站域名是 vki.cc，本意是想使用 www.vki.cc 也能访问网站。于是在域名解析那里使用 cname 将 www.vki.cc 指向 vki.cc，就有了403错误。</p>



<p class="has-black-color has-text-color wp-block-paragraph">尝试了下，将解析方式改为A记录指向服务器IP就能访问了。</p>



<p class="has-black-color has-text-color wp-block-paragraph">虽然可能造成CDN无法加速，不过问题不大。</p>



<p class="has-vivid-red-color has-text-color wp-block-paragraph">主机记录为@时，记录类型 cname 与邮箱解析 MX 冲突</p>



<p class="has-black-color has-text-color wp-block-paragraph">​换句话说，如果我使用CDN服务，就不能同时<strong>以 vki.cc 访问网站</strong>和<strong>使用 vki.cc 域名邮箱</strong>。</p>



<p class="has-black-color has-text-color wp-block-paragraph">这是我没有料到的，以前没有用CDN的时候，域名直接A记录指向服务器IP，同时使用域名邮箱没有问题。现在这两个冲突，只能选一个，只好放弃邮箱了Orz</p>



<p class="has-black-color has-text-color wp-block-paragraph">搜了一下，有些域名服务商使用link表什么的骚操作可以避免冲突，不过我邮箱用得少，而且转出域名太麻烦，就懒得折腾了，有兴趣可以了解下。</p>



<h2 class="wp-block-heading" id="2luDB"><span id="cdn%e5%8a%a0%e9%80%9f">CDN加速</span></h2>



<p class="wp-block-paragraph">这个有空写个文章详细说，先放两张结构图吧，一个前，一个后。</p>



<p class="wp-block-paragraph"><strong>未使用CDN</strong></p>



<figure class="wp-block-image size-large"><img  loading="lazy"  decoding="async"  width="746"  height="361"  src="https://oss.vki.cc/wp-content/uploads/2021/04/1617336545-2021-04-02-9.png"  alt=""  class="wp-image-828"  title="image.png" ></figure>



<p class="wp-block-paragraph"><strong>使用CDN</strong></p>



<figure class="wp-block-image size-large"><img  loading="lazy"  decoding="async"  width="1440"  height="726"  src="https://oss.vki.cc/wp-content/uploads/2021/04/1617336546-2021-04-02-10.png"  alt=""  class="wp-image-829"  title="image.png" ></figure>
<p><a href="https://vki.cc/greattruth/experience/819/">网站迁移笔记</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vki.cc/greattruth/experience/819/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Unity 内存管理</title>
		<link>https://vki.cc/greattruth/engine/731/</link>
					<comments>https://vki.cc/greattruth/engine/731/#respond</comments>
		
		<dc:creator><![CDATA[白露]]></dc:creator>
		<pubDate>Wed, 22 Jul 2020 12:10:38 +0000</pubDate>
				<category><![CDATA[引擎相关]]></category>
		<category><![CDATA[Unity]]></category>
		<category><![CDATA[Unity 3D]]></category>
		<category><![CDATA[优化]]></category>
		<category><![CDATA[内存]]></category>
		<category><![CDATA[原创]]></category>
		<guid isPermaLink="false">https://bye6.cc/?p=731</guid>

					<description><![CDATA[<p>我胡汉三又回来啦！~ 内存交换 标准交换在内存与备份存储之间移动进程，备份存储通常是快速磁盘，它应足够大，以容纳所有用户的所有内存映像的副本，并且它应提供对这些存储器映像的直接访问。 移动设备不支&#8230;</p>
<p><a href="https://vki.cc/greattruth/engine/731/">Unity 内存管理</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<pre class="wp-block-preformatted">我胡汉三又回来啦！~</pre>



<h4 class="wp-block-heading" id="o8CfU"><span id="%e5%86%85%e5%ad%98%e4%ba%a4%e6%8d%a2">内存交换</span></h4>



<p class="has-text-align-left has-black-color has-text-color wp-block-paragraph">标准交换在内存与备份存储之间移动进程，备份存储通常是快速磁盘，它应足够大，以容纳所有用户的所有内存映像的副本，并且它应提供对这些存储器映像的直接访问。</p>



<p class="has-black-color has-text-color wp-block-paragraph"><strong>移动设备不支持任何形式的内存交换。</strong></p>



<ul class="wp-block-list"><li>移动设备通常采用闪存，而不是空间更大的硬盘作为它的永久存储。导致的空间约束是移动操作系统设计者避免交换的原因之一；</li><li>闪存写入次数的限制；</li><li>内存与闪存之间的吞吐量差；</li></ul>



<p class="has-black-color has-text-color wp-block-paragraph">当空闲内存降低到一定阈值以下时：</p>



<ul class="wp-block-list"><li>iOS：不釆用交换，而是要求应用程序自愿放弃分配的内存。只读数据（如代码）可从系统中删除，以后如有必要再从闪存重新加载。已修改的数据（如堆栈）不会被删除。然而，操作系统可以终止任何未能释放足够内存的应用程序。</li><li>Android：不支持交换，釆用类似 iOS 使用的策略。如果没有足够可用的空闲内存，则它可以终止进程。然而，在终止进程之前，Android 将其应用程序状态写到闪存，以便它能快速重新启动。</li></ul>



<p class="has-black-color has-text-color wp-block-paragraph">iOS支持内存压缩，Android不可以。</p>



<p class="has-black-color has-text-color wp-block-paragraph">Android 内存单位为Page，大小一般为4k，以分配单位回收，分为用户态和内核态，不可越界访问。</p>



<p class="has-black-color has-text-color wp-block-paragraph">Low Memory Killier：内存杀手。</p>



<p class="has-black-color has-text-color wp-block-paragraph"><strong>Android服务优先级（从高到低），当内存不够时从优先级低的开始销毁</strong>：</p>



<ul class="wp-block-list"><li>Native</li><li>System 当杀到这一层级的时候手机重启</li><li>Persistent</li><li>Foreground 前台应用，具体表现为APP闪退</li><li>Perceptible</li><li>Service</li><li>Home 桌面</li><li>Previous 上一个使用的APP</li><li>Cached 后台APP</li></ul>



<h4 class="wp-block-heading" id="36R9c"><span id="unity%e5%86%85%e5%ad%98">Unity内存</span></h4>



<p class="has-black-color has-text-color wp-block-paragraph">按类型分为 <strong>Native Memory </strong>和<strong> Managed Memory</strong></p>



<p class="has-black-color has-text-color wp-block-paragraph">Editor与Runtime不同</p>



<ul class="has-cyan-bluish-gray-color has-text-color wp-block-list"><li data-lake-id="11ad0f3d261d1bdfaad1f6170d68b6da">Editor模式会尽可能的加载资源（启动较慢）</li><li data-lake-id="920753378d0cef5c0e9628ab6efaa826">Runtime按需加载</li></ul>



<p class="has-black-color has-text-color wp-block-paragraph">Unity封装了C++中与申请内存相关的函数，并添加了Label用于统计。</p>



<h4 class="wp-block-heading" id="FzmP4"><span id="audio">Audio</span></h4>



<p class="wp-block-paragraph"><strong>DSP Buffer</strong></p>



<ul class="wp-block-list"><li data-lake-id="b320a7254b16d587a6f73bceb02ab68b">过大会导致声音有延迟</li><li data-lake-id="988feb3af3915b96c8fd2c5133f0b1ab">过小会使CPU负担上升</li></ul>



<p class="wp-block-paragraph"><strong>Force to mono</strong></p>



<p class="wp-block-paragraph">双声道改单声道（尽量，绝大多数声音不需要双声道）</p>



<p class="wp-block-paragraph"><strong>Compress Format</strong></p>



<h4 class="wp-block-heading" id="e3S92"><span id="code-size">Code Size</span></h4>



<p class="wp-block-paragraph">慎用模板泛型，</p>



<h4 class="wp-block-heading" id="UfI9X"><span id="assetbundle">AssetBundle</span></h4>



<p class="wp-block-paragraph">Type Tree</p>



<p class="wp-block-paragraph">当Unity版本与APP版本一致时可以关掉</p>



<ul class="wp-block-list"><li style="list-style-type: none;">
<ul data-lake-id="3e53979f08d5f031e7fefefd919b79c4">
<li data-lake-id="6feee38208c0af4aca7e153e5c19465c">减小内存</li>
<li data-lake-id="2f7a8c2489975976f9ad934aa76d1b4b">减小包大小</li>
<li data-lake-id="47ac6b264aafb950d9d0f23e668ee5e0">Build和Runtime变快</li>
</ul>
</li></ul>



<p class="wp-block-paragraph"><strong>压缩方式</strong></p>



<p class="wp-block-paragraph">LZ4 推荐</p>



<p class="wp-block-paragraph">LZMA 弃用</p>



<p class="wp-block-paragraph"><strong>extra</strong></p>



<p class="wp-block-paragraph">不建议每个文件打一个AssetBundle包</p>



<p class="wp-block-paragraph">因为它们有相同的“头”，仅数据部分不同，容易浪费空间。</p>



<h4 class="wp-block-heading" id="dNP1Y"><span id="resources">Resources</span></h4>



<p class="wp-block-paragraph">通过红黑树进行索引，每次启动游戏都要分析加载红黑树，拖慢启动时间。其次整个游戏生命周期都会存在与内存中。</p>



<h4 class="wp-block-heading" id="KpCjw"><span id="texture">Texture</span></h4>



<p class="wp-block-paragraph"><strong>Upload Buffer</strong></p>



<p class="wp-block-paragraph"><strong>Read&amp;Write</strong></p>



<p class="wp-block-paragraph">没有特殊需求就关掉write</p>



<p class="wp-block-paragraph"><strong>Mipmap</strong></p>



<p class="wp-block-paragraph">同样没有特殊需求就关掉</p>



<h4 class="wp-block-heading" id="x73lJ"><span id="mesh">Mesh</span></h4>



<p class="wp-block-paragraph"><strong>Read&amp;Write</strong></p>



<p class="wp-block-paragraph"><strong>Compresssion</strong></p>



<h3 class="wp-block-heading" id="rbJ5r"><span id="managed%e5%86%85%e5%ad%98">Managed内存</span></h3>



<p class="has-black-color has-text-color wp-block-paragraph">VM内存池</p>



<p class="has-black-color has-text-color wp-block-paragraph">VM会返还内存给OS</p>



<p class="has-black-color has-text-color wp-block-paragraph">条件：一个block连续6次GC没被访问到</p>



<h4 class="wp-block-heading" id="epv2S"><span id="unity-gc-%e7%ae%97%e6%b3%95">Unity GC 算法</span></h4>



<pre class="wp-block-preformatted">貌似不采用CLR的GC机制_(:з」∠)_（参见另一篇文章，传送门）</pre>



<p class="has-black-color has-text-color wp-block-paragraph"><strong>Boehm GC</strong></p>



<p class="has-black-color has-text-color wp-block-paragraph">旧版垃圾回收方式，新版Unity已弃用。</p>



<p class="has-black-color has-text-color wp-block-paragraph"><strong>不分代</strong>（速度更快）</p>



<p class="has-black-color has-text-color wp-block-paragraph"><strong>不压缩</strong>（因为一些历史原因。。跟mono有关）</p>



<p class="has-black-color has-text-color wp-block-paragraph"><strong>Incremental GC（渐进式GC）</strong></p>



<p class="has-black-color has-text-color wp-block-paragraph">Unity2019.3之后使用，新一代垃圾回收算法。</p>



<p class="has-black-color has-text-color wp-block-paragraph">主要目的为了解决旧版GC导致主线程卡顿（旧版GC时会暂停主线程）的问题，<strong>分帧解决，且分代</strong></p>



<h4 class="wp-block-heading" id="4mYDq"><span id="gc%e6%9c%ba%e5%88%b6%e8%80%83%e9%87%8f">GC机制考量</span></h4>



<ul class="wp-block-list"><li data-lake-id="660957963fddbea01fd4ec0bcda75a61">回收能力</li><li data-lake-id="13580033e8e418799d5f260ba564a88d">暂停时长</li><li data-lake-id="522b2a4651dc451b9655e3c8fdf7d6cf">碎片化</li><li data-lake-id="2547a49c18f9244054ccb157d5bc822c">额外消耗</li><li data-lake-id="fdb31eab183fabe0398e946148dd5263">可扩展性</li><li data-lake-id="f3c2da301939823205f12211aedc759a">可移植性</li></ul>



<h4 class="wp-block-heading" id="J5Onq"><span id="%e5%86%85%e5%ad%98%e7%a2%8e%e7%89%87%e5%8c%96">内存碎片化</span></h4>



<p class="has-black-color has-text-color wp-block-paragraph">内存碎片通常分为内部碎片和外部碎片：</p>



<ol class="wp-block-list"><li>内部碎片是由于采用固定大小的内存分区，当一个进程不能完全使用分给它的固定内存区域时就产生了内部碎片，通常内部碎片难以完全避免；</li><li>外部碎片是由于某些未分配的连续内存区域太小，以至于不能满足任意进程的内存分配请求，从而不能被进程利用的内存区域。</li></ol>



<p class="has-black-color has-text-color wp-block-paragraph"><strong>先加载消耗大内存的资源，再加载小内存</strong></p>



<h4 class="wp-block-heading" id="FPdj0"><span id="%e5%83%b5%e5%b0%b8%e5%86%85%e5%ad%98">僵尸内存</span></h4>



<ul class="wp-block-list"><li data-lake-id="bc5462d9e5d49a7e6c32d79622a65214">无用内存</li><li data-lake-id="14915a15a5f640a5f8e01cbe11540cca">没有释放</li><li data-lake-id="796e8d72732ba18a118b944039f28f34">可通过代码管理和性能工具分析</li></ul>



<h4 class="wp-block-heading" id="7Bfhj"><span id="setactive">SetActive</span></h4>



<p class="wp-block-paragraph">TODO。。。</p>



<p class="wp-block-paragraph">都说要少用，但是不清楚机制，有机会看一下Unity源码 Orz</p>



<h3 class="wp-block-heading" id="QSWFN"><span id="%e4%bc%98%e5%8c%96%e5%bb%ba%e8%ae%ae">优化建议</span></h3>



<ul class="wp-block-list"><li data-lake-id="d69d8eee51298fd6887027e6e0e00d34"><strong>使用Destroy，别用null<br></strong></li><li data-lake-id="f2c16401a975b5ae82b4a625bbf6f445"><strong>Class/Struct</strong></li></ul>



<ul class="wp-block-list"><li data-lake-id="d566bb88ddc562cd4fa58dc8340331bf">少于8个字节的数据可用Struct代替Class（存疑）</li></ul>



<ul class="wp-block-list"><li data-lake-id="fdc6d72d868ae679fe6380d181f3b88d"><strong>池中池</strong></li><li data-lake-id="4334e0ca514037df778864799063153d"><strong>闭包和匿名函数</strong></li></ul>



<ul class="wp-block-list"><li data-lake-id="1250b809d7a3beea6d2bac105f387a0d">少用，在IL2CPP里这些都会new class</li><li data-lake-id="413a0dbfac105ff19edbffae41a3061e"><strong>协程</strong></li></ul>



<ul class="wp-block-list"><li data-lake-id="53c02b79234a6494d0ac6d3c4caf0935">用的时候再产生，不用的时候及时终止</li><li data-lake-id="c1e7ec9e3126fc936fc53cac7fa4b6bc">不要当线程使用</li><li data-lake-id="6d7ccc3bc281cf367da7a0f4e14430ad"><strong>配置表</strong></li></ul>



<ul class="wp-block-list"><li data-lake-id="f80c86b757dd40ebb7835f06cfb868b8">excel、json等，按需加载，不然会很占用内存空间</li><li data-lake-id="200224045513b88bcbc82c20699f1adb"><strong>单例模式 Singleton</strong></li></ul>



<ul class="wp-block-list"><li data-lake-id="82ebf0cbb49cdc3719ae3174695d3c4c">慎用，生命周期较长，占用内存</li></ul>
<p><a href="https://vki.cc/greattruth/engine/731/">Unity 内存管理</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vki.cc/greattruth/engine/731/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>C#清除委托列表的正确用法</title>
		<link>https://vki.cc/greattruth/experience/709/</link>
					<comments>https://vki.cc/greattruth/experience/709/#comments</comments>
		
		<dc:creator><![CDATA[白露]]></dc:creator>
		<pubDate>Fri, 26 Jul 2019 06:15:32 +0000</pubDate>
				<category><![CDATA[咸鱼心得]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Unity]]></category>
		<guid isPermaLink="false">https://bye6.cc/?p=709</guid>

					<description><![CDATA[<p>近期将项目Unity版本从5.6升级到了2018.4.4，在解决完一系列可见语法错误后。遇到了个很神奇的错误，这在升级前是没有的，经过排查，最后锁定在下面的代码上（dramaEndBuildMapCallBack为Action类型）： 可&#8230;</p>
<p><a href="https://vki.cc/greattruth/experience/709/">C#清除委托列表的正确用法</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">近期将项目Unity版本从5.6升级到了2018.4.4，在解决完一系列可见语法错误后。遇到了个很神奇的错误，这在升级前是没有的，经过排查，最后锁定在下面的代码上（dramaEndBuildMapCallBack为<strong>Action</strong>类型）：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">if (this.dramaEndBuildMapCallback != null)
{
	this.dramaEndBuildMapCallback();
	this.dramaEndBuildMapCallback = null;
}</pre>



<p class="wp-block-paragraph">可以看出，该块代码想表达的意思是执行完委托就清空。事实上在Unity5.6的时候它确实可以正常工作，但是随着Unity升级，.Net版本也升级到4.x，这种方式失效了。</p>



<p class="wp-block-paragraph">经过查询，找到了一个可以正常清空委托列表的方法，首先通过<strong>GetInvocationList()</strong>方法获取委托列表，然后遍历，删掉。。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">/// &lt;summary>
/// 清除委托列表。
/// &lt;/summary>
/// &lt;param name="action">&lt;/param>
public static void ClearDelegage(Action action)
{
	if (action != null)
	{
		Delegate[] delegates = action.GetInvocationList();
		for (int i = 0; i &lt; delegates.Length; i++)
		{
			action -= delegates[i] as Action;
		}
	}
}</pre>



<p class="wp-block-paragraph">好想吐槽一下，为什么C#没提供一个像链表Clear()那样的方法来清除委托列表呢_(:з」∠)_</p>
<p><a href="https://vki.cc/greattruth/experience/709/">C#清除委托列表的正确用法</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vki.cc/greattruth/experience/709/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Unity安卓项目踩坑日记</title>
		<link>https://vki.cc/greattruth/experience/692/</link>
					<comments>https://vki.cc/greattruth/experience/692/#comments</comments>
		
		<dc:creator><![CDATA[白露]]></dc:creator>
		<pubDate>Tue, 23 Apr 2019 09:38:57 +0000</pubDate>
				<category><![CDATA[咸鱼心得]]></category>
		<category><![CDATA[Unity]]></category>
		<category><![CDATA[Unity 3D]]></category>
		<category><![CDATA[原创]]></category>
		<category><![CDATA[安卓]]></category>
		<category><![CDATA[心得]]></category>
		<category><![CDATA[教程]]></category>
		<guid isPermaLink="false">https://bye6.cc/?p=692</guid>

					<description><![CDATA[<p>失踪人口回归~ 最近在研究游戏在安卓平台的打包，遇到了不少坑，特此写个文章记录下~ 1.Unity与安卓的交互 虽然Unity可以直接导出apk安装包，但由于有些方法，比如读取手机相册图片，获取权限等，或者是添加&#8230;</p>
<p><a href="https://vki.cc/greattruth/experience/692/">Unity安卓项目踩坑日记</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></description>
										<content:encoded><![CDATA[<p><span style="color: #808080;"><em>失踪人口回归~</em></span></p>
<blockquote><p>最近在研究游戏在安卓平台的打包，遇到了不少坑，特此写个文章记录下~</p></blockquote>
<h2 id="1-unity%e4%b8%8e%e5%ae%89%e5%8d%93%e7%9a%84%e4%ba%a4%e4%ba%92">1.Unity与安卓的交互</h2>
<p>虽然Unity可以直接导出apk安装包，但由于有些方法，比如读取手机相册图片，获取权限等，或者是添加SDK都不能直接通过Unity实现。我们一般用Unity导出安卓工程，再通过Android Studio修改之后打出apk包。</p>
<p>通常情况下每次打包都需要修改，为了偷懒~</p>
<p>这里提供个堪称一劳永逸的解决办法，请看文章~</p>
<p><span style="color: #ff6600;"><a style="color: #ff6600;" href="https://www.jianshu.com/p/86b275da600e" target="_blank" rel="noopener noreferrer">Unity与Android交互方案优化版</a></span></p>
<p><span style="text-decoration: underline;"><a href="https://www.jianshu.com/p/86b275da600e" target="_blank" rel="noopener noreferrer">https://www.jianshu.com/p/86b275da600e</a></span></p>
<h2 id="2-android-aar%e6%96%87%e4%bb%b6%e4%bf%ae%e6%94%b9">2.Android aar文件修改</h2>
<p><span style="color: #808080;"><em>今天刚踩得坑，麻蛋！</em></span></p>
<p>不同于jar包只能包含java代码，aar包还可以保存贴图等资源，相当于一个后缀是aar的压缩包。有时候第三方sdk提供的也是aar文件，当需要修改里面的东西时，比如删掉里面一个jar<strong>。切记不能采取先解压，删除完毕再压缩回去，后缀改成aar的操作。这样修改后Android Studio用不了！！！</strong></p>
<p>正确方法是以解压的形式打开（不是解压压缩包），一般Windows平台的压缩软件都可以，然后在压缩软件里增加或删除文件操作。</p>
<h2 id="3-android-aar%e6%96%87%e4%bb%b6%e8%af%bb%e5%8f%96">3.Android aar文件读取</h2>
<p><span style="color: #808080;"><em>同上，又是个巨坑，匪夷所思！</em></span></p>
<p>我目前的项目在安卓端是这样的，把用到的sdk和需要在安卓端才能调用的方法打成aar包，然后添加到Unity导出的安卓工程里，但是不知道为什么在运行的时候会报以下的错误;</p>
<p><img  loading="lazy"  decoding="async"  class="aligncenter size-full wp-image-694"  src="https://cdn.vki.cc/wp-content/uploads/2019/04/QQ20190423-0.jpg"  alt=""  width="661"  height="83"  srcset="https://cdn.vki.cc/wp-content/uploads/2019/04/QQ20190423-0.jpg 661w, https://cdn.vki.cc/wp-content/uploads/2019/04/QQ20190423-0-300x38.jpg 300w"  sizes="auto, (max-width: 661px) 100vw, 661px" ></p>
<p>说的是BGPlayerService类重复，可是我的找遍了真个项目和aar包也只有一个类，根本没有重复的地方。研究了很久都无解，就在准备放弃治疗的时候顺便改了下build.gradle的dependencies里的语句，从</p>
<pre class="pure-highlightjs"><code class="null">dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation project(':localizedappnamelib')
    implementation 'com.android.support:support-core-utils:24.2.0'
    implementation (name:'findingparadiselib-debug',ext:'aar')
}</code></pre>
<p>改成了</p>
<pre class="pure-highlightjs"><code class="null">dependencies {
    implementation (name:'findingparadiselib-debug',ext:'aar')
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation project(':localizedappnamelib')
    implementation 'com.android.support:support-core-utils:24.2.0'
}</code></pre>
<p>然后特么问题解决了！</p>
<p>？？？</p>
<p>我怀疑可能是implementation fileTree(include: [&#8216;*.jar&#8217;], dir: &#8216;libs&#8217;)把aar包里的jar也算进去了_(:з」∠)_</p>
<h2 id="4-%e7%94%9f%e6%88%90%e7%ad%be%e5%90%8d%e5%ae%89%e8%a3%85%e5%8c%85%e7%9a%84%e6%97%b6%e5%80%99%e6%8a%a5%e9%94%99">4.生成签名安装包的时候报错</h2>
<p>在第一次生成签名安装包的时候可能提示缺少xxx.pom（忘了是不是pom后缀了，反正差不多）和xxx.jar组件，这个报错在生成debug包的时候是不出现的。具体原因我不清楚，我猜是打包的时候需要这样依赖包，但是谷歌的官方站点没有（Android Studio提供的链接点进去404）。</p>
<p>两个解决办法，一是手动下载这些依赖包并放到指定的目录中去，不过不建议这样做，一个一个下载再放到指定目录太麻烦了。</p>
<p>第二中解决方法是使用阿里提供的站点，具体操作是将以下的代码方放到安卓工程的build.gradle文件的repositories代码块中去：</p>
<pre class="pure-highlightjs"><code class="null">maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' }
maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' }
maven { url 'http://maven.aliyun.com/nexus/content/repositories/gradle-plugin' }</code></pre>
<p>这样打包的时候Android Studio会自动去这些站点下载需要的依赖包，问题解决。</p>
<h2 id="5-execution-failed-for-task-appndkbuild">5.Execution failed for task &#8216;:app:ndkBuild&#8217;.</h2>
<p>该问题是在对一个老游戏升级的时候出现的，表现为无法打包，报错：</p>
<pre class="pure-highlightjs"><code class="null">Process 'command 'D:\SDK\***/ndk-build.cmd'' finished with non-zero exit value 2</code></pre>
<p>上网搜索，普遍的解决方式是使用低版本的NDK，又或者是删除src/main/obj文件夹里的内容，尝试无果。</p>
<p>最终发现是build.gradle版本较老，且使用ndkBuild的方式打包，不适用于新版的Android Studio，修改之后问题解决</p>
<p>修改前：</p>
<pre class="pure-highlightjs"><code class="null">android {
    signingConfigs {
    }
    compileSdkVersion 26
    buildToolsVersion '23.0.3'
    defaultConfig {
        applicationId "com.xd.swords.and"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 18
        versionName "1.1.7"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    sourceSets.main.jni.srcDirs = []
    // disable automatic ndk-build call, which ignore our Android.mk
    sourceSets.main.jniLibs.srcDir 'src/main/libs'
    // call regular ndk-build(.cmd) script from app directory
    task ndkBuild(type: Exec) {
        workingDir file('src/main')
        commandLine getNdkBuildCmd()
    }
    tasks.withType(JavaCompile) {
        compileTask -&gt; compileTask.dependsOn ndkBuild
    }
    task cleanNative(type: Exec) {
        workingDir file('src/main')
        commandLine getNdkBuildCmd(), 'clean'
    }
    clean.dependsOn cleanNative
}</code></pre>
<p>修改后：</p>
<pre class="pure-highlightjs"><code class="null">android {
    signingConfigs {
    }
    compileSdkVersion 26
    buildToolsVersion '23.0.3'
    defaultConfig {
        applicationId "com.xd.swords.and"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 18
        versionName "1.1.7"
        ndk {
            abiFilters 'armeabi-v7a'
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        ndkBuild {
            path "src/main/jni/Android.mk"
        }
    }

    // 下面部分代码应该也可以删掉。
    sourceSets.main.jni.srcDirs = []
    // disable automatic ndk-build call, which ignore our Android.mk
    sourceSets.main.jniLibs.srcDir 'src/main/libs'
    // call regular ndk-build(.cmd) script from app directory
}</code></pre>
<p>对比前后，主要是删除了旧的ndkBuild代码，然后设置abiFilters等。</p>
<p><a href="https://vki.cc/greattruth/experience/692/">Unity安卓项目踩坑日记</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vki.cc/greattruth/experience/692/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>【设计模式】06 &#8211; 工厂模式</title>
		<link>https://vki.cc/greattruth/experience/688/</link>
					<comments>https://vki.cc/greattruth/experience/688/#comments</comments>
		
		<dc:creator><![CDATA[白露]]></dc:creator>
		<pubDate>Thu, 28 Feb 2019 11:37:48 +0000</pubDate>
				<category><![CDATA[咸鱼心得]]></category>
		<guid isPermaLink="false">https://bye6.cc/?p=688</guid>

					<description><![CDATA[<p>定义一个可以产生对象的接口，但是让子类决定要产生哪一个类的对象。工厂方法模式让类的实例化程序延迟到子类中实施。 工厂模式，顾名思义，主要用在“大量生产某个东西”的地方，并且是有组织，有规律的进行&#8230;</p>
<p><a href="https://vki.cc/greattruth/experience/688/">【设计模式】06 &#8211; 工厂模式</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<pre class="wp-block-preformatted">定义一个可以产生对象的接口，但是让子类决定要产生哪一个类的对象。工厂方法模式让类的实例化程序延迟到子类中实施。</pre>



<p class="has-black-color has-text-color wp-block-paragraph">工厂模式，顾名思义，主要用在“大量生产某个东西”的地方，并且是有组织，有规律的进行生产。在程序开发中，会遇到大量生成某个对象的情况。比如游戏中，需要生成若干敌人，这些敌人既可以是相同的，也可以是有差别的，像模型，属性等。如果每次生成的时候，都对其进行手动初始化，改下属性啦，位置什么的，肯定很麻烦，还容易出错~</p>



<p class="has-black-color has-text-color wp-block-paragraph">那么有没有一种可以将这些生成行为规范化的方法呢？</p>



<p class="has-black-color has-text-color wp-block-paragraph">那就是工厂模式~</p>



<h2 id="%e7%ae%80%e5%8d%95%e5%b7%a5%e5%8e%82%e6%a8%a1%e5%bc%8f" class="wp-block-heading">简单工厂模式</h2>



<p class="has-black-color has-text-color wp-block-paragraph">举个栗子，巧克力和香子兰肚子饿了，她们要吃好吃的。巧克力说她要吃蛋糕，香子兰说她只要一杯奶茶就够了。以编程的角度看，我们需要生成蛋糕和奶茶。常规思路代码如下:</p>



<p class="has-black-color has-text-color wp-block-paragraph">首先定义蛋糕类和奶茶类;</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public class Cake
{
    public Cake()
    {
        Console.WriteLine("生成蛋糕。");
    }
}

public class MilkTea
{
    public MilkTea()
    {
        Console.WriteLine("生成奶茶。");
    }
}</pre>



<p class="has-black-color has-text-color wp-block-paragraph">然后在Main方法里生成;</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public static void Main(string[] args)
{
    string str = Console.ReadLine();

    Cake cake;
    MilkTea milkTea;
            
    if (str == "蛋糕")
    {
        cake = new Cake();
    }
    else if (str == "奶茶")
    {
        milkTea = new MilkTea();
    }
}</pre>



<p class="has-black-color has-text-color wp-block-paragraph">可以看到，这样的代码存在不小的问题，首先是即便是生成一种，我们也要把其他所有食物进行声明，不然无法获得引用，当然这个可以通过定义一个食物父类来解决。另外如果要增加别的食物，需要修改main方法。如果食物种类少了还好，但是当达到成百上千时，结构会非常臃肿，而且违反了开闭原则。</p>



<p class="has-black-color has-text-color wp-block-paragraph">接下来我们使用<strong>简单工厂模式</strong>改写下：</p>



<p class="has-black-color has-text-color wp-block-paragraph">首先定义一个食物基类Food，并让蛋糕和奶茶继承：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public abstract class Food
{
        
}

public class Cake : Food
{
    public Cake()
    {
        Console.WriteLine("生成蛋糕。");
    }
}

public class MilkTea : Food
{
    public MilkTea()
    {
        Console.WriteLine("生成奶茶。");
    }
}</pre>



<p class="has-black-color has-text-color wp-block-paragraph">然后定义工厂类用来产生食物：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public class FoodFactory
{
    public Food CreateFood(string foodName)
    {
        if (String.IsNullOrEmpty(foodName))
        {
            return null;
        }

        Food food = null;
        switch (foodName)
        {
            case "蛋糕":
                food = new Cake();
                break;
            case "奶茶":
                food = new MilkTea();
                break;
                
            default:
                break;
        }

        return food;
    }
}</pre>



<p class="has-black-color has-text-color wp-block-paragraph">最后在Main方法里调用：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public static void Main(string[] args)
{
    FoodFactory factory = new FoodFactory();
                
    string str = Console.ReadLine();
    Food food = factory.CreateFood(str);
}</pre>



<p class="has-black-color has-text-color wp-block-paragraph">可以看到，这次Main方法简洁了许多，通过使用简单工厂模式，成功的将“生成”和“使用”进行分离，极大的降低了耦合度。前台只需要知道通过传参获得想要的对象就够了。至于怎么生成那是后台工厂类的事。之后如果想增加新的食物，只需要改一下新建一个类并继承自Food，再switch里的参数就行了。</p>



<h2 id="%e5%b7%a5%e5%8e%82%e6%96%b9%e6%b3%95%e6%a8%a1%e5%bc%8f" class="wp-block-heading">工厂方法模式</h2>



<p class="has-black-color has-text-color wp-block-paragraph">看过了简单工厂模式，是不是觉得已经没问题了呢？然而以上只是为了演示，并没有多少功能性的代码。试想一下，在实际开发中，如果需要很多与对象创建有关的代码，而且需要创建很多对象，是不是意味着要在这个简单工厂类里编写很多方法。比如说蛋糕要调整大小，奶茶要调整甜度等等。这样的话每次都需要打开这个简单工厂类进行修改。。</p>



<p class="has-black-color has-text-color wp-block-paragraph">接下来研究<strong>工厂方法模式</strong>：向上代码：</p>



<p class="has-black-color has-text-color wp-block-paragraph">这次对工厂类进行了抽象：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public interface IFoodFactory
{
    Food Create();
}

public class CakeFactory : IFoodFactory
{
    public Food Create()
    {
        return new Cake();
    }
}
    
public class MilkTeaFactory : IFoodFactory
{
    public Food Create()
    {
        return new MilkTea();
    }
}</pre>



<p class="has-black-color has-text-color wp-block-paragraph">Main里调用，这次取消了判断代码。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public static void Main(string[] args)
{
    IFoodFactory cakeFactory = new CakeFactory();
    IFoodFactory milkFactory = new MilkTeaFactory();
    
    Food cake = cakeFactory.Create();
    Food milkTea = milkFactory.Create();
}</pre>



<p class="has-black-color has-text-color wp-block-paragraph">这个模式是对简单工厂模式的解耦，在工厂方法模式中一个子类对应一个工厂类，而这些工厂类都实现于一个抽象接口。相当于把原来的一个简单工厂类进行了拆分。降低了耦合度。</p>



<h2 id="%e6%8a%bd%e8%b1%a1%e5%b7%a5%e5%8e%82%e6%a8%a1%e5%bc%8f" class="wp-block-heading">抽象工厂模式</h2>



<p class="has-black-color has-text-color wp-block-paragraph">其实绝大多数情况下使用以上的模式就可以了，不过还有种情况，巧克力除了要吃蛋糕还要喝奶茶，香子兰除了喝奶茶还要吃蛋糕。这种情况利用上面的方法也不是不可以，可以如果她们还要冰淇淋，饼干，薯片等等一大堆的东西呢。总不能在main方法里写一大堆调用方法吧。</p>



<p class="has-black-color has-text-color wp-block-paragraph">接下来介绍另一种工厂模式：<strong>抽象工厂模式</strong>。先上代码：</p>



<p class="has-black-color has-text-color wp-block-paragraph">这次对工厂和单个种类的食物进行了抽象~</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public abstract class Cake
{
        
}

public class StrawberryCake : Cake
{
    public StrawberryCake()
    {
        Console.WriteLine("生成草莓蛋糕。");
    }
}

public abstract class MilkTea 
{
        
}

public class RedBeanMilkTea : MilkTea
{
    public RedBeanMilkTea()
    {
        Console.WriteLine("生成红豆奶茶。");
    }
}</pre>



<p class="has-black-color has-text-color wp-block-paragraph">将巧克力与香子兰当做工厂：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public abstract class IFoodFactory
{
    public abstract Cake CreateCake();
    public abstract MilkTea CreateMilkTea();

    public virtual void Create()
    {
        CreateCake();
        CreateMilkTea();
    }
}

/// &lt;summary>
/// 巧克力。
/// &lt;/summary>
public class ChocolatFactory : IFoodFactory
{
    public override Cake CreateCake()
    {
        // 此处可以增加条件，生成不同的对象
        return new StrawberryCake();
    }

    public override MilkTea CreateMilkTea()
    {
        // 此处可以增加条件，生成不同的对象
        return new RedBeanMilkTea();
    }
}
    
/// &lt;summary>
/// 香子兰
/// &lt;/summary>
public class VanillaFactory : IFoodFactory
{
    public override Cake CreateCake()
    {
        // 此处可以增加条件，生成不同的对象
        return new StrawberryCake();
    }

    public override MilkTea CreateMilkTea()
    {
        // 此处可以增加条件，生成不同的对象
        return new RedBeanMilkTea();
    }
}</pre>



<p class="has-black-color has-text-color wp-block-paragraph">然后调用：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="csharp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public static void Main(string[] args)
{
    IFoodFactory chocolat = new ChocolatFactory();
    IFoodFactory vanilla = new VanillaFactory();
                
    chocolat.Create();
    vanilla.Create();
}</pre>
<p><a href="https://vki.cc/greattruth/experience/688/">【设计模式】06 &#8211; 工厂模式</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vki.cc/greattruth/experience/688/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>使用C#实现POST消息发送和读取</title>
		<link>https://vki.cc/greattruth/language/634/</link>
					<comments>https://vki.cc/greattruth/language/634/#comments</comments>
		
		<dc:creator><![CDATA[白露]]></dc:creator>
		<pubDate>Fri, 21 Dec 2018 07:41:18 +0000</pubDate>
				<category><![CDATA[语言相关]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Post]]></category>
		<category><![CDATA[原创]]></category>
		<category><![CDATA[心得]]></category>
		<guid isPermaLink="false">http://www.icemaplen.com/?p=634</guid>

					<description><![CDATA[<p>之前给一个大佬做有赞应用时候遇到了post收发的问题，其实实现起来很简单的。无奈网上关于这方面的资料很少，特此整理一下，希望帮助有需要的人_(:з」∠)_</p>
<p><a href="https://vki.cc/greattruth/language/634/">使用C#实现POST消息发送和读取</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></description>
										<content:encoded><![CDATA[<p><span style="color: #808080;">啊，好久没写过博客了_(:з」∠)_</span></p>
<blockquote><p><span style="color: #333333;">之前给一个大佬做有赞应用时候遇到了post收发的问题，其实实现起来很简单的。无奈网上关于这方面的资料很少，特此整理一下，希望帮助有需要的人_(:з」∠)_</span></p></blockquote>
<h2 id="%e5%8f%91%e9%80%81">发送</h2>
<p>直接上代码了，关键的地方我加了注释，应该不难理解。</p>
<p>C#提供了相关的API，很方便。这段代码可以直接拿去用_(:з」∠)_</p>
<p>大致步骤就是创建Request-填充信息-发送。</p>
<pre class="pure-highlightjs"><code class="cs">/// &lt;summary&gt;
/// Post提交数据
/// &lt;/summary&gt;
/// &lt;param name="postUrl"&gt;URL&lt;/param&gt;
/// &lt;param name="paramData"&gt;参数&lt;/param&gt;
/// &lt;returns&gt;&lt;/returns&gt;
string PostWebRequest(string postUrl, string paramData)
{
    string ret = string.Empty;
    try
    {
        byte[] byteArray = Encoding.UTF8.GetBytes(paramData);
        HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(new Uri(postUrl));
        // 提交方法，POST/GET等
        webReq.Method = "POST";
        // ContentType
        webReq.ContentType = "application/x-www-form-urlencoded";
        webReq.ContentLength = byteArray.Length;

        Stream newStream = webReq.GetRequestStream();
        //写入参数
        newStream.Write(byteArray, 0, byteArray.Length);
        newStream.Close();
        HttpWebResponse response = (HttpWebResponse)webReq.GetResponse();
        StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
        ret = sr.ReadToEnd();

        sr.Close();
        response.Close();
        newStream.Close();
    }
    catch (Exception ex)
    {
        return ex.Message;
    }
    return ret;
}</code></pre>
<h2 id="%e6%8e%a5%e6%94%b6">接收</h2>
<p>接收消息模块我分了两部分，一是主体，如何调用api进行监听。这部分需要另开一个线程来处理。</p>
<pre class="pure-highlightjs"><code class="cs">/// &lt;summary&gt;
/// 监听post消息，在收到消息后发送响应消息。
/// 建议使用死循环反复监听，且开启一个线程执行该方法。
/// &lt;/summary&gt;
void ListenWebRequest()
{
    // string url= "需要监听的url范围";
    // 示例：(这里有个坑，见下文)
    string url = "http://127.0.0.1:2333";
    try
    {
        HttpListener listener = new HttpListener();
        listener.Prefixes.Add(url);

        // 开始监听端口，接收客户端请求
        listener.Start();

        // 阻塞主函数至接收到一个客户端请求为止
        // 也可以使用listener.BeginGetContext()异步监听
        HttpListenerContext context = listener.GetContext();
        HttpListenerRequest request = context.Request;
        HttpListenerResponse response = context.Response;

        // msg即为body内容
        string msg = GetBodyString(request);

        // 通过QueryString索引获取post参数
        string sample = request.QueryString["sample"];

        // 收到信息后返回数据
        string responseString = "响应数据";
        response.StatusCode = 200;
        // header头信息
        response.Headers.Add("Access-Control-Allow-Origin", "*");
        // contentType 类型
        response.ContentType = "application/json";
        response.ContentEncoding = System.Text.Encoding.UTF8;
        byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
        response.ContentLength64 = buffer.Length;
        Stream output = response.OutputStream;
        output.Write(buffer, 0, buffer.Length);

        output.Close();
        listener.Stop();
        listener.Close();
    }
    catch (Exception ex)
    {
        // TODO
    }
}</code></pre>
<p>第二部分，由于接收到的post消息中body内容是以流格式的，还需要将其转换为string。</p>
<pre class="pure-highlightjs"><code class="cs">/// &lt;summary&gt;
/// 将body内容解析为String。
/// &lt;/summary&gt;
/// &lt;returns&gt;The body string.&lt;/returns&gt;
/// &lt;param name="request"&gt;Request.&lt;/param&gt;
string GetBodyString(HttpListenerRequest request)
{
    string result = null;
    using (System.IO.Stream body = request.InputStream) // here we have data
    {
        using (System.IO.StreamReader reader = new System.IO.StreamReader(body, request.ContentEncoding))
        {
            result = reader.ReadToEnd();
        }
    }

    if (string.IsNullOrEmpty(result))
    {
        // TODO 读取到空消息
    }
    else
    {
        // TODO 成功读取消息
    }

    return result;
}</code></pre>
<h2 id="%e6%8e%a8%e8%8d%90%e4%b8%80%e4%b8%aapost%e6%b5%8b%e8%af%95%e5%b7%a5%e5%85%b7">推荐一个post测试工具</h2>
<p><span style="color: #ff9900;"><strong>Restlet Client &#8211; REST API Testing</strong></span></p>
<p>谷歌浏览器插件：<span style="color: #0000ff;"><a style="color: #0000ff;" href="https://chrome.google.com/webstore/detail/restlet-client-rest-api-t/aejoelaoggembcahagimdiliamlcdmfm?utm_source=chrome-ntp-icon">下载链接（需要翻墙）</a></span></p>
<p><span style="color: #333333;">炒鸡好用！</span></p>
<p><img  loading="lazy"  decoding="async"  class="aligncenter wp-image-636 size-full"  src="https://cdn.vki.cc/wp-content/uploads/2018/12/20181221-155002.png"  alt=""  width="1023"  height="551"  srcset="https://cdn.vki.cc/wp-content/uploads/2018/12/20181221-155002.png 1023w, https://cdn.vki.cc/wp-content/uploads/2018/12/20181221-155002-300x162.png 300w, https://cdn.vki.cc/wp-content/uploads/2018/12/20181221-155002-768x414.png 768w, https://cdn.vki.cc/wp-content/uploads/2018/12/20181221-155002-750x404.png 750w"  sizes="auto, (max-width: 1023px) 100vw, 1023px" ></p>
<h2 id="%e7%a2%8e%e7%a2%8e%e5%bf%b5">碎碎念</h2>
<p>就是在接收方法那绑定的url，我当时用的腾讯云的服务器（window系统）测试的。是这样的，</p>
<p>如果url是localhost或127.0.0.1，在服务器本机测试一切正常。但如果从外网向其post数据，会报400或405错误。</p>
<p>如果url是服务器外网IP呢，干脆直接抛异常，好像是无法绑定。</p>
<p>最后尝试了下将url改成内网IP，一切OK。不过我还是不知道是什么原因_(:з」∠)_</p>
<p><a href="https://vki.cc/greattruth/language/634/">使用C#实现POST消息发送和读取</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vki.cc/greattruth/language/634/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>【设计模式】05 &#8211; 模板方法模式</title>
		<link>https://vki.cc/greattruth/experience/599/</link>
					<comments>https://vki.cc/greattruth/experience/599/#respond</comments>
		
		<dc:creator><![CDATA[白露]]></dc:creator>
		<pubDate>Thu, 30 Aug 2018 07:45:12 +0000</pubDate>
				<category><![CDATA[咸鱼心得]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[心得]]></category>
		<category><![CDATA[设计模式]]></category>
		<guid isPermaLink="false">http://www.icemaplen.com/?p=599</guid>

					<description><![CDATA[<p>在一个操作方法中定义算法的流程，某些步骤由子类完成。模板方法模式让子类在不变更原有算法流程的情况下，还能重新定义其中的步骤。 举个栗子，现在巧克力和香子兰两个人肚子饿了，要吃饭。吃饭的过程有三&#8230;</p>
<p><a href="https://vki.cc/greattruth/experience/599/">【设计模式】05 &#8211; 模板方法模式</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<pre class="wp-block-preformatted">在一个操作方法中定义算法的流程，某些步骤由子类完成。模板方法模式让子类在不变更原有算法流程的情况下，还能重新定义其中的步骤。</pre>



<p class="has-black-color has-text-color wp-block-paragraph">举个栗子，现在巧克力和香子兰两个人肚子饿了，要吃饭。吃饭的过程有三个步骤：</p>



<p class="has-black-color has-text-color wp-block-paragraph">1.吃配菜；</p>



<p class="has-black-color has-text-color wp-block-paragraph">2.吃主食；</p>



<p class="has-black-color has-text-color wp-block-paragraph">3.喝饮料；</p>



<p class="has-black-color has-text-color wp-block-paragraph">虽然吃饭步骤一样，可是两个人吃的东西却不同。巧克力配菜要吃西红柿炒鸡蛋，主食要吃馒头，饮料要喝可乐；而香子兰要吃麻婆豆腐，米饭和牛奶。</p>



<p class="has-black-color has-text-color wp-block-paragraph">如果用程序表示的话，可以看出二者使用相同的验算流程，且实现的内容不一样。一般情况下我们会分别给二者写一套代码，这样做没有什么问题。可是如果后续开发需要添加新的功能呢，比如说红豆，时雨也要吃饭饭。或者她们想改变下吃饭的流程，先吃主食再吃配菜呢？是不是要为每套代码做出修改？显而易见这样的实现不太好，那么有没有一种好的实现方法呢？</p>



<p class="has-black-color has-text-color wp-block-paragraph">答案就是使用<strong>模板方法模式（Template）</strong></p>



<p class="has-black-color has-text-color wp-block-paragraph"><em><span style="color: #808080;">偷来的结构图_(:з」∠)_</span></em></p>



<div class="wp-block-image"><figure class="aligncenter"><a href="https://cdn.vki.cc/wp-content/uploads/2018/08/944365-5c903d791a9d001f.png" target="_blank" rel="noopener"><img  loading="lazy"  decoding="async"  width="500"  height="480"  src="https://cdn.vki.cc/wp-content/uploads/2018/08/944365-5c903d791a9d001f.png"  alt=""  class="wp-image-602"  srcset="https://cdn.vki.cc/wp-content/uploads/2018/08/944365-5c903d791a9d001f.png 500w, https://cdn.vki.cc/wp-content/uploads/2018/08/944365-5c903d791a9d001f-300x288.png 300w"  sizes="auto, (max-width: 500px) 100vw, 500px" ></a></figure></div>



<p class="wp-block-paragraph"><strong>1.定义一个算法的流程，即是很明确的定义算法的每一个步骤，并写在父类的方法中，而每一个步骤都可以是一个方法的调用。</strong></p>



<p class="wp-block-paragraph"><strong>2.某些步骤由子类完成。</strong></p>



<h2 id="%e4%bb%a3%e7%a0%81%e5%ae%9e%e7%8e%b0" class="wp-block-heading">代码实现</h2>



<h3 id="%e7%ae%97%e6%b3%95%e5%ae%9a%e4%b9%89%e7%b1%bb%ef%bc%9a" class="wp-block-heading">算法定义类：</h3>



<p class="wp-block-paragraph">由一个主方法和三个步骤方法组成，主方法不可重写，步骤方法必须在子类实现：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">abstract class IEat
{
    public void Eat()
    {
        EatDishes();
        EatStaple();
        Drink();
    }

    protected abstract void EatDishes();
    protected abstract void EatStaple();
    protected abstract void Drink();
}</pre>



<h3 id="%e7%ae%97%e6%b3%95%e6%ad%a5%e9%aa%a4%e7%9a%84%e5%ae%9e%e7%8e%b0%e7%b1%bb%ef%bc%9a" class="wp-block-heading">算法步骤的实现类：</h3>



<p class="wp-block-paragraph">巧克力：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">class ChocolatEat : IEat
{
    protected override void EatDishes()
    {
        Console.WriteLine("配菜：西红柿炒鸡蛋");
    }

    protected override void EatStaple()
    {
        Console.WriteLine("主食：馒头");
    }
    protected override void Drink()
    {
        Console.WriteLine("饮料：可乐");
    }
}</pre>



<p class="wp-block-paragraph">香子兰：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">class VinillaEat : IEat
{
    protected override void EatDishes()
    {
        Console.WriteLine("配菜：麻婆豆腐");
    }

    protected override void EatStaple()
    {
        Console.WriteLine("主食：米饭");
    }
    protected override void Drink()
    {
        Console.WriteLine("饮料：牛奶");
    }
}</pre>



<p class="wp-block-paragraph">在Main中调用：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">static void Main(string[] args)
{
    ChocolatEat chocolat = new ChocolatEat();
    VinillaEat vinilla = new VinillaEat();

    Console.WriteLine("巧克力吃了：");
    chocolat.Eat();

    Console.WriteLine("-------------");
    Console.WriteLine("香子兰吃了：");
    vinilla.Eat();
}</pre>



<p class="wp-block-paragraph">结果：</p>



<div class="wp-block-image"><figure class="aligncenter"><img  loading="lazy"  decoding="async"  width="318"  height="218"  src="https://cdn.vki.cc/wp-content/uploads/2018/08/20180830154449.png"  alt=""  class="wp-image-603"  srcset="https://cdn.vki.cc/wp-content/uploads/2018/08/20180830154449.png 318w, https://cdn.vki.cc/wp-content/uploads/2018/08/20180830154449-300x206.png 300w"  sizes="auto, (max-width: 318px) 100vw, 318px" ></figure></div>



<h2 id="%e7%bb%93%e8%ae%ba" class="wp-block-heading">结论</h2>



<p class="has-black-color has-text-color wp-block-paragraph">模板方法模式将可能出现重复的“算法流程”从子类提升到父类中，减少重复的发生，并且也开放子类参与算法中各个步骤的执行或优化。但如果“算法流程”开放太多的步骤，并要求子类必须全部重新实现的话，反而会造成实现的困难，也不容易维护。</p>
<p><a href="https://vki.cc/greattruth/experience/599/">【设计模式】05 &#8211; 模板方法模式</a>最先出现在<a href="https://vki.cc">初晴神社</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://vki.cc/greattruth/experience/599/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
