乱码三千 – 分享实用IT技术

乱码三千 – 码出一个新世界


  • 首页

  • 归档

  • 搜索

Hexo Process Out of Memory解决文章过多 内存溢出问题

发表于 2020-08-31

hexo内存溢出问题

当hexo生成文章大概在1000以上的时候,便有可能出现该问题.

1
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory

解决方法:

1
2
# 找到hexo 命令的位置
which hexo

编辑hexo命令的bin文件的第一行为以下内容.

1
#!/usr/bin/env node --max_old_space_size=8192

便暂时解决内存溢出的问题.

hexo的性能

hexo的性能相对hugo确实差太多,同样1000+的页面,用hugo却只用了不到2s. 这让我开始开始犹豫是否要放弃使用hexo.

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

关于Android Studio设置代理的问题

发表于 2020-06-22

AndoridStudio报错 “Failed to parse host xxx.xxx”

你会发现, 单纯在 HTTP Proxy 里边 取消代理设置是不行的,依然报错

解决方法:

找到你C盘用户 文件夹下的.gradle 下 , 看看有没有一个叫gradle.properties 文件,修改里边的网络代理配置信息,或者直接把该文件夹删除。

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

关于Andriod热更新

发表于 2020-06-19

项目场景:

  • 紧急发现了一个bug,影响用户体验,阻断项目流程。这个时候,只能紧急发布一个强制更新的新版本,让用户升级。
  • 最近百团大战开始。需要增加一个活动弹窗入口,越快越好。这个时候,只能紧急发布一个强制更新的新版本,让用户升级。

存在需求:

1
可不可以不让用户重新安装就可以解决上述场景?

什么是热更新:

1
让应用能够在无需重新安装的情况实现更新,帮助应用快速建立动态修复能力。

从上面的定义来看,热补丁节省Android大量应用市场发布的时间。同时用户也无需重新安装,只要上线就能无感知的更新。

热更新的原理:

现在市面上主流的几大热更新技术:

  • 淘宝 Dexposed
  • 支付宝 AndFix
  • Qzone 超级热补丁
  • 微信 Tinker

Dexposed

基于 Xposed 实现的无侵入的运行时 AOP (Aspect-oriented Programming) 框架,可以实现在线修复 Bug,修复粒度方法级别,这也就意味着我们没有办法进行类的增减操作。而且由于对 ART 虚拟机不支持,导致其对 Android 5.0、6.0 均不支持,使用局限性太大。

AndFix

native hook 方式,其核心部分在 JNI 层对方法进行替换,替换有问题的方法,修复粒度方法级别,无法在类中新增和删减字段,可以做到即时生效。也就是运行时生效。但是因为它的核心部分在JNI,所以会出现很多适配兼容的问题。因为国内的rom厂商多才多艺.

超级热补丁

使用新的 ClassLoader 加载 patch.dex,hack 默认的 ClassLoader,替换有问题的类,修复粒度类级别,一般无法做到即时生效,需要在应用下一次启动时生效。但是在art虚拟机中,如果改变了类变量,和方法名,有可能导致内存错乱的问题,没有开源这个项目。但在github上的Nuwa采用了相同的方式,这个是开源。

Tinker

dex 文件全量替换,基于 DexDiff 技术,对比修复前后的 dex 文件,生成 patch.dex,再根据 patch.dex 更新有问题的 dex 文件。简单来说,在编译时通过新旧两个Dex生成差异patch.dex。在运行时,将差异patch.dex重新跟原始安装包的旧Dex还原为新的Dex。这个过程可能比较耗费时间与内存,所以我们是单独放在一个后台进程:patch中。为了补丁包尽量的小,微信自研了DexDiff算法,它深度利用Dex的格式来减少差异的大小。

热更新方案的比较:

Tables Tinker Qzone Andfix Dexposed
类替换 yes yes no no
lib替换 yes no no no
资源替换 yes yes no no
全平台支持 yes yes yes no
即时生效 no no yes yes
性能损耗 较小 较大 较小 较小
补丁包大小 较小 较大 一般 一般
开发透明 yes yes no no
复杂度 较低 较低 复杂 复杂
gradle支持 yes yes no no
接口文档 丰富 一般 一般 较少
占rom体积 较大 较小 较小 较小
成功率 较好 最高 一般 一般

热更新的使用场景:

热补丁技术也可以理解为一个动态修改代码与资源的通道,它适合于修改量较少的情况。

我们看一下微信的版本升级的情况:

Tables 普通升级 布丁升级
数据大小 33M 145K
更新速度 10天 1天(70%)
自动升级 wifi 移动网络

以Android用户的升级习惯,即使是相对活跃的微信也需要10天以上的时间去覆盖50%的用户。使用补丁技术,我们能做到1天覆盖70%以上。这也是基于补丁体积较小,可以直接使用移动网络下载更新。

