游戏 生活

游戏是可以SL的人生,人生是没有NG的游戏


  • 首页

  • 标签21

  • 分类7

  • 归档7

  • 搜索

mac 下的 http server

发表于 2018-03-08 | 分类于 mac
字数统计: 930 | 阅读时长 ≈ 0:01
  • 最后更新: 2018-03-08
  • 修改记录:
    • 2018-03-08 初稿

Mac是一个对开发者特别友好的操作系统,除了好看的UI、好用的控制台,还有Terminal都是开发者的好助手。
比如在很多时候,通常需要在本地起一个http服务。

以 Mac 下的 http server 为例子。

1. Python起一个静态server

  • 默认8000端口
1
python -m SimpleHTTPServer
  • 可以指定端口:
1
python -m SimpleHTTPServer 8080

python 会以当前目录作为根目录起一个本地server。

2. PHP 自带 web server

  • 启动php Web Server
1
php -S localhost:8080
  • 指定网站根目录,-t命令
1
php -S localhost:8080 -t /www
  • 支持远程访问
1
php -S 0.0.0.0:8080 -t /www

3. Apache

Mac自带的Apache服务器,默认80端口,启动后直接访问 locahost 可以看到 It works! 页面。

  • 查看版本信息
1
apachectl -v
  • 启动与关闭Apache,该操作需要root权限
1
sudo apachectl start|stop

默认 wwww 跟目录在 /Library/WebServer/Documents, apache的安装目录在 /etc/apache2.

4. nodeJS

推荐使用 browser-sync

1
npm install -g browser-syncnpm install --save-dev browser-sync

然后在package.json文件中指定启动命令:

1
"scripts": { "start": "browser-sync start --server --files '*.css, *.html' "}

browser-sync 不仅可以当做http server,而且当目录下的html、js、css保存时还可以自动刷新页面,提高开发效率,重点推荐这个。

android 底部导航栏规范

发表于 2016-04-25
字数统计: 2.8k | 阅读时长 ≈ 0:03
  • 最后更新: 2016-04-27
  • 修改记录:
    • 2016-04-25 初稿
    • 2016-04-27 增加截图

0. 前言:

最近 Google 在 Material Design 设计规范中加入底部导航栏(Bottom navigation)设计(参考文档: Components - Bottom navigation ),现在我们先来学习 Bottom navigation 在设计、使用、交互、风格、尺寸的一些规范。水平有限,如理解有误,请多多吐槽。

1. 简单介绍

底部导航栏(Bottom navigation)这种设计最初是参考了 iOS 的设计规范,Google 在 Android 的设计规范上其实一直不推荐采用的。但是,很多 Android App 中都是随处可见的,如支付宝、微信、QQ等都使用了,它允许用户可以在多个顶级视图之间快速切换。

注意

底部导航栏推荐在手机上使用,平板或者桌面系统还是推荐侧边的导航栏

  • 移动设备
  • 较大显示设备,比如平板,桌面应用

2. 如何使用

(1)底部导航栏需要有 3-5 个标签(tab),并且每个tab选择的视图重要性要相似,对于少于 3个 tab 的情况,是不推荐使用 Bottom navigation 的。

  • 正确方式:
  • 错误方式:

(2)如果标签很多,比如有超过了 5 个这种情况呢?Google 也是不提倡使用 Bottom navigation 的,可以用 Drawer navigation 替换。

  • 正确方式:
  • 错误方式:

3. 风格样式

(1)标签 Icons 和文字的颜色选择是很重要的,一亮一暗才能有对比,用户才很快知道你选择了哪个,如果五颜六色,你是很难分清选择了哪个的。

* 当前标签是选中状态的时候,显示图标和文字。
* 当导航栏只有三个标签的时候,他们的图标和文字都应该被显示。
* 当导航栏有四个或者五个标签的时候,在非选中状态的时候只显示他们的图标即可。
* 导航栏选中的标签使用应用的主色来填充图标和文字
* 如果导航栏有背景色,使用白色来填充图标和文字
  • 正确方式:

  • 错误方式:

(2)标签的文字说明要简短而有意义,避免太长的,也不提倡太长了换行和省略的方式

  • 正确方式:
  • 错误方式:

注意:

    1. 选中的标签要展示高亮图标和文字
    1. 如果是3个标签的时候,要展示 Bottom navigation bar 中所有的图标和文字
    1. 如果是多于 3 个标签的情况,没选中的 tab 只需要展示图标就可以,不用文字说明

4. 行为交互

(1)用户上拉列表时,隐藏Bottom navigation,下拉列表时,显示Bottom navigation

(2)点击Bottom navigation Icon 的时候,不能打开菜单选择或者其他弹窗操作,而只是刷新当前视图的内容,如下图:

(3)不推荐使用手势在视图内容区域切换视图

正确方式:

错误方式:

5. 固定风格导航栏尺寸设计

(1)Bottom navigation对于尺寸的要求还是挺严格的,标签选中和没选中都有细微的差别。

效果如下:

(2)具体的一些尺寸:

  • 最大宽度: 168 dp
  • 最小宽度: 120 dp(大屏幕设备),104 dp(小屏幕设备)
  • 高度: 56 dp
  • 图标: 24*24 dp
  • 内容对齐模式: 文字和图片水平居中
  • 边距(padding):
    • 选中的标签 ,图片上边距 6 dp;未选中标签,图片上边距 8 dp
    • 文字下边距 10 dp
    • 文字左右各 12 dp
  • 文字:
    • 选中标签 Roboto Regular 14sp
    • 未选中标签 Roboto Regular 12sp

移动设备横屏效果

平板效果

6. 切换风格导航栏尺寸设计

(1)选中和未选中的效果更明显

效果:

(2) 尺寸规范:

  • 选中标签
    • 最大宽度:168 dp
    • 最小宽度: 96 dp
  • 未选中标签
    • 最大宽度: 96 dp
    • 最小宽度: 64 dp
  • 高度: 56 dp
  • 图标: 24*24 dp
  • 内容对齐模式: 文字和图片水平居中
  • 边距(padding):
    • 选中的标签 ,图片上边距 6 dp;未选中标签,图片上边距 16 dp
    • 下边距 10 dp
  • 文字:Roboto Regular 14sp

移动设备横屏模式:

平板模式:

7. 杂谈

Android官网中有这么一句话:

Don’t use bottom tab bars

