研究生的第一个假期作业终于在今天结束了。寒假作业评比一共三轮，一共7个组，我们拿到了第一名，小组奖金900元虽然金额不大，也算是没有辜负我们组所有人这么长时间花费的努力。所以，简单记录一下这个寒假的工作吧~接收到任务1月10号学校放假，那时候新型冠状病毒还并不是一个新闻。我以为这又是一个枯燥的寒假。本来我们的寒假期间是1月10号~2月5号（正月十二）所以虽然我们腊月都感到截止日期的逐渐逼近，但我们还都是心照不宣的不讨论寒假作业这件事情。到了正月初四，组长终于开始意识到“问题很大”。于是组长贴心的下午两点叫我们起床，开工。那个时候虽然只是听说疫情挺严重，学校的通知也只是正月15之前禁止到校，但谁也不知道疫情竟然已经如此严重了，延迟开学居然延迟3月份还未能开学。[album type="photos"][/album]1月28号，正月初四，如果按照既定的时间截止的话，时间已经很紧急了，但是我丝毫没有开始写代码的欲望，甚至还有一种莫名的自信。但是组长的任务安排也都是有更近的截止时间，所以每天下午3点左右简直就是灵魂拷问时间啊2月4号之后，公司那边要求2月7号提供项目目前的进度报告：所以我们必须在2月6号左右就得完成一个大概的初始版本。也就是至少客户端可以直播对话。需要认真起来了然后这个时候，我对整个项目的流程都不是很清楚，拉流推流啥意思。recorder、grabber是啥。player、client、capture关系又是什么，我真的不太清楚。关于我们这次视频会议，公司之前在直播这方面有所研究，一直开发维护theia项目，这个项目的功能就是主播直播，观众观看、发弹幕、连线之类的。不管怎样，到了2月7号晚上我们基本有一个可以用的版本了。界面啥的基本都是沿用theia项目，我们为了简化流程，客户端拉流的只拉了一个多个用户的混合流。就这样有一周暂时没怎么写寒假作业，2月15号我觉得学一下javafx，重新写一下客户端这边界面，现在的界面颇有一股复古的气息。界面重构以及流程梳理本科的时候用netbeans写过java的桌面端程序，netbeans是可以直接拖控件就创建好界面的。现在使用Scala+javafx，于是学习了screnbuilder的用法还有jxml的用法，以及在网上找到了一个Scala+javafx的helloworld demo。到了19号我基本把界面重构出来，也把整个客户端的流程重新设计了一下，整个client模块我基本是重新写了一遍。到了2月25号的时候，对照寒假作业的要求，发现我们还有一些功能没有完成：这几个功能让整个会议的功能变得复杂了很多。除此之外，之前client只拉一个混合后的流的做法虽然简单，但是问题是显而易见的，延迟太高，5、6s左右，而且用户自身的画面和声音在混合流中，都有延迟，就很奇怪。所以，最后我们还是决定客户端拉多个用户的流，这样问题是增加了客户端这边的内存开销，但是效果是延迟更低，会议效果会更好。不仅如此，在最后的几天根据发言人、支持人身份对客户端的画布进行动态分配。写代码是一回事，等真正一起联调测试的时候，bug就是一个个的浮现水面了。准备最后的评选2月28号，我们开始了第一次测试，当时由于我开了全局代理，导致 new Image(url) 这个地方无法对图片进行加载，以至于我一打开会议界面就卡死了。所以第一次测试只有我们组的另外两个人。当时测试问题还比较少。到了29号晚上3个人测试，问题就变得特别多，不仅客户端这边，后台那边对ws消息的处理也有一些问题。3月1号下午我们又开始了测试，这才基本没什么影响使用的大功能了。3月2号开始了第一轮评比，7进3，我们录制一个4~5分钟视频发到视频网站上，然后研二的学长学姐们评比3月4号上午开始第二轮，3进2，同样是录制一个1~2分钟的视频发到视频网站上，然后参赛其他组评比3月4号下午开始进行第三轮，2进1，是参赛其他组直接观看web端的录像（混流后的）进行评比。由于我们客户端界面好看，第二轮我们录制的视频还特意做了一个小剧本，配上朱一旦的「完美搭档」的bgm，第三轮我们web查看的录像的混流效果很好，延迟较低，而且音画基本同步。所以最后，我们拿到了第一名。从一开始我们感觉好像来不及，到最后我们每个人都是朝着第三轮的目标开始完善自己的部分。也算是没有辜负我们的努力吧~最后的完成的界面：同时还有web端可以查看和管理录像。相关技术和工具：scalajavafxsrs 流服务器ffmepeg项目地址：https://github.com/HiddenTrackMJ/geek有兴趣欢迎star,fork~
苹果IOS/Ipad OS 10.13 OSX10.15 全系列更新后，有一个内置软件变化很大，那就是「提醒事项」。使用Android设备，尝试了无数款规划类的应用，比如什么四象限类型的。但没能坚持下来规划任务。但是Microsoft Todo 还是留在了我的Android手机上，虽然也没怎么使用。换了iphone之后，也是在研究生生活中，每天其实有很多琐碎的事情，如果不记录下来，简直一团糟（和本科混吃等死很不一样）ToDo 在iPhone上同样适用，与此同时微软时隔几年之后在Mac也上架了ToDo,使得这款应用成为一个跨平台的利器。但随着不断使用中，发现了一些我很不喜欢的地方，新版本的「提醒事项」，有着ToDo的影子，所以考虑是否切换到该内置应用。下面是两款软件的对比，也可以给大家做一些参考：ToDo设计问题「我的一天」「我的一天」这个微软介绍的截图中的“特性”。但却很有问题。对任意一个任务可以选择「添加到我的一天」，但是「我的一天」和「已计划的日程」却是完全独立的。「我的一天」里面的任务却不会出现在「已计划的日程」，除非你再次给这个日程添加一个「截止日期」/「提醒日期」为今天。这看起很复杂。如果我需要安排今天的任务，而且希望今天的任务也出现在「已计划的日程」中，则必须操作两次……对于「提供我的一天的建议」这个功能似乎很酷，但，其实就是显示一些未完成的清单列表嘛…… 我几乎没使用过这个功能。「任务」到底是什么任务？很早，ToDo 是没有文件夹分类清单，只能添加清单一级。所以「任务」这一栏就显示了所有的任务。后来增加了清单文件夹📂这一个功能，「任务」里面只会显示“未归类的任务”。对于我来说，「任务」这一栏就是空的，因为我所有的任务都会归属某个清单。这个功能就很……截止日期和提醒日期对于每个任务，ToDo 有两个时间设置。看起来，这两个时间的定义不同，但是实际操作起来却没什么必要。一个任务提醒你需要去完成了，为什么这个时间点不是截止时间呢。除非这个任务是时间跨度很长。但非常不建议添加这样模糊的任务到清单中。每一个任务尽量是具体的、可以很快执行完毕的，如果是长期任务一定要进行划分，否则这样的模糊的任务很大可能是无法进行的。我在使用ToDo的时候设置这两个时间完全就是跟随心情，有时候点截止日期，有时候点提醒日期，这让我很迷茫，尽管这两种方式都会让任务出现在「已计划日程」中。容易误触「已完成」在IOS上，如果选择「隐藏已完成任务」，在任务清单中，选中 一项未完成的任务 的「完成按钮」，这项任务就会立即消失在这个列表中。天知道，我已经很多次的误触这个「完成按钮」，导致不知道把哪个任务不小心给点「已完成」了。正确的体验应该是，点击已完成，不会立即消失，而是再次刷新列表的时候再去消失这一项。优点优点也有一些。跨平台，Android、IOS，Mac，Linux（虽然用不到）。但是我偶尔会用我的旧Android手机睡觉前看新闻（嘿嘿就是focus软件），偶尔也会记录一些突然想起来的任务。动画很不错。很多操作都有动画，比如任务消失，显示在清单列表中，列表的显示等排序的方式很多一些小细节做的比较完善一点，我喜欢的是这个：选择「提醒时间」的时候，会有「今日晚些时候」，而对于「明天」、「下周」这样的选项也会有一个默认的时间（不仅仅是日期）。因为很多时间，我的任务并不是精确到某个时间点，而是某天内完成都可以的，自己再去选择一个时间就有点麻烦和纠结了。对于今天超时的任务，选择「今日晚些时候」就很方便的再拖一会儿。提醒事项设计优点几乎ToDo的设计缺陷（我认为的）都修正了。「今天」的任务列表是“提醒时间为今天”的任务列表，没有对任务进行「添加到今天」这一步多余步骤。「全部」类似于ToDo的「任务」的功能，但是这里会“按照清单”分别显示所有的任务只有提醒日期一个时间设置，更简单IOS上，对任务点击已完成，不会立即消失。（但是Mac上也会立即消失，好在可以command+z 撤销操作）上面最膈应我的四个问题，「提醒事项」都修复了。除此之外还有一些亮点：能够对自然语言进行解析，这个Android 版本的Todo也是支持的，但是到了苹果平台就没有了，可能还来得及迭代。和电脑端可以使用「接力」任务提醒自动关联日历的日程中会在日历📅的小部件中显示，系统提醒非常及时和准确。但自身仍然也有一些问题。缺点提醒我，需要手动指定一个时间没有想ToDo那样的默认时间，就有点麻烦了！没有指定时间的任务如果一个任务的提醒时间只指定日期，但不指定时间，这个任务不会自动添加到日历的日程中，也不会有弹窗提醒。不提醒可以理解，但好歹应该加到日历中的日程中吧。同样的在IOS 13.12中，联系人的生日如果只选择日期，不选择年份不会添加到日历的日程中，在新版本中修复了这个问题。所以有理由相信这个设计问题被忽视了。这三个缺点看起来似乎没有ToDo的设计缺陷要重要，但是实际上对于体验上却又致命的关键。除此之外，两款软件的功能几乎一致。在多平台的速度上没有太大差别。界面细节“很”简陋对点击「已完成」/「未完成」没有震动的反馈，甚至在IOS上，已完成任务和未完成任务的样式是一模一样的，已完成的任务没有删除线，也没有文字变灰……很难受几乎为0的动画效果，非常不符合苹果的风格，很僵硬，甚至Mac的「提醒事项」双击任务有的时候会卡顿。子任务在任务编辑页面的最底下Mac上的显示方式还好一点，会将任务和子任务同时在列表中显示出来，子任务会有一个缩进。而在IOS的应用中，子任务的添加在任务编辑页面的最下方，你好歹也放在前面呀，子任务这个功能这么不重要吗……[album type="photos"][/album]最后最后决定，迁移使用苹果的「提醒事项」。我倾向使用内置的应用，一开始我甚至准备使用苹果的内置地图应用，但在国内比高德和百度差太远，就放弃了。华为的Android机器上用来记录的我一直使用的是便签而不是第三方应用。因为内置应用一般上更简单，更少的商业化。以上内容仅供参考~
我的选择Mac端：mweb 2.0移动端 ： 1writer这样我的跨平台写作流程是：在iCloud里面设置一个文件夹 写作文档。电脑端使用mweb的外部模式，打开该文件夹，进行写作。同时iCloud会进行同步。移动端使用1writer 打开外部文件夹，可以继续写作。[album type='photos'][/album]其他同类型应用使用体验一点吐槽首先统一吐槽一些软件，非得自己搞什么“文档库”，文档都不知道被软件存储到什么位置，然后必须使用软件内部的iCloud同步才能跨平台写作。最关键的是文件也不知道存储在iCloud的什么位置。我个人觉得写作软件：首先做好显示外部文件夹层次和文件就可以了。现在手机、电脑设备的文件系统都比较完善了。没必要搞什么应用内部的文档库，给我一种不安全感，因为我不知道文件存哪了其次应用内部也完全没必要搞iCloud同步，如果软件本身就支持打开iCloud外部文件夹，同步问题就自然解决了。而不是把用户的文档数据转成应用内部的数据，然后再用iCloud同步，这不是多此一举吗。评分标准满分100分。写作软件只要有以下优点，我个人就觉的是一款完美的软件：价格合理（20分）细节处理（比如主题切换、空格习惯设置、实时预览等等）（20分）支持对文件夹、标题和内容快速搜索，搜索结果呈现简单明了（10分）显示外部文件夹的层次结构以及相应的内容（20分）markdown解析流畅，对中文支持好，支持大部分的markdown语法（10分）对图片的上传有一个合理的方式（10分）至于能够同步博客、medium之类的功能对于我个人来说可有可无（5分）ui颜值不错，太丑的界面实在让人觉得 ...当然习惯了可能就好了吧（5分）除此之外的什么聚焦模式，打字机模式，有则优，没有其实根本也不影响写作体验。（0分）bear（移动端）（75分）[album][/album]字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务同步(5')UI界面(5')说明10元/月各种小功能考虑的比较好不支持支持内容、标题解析的样式好看，可以换主题 不支持同步博客之类的不是ios原生那种，但却是很精致得分1020010101005但是单就不支持外部文件夹对于我来说已经不会使用了。ia writer（移动端）（38分）因为我先用了ia writer，觉得差强人意，于是决定在移动端使用这款软件，没想到用户体验几乎为0。而且售价60，我不知道谁给他的勇气。[album][/album]字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务同步(5')UI界面(5')说明60元/买断自带markdown小键盘，有聚焦模式不支持支持搜索内容、标题，但搜索结果只有标题，没有匹配的内容差强人意，不支持公式如果是iCloud的文档，图片会自动放到iCloud里面，本地文档是无法上传图片的无不说了得分1015085500缺点：界面劝退，线条蓝色图标，我感觉像是上世界的Android风格，完全没有iOS的界面元素不支持外部文件夹打开，只支持外部某个文件打开，劝退1writer（移动端）（76分）[album][/album]字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务发布(5')UI界面(5')说明30元/买断有markdown工具条，还有一些个性化的设置我在iOS平台唯一看到的支持外部文件夹的软件只支持搜索标题差强人意会把图片自动放到相同文件夹类，无法自己设置无中等偏上，有改进的空间，虽然用了一些iOS设计风格，但整体上还是不够iOS得分20152055803ulysses（移动端）（45分）[album][/album]字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务发布(5')UI界面(5')说明28元/月，太贵了，iCloud一个月才6月呢这个软件打开你就能感受到花了很多功夫不支持支持内容、标题搜索，搜索界面也很好看不支持表格，样式还是很好看的没找到本地图片上传，只找到一个附件，但是和文章是分离开来的支持wp,ghost,mediumios原生界面，完美得分5200105055ia writer（电脑端）（66分）因为有试用期，我就下载了。基本功能还行，流畅性不够，尤其是右侧的编辑器区域，滚动起来就是不流畅，就是不流畅。而且聚焦模式用起来很……很不舒服。（可能我不习惯这个聚焦模式，内容始终显示在中间，上面还会有空白）语法高亮对中文也是没有作用的。字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务同步(5')UI界面(5')说明198元/买断差强人意，不支持更换主题颜色，写作的背景是灰色的，也不支持修改背景。灰色背景看得我很难受。支持支持，搜索速度很快没有代码高亮不支持从本地直接复制图片，也不支持调用第三方图床不支持差强人意得分51520108053Mweb（电脑端）（85分）[album][/album]这款是我在Mac端体验最好的，但是因为我刚买没多久就升级，而且需要重新购买，让我一直对这款软件有点意见！！我昨天升级了mweb3.0，有14天的体验期，我决定体验一段时间，如果没有优惠的话还是降级到mweb2.0，毕竟旧版本功能足够我使用，只是界面没有新版好看而已。支持多tab打开文档，这个功能非常好用，也是唯一一个我看到有这个功能的软件字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务同步(5')UI界面(5')说明128元/买断设置项实在太多了，切换主题都会小问题支持文档库和外部文件夹完美支持文字多了，滚动会卡（3.0专属bug），对表格的编辑支持也不够好支持第三方图床，也支持自动拷贝到文档文件夹下的media目录中支持多种，除了typecho基本都支持了我个人比较喜欢，相比ulysses还是差了一点得分1520201021053typora（电脑端）（95分）这是一款完全免费，也是很多人推荐过的软件，我很早以前一直使用，非常不错如果只是写markdown文档来说，这款软件非常棒，但在管理文件夹这方面是弱势，因为无法记忆上次打开的文件夹，每次启用都是一个新的空白文档。 现在已经支持打开设置一个固定文件夹了！！完美了但是整体设计，给我一种太轻巧的感觉以至于不太适合文件夹的展示，可能我没习惯。字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务同步(5')UI界面(5')说明免费对外观、markdown、图片、编辑器的设置基本都考虑到了支持支持用过所有软件中，解析最快，最流流畅的，完全是为markdown而生支持本地图片上传，不支持调用第三图床，但支持下载网络图片，支持ipic插件不支持简洁，美观得分20202010101005Ulysses （电脑端）（70分）也是在体验期下载，后面就没使用了界面好看，但是不支持实时预览，你如果想要看markdown的效果，得以pdf、text等某种文本导出才可以。[album][/album]字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务同步(5')UI界面(5')说明28/月有些细节有问题，比如 !> 会自动转化成 \!>,而且还没有开关，让人抓狂支持支持没有发现什么问题不支持从本地直接复制图片，也不支持调用第三方图床支持界面很好看得分515201010055tea (电脑端)（47分）[album][/album]完全免费，支持mac windows，移动端就没有了。这款软件我很早体验过一次，它自带的material风格（点击会有波纹效果）让我感觉和osx系统非常的不匹配。一种很不原生的性能很差的感觉。事实上，在文字编辑的渲染，总给我一种延迟的感觉。但是基于插件式的软件，很有创新点。有点类似vscode。但vscode更强大。这让我想起来，vscode本身也可以作为一个不错的markdown编辑器，但总给我一种杀鸡用牛刀的感觉。而且vscode也无法记住上一次打开的文件夹。还有一点vscode风格也是沿用Windows系统上面的。还有一个巨坑就是主打的所见即所得模式居然不能直接看到markdown的源码，必须导出为新的markdown文档，才能看到……我服了字段价格(20')细节处理(20')支持外部文件夹(20')搜索功能(10')markdown解析(10')图片上传(10')第三方服务同步(5')UI界面(5')说明免费还没简陋，我个人感觉不支持只支持当前文档搜索还可以无无还可以得分20100210005除此之外，我还尝试了ios上面的 markdown（够简洁，基本免费，但是不支持文件夹），pretext（更简洁，markdown预览不支持中文，不支持文件夹，基本就一个功能，编写markdown文件）总结中国开发者在markdown编辑器开发这方面已经越来越出色了。在界面、功能性上完全不输国外开发者。希望国内有一个和1writer一样设计思想的更优质产品出现。手机端，支持引入外部文件夹，我目前只发现了1writer。虽然bear、ulysses界面好看，但是贵啊，而且不支持外部文件夹。电脑端，基本都支持打开外部文件夹（除了 tea）。要免费，选择typora就好了，已经满足我的需求。或者也可以考虑mweb，在有优惠的时候。最不推荐购买的： ia writer 包括移动端和pc端，界面太丑。价格也不算便宜，至少在中文文档这方便稳定性、流畅性很差，不值。bear、ulysses 虽然订阅制，虽然很贵，但是购买之后不会觉得不值。从价格上，bear比ulysses更有性价比，两款软件功能很类似。ulysses界面我是最喜欢的，其次bear，次之mweb完~
环境要求mac OS/linux（其他操作系统类似）酷Qdocker+CoolQ HTTP API 插件NoneBotdocker如果是windows服务器或者电脑也可以安装酷Q，mac linux/linux服务器只能装酷Qdocker（推荐docker安装）!> 也可以直接使用「CoolQ HTTP API 插件」作者集成的docker镜像包，这样就无需配置「CoolQ HTTP API 插件」了。简单介绍简单介绍一下原理是什么。在酷Q中登录你的QQ账号，「酷Q」接管你的消息，并将QQ的所有消息请求和发送进行封装成一些接口，供“插件”进行进一步接管和调用。酷Q的插件后缀都是.cpk，需要安装在酷Q的内部。「CoolQ HTTP API 插件」作用就是专门针对“http请求以及信息处理”方面的插件，使用的语言当然是与酷Q一致的（易语言好像是）会易语言的开发者还是蛮少的吧...emmm「NoneBot」就像一个翻译官，将「CoolQ HTTP API 插件」的插件全部转变成“Python的方法”。「CoolQ HTTP API 插件」有很多个不同语言的翻译官，方便使用不同语言的开发者直接处理酷Q的信息。这样，我们直接写Python文件，就能直接接收或者发送QQ消息啦！开始开始安装！#1. 安装docker brew install docker #2. 安装酷Q+http插件集成镜像 docker pull richardchien/cqhttp:latest #3. 安装nonebot pip3 install nonebot 我们在docker中先启用酷Qdocker run -ti --rm --name cqhttp-test -v $(pwd)/coolq:/home/user/coolq -p 9001:9000 -p 5701:5700 -e COOLQ_ACCOUNT=123456 -e CQHTTP_POST_URL=http://example.com:8080 -e CQHTTP_SERVE_DATA_FILES=yes richardchien/cqhttp:latest 上面的命令如果需要改，就改9001和5701就可以了，其它的不用修改。其中：9001端口号就是登录酷Q的端口号5001端口号是HTTP API 插件开放的端口，这个不用管。如果需要启用多个酷Q，保证这个端口号和容器名称cqhttp-test不重复即可启动之后，打开127.0.0.1:9001，如果部署到Linux服务器上面就是ip地址:9001打开页面之后，会要求输入密码，密码默认是MAX8char。进入之后就是一个类似QQ登录界面，输入账号密码即可。（第一次登录可能要输入验证码）此时，酷Q其实就是以一个安卓手机的身份登录了你的QQ，并接管了你的所有消息处理。「CoolQ HTTP API 插件」同时也在开放接口供其他插件进行调用。接下来，我们需要使用nonebot 模块，使用Python代码接管QQ消息的接收和发送。在coolq/data/app/io.github.richardchien.coolqhttpapi/config/里面有<你的登录的QQ号>.json 文件，打开，并修改（如果没有则增加）三个变量为对应的值{ "ws_reverse_api_url": "ws://127.0.0.1:9999/ws/api/", "ws_reverse_event_url": "ws://127.0.0.1:9999/ws/event/", "use_ws_reverse": true }!> 如果docker部署电脑上的，把 127.0.0.1 改成host.docker.internal!> 如果docker是部署在Linux服务器上的，把127.0.0.1改成172.17.0.1这个操作就是让「CoolQ HTTP API 插件」把接收信息和发送的信息借给noneboot 模块来接管。所以，开始通过noneboot模块，使用Python开发吧。可以用pycharm ide，当然也可以直接新建一个文件夹，使用命令行启用。我们直接用命令行来启动。cd ~/Document mkdir qqboot_test touch boot.py cd qqboot_test & mkdir plugin 第一条指令cd切换到指定目录，后面的指令，你可以完全一样。打开boot.py，我们复制粘贴下面的代码：import nonebot from os import path if __name__ == '__main__': nonebot.init() nonebot.load_plugins( path.join(path.dirname(__file__),'plugins'), 'plugins' ) nonebot.run(host='127.0.0.1', port=9999)我们之前用ws://127.0.0.1:9999/ws/api/接管「CoolQ HTTP API 插件」插件的消息处理。上面的代码的作用启动noneboot模块，并且绑定到host的指定端口处。!> 如果是本机电脑运行的boot.py 程序，host填写127.0.0.1即可!> 如果是Linux服务器部署的boot.py 程序，host 改成0.0.0.0上面我们在qqboot_test项目里面新建了一个plugins文件夹，并且在boot.py指明插件文件夹的位置了。现在在这个插件文件夹创建一个文件吧。touch user.py在具体开发之前简单介绍一下noneboot的处理逻辑。有两种方式：noneboot 接收到用户发来的消息（自然语言），通过匹配消息的关键字，执行指定的命令函数。noneboot 直接接收命令，然后直接指定命令函数代码用户发送/文字的格式表示发送文字为名称的命令我们通过第一种方式处理用户自然语言的信息。比如，当用户发下面的句子今天天气怎么样？ 天气 天气好吗 北京天气在user.py文件里面编辑：@on_natural_language(keywords={'天气'}) async def _(session: NLPSession): # 返回意图命令，前两个参数必填，分别表示置信度和意图命令名 return IntentCommand(60.0, 'weather') @on_command('weather', aliases=('天气')) async def weather(session: CommandSession): # 从会话状态（session.state）中获取城市名称（city），如果当前不存在，则询问用户 city = session.get('city', prompt='你的城市是') weather_report = await get_weather(city) # 向用户发送天气预报 await session.send(weather_report) async def get_weather(city: str) -> str: # 这里简单返回一个字符串 return city+"的天气不错"代码分为三个部分：第一部分是解析自然语言，只要包含keywords里面的关键字就会进行对应的命令处理weather第二部分是命令处理函数，aliases是别名，意思是通过/[aliases]这种方式就可以直接执行命令函数第三部分是异步请求返回天气预报，这个正是noneBoot的一个特色。最后就是启动这个项目即可：python3 boot.py这个命令启动之后，你会看到console输出中有酷Q接管的所有信息输出。以上就是一个简单的QQ聊天机器人的开发以及部署。把机器人部署到服务器的docker上面，如果愿意可以有很多的创意，比如夜晚时间接收到任何信息都会自动回复“夜晚休息，上线后处理”但唯一可能的问题是docker中启动酷Q后，手机上不能再次登录相同的QQ号了。祝使用愉快~
当我从Linux转换开始使用macOS的时候，我发现macOS的终端很丑，它长这样：于是我找到这样一篇文章：Mac OS 终端 iTerm2 并配置 zsh。所以什么是终端，什么是zsh。终端很久很久以前，终端这个概念是这样的：是一种硬件，是一种输入输出设备，用于和计算机交换信息。终端的工作：用户通过终端输入命令，终端将命令传入计算机，执行后，并把结果输入到终端上显示。从这个概念不难可以看到电传打印机符合这个概念：但是现在计算机硬件一体化程度越来越高，硬件质量也越来越强，输入输出设备完全没必要单独用一个硬件和计算机进行交换信息了，取而代之使用软件终端和计算机进行交互信息。这就是终端模拟器 (Terminal Emulator)，也就是我们现在所讨论的终端。除了它不是硬件，它符合终端的概念和工作流程。举个例子，下面这些软件都是终端：1GNU/Linux：gnome-terminal、Konsole；macOS：Terminal.app、iTerm2；Windows：Win32 控制台、ConEmu 等。shell我之前很迷惑一个问题是，有终端就够了嘛，输入命令，执行并把结果输出，要shell干什么。更让人抓狂的shell还有很多种类，bash,cshell,zsh。这些到底是做什么的？所以需要明确下终端负责的工作。终端的工作范围：接收用户的输入，显示传过来的输出。（此外还提供一些优化用户体验的功能，比如支持文字复制粘贴、软件背景颜色的调整等）终端的工作其实是很少的哦，简单来说3点：GUI软件界面、接收输入、显示输出。而shell的核心工作就是操控计算机内核（如新建文件等等），即访问操作系统中的某个具有一定功能、可以处理具体事务的文件(二进制文件 ---- 此类文件经常被称作工具)、或包含一定内容的文件(文本文件)2shell分为：图形化 shell（图形化的桌面环境），如Windows Explorer （文件管理器）、 Linux的桌面环境有：KDE、GNOME、CDE、 XFCE等。命令行 shell。如bash 、sh 、csh 、ksh我们一般讨论shell都是指命令行shell。shell为什么叫做命令解释器不管是GUI shell 还是命令行 Shell，其实都是在解释命令，只不过GUI shell隐藏解释命令细节，用户通过点击鼠标的操作操作内核命令。shell为什么叫做壳如上图，shell通过解析命令调用 系统调用和相关API操作内核命令，进而读写硬件完成任务。shell是最外的一层的接收用户操作，像一层壳一样裹在整个调用过程中。为什么有这么多命令行shellshell与shell脚本语言并不完全相同的概念。shell除了包括对命令的语法规范（shell语言）和对命令解析，还包括一些额外的功能（如命令补全、主题等等）。shell语言和我们学习的任何其他高级语言c,python,java没有太大区别。不同的shell语言可以看做java语言的不同版本。不同shell的区别主要是两方面：脚本语言语法有差异功能、外观、自定义美化上有差异脚本语言早期的sh甚至不支持给长命令设置别名的语法。3zsh虽然完全兼容bash的语法，但还有一些语法差异，具体可以看Zsh和Bash，究竟有何不同 坑很深自定义功能配置除非我们需要编写shell脚本，普通用户一般很难发现不同shell脚本语言差异。对文件操作ls、pwd等等这些常用命令所有shell都一样支持。所以我们普通用户更关注是细节、外观、功能上的差距。这一点zsh做的格外的优秀。比如色彩高亮、命令提示、智能补全、快速跳转等等附加功能。4我们在讨论配置终端的时候，实际上是在讨论配置shell。为什么zsh如此受欢迎？这个较新的shell与bash兼容，但包括更多的功能。 zsh shell提供内置的拼写校正，改进的命令行完成，充当shell插件的可加载模块，允许您在命令行上别名文件名或其他任何东西的全局别名，而不仅仅是命令，以及更多主题支持。 它像bash，但有很多附加功能，附加功能和可配置选项，你可能会喜欢，如果你在命令行上花费大量的时间。3总结： 终端就是软件，是最外层的，shell = shell脚本语言解析器+编辑、外观等额外功能不同shell的切换输入cat /etc/shells，可以查看本机上的所有shell：临时切换比如切换bash，直接输入bash，即可切换到bash环境下，输入exit，复原默认shell环境。默认切换5#使用系统自带的 zsh：输入密码成功切换，重启终端即可使用 zsh。 chsh -s /bin/zsh #使用第三方的 zsh，如从 brew 安装的 zsh。 sudo chsh -s /usr/local/bin/zsh 命令行界面 (CLI)、终端 (Terminal)、Shell、TTY，傻傻分不清楚？ ↩ Shell 是用来解决什么问题的？ - Bryan Yuan的回答 - 知乎 ↩ Bash，Zsh和其他Linux Shell之间有什么区别？ ↩ 为什么说 zsh 是 shell 中的极品？ - 韦易笑的回答 - 知乎 ↩ 终端中的 zsh 和 bash ↩
前言之前学校开的选修课《数据挖掘》，布置的两道算法题，时间有限完成其中一道：用Apriori算法求特定支持度的频繁项集。算法本身不难，java萌新我却花费了一天的时间，特此记录。算法描述我们目的是求出项数为K的频繁项集即L(K)。Apriori算法的核心步骤是：L(K-1)通过自连接求出项数为K的候选项集合C(K)通过对C(K)进行一系列处理（剪枝 + 支持度判断） 得到L(K)集合在说明下面更多内容之前，先对一部分概念进行说明：事务：如第一张图中，每一行就是一个事务，如 T100 I1,I2,I5。其中T100是事务编号，这个随便命名，只要保证唯一即可。后面的I1 I2 I5是该事务中的项数据集：如图一，整个表就是数据集，就是所有的事务的集合。即D = {T100,T200…}项集：包含若干个项（如I1 、I2这样的项）的集合。可以看出项集和事务似乎有点项。但是项集更随意一点，任意几个项都能组成一个项集。但是事务是人为规定好的。可信度：项集在数据集中出现的重复次数。项集不一定需要在事务中连续出现。如{I1,I2,I5}项集在数据集中出现的次数为2次，分别是第一个和倒数第二个事务。接下来，详细分析算法中的两个步骤：1. 自连接：怎么个连接法呢？以图二中的L2频繁集的集合为例：项集支持度{I1,I2}4{I1,I3}4{I1,I5}2{I2,I3}4{I2,I4}2{I2,I5}2判断两个项集是否可以自连接要看两个项集的K-1项是否完全相同。如果满足条件，连接后的项集 = 第一个项集 + 第二个项集的最后一个元素。比如{I1 I2}与{I1 I3}满足自连接条件，连接后的项集为{I1 I2 I3}。2. 剪枝：剪枝是这个算法的核心，如果不进行这个步骤的话，也能得出正确结果，但是时间就会大大增加了。剪枝的核心是若某个集合存在一个非空子集不是频繁项集，则该集合不是频繁项集。我们通过自连接组成新的K项的候选项集后，需要通过剪枝判断是否满足条件。即找出该候选项集的含有(K-1)项的子集，并分别判断每个子集是否存在于K-1项频繁项集里。只要有一个不存在，那么该K项候选项集也不可能是频繁项集。这里我们只需要找含有K-1项的子集，而不用找所有的子集，因为这个算法是从下不断递归上来的，含有更少项的子集肯定是在频繁子集里的。问题关键是怎么寻找某个K项集的K-1项子集呢？很简单，依次把K项集中的每个元素去掉一个就可以了。完成了这两步骤，就完成了这个算法了核心步骤。3. 对精简后候选集统计每一个项集的支持度4. 根据最小支持度将候选项集转换为K项频繁项集合可以看出，如果不剪枝的话，第三步的工作量是非常大的。剪枝过后，候选集的体积大大减小了。但是获取含有一项的频繁项集因为没有候选项集，就需要特殊处理。这个很简单，就是统计数据集中的每一项的支持度，并和最小支持度进行比较，得到含有一项的频繁项集。代码分析github项目地址：https://github.com/ihewro/Aprioriimport java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.*; /** * @author hw * 算法名称：数据挖掘：Apriori算法求特性支持度下的所有的频繁集 * 算法原理： * * 0. 获取C1的时候，因为没有L(0)频繁集，所以单独处理一个函数 * 1. 已经频繁集L(k-1)，{}自连接+剪枝}求出精简后的候选集C(k) * 2. 根据精简后的候选集C(k)根据求出频繁集L(k) * * 数据结构： * 1. 使用List<String> 存储整个数据库的数据，string代表每一行的数据 * 2. 使用HashMap<String,Integer> 存储项集以及项集的重复次数，便于查找。是{项集}的集合。 * 3. 每个项集用String类型存储。项集中的每一项使用String.split(" ")方法变成数据，以便取用项集中的每一项 * * 给定的数据格式： * 1. 每个数据以空格隔开 * 2. 每一行代表一个事务T,事务编号就是行号 * * 示例数据： * 1. {11，12，13}是一个频繁项集，是属于HashMap<String,Integer>中的一个元素，数据类型为String. * 2. Set<String> = HashMap.keySet(); 用来存储HashMap的所有key的值，也就是存储所有的项集（不包括项集的重复次数） * * 相关概念： * 1. 支持度百分比 * 2. 支持度 * 3. 事务 * 4. 候选项集 * 5. 频繁项集 */ public class Main { private static double SUPPORT_PERCENT = 0.01; private static List<String> data2DList = new ArrayList<>(); public static void main(String[] args) { System.out.println("===================Apriori算法主程序界面===================="); //0. 输入数据 /*Scanner in=new Scanner(System.in); SUPPORT_PERCENT = in.nextDouble();*/ //1. 导入数据 importData(); //2. 进行算法处理 apriori(); } /** * Apriori算法主程序，需要递归处理 * * @return */ public static void apriori(){ //扫描整个数据库D，对每一项进行计数，获得一项的{候选项集合} Map<String, Integer> stepFrequentSetMap = new HashMap<>(); System.out.println("\n=====================第" + 1 + "次扫描的频繁项集列表======================" + "\n"); stepFrequentSetMap.putAll(getFrequentSets(findCandidateOneSets())); Set<String> stringSet = stepFrequentSetMap.keySet(); for (String string: stringSet){ System.out.println("频繁集：" + string + "支持度:" + stepFrequentSetMap.get(string)); } System.out.println("\n频繁项集的个数：" + stringSet.size()); int i = 1; //当生成的频繁项集为空的时候，退出循环 while(stepFrequentSetMap != null && stepFrequentSetMap.size()>0){ i++; //打印当前的频繁项集的信息 System.out.println("\n=====================第" + i + "次扫描的频繁项集列表======================" + "\n"); stepFrequentSetMap = getFrequentSets(getMinCandidate(stepFrequentSetMap)); if (stepFrequentSetMap != null){ stringSet = stepFrequentSetMap.keySet(); for (String string: stringSet){ System.out.println("频繁集：" + string + "支持度:" + stepFrequentSetMap.get(string)); } System.out.println("\n频繁项集的个数：" + stringSet.size()); } } } /** * 导入数据 * * @return 集合和集合，即二维集合 */ private static void importData(){ File file = new File("retail.dat"); try { //文件存在且为文件类型执行接下来的操作 if (file.isFile() && file.exists()){ InputStreamReader reader = new InputStreamReader(new FileInputStream(file), "UTF-8"); BufferedReader bufferedReader = new BufferedReader(reader); String lineTxt; while ((lineTxt = bufferedReader.readLine()) != null){//读取文件中的一行 data2DList.add(lineTxt); } reader.close(); }else { System.err.println("找不到指定文件！"); } }catch (Exception e){ System.err.println("读取文件内容出错！"); e.printStackTrace(); } } /** * 寻找1项的候选集C1（起始化特殊的处理） * * @return 返回map集合，这个集合是每一项以及对应的重复次数：key->value * * key：每一个候选项/频繁项 * value：相应候选项/频繁项的重复次数 */ private static HashMap<String, Integer> findCandidateOneSets() { HashMap<String, Integer> resultSetMap = new HashMap<>(); for(String dataList :data2DList) { String[] dataString = dataList.split(" "); //查询map集合中是否有该元素，如果没有，加入该元素，否则给该元素对于的value(重复次数）+1 for (String string :dataString){ string += " "; if (resultSetMap.get(string) == null){ resultSetMap.put(string,1); }else { resultSetMap.put(string,resultSetMap.get(string) + 1); } } } return resultSetMap; } /** * 从L(k-1)频繁集合中得到精简的C(k)候选集 * * 1. {连接 + 剪枝} = {精简后的候选集C(k)} * 是对L(k-1)频繁集的每一个集合，进行[边连接成候选集，边剪枝]而不是先全部生成了{候选集}，然后再进行一次大循环，进行剪枝，这样会增加时间复杂度 * * 先验原则：若某个集合存在一个非空子集不是频繁项集，则该集合不是频繁项集 * * 1. 根据先验原则压缩候选集大小后得到精简的候选集集合 * 2. 对精简过的候选项集进行累加计数 * * @param frequentMapSet * @return 返回C(k)候选集合 */ private static Map<String, Integer> getMinCandidate(Map<String, Integer> frequentMapSet){ //需要返回的精简过后的候选项集 Map<String,Integer> minCandidateMapSet = new HashMap<>(); //导入频繁项集的KEY Set<String> frequentSet = frequentMapSet.keySet(); /** * 1. {自连接过程:产生候选项集} * * 对于每一个项集，拆开成项的数组。将该项集的每一项与其他项集的每一项比较， * 如果有不重复的项，就将该项与原项集链接起来，组成K项的候选项项集。 */ for (String frequentItemList1: frequentSet){ for (String frequentItemList2: frequentSet){ String[] itemArray1 = frequentItemList1.split(" "); String[] itemArray2 = frequentItemList2.split(" "); //自连接生成的候选项集 String linkString = ""; boolean flag = true;//是否可以连接 /** * 判断是否可以自连接的条件： * 1. 前K-1项必须相同 * 2. itemArray1的最后一项必须小于第二个项集的最后一项 * 自连接过程： * 将第一个项集与第二个项集的最后一项连接起来 */ for (int i =0;i<itemArray1.length -1 ;i++){ if (!itemArray1[i].equals(itemArray2[i])){ flag = false; break; } } if (flag && itemArray1[itemArray1.length - 1].compareTo(itemArray2[itemArray1.length -1]) < 0){ linkString = frequentItemList1 + itemArray2[itemArray2.length - 1] + " "; } /** * 2. {剪枝过程，也就是先验规则的使用} * * 找出该候选集的所有子集，并判断每个子集是否属于频繁子集 */ boolean hasInfrequentSubSet = false;// 是否有非频繁子项集，默认无 if (linkString != ""){//自连接成功 //System.out.println(linkString); //候选项集的所有项的数组 String[] itemArray = linkString.split(" "); //*重点：找出该候选集的所有子集，实际操作的时候只需要找出比候选集少一项的子集集合 //*重点：因为这都是不断的递归上来的，项数更小的项集肯定是频繁项集 for (int i = 0; i <itemArray.length; i++){ String subString = ""; for (int j = 0;j<itemArray.length; j++){ if (j!=i){ subString += itemArray[j] + " "; } } if (frequentMapSet.get(subString) == null){ hasInfrequentSubSet = true; break; } } }else{ hasInfrequentSubSet = true;//这里并不是代表存在非频繁子集，只是表示没有自连接成功，没有找到候选集 } //自连接成功，加入到候选集集合中 if (!hasInfrequentSubSet){ minCandidateMapSet.put(linkString,0); } } } /** * 3. {对生成的候选集进行统计支持度} */ Set<String> minCandidateSet = minCandidateMapSet.keySet(); // 将每一行的候选项集，由String数据变成字符串数组。 // 将每一行的事务，由String转换成List<String> for (String itemList: minCandidateSet){ String[] strings = itemList.split(" "); int num = 0; for (String data:data2DList){ List<String>dataList = Arrays.asList(data.split(" ")); Boolean flag = true; //如果候选项集中有一项在当前事务中找不到，支持度则不会增加 for (int i =0;i < strings.length;i++){ if (!dataList.contains(strings[i])){ flag = false; break; } } if (flag){ minCandidateMapSet.put(itemList,minCandidateMapSet.get(itemList) + 1); } } } return minCandidateMapSet; } /** * 从精简后的C(k)候选集中得到L(k)频繁集合 * * {统计精简后的候选集C(k)的重复次数} = {最后得到L(k)频繁集} * * 1. 对精简过的候选集进行判断（之前已经做好了计数工作），不满足支持度的进行排除 * * @param minCandidateMapSet * @return */ private static Map<String, Integer> getFrequentSets(Map<String, Integer> minCandidateMapSet){ if (minCandidateMapSet == null){ //精简后的候选集为空，表示当前项数的候选集不存在，此时需要结束该算法了 System.err.println("候选项集为空"); return null; }else{ Map<String,Integer> frequentMapSet = new HashMap<>();//需要返回的频繁项集 Set<String> minCandidateSet = minCandidateMapSet.keySet();//获取候选项集的KEY，也就是所有的项集的具体数据 Double SUPPORT = (data2DList.size() * SUPPORT_PERCENT);//最小支持度 //Double SUPPORT = 5.0; System.out.println("最小支持度为：" + SUPPORT + " 候选项集的大小为：" + minCandidateMapSet.size() + "\n"); for (String itemListString: minCandidateSet){ //如果该项集的重复次数大于或者等于最小支持度，就把该项加入到频繁项即中 if (minCandidateMapSet.get(itemListString) >= SUPPORT){ frequentMapSet.put(itemListString,minCandidateMapSet.get(itemListString)); } } if (frequentMapSet.size() == 0){ //计算得到的频繁项集为空，表示此时循环应该结束了 return null; }else{ return frequentMapSet; } } } }
接触typecho一段时间，大家了解的插件应该是CommentToMail(by 羽飞) ，最新版本更新时间是2014-04-25。我之前也尝试了N次，最终是成功用上了这个插件。但是有好多小伙伴因为各种各样的玄学原因，无法正常使用。所以，这里推荐另一款插件CommentMailPlus，是小伙伴yorkchou 推荐的。他的博客也写了一篇介绍，下面我来详细说明一下 使用方法。（本地测试有效）网址在这：https://www.mailgun.com/插件下载地址：https://github.com/oott123/CommentMailPlus注册账号并验证账号因为网站是英文的，所以，我做了一个图↓图示应该很清楚了，一共6步。第6步点击【Domains】栏目，可以看到mailgun默认分配了一个很长很长很长的域名，即Sandbox subdomains。【注意】：这个域名是mailgun仅仅用来测试用的，如果直接使用这个域名配置的话，是只能向Authorized Recipients列表的成员发信。向其他人发现会被拒绝，错误信息： "reason": "Sandbox subdomains are for test purposes only. Please add your own domain or add the address to authorized recipients in Account Settings.",所以，我们要绑定自定义域名来实现向所有用户（评论者）发信。无需绑定信用卡。绑定自定义域名第一步点击顶部的【Domains】栏目，下面有一个黑色的按钮Add New Domain，戳它！第二步填写Domain Name。建议填写【二级域名】，比如我的域名是ihewro.com，就填mail.ihewro.com。当然如果你域名多的话，拿一个闲置的顶级域名也没关系。第三步出现一个界面【Now Follow These Steps To Verify Your Domain】，就是验证你的域名的步骤：访问你的域名解析商，比如我使用万网，就去打开万网的后台为发信添加DNS记录：这里有两种方式，一种是添加txt类型一种是添加mx类型我使用第一种，感觉更简单一点。唯一注意的是，在万网填写主机记录要去掉你的根域名部分。比如Hostname为mail.ihewro.com，在填主机记录里填写的是mail即可。为追踪信息添加DNS记录：这个可以不填，不影响发信，但是可能影响其他功能。填写方式和注意部分和上面一样。比如我的万网后台填写记录就是这样的（下图↓）：​然后就绑定成功后，会在【Domains】栏目中看到你的域名前面是绿色的。后台插件的配置Mailgun API 密钥即API KeyMailgun域名即你的自定义域名，比如mail.ihewro.com发件人邮箱即Default SMTP Login好啦！一切大功告成！其他其他需要了解的是顶部导航条了。【logs】里面显示的是发信记录。可以详细看见每次发现的信息。就这样，逃～
最近才接触到linux。一开始使用的是ubuntu.后来发现了国产操作系统 deepin，被界面打动了，立即换成了 deepin 15.3.今天上午官方放出了15.4 beta版本公测（正式版本将在今年的4月份开始推送），所以要体验，只能通过.iso文件全新安装了。下了课，就安装上了 linux deepin 15.4 桌面操作系统。下面是简单的体验。先说说缺点。第一，热区功能有点问题，非常不灵敏，时不时出现失效问题。15.3中没有发现这个问题。15.4 优化了效果之后，就变成如下图的情况：第二，调整声音时候的反馈生效十分刺耳。建议参考win 7的效果优化一下反馈音效。(重启几次，发现效果好了一点，不怎么刺耳)第三，正在打开软件的时候，鼠标没有正在加载的图示。这样用户无法知道刚才的点击启动操作是否有效，造成多次点击。使用ubuntu的时候，发现这点做的很好。第四，顶部状态栏仍然没有。这个特性再mac系统和ubuntu中都有，虽然windows一直没有，但我觉得，这个顶部状态栏还是非常好用方便的。第五，控制中心的天气似乎没有调整地区的功能，而且UI有点错位的问题。（见下图）第六，调整字体大小的选项现在只有3个选项了？只能调整10，12，16px. V15.3中可调整范围还是很大的，而且间隔是1px的调整。希望这点官方会保留以前的设置。第七，控制中心的设置，鼠标放在图标上没有具体说明。初次使用这个系统的话，不知道每个图标的具体含义。（见下图）优点或者说是改进也是蛮多的。第一，动画效果较15.3更多了一点。比如窗口的打开和关闭的动画效果更自然。但是点击关闭出现“关机/重启/注销/待机/锁定”界面仍然没有动画，很突然的感觉第二，深度音乐的界面和功能大大改进了。以前界面可能是仿很多年前的QQ音乐的UI，现在重写了变得很大气。第三，切换系统源的时候，以前是先出现所有镜像源的列表再逐渐显示出各个源的速度，现在是先后台检测完所有镜像源的速度，然后再直接显示出来。当然还有一些更新，在官方的发行说明中又详细介绍，不再赘述。最后，附上一些截图，给没有升级的，或者想试试deepin系统的小伙伴们。deepin 确实很不错奥！注：封面图来自deepin论坛（找不到发帖地址了，侵删）
本篇单纯为个人记录，仅以备用与参考，仅适用于vivo X5max+ 2015-02-25b版本 ，360 也木有任何广告费~ root手机对各位机友来说，应该是家常便饭，但对于vivo手机真的是！很麻烦！首先这款手机查遍了互联网，也找不到第三方rec,就根本没办法通过线刷[超级权限SU]来破解root，只能用360root，尽管流氓，还得破解了。但是root成功后，一旦重启，system仍然会被锁，就成为[不完全root]，只能看看wifi密码之类的，不能修改核心文件。所以，一切需要写入system的操作都得在没重启之前。之前一直用[Google相册],不得不说Google公司技术确实没话说，国产软件还没有与Google相册相平衡的。我喜欢的这个软件有几点：自动备份并且，不限量的免费上传空间额度（高清画质）自动整理与归纳相册内容但是！更新后，一进入，一会儿就自动退出，连相片都没办法备份了（在挂V P N前提下）。Google 三件套之前一直没有刷到系统内置应用，所以想通过root，直接把Google三件套刷到系统中去，提高应用的稳定性。前期工作备份手机的信息（联系人，信息，重要相册之类）恢复出厂设置，清除SD卡所有内容（SD杂乱的东西都不要了！太占空间！）通过电脑把官方签名的降级ROM包下载到SD卡中 [PD1408BL_B_1.7.18-update-full.zip],其他版本的ROM包不确定能否root成功。刷降级包关机。然后开机键+上音量键 同时按，选择[recovery mode]，进入rec 模式双清缓存和用户应用数据，双清，防止后续可能出现未知错误把降级包刷进系统，出现[正在更新系统……]的界面启动手机(reboot phone)使用360root 获取系统权限下载电脑端的360 root，按照提示，会发现root成功！这时候千万不要关机，不要重启！否则一切功亏一篑下载[酷安]与[shadowsocks-Android]，在酷安中搜索[Link2SD]、[GO谷歌安装器] 、[绿色守护]、Lux。先挂上VPN，然后打开[GO谷歌安装器]，赋予root权限，直接把Google三件套刷进系统，然后在这个软件中选择[谷歌套件更新]，不然的话就提示，Google play 服务出错，闪退。在Google Play store 中下载[Google 相册]。使用[Link2SD]把[绿色守护]、[Lux]、[Google 相册] 转换为系统应用(注2:)。这时候，重启一下手机，Google相册正常使用了，Hooray!* 注1：[酷安]软件市场强烈推荐，因为上面的APP都是正版，评论区对于付费软件还有破解版（尽管并不好，但是很方便），[Link2SD]获取root权限后，可以直接把应用变成系统应用。[绿色守护]、[Lux] 为个人爱好，可以不装。* 注2：同上，[绿色守护]、[Lux] 为个人爱好，可以不刷进系统。至此，一切看起来差不多了~vivo X5max+上似乎没办法装Xposed 模块，所以更高级的玩法也没办法实现了。
2017年是一个https加密时代，最新的chrome浏览器，任何没有https协议的表单页面都会被标记为不安全。https不仅能反劫持运营商的广告，并且还能让你的网址多一个s（逃）。如果你是VPS或者独立IP或者主机面板有导入SSL证书的选项，那么给博客添加SSL都会有大把的教程帮助你快速搞定。但是如果你的主机和我一样，是共享IP的渣渣虚拟主机，主机商又不提供SSL证书安装服务，那只能通过CDN在传输过程中全站HTTPS，这种方式更简单。以下介绍的就是使用又拍云的CDN服务配置全站HTTPS。注1：又拍云有一个[又拍云联盟]的站长扶持计划，只要在博客页脚加上upyun的logo，通过审核后，每个月有免费的15G流量和10G存储空间，对于我们这种个人博客基本够了。注2：除了国内的又拍云还有国外的cloudlfare同样支持免费CDN服务，我以前用过一次，效果不是很理想。首先进入又拍云首页,点击控制台，好了进入到了管理界面了。滑动到页面最底部，有一栏叫做[又拍云服务]，下面有一个栏目叫[全网加速服务],对，没有看错，就是这个栏目，点击[立即创建]。好了，进入好正题了。这个页面分为三栏目：创建服务回源配置为服务授权（操作员）第三项不用考虑，可以直接跳过！（第三项不用考虑，可以直接跳过！）创建服务又拍云和七牛的后台管理不一样，使用前，使用任何服务，都需要先创建一个服务项目。以我为例，名称为：ihewro。源站类型：当然填写自主源了。源站指的就是我们主机上的数据。加速域名：当然是自己博客的域名：www.ihewro.com。前提是要备案，回源配置回源是什么回事呢？其实也很好理解。使用了CDN服务后，用户访问你的博客是这样的一个过程：用户输入域名地址DNS服务器解析得到又拍云的CDN的CNAME记录根据用户地理位置 对CNAME记录继续解析获取距离最近的缓存服务器的IP地址，浏览器向缓存服务器发出请求缓存服务器根据浏览器提供的域名，通过Cache内部专用DNS 解析得到真正主机的IP地址。缓存服务器向真实主机IP地址发起请求缓存服务器从实际IP地址得得到内容以后，一方面在本地进行保存，以备以后使用，二方面把获取的数据返回给客户端，完成数据服务过程；看了上面对CDN过程是不是有了很明确的概念了，什么？根本没看懂？好吧，简单来说的过程是这样：输入域名——解析出 CNAME记录（CDN服务商提供的）——解析出地理位置最近的缓存服务器的IP地址——解析出真实主机的IP地址（主机商提供的）——向主机发起数据请求——得到数据并且存储到缓存服务器上——over！所以呢，CDN的关键就是通过CDN服务商提供的CNAME记录，使用户每次请求都是访问距离最近的缓存服务器，缓存服务器如果已经有数据了，直接返回数据。如果缓存服务商没有请求的数据，就继续向真实主机发起数据请求。而向真实主机请求数据的过程就是回源的过程。如果你还是很模糊，也没关系，上面那些都不重要！重要的是下面：第一项是填写[回源 Host],可以不填，那就不填！其实就是上面的第四步中的浏览器提供的域名。第二项是回源协议，一般共享IP地址的主机不能自主上传SSL证书的，如果强行以https访问，则会自动跳转到主机商设定的固定页面（我的主机就是这样），所以只能选HTTP,不影响后面配置全站HTTPS。（这项之后也可以修改的）第三项是线路配置，就是回源配置。回源地址是什么意思呢？就是CDN通过回源地址来获取你的真实主机上面的内容。回源地址分为：源站IP和源站地址两种。源站IP就是你主机的真实IP。一般主机商提供了A记录就是源站IP地址，直接填上IP地址即可！后面不需要改。如果回源地址填写的是域名地址，这个域名地址不能和加速域名相同，虽然这个域名叫做源站域名，其实可以理解为通过解析这个域名来获得源站IP的域名。这个[源站]非你想的那个[源站]。更换DNS解析记录如果上面CDN原理弄清楚了，会发现DNS解析记录必须更改为CDN服务商的提供的CNAME记录。这个记录在哪呢？点开 控制台——服务 可以看见你刚才新建的那个服务名称。点击配置，再点击解析，就可以看到CNAME记录。去你的域名解析管理那儿把你的域名CNAME记录指导这儿就行了！到这儿，全站CDN基本上大功告成了，可以用奇云测测试你的网站，可以看到有很多个节点！接下来就是配置全站HTTPS，后面就很简单了。配置全站HTTPS在该服务下，有一个HTTPS的栏目，点击管理，就可以自主申请免费HTTPS证书，流程都有说明了，不再赘述。申购是自动流程化，一般一个小时之内就能审核成功，然后在工具箱——SSL证书服务上面，点击HTTPS配置，就可以选择强制HTTPS访问了。最后一丁点文中图片如果是http资源一定要改成https地址，通过数据库的SQL语句,先进入到该表下，然后执行：UPDATE 表名 SET 字段名= REPLACE( 替换前的字段值, '替换前关键字', '替换后关键字' ) 举个栗子，替换文章内容页面的http链接，就是这样：UPDATE typecho_contents SET text= REPLACE( text, 'http://', 'https://' ); 注：text就是文章的内容的字段名称。文中内容为个人总结，CND原理部分参考：CDN工作原理CDN的原理以及其中的一些技术如有知识性错误，欢迎拍砖指出。相关文章：多说适配HTTPS完~