热更新使用限制

  • 补丁只能针对单一客户端版本,随着版本差异变大补丁体积也会增大;
  • 补丁不能支持所有的修改,例如AndroidManifest;
  • 补丁无论对代码还是资源的更新成功率都无法达到100%。

如何在一个项目中增加热更新功能?

  • 在工程目录 build.gradle 文件中添加插件依赖。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    buildscript {
    repositories {
    jcenter()
    }
    dependencies {
    // tinkersupport插件,其中latest.release指代最新版本号,也可以指定明确的版本号,例如1.0.8
    classpath "com.tencent.bugly:tinker-support:latest.release"
    }
    }
  • 在app module 下的build.gradle 文件中添加 配置

    1
    2
    3
    4
    5
    6
    dependencies {
    compile "com.android.support:multidex:1.0.1"
    compile 'com.tencent.bugly:crashreport_upgrade:latest.release'
    }
    // 依赖插件脚本
    apply from: 'tinker-support.gradle'
  • 在同级目录下创建 tinker-support.gradle

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    apply plugin: 'com.tencent.bugly.tinker-support'

    def bakPath = file("${buildDir}/bakApk/")

    /**
    * 此处填写每次构建生成的基准包目录
    */
    def baseApkDir = "app-0912-17-04-44"
    /**
    * 对于插件各参数的详细解析请参考
    */
    tinkerSupport {

    // 开启tinker-support插件,默认值true
    enable = true

    //自动生成tinkerId,无须关注此。默认为false
    //autoGenerateTinkerId = true

    tinkerEnable = true

    // 指定归档目录,默认值当前module的子目录tinker
    autoBackupApkDir = "${bakPath}"

    // 是否启用覆盖tinkerPatch配置功能,默认值false
    // 开启后tinkerPatch配置不生效,即无需添加tinkerPatch
    overrideTinkerPatchConfiguration = true

    // 编译补丁包时,必需指定基线版本的apk,默认值为空
    // 如果为空,则表示不是进行补丁包的编译
    // @{link tinkerPatch.oldApk }
    baseApk = "${bakPath}/${baseApkDir}/com.nongfenqi.sherlock-release-v2.3.2_32.apk"
    // 对应tinker插件applyMapping
    baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"

    // 对应tinker插件applyResourceMapping
    baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"

    // 构建基准包和补丁包都要指定不同的tinkerId,并且必须保证唯一性
    tinkerId = "2.3.2-0912-patch"

    // 构建多渠道补丁时使用
    // buildAllFlavorsDir = "${bakPath}/${baseApkDir}"

    // 是否启用加固模式,默认为false.(tinker-spport 1.0.7起支持)
    // isProtectedApp = true

    // 是否开启反射Application模式
    enableProxyApplication = false

    }

    /**
    * 一般来说,我们无需对下面的参数做任何的修改
    * 对于各参数的详细介绍请参考:
    * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
    */
    tinkerPatch {
    //oldApk ="${bakPath}/${appName}/app-release.apk"
    tinkerEnable = true
    ignoreWarning = false
    useSign = true
    dex {
    dexMode = "jar"
    pattern = ["classes*.dex"]
    loader = []
    }
    lib {
    pattern = ["lib/*/*.so"]
    }

    res {
    pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
    ignoreChange = []
    largeModSize = 100
    }

    packageConfig {
    }
    sevenZip {
    zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
    // path = "/usr/local/bin/7za"
    }
    buildConfig {
    keepDexApply = false
    //tinkerId = "1.0.1-base"
    //applyMapping = "${bakPath}/${appName}/app-release-mapping.txt" // 可选,设置mapping文件,建议保持旧apk的proguard混淆方式
    //applyResourceMapping = "${bakPath}/${appName}/app-release-R.txt" // 可选,设置R.txt文件,通过旧apk文件保持ResId的分配
    }
    }
  • 权限配置以及activity配置。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_LOGS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>


    <activity
    android:name="com.tencent.bugly.beta.ui.BetaActivity"
    android:theme="@android:style/Theme.Translucent" />
  • 混淆配置

1
2
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}

本文转载自:https://www.dazhuanlan.com/2019/12/31/5e0b56c7943ed/

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

解决国内访问github过慢的问题

发表于 2020-06-19