Other platforms use the bottom tab bar to switch between the app’s views. Per platform convention, Android’s tabs for view control are shown in action bars at the top of the screen instead. In addition, Android apps may use a bottom bar to display actions on a split action bar.

You should follow this guideline to create a consistent experience with other apps on the Android platform and to avoid confusion between actions and view switching on Android.

之前说好的Meterial Design 规范说变就变,自己都不遵守,有何规范可言。

Google+ 截图

然而,Google +已经加入了底部导航栏(Bottom navigation),打脸归打脸,决定这一因素的还是用户。

我们说说 Drawer navigation,不要觉得疑惑,关 Drawer 什么事?没错,就是和它有关,认真想想,使用 Drawer 切换视图时,使用方式是点击按钮或者手势滑动打开,然后才选择跳转的标签,虽然节省了手机视图空间,但是操作较不方便以致使用率低,从而降低其他页面的跳转率这个缺点是不能忽视的。
所以说,相对于 Drawer navigation , Bottom navigation 就有优势了。

然后,对于我们开发来说,又是一大难点了。

比如 FB、SnackBar 等显示的方式,个人觉得都可以不需要 FB 了,至于SnackBar,看下图:

这这这,原谅我欣赏不了这种美。

总结:被坑的最惨的还是 Android 程序员!

Android View 的事件机制详解

发表于 2016-04-13 | 分类于 android
字数统计: 3.9k | 阅读时长 ≈ 0:04
  • 最后更新: 2016-04-13
  • 修改记录:
    • 2014-04-13 初稿
    • 2014-04-18 补充示例

1. View 事件分发机制

View 是 Android UI 的一个基础,而事件分发机制是 View 的一个核心知识点更是一个难点,很多同学对这个问题都会比较困惑。而 View 的另一个难点滑动冲突的解决也依赖于事件分发机制。

1.1 MotionEvent

MotionEvent 用来描述用户触发的屏幕动作的事件。

当你的一个手指在屏幕上滑动一下时,系统会产生一系列的触摸事件对象。典型的事件包括:

  • ACTION_DOWN = 0 —— 手指触摸到屏幕
  • ACTION_UP = 1 —— 手指从屏幕上移开
  • ACTION_MOVE = 2 —— 手指在屏幕上移动
  • ACTION_CANCEL = 3 —— 动作取消
  • ACTION_OUTSIDE = 4 —— 动作超出边界
  • ACTION_POINTER_DOWN = 5 —— 已有一个点被按住,再按下另外一个点
  • ACTION_POINTER_UP = 6 —— 多点被按下的时候,非最后一个点抬起

正常情况下,一次手指触摸屏幕可能产生的事件:

  • 按下后直接松开 序列为: DOWN -> UP
  • 按下后移动一阵之后在松开 序列为: DOWN -> MOVE … -> MOVE -> UP

同时我们可以通过 MotionEvent 得到点击的 坐标。为此,提供了 2组 函数 getX/getY 和 getRawX/getRawY 。区别在于:

  • getX/getY 返回相对于当前 View 左上角的坐标
  • getRawX/getRawY 返回相当于屏幕左上角的坐标

1.2 TouchSlop

TouchSlop 是系统能识别出来的被认为是滑动的最小距离。也就是说,手指在屏幕上滑动时,如果两次滑动的距离小于这个常量,那么就不会被识别成滑动。

TouchSlop 可以用来防止按键位置的抖动。这个常量是设备相关的,可以通过 ViewConfiguration.getScaledTouchSlop() 来获取。

2. 点击事件的相关处理函数

一个点击事件 (MotionEvent) 产生以后,系统需要把这个事件传递给一个具体的 View,而这个传递的过程就是事件的分发过程。点击事件的分发过程由 3 个方法来共同完成:

  • public boolean dispatchTouchEvent (MotionEvent ev)

用来进行事件分发。如果事件能够传递给当前的 View ,那么这个方法就一定会被调用,返回结果受当前 View 的 onTouchEvent 和 子 View 的 dispatchTouchEvent 的影响。返回事件是否被消费。

  • public boolean onInterceptTouchEvent (MotionEvent ev)

在 dispatchTouchEvent 内部调用,用来判断是否拦截某个事件。返回是否拦截事件。

  • public boolean onTouchEvent(MotionEvent ev)

在 dispatchTouchEvent 内部调用,用来处理点击事件,返回结果表示是否消费了当前事件。

3. 举个栗子

  • 下面是一个自定义的 Activity

  • 布局简略关系

  • 事件分发

下图显示了,没有拦截时候的事件分发顺序

手指在 View1 上操作时事件分发顺序
当按下事件没有被拦截,那么所有状态的事件都由Activity进行处理

  • 事件消费
    通过 dispatchTouchEvent 对事件进行处理,当返回值为 true 的时候表示消费了事件。

    ViewGroup1 中的 dispatchTouchEvent 直接返回 true
    手指在 View1 上操作时事件分发顺序
    事件传递到 ViewGroup1 后被消费,后续事件没有分发给子控件
    

  • 事件拦截
    通过 onInterceptTouchEvent 拦截事件,当返回值为 true 的时候拦截事件

    ViewGroup2 中的 onInterceptTouchEvent 直接返回 true
    手指在 View1 上操作时事件分发顺序
    事件传递到 ViewGroup2 后被拦截,不会再分发给子控件
    

  • 事件处理
    当 onTouchEvent 返回 true ,表示事件被当前控件消费

    ViewGroup2 中的 onInterceptTouchEvent 直接返回 true
      ViewGroup2 中的 onTouchEvent 方法中添加 按下事件 返回 true
    当手指对View1点击、移动、抬起时
    事件传递到 ViewGroup2 后被拦截,后续事件先发送给 ViewGroup2 处理,然后返回 Activity 处理
    

ViewGroup2 中的 onInterceptTouchEvent 直接返回 true
  ViewGroup2 中的 onTouchEvent 方法中 直接返回 true
当手指对View1点击、移动、抬起时
事件传递到 ViewGroup2 后被拦截,后续事件全部由 ViewGroup2 处理

  • 下面来看下 button 的处理

    用 Button1 替换 View1
    其余函数使用默认实现
    button 默认就是直接截获和消费了事件
    

View 的 *clickable* 属性的效果,就如同 button 一样。

4. dispatchTouchEvent 伪代码

  • 对于一个根 ViewGroup 来说,点击事件产生以后,会首先传递给它,此时 它的 dispatchTouchEvent 就会被调用
  • 如果这个 ViewGroup 的 onInterceptTouchEvent 返回 true 就表示它要拦截事件,那么接下去的事件都会由这个 ViewGroup 处理,即 onTouchEvent 会被调用
  • 如果这个 ViewGroup 的 onInterceptTouchEvent 返回 false 就表示它不会拦截事件,当前事件就会传递给子控件,接着调用子控件的 dispatchTouchEvent
  • 如此反复直到事件被最终处理
  • 当一个 View 需要处理事件时,如果设置了 onTouchListener,则优先处理 onTouchListener 的 onTouch 事件,根据 onTouch 的返回值,来决定 onTouchEvent 是否被调用(true 表示消费了事件,不会再调用了 onTouchEvent)。如果设置了 onClickListener ,则会在 onTouchEvent 中被调用。
  • 如果 View 的 onTouchEvent 返回 false,即没有消费事件,就会调用父控件的 obTouchEvent ,直到 Activity 的 obTouchEvent。

5. 事件传递机制的结论

  1. 同一个事件序列是指从手指接触屏幕的那一刻开始,直到手指离开屏幕的那一刻结束。在这个过程中产生了一系列的事件合集。这个事件序列从 DOWN 开始,中间含有数量不等的 MOVE 事件,最终以 UP 事件结束。
  2. 正常情况下,一个事件序列只能被一个 View 拦截且消耗。
  3. 某个 View 一旦决定拦截事件,那么这个事件序列都由它来处理,并且它的 onInterceptTouchEvent 不会再被调用,即后续事件不会再去询问是否要拦截。
  4. 某个 View 一旦开始处理事件,如果它不消费 ACTION_DOWN 事件(onTouchEvent 返回 false),则后续的事件不会交给它来处理,事件会交由它的父控件来处理(父控件的 onTouchEvent 会被调用)。
  5. 如果 View 不消费除 ACTION_DOWN 以外的事件,那么这个点击事件会消失,此时父控件的 onTouchEvent 并不会被调用,并且当前 View 能收到后续事件,最终这些事件会交由 Activity 来处理。
  6. ViewGroup 默认不拦截事件,onInterceptTouchEvent 默认返回 false。
  7. View 没有 onInterceptTouchEvent ,一旦有事件到达,那么 onTouchEvent 就会被调用。
  8. View 默认会消费事件,即 onTouchEvent 返回 true,除非它是不可点击(clickable 和 longClickable 同时为 false),其中 longClickable 默认为 false;clickable 则需要根据控件讨论,button 默认返回 true;textView 默认返回 false
  9. View 的 enable 属性不影响 onTouchEvent 的默认返回值。
  10. onClick 发生的前提是当前 View 可点击,且收到了 down 和 up 事件。
  11. 事件传递总是从父控件传递到子控件,子控件可以通过 requestDisallowInterceptTouchEvent 来干预父控件的事件分发,但是 ACTION_DOWN 除外。

6. 参考

  • 公共技术点之 View 事件传递
  • View的事件分发机制

hexo 的 cdn 加速

发表于 2016-03-31 | 更新于: 2016-04-10 | 分类于 hexo , 博客
字数统计: 2.3k | 阅读时长 ≈ 0:02
  • 最后更新: 2016-03-31
  • 修改记录:
    • 2014-03-31 初稿
    • 2014-04-10 增加效果对比

最近用 hexo 搞了个博客,非常好用。不过,作为不折腾不舒服斯基,还是有很多可以瞎折腾的。

为啥要加速

  1. 目前的博客是依托于 github 的 pages 的,所以,页面访问速度不甚理想,图片的访问更甚。
  2. 另外,使用了 Next 的主题,里面使用了谷歌的字体,基于某种神秘的力量,天朝无法正常访问。
  3. 博客页面使用了很多第三方的开源库,这些 css 或者 js 位于世界的各个角落,所以访问速度也是各不相同。

总的一句话,页面呈现的速度太慢,需要优化。

啥是 CDN

内容分发网络(Content delivery network或Content distribution network,缩写:CDN)是指一种通过互联网互相连接的电脑网络系统,利用最靠近每位用户的服务器,更快、更可靠地将音乐、图片、视频、应用程序及其他文件发送给用户,来提供高性能、可扩展性及低成本的网络内容传递给用户。
                    --来自 不存在的百科网站

左边的是单服务器模式,右边的就是 CDN 模式

实现

废话不多说,直接上方法。

主题的 CDN 静态文件加速

主要使用 cdn 来加速主题中使用的前端库。目前我使用的是 next 主题,就拿这个主题动手,其他的主题也是类似,可以参照修改。

国内常用的 CDN 服务

1. 百度CDN公共库

百度公共CDN为站长的应用程序提供稳定、可靠、高速的服务,包含全球所有最流行的开源JavaScript库。

  • 官网:http://cdn.code.baidu.com/
2. 360网站卫士

提供了由360网站卫士CDN驱动的常用前端公共库以及和谐使用Google公共库&字体库的调用方法

  • 官网:http://libs.useso.com/
3. BootCDN

稳定、快速、免费的开源项目 CDN 服务

  • 官网:http://www.bootcdn.cn
4. 又拍云
  • 官网:http://jscdn.upai.com/
5. 七牛
  • 官网: http://jscdn.upai.com/
6. 新浪云

新浪云计算是新浪研发中心下属的部门,主要负责新浪在云计算领域的战略规划,技术研发和平台运营工作。主要产品包括 应用云平台Sina App Engine(简称SAE)。

SAE的CDN节点覆盖全国各大城市的多路(电信、联通、移动、教育)骨干网络,使开发者能够方便的使用高质量的CDN服务。

  • 官网: http://lib.sinaapp.com/

增加主题配置

在主题的配置文件 _config.yml 中增加 CDN 配置:

1
2
3
4
5
6
7
# cdn
cdn:
enable: true
# bootcdn support: font-awesome,fancybox
server: bootcdn
# 360cdn support:font_lato,fancybox,font-awesome(only 4.2.0)
# server: 360cdn

其中:

  • ==cdn.enable== 控制是否启用 CDN
  • ==cdn.server== 定义了使用的 CDN 服务商

文件修改

修改 head.swig,目录是:

根据上面的参数,设置下对应的地址就可以了。修改完的代码:

1
2
3
4
5
6
7
8
9
10
11
{% if theme.fancybox %}
{% if theme.cdn.enable %}
{% if theme.cdn.server == "360cdn" %}
<link href="http://libs.useso.com/js/fancybox/2.1.5/jquery.fancybox.css" rel="stylesheet" type="text/css"/>
{% elif theme.cdn.server == "bootcdn" %}
<link href="//cdn.bootcss.com/fancybox/2.1.5/jquery.fancybox.css" rel="stylesheet" type="text/css"/>
{% endif %}
{% else %}
<link href="{{ url_for(theme.vendors) }}/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css"/>
{% endif %}
{% endif %}

其他的代码也类似。

博客内容的 CDN 加速

主要是使用 cdn 来加速博客文章中使用的图片

使用七牛云存储来加速图片

  • 登录七牛网站,上传图片
  • 复制图片的外链,替换对应图片

效果对比

使用 cdn 之前

使用 CDN 之后

  • bg.jpg 加载时间从 4.17 秒减到了 264.7 毫秒 ( 文件越大,效果越明显)

后记

使用了 CDN 可以明显改善访问速度,但是博客页面本身访问还是存在一定问题的,github 网站也有可能 短时间或者永久的 “不存在”,接下来,会说说如何增加国内的镜像。

我的博客时代

发表于 2016-03-19 | 更新于: 2016-03-19 | 分类于 博客
字数统计: 5.1k | 阅读时长 ≈ 0:05

序

算起来,我是从2006年左右开始在 MSN 空间上写博客的(2005年为博客元年),那几乎是博客最辉煌的时代,随着 MSN 空间的关闭,那段时间写的内容都已经找不回来了。

后来,又陆续折腾过好多的博客空间,还自己买过主机,搭过 wordpress。断断续续的写过不少内容,不过最后也都慢慢的归于沉寂。

最近,我又立了 Flag,重新搭了博客,开始写东西了。这一两年随着微博、微信和公众号的兴起,总有人在说博客已死,但是,我觉得一个真正想要去书写文字的人根本不会在乎博客是不是死了,甚至于根本不在意博客本身,他们只会关心自己接下来写什么,是不是还要继续写下去。博客只是一个平台,写作也只是个手段,留下来的内容才是目的,有没有人看,有没有人评论也没有什么关系,重要的是自己在书写文字,写作本身就是意义。

虽然我很晚才知道这一点,但是我仍然很庆幸。

前博客时代 (2006-2009)

这段时间是博客最辉煌的时代,当初我也迷茫地在很多个网站上开过博客:

  • blogspot:曾经叫Blogger,全球最大,现被墙。
  • blogcn:中国当时很火的博客社区,跟后来的新浪博客相呼应,新浪博客名人扎堆,blogcn草根多多。
  • wordpress:很多独立博客都是用wordpress搭建的,不仅是工具,还是平台,现被墙。
  • QQ空间:每人都有一个QQ号,每个人都有一个QQ空间,你必须承认这一点。
  • 百度空间:百度出过IM,叫百度HI,你造吗?百度空间那时也火了一阵。
  • MSN space:在2005年有一个MSN Space是很牛叉的。现在MSN Space关了,将用户都倒进了新浪博客。
  • 博客巴士:博客巴士是当时做得挺不错的专业博客站点,不过博客大潮一过就消失得差不多了。
  • 牛博网:博客界的一朵奇葩,罗永浩创办,想打造一流的言论平台,但后因政治内容被墙,接着罗永浩挥刀自宫,搞了一个无政治内容的“嫣牛博”,阉了也活不了,最后被迫关闭。老罗后来搞英语培训,现在搞手机。
  • 新浪博客:新浪博客是新浪在2005年转型的成功之作,搞了「明星政策」邀请了一大堆的名人开博,一时风头无两,什么名人都要开个博客才算是顺应了互联网潮流。

    那几年也出现了大批的独立博客:

  • 和菜头:槽边往事
  • 洪波:牛乱弹琴
  • William Long:月光博客
  • 小众软件:Scavin
  • It Talks:魏武挥的blog
  • Jason Ng:可能吧
  • 萧秋水:逍遥游
  • 爱范儿
  • 善用佳软
  • 望月的博客
  • ……

    博客时代到来,信息量暴增,如果处理这些博客的信息成了一个问题。

后博客时代

到2009年,微博顺利地接替了青黄不接的博客,微博如火如荼地扩张起来,信息变得的碎片化,也有公司留意到了这一点,一种介乎于博客与微博的所谓「轻博客」应运而生,始祖就是2007年的Tumblr,随后,国内出现了点点网和LOFTER,但是轻博客的发展并不如当初的博客和微博,不温不火,一直徘徊在边缘地带,进入不了大众市场。

到2012年,Twitter的老大出来搞了一个Medium,博客似乎又开始以另外的一种形式回归到大众的眼前,随后国内的有了简书和十五言这样的写作平台,目的就是让人开始从信息碎片时代回归到自己的内心。这就是后博客时代,微博的热闹变成了喧嚣,人们从关注社会新闻明星动态到关心自我成长,各种信息形态的变化最终也离不开内容的创造,思想不停地汇集分散,文字在各种平台上流淌,最终还会归结到各自的内心中。

前博客时代的人倾向于功利,希望被关注,希望被喜欢,独立博客不断地寻求SEO,各种的优化,而后博客时代的人专注于内心与分享,不哗众取宠,不争名夺利,默默地前进。吹尽黄沙始到金,热潮会退去,博客只属于一部分人。但是,博客终不会消失,只要人存在,它就会一直存在。

为什么你要写博客

选择

我知道现在可能说这话已经不合时宜,毕竟博客时代都已经过去了,现在流行的是碎片化的知识分享。再号召大家用过时的东西是不是有点逆流而上?

我曾经也问过自己这个问题,但是我觉得,博客时代过去跟我们要写博客是没有多大关系的,就好像你的读书时代已经过去你就不再读书一样。

判断一件事情值不值得去做有一个方法:拿出一张白纸,在左边写不值得做的原因,然后在右边写值得做的原因,写完一比较,一权衡,自然能够得出结果。

所以,我在这里列出要写(独立)博客的原因,供大家去选择,然后填在你白纸的右边。这不是一个建议,而是一个选择,这个选择蕴藏着我也不知道的可能。

博客的内容

写博客不难,你可以当作是生活的记录,这样的记录不能说没有意义,但是对你的帮助也仅限于回忆而已。文字的意义在于,书写出来的东西是思考的结果,一篇文字可以把当时你的所思所想完整的展现出来。