通过查看下载链接,能够发现最终被指向到Amazon的服务器(http://github-cloud.s3.amazonaws.com)了。由于国内访问亚马逊网站非常慢,我们需要修改Hosts文件来实现流畅访问。

第一步,打开本机上的Hosts文件

首先,什么是Hosts文件?

在互联网协议中,host表示能够同其他机器互相访问的本地计算机。一台本地机有唯一标志代码,同网络掩码一起组成IP地址,如果通过点到点协议通过ISP访问互联网,那么在连接期间将会拥有唯一的IP地址,这段时间内,你的主机就是一个host。

在这种情况下,host表示一个网络节点。host是根据TCP/IP for Windows 的标准来工作的,它的作用是包含IP地址和Host name(主机名)的映射关系,是一个映射IP地址和Host name(主机名)的规定,规定要求每段只能包括一个映射关系,IP地址要放在每段的最前面,空格后再写上映射的Host name主机名 。对于这段的映射说明用“#”分割后用文字说明。

~Windows

Hosts文件的路径是:

C:\Windows\System32\drivers\etc

由于文件没有后缀名,可以利用鼠标右键点击,选择用记事本打开

~Mac

终端内输入:

sudo vim /etc/hosts

打开之后,我们就要向里面追加信息了。

第二步,追加域名的IP地址

我们可以利用https://www.ipaddress.com/ 来获得以下两个GitHub域名的IP地址:

(1) github.com

(2) github.global.ssl.fastly.net

打开网页后,利用输入框内分别查询两个域名:

先试一下github.com(也可直接访问):http://github.com.ipaddress.com/#ipinfo

在标注的IP地址中,任选一个记录下来。

再来是github.global.ssl.fastly.net(也可直接访问):http://github.global.ssl.fastly.net.ipaddress.com/#ipinfo

将以上两段IP写入Hosts文件中:

1
2
151.101.185.194 github.global.ssl.fastly.net
192.30.253.112 github.com

保存。

第三步,刷新 DNS 缓存

在终端或CMD中,执行以下命令:

ipconfig /flushdns

收工。

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

常见静态网站托管平台使用及多节点部署方案

发表于 2020-05-27

前言

对于 Hexo 来说,我们使用它来部署博客是因为无后端运维和高速渲染页面等优点。选择一个合适的托管平台对于博客来说十分重要,可以免费使用且稳定高速的平台是不存在的,我们总是需要做出妥协。我使用了 Github Pages、Coding Pages、Gitee Pages、Netlify 和 Vercel 来部署博客,以下为我的使用报告。

常见托管平台

11

Github Pages

免费扩展性强无限制性

使用体验:可以与仓库无缝对接,高效部署,但是没用设置国内节点,在国内访问速度较慢,作为一个海外节点还是非常不错的。相对而言,使用 jsdelivr 来加速网站相关文件可以满足基本使用。查看 Github Status,Pages 服务会出现偶尔挂掉的情况,但多数仓库文档、演示等都选择了 Github Pages 服务。

使用及扩展:提供二级域名,支持域名绑定及免费 SSL 证书。网站内容与仓库保存一致,自动推送。通过使用 Github Actions 具有较强扩展性。

Netlify

免费扩展性强无限制性

使用体验:Netlify 的节点设置在海外,但 Netlify 的服务速度尚可,国内部分地区可以到达高速服务。在使用 CDN 的情况下,把网站部署在 Netlify 是可以比较好的选择。Vuejs 和 Hexo 的官网都部署在 Netlify 上,其稳定性可想而知。Netlify 虽然拥有付费功能,但是基本上我们需要使用到的服务都免费。

使用及扩展:提供二级域名,支持域名绑定及免费 SSL 证书。Netlify 支持 Github 或者 Gitlab 等账号登录,如果仓库已经是静态网站文件,每次 Push 到仓库 Netlify 都会自动部署。支持 Build Command,源文件也可以通过提供的环境自动编译或渲染,类似于一款 CI,与 Github Pages 功能相近。

Vercel

免费扩展性强无限制性

使用体验:Vercel 的体验情况总体和 Netlify 相近,节点设置在海外,访问速度尚可。前身是 now.sh,作为一个高质量的静态托管平台,Vercel的使用体验非常好,是一个可选的优秀平台。

使用及扩展:提供二级域名,支持域名绑定及免费 SSL 证书。支持 Github 或者 Gitlab 等账号登录,如果仓库已经是静态网站文件,每次 Push 到仓库都会自动部署。Vercel 打出了 free forever 的口号,也就是说在非商用的情况下,个人可以永久免费使用。支持设置环境并执行相关命令,自动部署不在话下。

Coding

免费一般扩展性限制性

使用体验:Coding 是腾讯系的一个国内托管平台,对于人数较少的团体实行免费制度。服务器节点部署在国内,在国内使用访问速度较快。也是国内开放程度比较高的一个代码托管平台了,静态网站功能 Coding 最近改版了一下,相对于之前来说更稳定了一些。

使用及扩展:提供二级域名,支持域名绑定及免费 SSL 证书。基于 Kubernetes 的持续部署,可以人我们体验到与 DevOps 体系紧密结合的持续部署能力。持续中提供静态网站托管,但是静态网站托管需要实名和绑定手机号。

Gitee

免费(国内限制)扩展性较低限制性强

使用体验:Gitee 是一个国内托管平台,对比 coding 来说较为封闭。静态托管功能上拥有较大限制,且无法自动部署,功能残缺。

使用及扩展:提供二级域名,非付费版不支持自动部署、域名绑定及免费 SSL 证书。如果强制使用 https,可能会造成样式文件失效等问题。

TCB

付费扩展性高一般限制性

使用体验:TCB(Tencent CloudBase)采用 serverless 架构,提供静态托管服务。我的主站就是使用 TCB,相对而言因为付费了,所以效果较好,在全国各地有 CDN 节点,目前使用是因为腾讯的赞助计划,如果赞助计划失效了,价格过高可能会考虑切换平台。空间较大,流量较多,已经充当 CDN 使用了。

使用及扩展:提供二级域名,支持自动部署及 免费SSL 证书,但是 SSL 证书申请可能需要备案。扩展性较强,可以使用 CLI 工具或者 Tencent CloudBase Github Action 来部署。

多节点部署方案

几个仓库

Hexo 源码仓库

从图中可以看到使用了 Blog-Source 这个仓库为 Hexo 源码仓库,这个仓库有一个使用了两个 Github Actions,一个用来渲染博客文件并推送到 TCB 静态托管平台,一个用来渲染博客文件推送到各个 Git 仓库,理论上一个 Action 也可以完成这些任务,但是便于管理我选择了两个 Action。

推送至各个 Git 仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
name: Deploy to Repo(Github, Coding, Gitee)
on: [push]
jobs:
build:
runs-on: ubuntu-latest
env:
hTZ: Asia/Shanghai
steps:
- name: Checkout
uses: actions/checkout@v2
with:
ref: master
- name: Update Submodule
run: |
git submodule init
git submodule update --remote
- name: Setup Node
uses: actions/setup-node@v1
with:
node-version: "10.x"
- name: Hexo Generate
run: |
rm -f .yarnclean
yarn --frozen-lockfile --ignore-engines --ignore-optional --non-interactive --silent --ignore-scripts --production=false
rm -rf ./public
yarn run hexo clean
yarn run hexo generate
- name: Hexo Deploy
env:
SSH_PRIVATE: ${{ secrets.SSH_PRIVATE }}
GIT_NAME: vinceying
GIT_EMAIL: admin@vicne.pub
run: |
mkdir -p ~/.ssh/
echo "$SSH_PRIVATE" | tr -d '\r' > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan e.coding.net >> ~/.ssh/known_hosts
ssh-keyscan github.com >> ~/.ssh/known_hosts
ssh-keyscan gitee.com >> ~/.ssh/known_hosts
git config --global user.name "$GIT_NAME"
git config --global user.email "$GIT_EMAIL"
yarn run hexo deploy

推送至 TCB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
name: Deploy to Tencent CloudBase
on: push
jobs:
build:
runs-on: ubuntu-latest
env:
TZ: Asia/Shanghai
name: Deploy Hexo Souce Repo to Tencent CloudBase
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Node
uses: actions/setup-node@v1
with:
node-version: '10.x'
# NPM 环境及 Hexo 部署
- name: NPM
run: npm install
- name: Clean
run: ./node_modules/.bin/hexo clean
- name: Generate
run: ./node_modules/.bin/hexo generate
# Deploy static to Tencent CloudBase
- name: Deploy static to Tencent CloudBase
id: deployStatic
uses: TencentCloudBase/cloudbase-action@v1.1.1
with:
secretId: ${{ secrets.SECRET_ID }}
secretKey: ${{ secrets.SECRET_KEY }}
envId: ${{ secrets.ENV_ID }}
staticSrcPath: public

Github 博客页面仓库

这个作为使用 Github Pages 服务的仓库,同时在 Netlify 和 Vercel 的选择为源仓库,在每次推送至本仓库时,Netlify 和 Vercel 都会自动部署新文件。

CDN 文件仓库

这个仓库作为管理和存放一些需要推送到 CDN 的文件,比如 css 文件、图片和视频等,首先是为了便于管理及通过 Github Actions推送 到 TCB,其次是为了使用 Jsdelivr CDN 服务作为备用 CDN。

方案优点

  • 高效自动化,利用 Github Actions,每次只要 Push 到 Blog-Souce和Blog-file仓库就可以全仓库和全节点同步。
  • 便于管理文件,当主 CDN 失效后,直接替换 CDN 地址链接即可完成启用备用 CDN,且备份了文件。
  • 多设备管理,当切换设备后,直接在不安装环境的情况下直接 Clone 即可管理博客,但调试方面还是需要安装环境。特别是在 Github 的云端 IDE-Codespace 正式发布后,可以完全通过仓库管理博客。

本文转载自: HexoThemeFluid

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

H5通过scheme跳转指定Activity的几种方式

发表于 2020-05-20

如果是App内部WebView点击跳转指定Activity

第一种 在清单文件配置intent-filter

1
2
3
4
5
6
7
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="personal_page_info"
android:scheme="${app_scheme}" />
</intent-filter>

WebView设置:

mWebView.setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {

}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String httpurl) {
    if (httpurl.startsWith("app:")) {
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(httpurl));
        startActivity(intent);
    }
    return false;
}
});