我认为要开一个博客,博客的内容应该是这样的:

  1. 不是流水账、不牢骚、不抱怨……;
  2. 有目的地写,要务实,追求质量;
  3. 承认真实的自己,不要吹嘘,不要装逼,也无需讨好读者;
  4. 记录自己学习、思考、总结的过程;
  5. 分享你的故事、所得、感想、经验;

值得去写的原因

1. 重新认识自己

是不是很久没有跟自己对话了?我什么时候开始变得这样了?……

你可以尝试从回答一些问题开始,将你过去要回避的问题写下来,例如就可以从这个九个问题开始:

  1. 请你介绍一下你自己,你是个什么样的人?
  2. 你有什么理想吗?这个理想是怎么形成的?
  3. 你理想的伴侣关系是什么样的?你自己在这个伴侣关系中扮演什么样的角色?要承担什么样的责任?
  4. 你理想的事业是什么,你正在做的工作符合你的事业理想吗?这份工作对你的意义是什么?
  5. 你对亲子关系怎么看?对你来说,什么是一个理想的父亲(母亲),你期望自己成为这样一个理想父亲(母亲)吗?
  6. 你对钱怎么看,你认为赚到多少钱是足够的?如果你明天一早醒来,已经有足够的钱,你将会如何继续安排自己的生活?
  7. 对你来说,什么是理想的性生活?什么是理想的性道德,在你的性道德观中,什么样的性生活是禁忌的,需要避免的,什么样的性生活是美好的,需要得到鼓励和发展的?
  8. 你的择友标准是什么?什么样的人你会愿意交往,什么样的人你会拒绝和他交往?
  9. 你对死亡怎么看?你希望自己活到多少岁,你准备怎么度过从现在到死亡的这段时间?如果你要立遗嘱,这份遗嘱会怎么写?

以上的这九个问题摘自《很少人能顺畅回答这9个问题——心理治疗刚开始医生常常会先问你的 》by 李孟潮。

这些问题的答案你可以选择不公开,但是我强烈地建议写下来,只有在写的时候你才可以慎重地思考这些问题,而不会回避跳过或者留下空白,这是接受自己的第一步。

2. 提供持续学习的动力

我为自己设限每天写一些技术总结,信息的不断输出给我带来恐惧,我害怕有一天我写无可写,于是我不停地阅读,通过个人的知识管理促使自己不断学习,提高核心竞争力。

3. 积累更多的知识

博客并不是单纯的输出文字。

常常写着写着,你突然忘记了一个概念,于是各种谷歌百度,找回来这个概念的时候,你重温这个概念,可能还会顺便看了一下这个概念的其他东西。书写本身可以帮你更好的梳理知识,积累知识。

需要获取第一手的资料,寻找信息来源本身就是一个知识积累的过程,同时,你慢慢就学会了鉴别知识:什么是没有用的心灵鸡汤,什么是不值得关注的,还有,在这个过程中,你还养成你的心智。

4. 提高将事情讲清楚的能力

很多东西你以为懂了,但当你要写下来的时候,你就觉得无从下手了。

如果一件事情你不能讲清楚,十有八九你还没有完全理解。

将事情写下来,慢慢就可以提高你的逻辑思维能力,分析能力,写会迫使你在你脑中搭建一个有条理的框架。就像我写这篇文章一样,我将值得写博客的原因一点一点地罗列出来,事情就更加清晰,你也可以更好的思考问题。

5. 分享带来的连锁反应

“通过分享,你获得了直接而快速的回报,你最终或许会发现你已将版权和“保留所有权利”抛诸脑后。新的经济学准则是:参与你作品的人越多,回报越高。在分享主义里,如果你愿意你可以保留所有权,但是我乐于分享。” by 毛向辉 《分享主义:一场思维革命》

互联网精神其中最重要的就是分享,基于分享,你可以享受到社会化及互联网给你带来的种种便利和好处,你分享了一个知识,你就成为了互联网中的一个点,这个点的大小由你自己来决定,互联网的大潮会将你的这个点推送到它所能触及的每个角落,让需要的人得到,同时,你的这个点也会继续扩大,连接到整个网络,这个点有可能连接成一张网,而你就是这张网的中心。

6. 帮你找到志同道合的人

在微博,在朋友圈,你可能找不到跟你志同道合的人,而在博客,你可以通过看他的几篇文章就迅速地理解认同这个人,即使你没有见过这个人,但你也可以通过这种关联来相互学习。

如果你在一个领域有相当的了解,你将这些内容发在网络上,网络上跟你志趣相投的人也会被你吸引过来,根据吸引力法则,你是怎样的人你就被怎么样的人吸引,这就是博客所能赋予你的魅力。

即使博客没有被他人关注,我们依然可以找到同好,你可以自己将博文转载到其他站点,人们会通过搜索引擎找到你,有邮件、微博等工具,我们不乏与他人交流的途径。by Gabriel Weinberg《Why I blog》

7. 记录成长

隔一段时间,你再回头看你写的博客,你会发现自己正在通过这样的方式在不断的成长,这种成长在自己眼里是一种财富,在别人眼里是一张地图,你得到了收获,不断修正自己的错误,别人得到了指引,避免走弯路。

更多的情况是当你回望自己的时候你会发现自己是一个傻逼,so what,that is what I am!

8. 培养持续做一件事情的能力

开始是坚持,后来是习惯,接着就是喜欢。以后当有人对你说,「你写那么多有用的东西,你真的很厉害啊!」你可以笑而不语,也可以大声说道:「你妹,你不知道我开始的时候多么痛苦!」

让你长久地去跑步,你可能做不到;让你每个月看一本书,你也可能做不到;但让你持续地写一个博客,你可以做得到。

你不相信?你不试试你怎么知道?

默默地持续做一件事是一种难得的能力,也是一种难得的品质。

9. 讨论反思

每人都会有思维的盲点,就好像这篇文章一样,可能你觉得我可能说得不对,你可以反驳我,我欢迎这种讨论,因为讨论的过程中会产生各种的思维的碰撞,这种碰撞会让你反思,也会激发出你新的灵感,这种讨论反思给自己的带来巨大的受益。

互联网给你的反馈就是让你承受更多,接受更多,成为一个更好的人。

10. 搜寻到你意想不到东西