如果还需要传值的话, 在Activity中进行数据解析:

1
2
3
4
5
6
if (intent.dataString.orEmpty().isNotEmpty()) {
mUserId = intent.dataString.orEmpty().parseValue("userid", 0)
} else {
ActivityLauncher.bind(this)
}
if (mUserId == 0) finish()

第二种 不配置intent-filter的方式

只需在WebView的shouldOverrideUrlLoading方法中进行内链解析处理即可:

1
2
3
4
5
6
7
8
  @Override
public boolean shouldOverrideUrlLoading(WebView view, String httpurl) {
if (httpurl.startsWith("app://jump_activity")) {
Intent intent = new Intent(context, XXXActivity.class));
startActivity(intent);
}
return false;
}

如果是外部浏览器页面点击跳转指定Activity

只需在清单文件中对指定Acticity做intent-filter配置即可

1
2
3
4
5
6
7
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="personal_page_info"
android:scheme="${app_scheme}" />
</intent-filter>

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Hexo NexT 主题集成 utterance 评论系统

发表于 2020-05-20

使用 Hexo 的 Next 主题搭建的博客内置了很多种评论系统,如:gitalk,changyan 等。

这里讲解集成另外一种评论系统:utterance(https://utteranc.es/)

其配置非常简单,支持 Github 账号登录后才能评论。原理就是使用了 Github 的 Issues 功能来保存评论。

配置步骤

创建 Github 仓库

上面提到,utterance 使用 Github 保存评论,那我们就需要创建一个 repository 专门保存评论。

repository名称可以根据自己喜好取,这个后面会用到。

授权

用户在博客页面上输入评论,utterance 拿到这个评论后,自动的提交到上面刚创建仓库的 Issues 里。

所以我们需要授权 utterance 应用能访问仓库的 Issues。

点击链接: https://github.com/apps/utterances ,如下图所示:

主题配置

一,在主题的配置文件 _config.yml 文件中,加入如下配置:

位置可以放到其它评论配置的后面

1
2
3
4
5
6
7
8
9
# 整合 utterance 评论
utterance:
enable: true
# 仓库名字,格式:你的用户 ID/ 仓库名称,如:zhangsan/utterance_repo
repo:
# 主题
theme: github-light
# 映射配置
issue_term: pathname

二,在主题的 layout\_third-party\comments 文件夹下,创建 utterance.swig 文件,添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script type="text/javascript">
(function() {
// 匿名函数,防止污染全局变量
var utterances = document.createElement('script');
utterances.type = 'text/javascript';
utterances.async = true;
utterances.setAttribute('issue-term','{{ theme.utterance.issue_term }}')
utterances.setAttribute('theme','{{ theme.utterance.theme }}')
utterances.setAttribute('repo','{{ theme.utterance.repo }}')
utterances.crossorigin = 'anonymous';
utterances.src = 'https://utteranc.es/client.js';
// content 是要插入评论的地方
document.getElementById('gitment-container').appendChild(utterances);
})();
</script>

三,还是刚才那个文件夹,找到 index.swig 文件,加入如下配置:

1
2
{% elif theme.utterance.enable %}
{% include 'utterance.swig' %}

注意加在 endif 后面,如:

四,以上只是添加了脚本,还需添加 comment 样式。找到 layout\_partials\comments.swig 文件。在最后加入内容:

1
2
3
4
{% elif theme.utterance.enable %}
<div class="comments" id="comments">
<div id="gitment-container"></div>
</div>

如:

总结

以上就是整个集成 utterance 评论的流程,按照流程一步一步来应该是没问题的。

本文转载自:https://zhangzw.com/posts/20190720.html

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

如何给你的博客集成Gittalk or Gitment 第三方评论插件

发表于 2020-05-19

Gittalk or Gitment都是基于的GitHub Issues作为评论系统 它们的配置方式基本差不多

开始配置

第一步 :

首先需要到GitHub上去新建一个仓库用于存放评论的内容:

第二步:

在设置中打开isue功能, 默认是开启状态:

第三步:

需要注册一个Github Application:

注意两个URL就是你网站的域名。应用名称和描述和之前仓库的保持一致就行, 方便以后归类查找。

注册成功后接下来到了以下页面:

其中Client ID 和 Client Secret是我们需要的东西

第四步

gitalk评论插件配置

只需要将如下代码引入你想添加评论的 html 或者 jsp 页面中就可以使用了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<-- 引入 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
<script src="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js"></script>

<-- 添加一个容器-->
<div id="gitalk-container"></div>

<-- 生成 gitalk 插件-->
var gitalk = new Gitalk({
clientID: '56f73fbc5e79a466ea62', //Client ID

clientSecret: '26d8eb4f3b0de9ce02382103ffc32ba34c4671f4', //Client Secret

repo: 'newban_comment',//仓库名称
owner: 'songjianzaina',//仓库拥有者
admin: ['songjianzaina'],
id: location.href, // Ensure uniqueness and length less than 50
distractionFreeMode: false // Facebook-like distraction free mode
})

gitalk.render('gitalk-container')

如果你的博客使用的也是Next主题可以直接配置gitment评论插件

找到自己主题文件下的配置文件_config.yml中配置gitment即可

1
2
3
4
5
6
7
8
9
10
11
12
13
gitment:
enable: true
mint: true # RECOMMEND, A mint on Gitment, to support count, language and proxy_gateway
count: true # Show comments count in post meta area
lazy: false # Comments lazy loading with a button
cleanly: false # Hide 'Powered by ...' on footer, and more
language: # Force language, or auto switch by theme
github_user: 自己账号的用户名
github_repo: 刚刚创建仓库的名
client_id: xxx 刚刚得到的值
client_secret: xxx 刚刚得到的值
proxy_gateway:
redirect_protocol:

其他评论系统

image-20220708110541293

还有在线聊天系统

image-20220708110639326

详细介绍参见《Butterfly 安装文档(四) 主题配置》

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

在Gradle中声明一个Java可使用的变量

发表于 2020-05-18

生成Java常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
android {
buildTypes {
debug {
buildConfigField "int", "FOO", "42"
buildConfigField "String", "FOO_STRING", "\"foo\""
buildConfigField "boolean", "LOG", "true"
}

release {
buildConfigField "int", "FOO", "52"
buildConfigField "String", "FOO_STRING", "\"bar\""
buildConfigField "boolean", "LOG", "false"
}
}
}

你可以访问他们 BuildConfig.FOO

生成Android资源

1
2
3
4
5
6
7
8
9
10
android {
buildTypes {
debug{
resValue "string", "app_name", "My App Name Debug"
}
release {
resValue "string", "app_name", "My App Name"
}
}
}

您可以按照通常的方式访问它们。@string/app_name或R.string.app_name

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Markdown中图片插入的近似优雅法

发表于 2020-05-18

最终实现效果

  • 在图片上右键上传,自动生成markdown图片链接格式到剪切板中,任意地方Ctrl+v均可粘贴

  • 在文件上右键上传,自动生成markdown超链接格式到剪切板中,任意地方Ctrl+v均可粘贴

markdown介绍

  • Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式。
  • Markdown的语法简洁明了、学习容易,10分钟即可完全学会掌握。Markdown 的语法全由一些符号所组成,这些符号经过精挑细选,其作用一目了然。 即使没有渲染器,以纯文本方式阅读,也毫无障碍。
  • 对于开发人员还有一个特别棒的优点:得益于其纯文本属性,用markdown编写的文档、表格等,存放与svn/git/perforce以后,可以非常方便的使用比较工具,对比历史版本!这个word、excel……所不能或者不易办到的。

markdown的痛点

图片保存现状

成也萧何败也萧何,由于markdown的纯文本属性,决定了它不可能优雅的存储图片,在实践中有2种图形表示法

  • markdown标准

    1
    ![Alt text](/path/to/img.jpg)

    这样实际是存放的图片地址,而非图片本身

  • html转义

    1
    ![](data:image/*;base64,iVBORw0KGgoAAAANSUhEUgAAAJgAAADZCAIAAABTpG6/AAANaklEQVR4Ae2de1BU1x3H97ILC8gCRh5RjC9sEozPaQc7TiCO02o6+aMZp8M/plOn/8TEtv/V/OMM5Z9OdPJPk5ohbWfazNSJ0o6dSf6IEkubYlLt1PgAwReagK7A4gPEFVjY7Xf3wOYII=)

    以上是base64编码以后的二进制数据

上述2种方法,在书写markdown的过程中,都很不方便

便利的编辑器

  • typora

    在windows下最好的markdown编辑器,没有之一

    拖动图片到文档中,会自动拷贝图片到硬盘的指定位置,算是半自动化解决了问题

    但是依然存在问题:分享为博客后,这些图片的路径很难正确处理

理想中的解决方案

  • 在“我的电脑”中任意位置找到图片,右键点击“上传”,
  • 自动生成markdown格式![Alt text](http://***.***.***/***.jpg)或者[filedes](http://****.*****.***/***.zip)在剪切板中
  • 在任何文本/markdown编辑器中,直接Ctrl+V即可完成编写

准备工作

COS

对象存储(Cloud Object Storage,COS)

我们需要一个空间,用于存放图片或者文件,这里推荐使用腾讯云或者七牛云

他们提供的免费额度足够普通用户使用了

腾讯云的免费额度

资源类型 资源子类型 每月免费额度
存储空间 存储空间 50 GB
流量 外网下行流量 10 GB
流量 腾讯云 CDN 回源流量 10 GB
请求 读请求 100 万次
请求 写请求 100 万次

七牛云的免费额度

资源类型 免费额度
标准存储空间 0-10 GB
每月上传流量 无上限
标准存储每月写请求 Put / Delete 0-10 万次
标准存储每月读请求 Get 0-100 万次

本地环境

这里以腾讯云接口为例,本地需要nodejs运行环境

  • nodejs

  • 腾讯云SDK

    环境变量配置

  • 在windows环境变量中增加一项NODE_PATH,最好同时指向2处,例如:C:\Users\ZNMLR\node_modules和C:\Users\ZNMLR\AppData\Roaming\npm\node_modules

  • 前者对应npm的本地安装,后者对应npm的全局安装

开始优雅之旅

实现右键调用某bat

目标:在任意格式文件上单击右键,弹出菜单“上传”,然后调用指定bat

  • 打开注册表编辑器

    WIN+R调用运行库,输入regedit,会打开注册表编辑器

  • 找到计算机\HKEY_CLASSES_ROOT\*\shell

  • 新建项,名字随意

  • 再新建command子项,并修改右侧默认值

    图中的D:\1.bat是我测试用的,其存放于某合理的地方

  • 修改批处理内容,测试是否工作

    1
    2
    3
    4
    @echo off
    echo %1%
    pause
    exit

    该批处理暂时只获取输入参数,并打印出来

    然后等待用户敲个Enter就自动退出了

  • 右键点击任意文件

  • 查看运行效果

不那么严肃的声明

今天是我用nodejs的第二天,代码可能写不那么符合标准不那么优雅,见谅

如果发现有更好的优化方法,请发邮件通知我,谢谢 yangyunzhao#qq.com

安装腾讯云SDK

  • 安装nodejs运行环境

    这个没什么好说的,双击下一步就好了

  • 安装sdk

    1
    2
    3
    C:\Users\ZNMLR>npm i cos-nodejs-sdk-v5 --save -g
    + cos-nodejs-sdk-v5@2.4.0
    added 68 packages in 10.082s
  • 再次安装sdk

    不要问我为何有这一步,腾讯云文档这样写的

    先下载腾讯云SDK,解压,到指定目录执行命令

    1
    2
    3
    4
    5
    C:\Users\ZNMLR>d:
    D:\>cd cos-nodejs-sdk-v5-master
    D:\cos-nodejs-sdk-v5-master>npm install -g
    + cos-nodejs-sdk-v5@2.4.0
    updated 1 package in 6.33s

获取腾讯云上传鉴权码

  • 请到腾讯云注册账号并实名认证,

  • 新建存储桶

  • 获取APPID、SecretId、SecretKey、存储桶名称、所属地域

    以上步骤请自行阅读腾讯云文档,这里不做说明

编写上传脚本

官方示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 引入模块
var COS = require('cos-nodejs-sdk-v5');
// 创建实例
var cos = new COS({
AppId: '1250000000', // 修改为自己的appid
SecretId: 'AKIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // 修改为自己的SecretId
SecretKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // 修改为自己的SecretKey
});
// 分片上传
cos.sliceUploadFile({
Bucket: 'test', // 修改为自己的存储桶名称,由英文、数字和标点符号组成
Region: 'ap-guangzhou', // 修改为自己的所属地域,应该是纯英文的部分
Key: '1.zip', // 这个是远端的地址
FilePath: './1.zip' // 这个是本地地址
}, function (err, data) {
console.log(err, data);
});

执行js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
D:\>node 2.js
warning: AppId has been deprecated, Please put it at the end of parameter Bucket(E.g: "test-1250000000").
(node:11824) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
null { Location: '**********.cos.ap-shanghai.myqcloud.com/1.png',
Bucket: '*****',
Key: '1.png',
ETag: '"*******************-1"',
statusCode: 200,
headers:
{ 'content-type': 'application/xml',
'transfer-encoding': 'chunked',
connection: 'close',
date: 'Sun, 06 May 2018 03:38:13 GMT',
server: 'tencent-cos',
'x-cos-request-id': '************************' } }

上传成功了

敏感信息用*号代替了

但是文件在根目录下

###优化:上传到指定路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var filepath='D:/135.png';
var filename = filepath.substring(filepath.lastIndexOf("/")+1);
var today = new Date();
var year = today.getFullYear();
var month = today.getMonth() + 1;
var urlkey=year+"/"+(month<10?'0'+month:month)+"/"+filename;


// 引入模块
var COS = require('cos-nodejs-sdk-v5');
// 创建实例
var cos = new COS({
AppId: '*',
SecretId: '*',
SecretKey: '*',
});
// 分片上传
cos.sliceUploadFile({
Bucket: '*',
Region: '*',
Key: urlkey,
FilePath: filepath
}, function (err, data) {
console.log(err, data);
});

执行脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
D:\>node 2.js
warning: AppId has been deprecated, Please put it at the end of parameter Bucket(E.g: "test-1250000000").
(node:7620) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
null { Location: '*.cos.*.myqcloud.com/2018/05/135.png',
Bucket: '*',
Key: '2018/05/135.png',
ETag: '"*-1"',
statusCode: 200,
headers:
{ 'content-type': 'application/xml',
'transfer-encoding': 'chunked',
connection: 'close',
date: 'Sun, 06 May 2018 04:16:42 GMT',
server: 'tencent-cos',
'x-cos-request-id': '*=' } }

查看返回值Location生成路径已经有年月了

优化:上传后自动存放在剪切板

略

优化:生成markdown格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
var picsuffix=new Array(".jpg", ".png", ".bmp", ".jpeg");
function contains(arr, obj) {
var i = arr.length;
while (i--) {
if (arr[i] === obj) {
return true;
}
}
return false;
}

var filepath='D:/tk.db';
var filename = filepath.substring(filepath.lastIndexOf("/")+1);
var today = new Date();
var year = today.getFullYear();
var month = today.getMonth() + 1;
var urlkey=year+"/"+(month<10?'0'+month:month)+"/"+filename;
var suffix=filename.substring(filename.lastIndexOf("."), filename.length);

// 引入模块
var COS = require('cos-nodejs-sdk-v5');
// 创建实例
var cos = new COS({
AppId: '*',
SecretId: '*',
SecretKey: '*',
});
// 分片上传
cos.sliceUploadFile({
Bucket: '*',
Region: '*',
Key: urlkey,
FilePath: filepath
}, function (err, data) {
console.log(err, data);
const util = require('util');
var url='';
if (contains(picsuffix, suffix)) {
url='![](https://'+data.Location+')';
}
else {
url='[](https://'+data.Location+')';
}
require('child_process').spawn('clip').stdin.end(url);
});
  • 这次定义了数组picsuffix,实现了图片与文件生成不同的字符串
  • 实现了自动拷贝到剪切板的功能

关联bat与上传脚本

  • 修改之前的bat批处理文件

    1
    2
    3
    @echo off
    node D:\2.js %1%
    exit
  • 修改js脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    var picsuffix=new Array(".jpg", ".png", ".bmp", ".jpeg");
    function contains(arr, obj) {
    var i = arr.length;
    while (i--) {
    if (arr[i] === obj) {
    return true;
    }
    }
    return false;
    }

    var filepath=process.argv.splice(2).toString();
    var filename = filepath.substring(filepath.lastIndexOf("\\")+1);
    var today = new Date();
    var year = today.getFullYear();
    var month = today.getMonth() + 1;
    var urlkey=year+"/"+(month<10?'0'+month:month)+"/"+filename;
    var suffix=filename.substring(filename.lastIndexOf("."), filename.length);

    // 引入模块
    var COS = require('cos-nodejs-sdk-v5');
    // 创建实例
    var cos = new COS({
    AppId: '*',
    SecretId: '*',
    SecretKey: '*',
    });
    // 分片上传
    cos.sliceUploadFile({
    Bucket: '*',
    Region: '*',
    Key: urlkey,
    FilePath: filepath
    }, function (err, data) {
    if(err){
    console.log(err);
    }
    else{
    console.log(data);
    const util = require('util');
    var url='';
    if (contains(picsuffix, suffix)) {
    url='![](https://'+data.Location+')';
    }
    else {
    url='[](https://'+data.Location+')';
    }
    require('child_process').spawn('clip').stdin.end(url);
    }
    });
  • 大功告成

本文为转载自:http://blog.znmlr.cn/

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

1…394041…48

乱码三千

android程序员一枚,擅长java,kotlin,python,金融投资,欢迎交流~

479 日志
139 标签
RSS
© 2024 乱码三千
本站总访问量420537次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%