世界不止是你的家,你的公司,你的朋友圈,你应该去发现一个更大的世界,通过写博客,你会知道世界上还有很多人像你一样在写博客,这些人和知识正在世界的某个角落在等着你。

在写这篇文章的过程中,我才知道了Gabriel Weinberg,我才要将阳志平的博客重读一遍。写的过程会让你有很多新的发现,这些新的发现都值得你去再写下来,总结分享出去。

11. 一个人在做一件属于自己的事

很多你认为自己很牛逼的事情都是自己一个人做出来。

别人在刷微博,你在看书,别人在看穿越剧,你在学英文,别人在去唱K,你在写个人总结。吃饭也要找同伴,出游要找同伴,看电影要找同伴,你上一次一个人在做一件属于自己的事是在什么时候?

如果你想要清晰地思考,就必须远离人群。但是走得越远,你的处境就会越困难,收到的阻力也会越大。因为你没有迎合社会习俗,而是一步步地与它背道而驰。如果自己就是潮水的一部分 ,怎么能看见潮流的方向呢?你只能永远保持质疑,问自己,什么话是我不能说的?为什么?——Paul Graham《不能说的话》

12. 互联网的身份识别

一个长期的价值博客是一份很好的简历。这里的“简历”并非是狭义上的求职简历,毕竟现在还没有到价值博客的时代,很多人写博客都是到处转载或者干脆碎碎念,正因此面试官未必拿个人博客当成了解一个人的更可靠窗口。这里的“简历”是指一个让别人了解自己的窗口,虽然我们未必做得到像罗永浩、Keso这样的博客,个人的影响力已经足以支撑出一份事业(牛博和5gme),但至少你会因此而结识更多的人,你的博客价值越高,你结识的人就越牛,跟牛人交流又会让你的眼界得到极大的开阔,打开一扇又一扇你原本不知道的门,于是你就变得更牛… 这是一个良性循环。by 刘未鹏

最后

你可能想不到在白纸的左边(不值得写博客的原因)写什么了,想不到写个「博客时代已经过去」或者「我没有时间」也可以,但与此同时,你也可以用那些时间去思考一下「怎么做到长期写一个价值博客」。

Android 日志的优化实践

发表于 2015-11-04 | 更新于: 2016-03-30 | 分类于 android
字数统计: 6k | 阅读时长 ≈ 0:06
  • 最后更新: 2016-03-30
  • 修改记录:
    • 2015-11-04 初稿
    • 2015-11-10 增加日志需求
    • 2015-11-14 增加实现细节
    • 2016-03-30 图片本地化

1. 简介

在日常的 Android 开发中,日志打印是一项必不可少的操作,我们通过分析打印的日志可以分析程序的运行数据和情况。本文主要介绍了如何更好的使用日志。先来介绍下相关的原理,然后来说说怎么优化开发体验。

2. 哪些形式

  • System.out.println
    这是标准的Java输出方法,相信很多公司都不提倡使用,这里进行列举,目的是为了提醒大家不用。

  • Android Log
    Android 自身提供了一个日志工具类,那就是 android.util.Log。使用很简单,如下:

1
Log.i(LOGTAG, "onCreate");

我们在调试的时候经常会输出这行代码,这个方法有两个参数,一个是TAG,一个是真正要输出的内容。

3. TAG 的选取

TAG 选择虽然没有强制要求,一般来说只要是个字符串就行,但是好的 TAG 策略,可以提高开发体验。

3.1 选用人名

很多人都曾采用人名的形式,比如:

1
Log.i("lilei", "onCreate");

这样做的目标一是为了过滤方便,当一个人在写一个模块多个文件时,使用这个形式,过滤起来很容易帮助理解程序的执行情况。另外的目的就是为了表明日志周围代码的作者姓甚名谁。

然而,我却不推荐这种人名作为TAG的形式。原因如下:

  • 以人名作为关键字过滤,不易确定产生日志的类文件
  • 随着某个人模块实现的增加,过滤人名易产生来自其他模块的干扰信息。

3.2 动态选取

还有一种选取方式,就是:

1
private static final String LOGTAG = Settings.class.getSimpleName();

这样使用,得到的 LOGTAG 的值就是 Settings。这么做,开发版使用的时候非常方便;但是当应用打包发布时,Settings 这个类进行了混淆之后,类名变成了类似 a,b,c 这样的名称,LOGTAG 则不再是 Settings 这个值了。这样可能造成的问题就是,内部混淆有日志的包,我们无法再通过过滤 Settings 来得到相应的信息。

3.3 静态的 tag

一般比较推荐的形式就是以字符串字面量形式去设置 LOGTAG。如下,在Settings类中

1
private static final String LOGTAG = "Settings";

4. 屏蔽日志输出

主要是通过两种方式实现的,一个是在编译期时候屏蔽;一个是在运行时中处理。

4.1 运行时屏蔽

通常做法,是自定义一个类

1
2
3
4
5
6
7
8
9
public class L {
private static final boolean ENABLE_LOG = true;
public static void i(String tag, String message) {
if (ENABLE_LOG) {
android.util.Log.i(tag, message);
}
}
}

通过调用 L.i 来输出日志,通过修改 ENABLE_LOG 的值可以方便的切换是否屏蔽日志输出。

注意

1
Log.i(LOGTAG, "sdkVersion="+getVersion());

虽然可以屏蔽日志输出,但是并不会屏蔽语句中的代码 getVersion 执行,另外字符串的拼接也会被执行到。

4.2 编译期屏蔽

主要是利用 Proguard 在代码混淆时,去掉相关的日志代码。

1
2
3
-assumenosideeffects class com.logtest.L {
public static *** i(...);
}

可以用同样的方式来处理原生的日志打印

1
2
3
4
5
6
7
8
9
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** e(...);
public static *** i(...);
public static *** v(...);
public static *** println(...);
public static *** w(...);
public static *** wtf(...);
}

下面来个栗子,看看反编译的出来的代码:

先来个测试的代码,其他无关部分都先忽略掉:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MainActivity extends Activity {
private static final String LOGTAG = "MainActivity" ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dumpDebugInfo();
}
private void dumpDebugInfo() {
Locale defaultLocale = Locale.getDefault();
Log.i(LOGTAG, "sdkVersion=" + Build.VERSION.SDK_INT + "; Locale=" + defaultLocale);
}
}

然后使用 proguard 的配置

1
2
3
4
5
6
7
8
9
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** i(...);
public static *** v(...);
}
-assumenosideeffects class com.droidyue.logdemo.DroidLog {
public static *** i(...);
}

生成 apk 之后,反编译,得到 smali 源码:

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
# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
.locals 3
invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
const v0, 0x7f030017
invoke-virtual {p0, v0}, Lcom/test/logdemo/MainActivity;->setContentView(I)V
invoke-static {}, Ljava/util/Locale;->getDefault()Ljava/util/Locale;
move-result-object v0
new-instance v1, Ljava/lang/StringBuilder;
const-string v2, "sdkVersion="
invoke-direct {v1, v2}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
sget v2, Landroid/os/Build$VERSION;->SDK_INT:I
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object v1
const-string v2, "; Locale="
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v1
invoke-virtual {v1, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder;
move-result-object v0
invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
return-void
.end method

无论是运行时日志屏蔽还是编译期,message参数上发生的字符串拼接都依然存在。但是编译期屏蔽减少了方法调用(即方法进出栈操作).

5. 优化实践

本来文章到这里就可以结束了。介绍了原理,接下来小伙伴们就可以愉快的编码了。但是作为有追求的理想青年,我们还是可以利用 log 来提升开发体验的。

对于一个需要重复无数遍的函数,我们需要尽可能的简化

在应用开发中,我们可能需要无数次的写下:

1
Log.d(TAG, "This is a debug log");

很多时候,我们仅需要个输出,于是就有了第一个需求,省略 TAG,或者说,我们希望有一个东西可以帮我们定义好TAG,我们只需要写正真有意义的内容就行。

更进一步,虽然现在 IDE 都有了代码自动提示,但是能少打几个字总是好的。我们希望 L 来替代 Log

然后呢,我们希望 log 输出的更加美观。最好输出的有一个 超链接 ,点击就能跳转到相应的代码中

==这个功能只支持 Android Studio,Eclipse只能输出,无法跳转==

有时候,我们需要的只是输出一个简单的字符,有时候,我们又希望能把函数调用的堆栈也打印出来。

或者是

最好能支持数据的可视化,而不用手动去拼接字符串或者简单的通过 Object.toString() 来打印对象信息。

  • json 格式化

  • 数组等数据结构

对应输出

  • 其他对象

哦,还有。加个能控制 log的开关,另外能够方便的定制 log 的输出,比如输出到文件或者回传服务器

6. 实现

上面的需求实现的难度并不是很大,这里就大致介绍下实现的思路。

获取当前类名

1
private static String getClassName() { String result; StackTraceElement thisMethodStack = (new Exception()).getStackTrace()[2]; result = thisMethodStack.getClassName(); int lastIndex = result.lastIndexOf("."); result = result.substring(lastIndex + 1, result.length()); //如果调用位置在匿名内部类的话,就会产生类似于 MainActivity$3 这样的TAG //可以把$后面的部分去掉 int i = result.indexOf("$"); return i == -1 ? result : result.substring(0, i); }

输出函数文件信息

1
/** * log这个方法就可以显示超链 */ private static String callMethodAndLine() { String result = "at "; StackTraceElement thisMethodStack = (new Exception()).getStackTrace()[1]; result += thisMethodStack.getClassName()+ "."; result += thisMethodStack.getMethodName(); result += "(" + thisMethodStack.getFileName(); result += ":" + thisMethodStack.getLineNumber() + ") "; return result; }

数据可视化实现

  • array,map,set 等容器可以通过遍历显示
  • Object 可以通过反射方式,拿到变量以及变量的类型
  • xml,json 可以通过自定义格式化来实现

7. 后记

通过这篇文章,给大家介绍了一个日志输出优化的过程。通过实践,说明一个最简单的日志输出都是有很多可以优化的地方;有时候,一些小小的改造,可以极大的提升开发的体验。

最后的最后,你可能不太喜欢这样的日志输出。没关系,相信我们的最终目的是一致的,就是让开发越来越便捷,代码越来越优雅~

【无所不能的脚本】mac下的ramdisk

发表于 2014-01-19 | 更新于: 2016-03-30 | 分类于 osx
字数统计: 3.9k | 阅读时长 ≈ 0:04
  • 最后更新: 2016-03-30
  • 修改记录:
    • 2014-01-19 初稿,发布于 51cto
    • 2015-01-21 增加大小保护,发布于 51cto
    • 2016-03-30 合并之前的文章

0. 前言

现在流行苹果,mbp,mba,iphone,ipad,……关键是windows上各种流氓软件越来越多,加上工作需要,于是转投了 mac。好吧,入手的 mbp 看起来很美,实际上各种卡,整天看到小菊花转啊转。好吧,屌丝买不起高配,用的是最低端的,说起来,满满的都是泪,但是生活还得继续,只能自己想办法了。

优化第一步,加内存。不要问我为什么能加,老机器了,不说了,满满的都是泪。有钱的孩纸买机器的时候就加内存吧,普罗大众还是不要自己折腾吧。当然大神们可以自己动手换内存,这个也不说了。

第二步,上 SSD 吧。这个真心不错,但是好东西不便宜啊,能把ssd和hdd一样随便放东西的同学,略过这篇文章吧。我的mbp是用来开发的,乱七八糟的东西占了好大的空间,于是ssd就得不停的整理空间,删除缓存文件,就为了那几个 G 的容量。最近,还碰到过因为 ssd 剩余空间是0,无法开机的悲剧。

神圣的红领巾一直在前面引领着我们,生命不息,折腾不止……重点来了,我们可以利用内存来提速啊,这个在windows上已经烂大街了,不就是ramdisk么。拿出谷哥度娘,一番OOXX之后,发现没啥好用,倒是发现mac本身就能实现。身为屌丝IT男,必须自己折腾了。当然,折腾这个的前提是你的内存足够大。

@#!@¥#¥@%#¥%(一星期过去了……………………)

1. 实现

1. ramdisk

不说折腾的过程了,直接上结果吧。
先来说下大致的工作流程:

  1. 开机的时候,自动调用脚本创建内存盘,然后载入需要的数据。
  2. 关机的时候,自动调用脚本把内存盘数据回写硬盘,然后执行关机。

不复杂哈,一说就明白了吧,下面来点复杂的:

  1. 先来说,怎么再开机的时候运行脚本。
    先在你顺眼的地方建个目录,用来作为ramdisk的脚本和备份数据的工作目录。

    我的目录,在/etc/下面建了个ramdisk的目录,小伙伴照着做的话,注意下权限。

  2. 接下来,就创建一个 login.sh 的脚本,用来再开机的时候自动运行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #!/bin/sh
    # 设置内存盘的名称
    DISK_NAME=RamDisk
    MOUNT_PATH=/Volumes/$DISK_NAME
    # 设置备份文件的保存路径, 注意修改下面路径中的用户名称
    BAK_PATH=/etc/ramdisk/$DISK_NAME.tar.gz
    # 设置分配给内存盘的空间大小(MB)
    DISK_SPACE=1024
    if [ ! -e $MOUNT_PATH ]; then
    diskutil erasevolume HFS+ $DISK_NAME `hdiutil attach -nomount ram://$(($DISK_SPACE*1024*2))`
    fi
    # 隐藏分区
    chflags hidden $MOUNT_PATH
    if [ -s $BAK_PATH ]; then
    tar -zxf $BAK_PATH -C $MOUNT_PATH
    fi
* ==DISK_NAME== 用来指定内存盘的名字,随便改
* ==DISK_SPACE== 用来指定内存盘大小,这是上限值,一般情况下使用多少占多少的内存,所以放心使用吧
* ==BAK_PATH== 用来指定内存盘的保存路径
  1. 再创建一个logout.sh,用来在关机的时候调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #!/bin/sh
    DISK_NAME=RamDisk
    MOUNT_PATH=/Volumes/$DISK_NAME
    WORK_PATH=/etc/ramdisk
    BAK_PATH=$WORK_PATH/$DISK_NAME.tar.gz
    LISTFILE=$WORK_PATH/list
    if [ -e $MOUNT_PATH ] ; then
    cd $MOUNT_PATH
    tar --exclude-from $LISTFILE -czf $BAK_PATH .
    fi
* ==DISK_NAME== 用来指定内存盘的名字,要和之前的脚本一致
* ==BAK_PATH== 用来指定内存盘的保存路径,一样要和上面的一致
* ==LISTFILE== 忽略的文件列表

1.2 运行

ramdisk搞定,然后呢?当然是怎么使用了。

先在终端里面把 login.sh跑起来啊,跑起来。权限有问题的,可以用sudo方式。然后呢,去finder里面看看吧!

什么都没有?为什么?好吧,其实再脚本里面我把内存盘隐藏了,为啥要加这么帅的操作呢?里面有没有日本的爱情动画片。因为很多小伙伴都手贱,把ramdisk卸载了之后,就会各种悲剧了。所以,事先隐藏了之后,麻麻就再也不用担心了。

那怎么访问呢?有个神奇的办法,在finder的菜单里面找到前往,选择前往文件夹

然后,输入内存盘的加载路径,也就是脚本里面的MOUNT_PATH,接下来就是见证奇迹的时候了……

1.3 使用

有了内存盘,里面放什么呢?爱情动作片就算了,可以把cache放在里面,这样可以加速运行速度,也可以把经常需要频繁操作的目录放进去,比如chrome的配置啊什么的……

1.3.1 cache 目录

先来处理cache:

  • 先把系统的cache目录移动到内存盘上去
  • 然后通过符号链接把内存盘上的目录映射回原来的目录

    对应了

不会的,给个脚本

1
2
3
4
5
6
7
8
9
#!/bin/sh
# 设置内存盘的名称
DISK_NAME=RamDisk
MOUNT_PATH=/Volumes/$DISK_NAME
#Caches =》 ~/Library/Caches
mv ~/Library/Caches $MOUNT_PATH
ln -s /$MOUNT_PATH/Caches ~/Library/Caches

1.3.2 chrome 目录

同样处理下

对应的是

1.3.3 其他目录

照着上面的方法,可以把你想要搬的目录全部移动到 ramdisk 上去

再打开内存盘就能看到目录了

1.4 配置自动运行

脚本有了,关键是要能随着系统启动、关闭,自动运行起来。

在终端里面运行

1
2
defaults write com.apple.loginwindow LoginHook /etc/ramdisk/login.sh
defaults write com.apple.loginwindow LogoutHook /etc/ramdisk/logout.sh

可以通过

1
defaults read com.apple.loginwindow

来检查设置是否成功。

最后,重启机器看看。

2. 脚本高级配置

有了ramdisk以后,mac是不是不一样了?没感觉,好吧,ramdisk不是救世主,不能减少pm2.5的,只能提高部分文件的io效率,要想那啥的,换机器+ssd才是正道。

用了一阵子之后,发现ramdisk越来越大了没有?赶紧看下,果然啊,cache目录占了好多容量。

很大有没有,用软件清理么?不符合懒人哲学啊。作为有追求的屌丝it男,必须用脚本。

¥%¥#……%……&%……(再次省略无数的探索过程……)

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
#!/bin/sh
DISK_NAME=RamDisk
MOUNT_PATH=/Volumes/$DISK_NAME
WORK_PATH=/etc/ramdisk
BAK_PATH=$WORK_PATH/$DISK_NAME.tar.gz
LISTFILE=$WORK_PATH/list
#设置最大的cache大小(M)
MAX_CACHE_SIZE=50
#
cd $MOUNT_PATH
declare -a fa
i=0
# Caches 目录存在
if [ -d "$myPath"]; then
for file in $(du -s Caches/* | sort -n)
do
fa[$i]=$file
let i=i+1
done
fi
size=$((i/2))
echo "Caches file number:"$size
cd $WORK_PATH
echo ".?*">$LISTFILE
for((i=0;i<$size;i++))
do
if ((${fa[$((i*2))]}<(($MAX_CACHE_SIZE*1024*2)) ));then
echo "add:"${fa[$((i*2+1))]}
else
echo ${fa[$((i*2+1))]}>>$LISTFILE
fi
done
if [ -e $MOUNT_PATH ] ; then
cd $MOUNT_PATH
tar --exclude-from $LISTFILE -czf $BAK_PATH .
fi

简单说明下,就是每次关机会把cache目录下超过50M的目录直接清掉。

  • ==MAX_CACHE_SIZE== 可以指定最大的cache目录大小

重启机器,再来看下

于是世界终于和平了…………

kingzeus

kingzeus

游戏是可以SL的人生
人生是没有NG的游戏

7 日志
6 分类
16 标签
RSS
github weibo zhihu
Creative Commons
© 2018 kingzeus | 25k | 0:25
由 Hexo 强力驱动
|
主题 — NexT.Mist v6.0.4