2015年个人总结

拖延症治不好了的...


过去的2015年,发生了很多事儿,今儿就来总结一下吧。


生活

生活中最大的事儿,携手雪多多同学走近民政局的大门,在亲朋好友的祝福下走近婚姻的殿堂。



另外就是终于有假期出去好好玩儿了一回,厦门不错哒!



工作

上半年的工作确实不咋样。

从14年底开始,整了五个多月的xamarin,以为找到了移动开发的银弹,却被新领导无情拍死。最可惜的还是自己太懒,没有整理出点技术文章来。

后面淘金的重构,老想着单元测试和模块化,虽有收货,但也走了不少弯路。



下半年从度娘离职,跟着飞哥混。

终于真正开始做用户产品,财联社和蓝鲸两头转。以前总是搞定位、拍照、任务上传,最头疼的是上传不上和定位不了;现在搞的却是分享、评论、点赞...

想起离职后邓老大送给我的一句话“外面的世界更精彩”,it is true!



个人成长

成长嘛,总是有的啦。

写了快三年Java了,没有从前那么关注语法糖了。不过对依赖、解耦、AOP、不判空有了更深入的理解。同时维护两个以上的App,面对N多的渠道,一些从前没有太大意义的事儿变得可行,比如说模块化,比如说CI...感觉大有可为的样子。

在这里也得感谢飞哥拉我入伙,也感谢蓝鲸给了一个很好的平台。


懒虫就是想得太多,做得太少...此处脑补打哈欠的葱头。

下面是定目标环节:

  • 至少熟悉一门新语言(Javascript or Kotlin),并且像模像样做点东西;

  • 整一个开源项目自己玩玩儿;

  • 用Nodejs(没错,不纠结了,就是Node)重新做一下RealBlog,顺带把该死的MongoDB替换掉;

  • 买的书呢,花时间看看,书是要看的,不是装x用的。

上面的目标们似乎和工作毫无关系么?是啊是啊,工作目标有人逼,这几条只能自觉嘛。


新的一年里,希望自己能Level Up!

谢谢大家

试一试,离开IDE写Java代码

习惯了Intellij IDEA和Visual Studiod便利,偶尔来试一试没有IDE的环境下写Java代码的体验^_^


show代码之前先说环境,我的环境是OSX 10.11,Oracle Jdk 1.7,Sublime Text 3。

今天打算写的是一个最基本的依赖注入框架的实现,仅仅是构造注入,名字就叫NanoInject吧,确实很nano。


1、Hello World

命令行其实也没啥稀奇的,麻烦的就是把平常IDE自动给做的事情得手动一步一步做。

// NanoInject.java
public class NanoInject {
    public static void main(String[] args) {
        System.out.println("Hello world");
    }
}

终端下面:

> javac NanoInject.java && java NanoInject
Hello world

javac是用来编译的,java是用来执行的,Hello world能显示出来,就说明环境变量环境变量ok了。


2、引入第三方库的依赖

这一步,我们就试试JUnit吧。junit-4.12.jar依赖于hamcrest-core-1.3.jar,这个依赖关系可以在mvnrepository上查到,包的下载地址也在。把他们放在当前目录下面,然后新建一个NanoInjectTest:

// NanoInjectTest.java
import static org.junit.Assert.*;
import org.junit.Test;
  
public class NanoInjectTest {
    @Test
    public void test() {
        assertEquals(1, 1);
    }
}

第三方库的依赖,需要设置CLASSPATH,这里稍微麻烦些:

> export CLASSPATH="./hamcrest-core-1.3.jar:./junit-4.12.jar"
> javac NanoInject.java
> javac NanoInjectTest.java
> java org.junit.runner.JUnitCore NanoInjectTest
JUnit version 4.12
....
Time: 0.002
  
OK (1 tests)

如果有更多jar需要引入,这样就比较蛋疼了。也许我们可以写个shell遍历当前目录下所有的jar,然后通通加到CLASSPATH里去:

#!/bin/bash
# run.sh
 
class_path=''
for s in `ls -1`
do
    if [[ ${s:0-4} == '.jar' ]];then
        class_path="$class_path./$s:"
    fi
done
export CLASSPATH=$class_path
 
javac NanoInject.java
javac NanoInjectTest.java
java org.junit.runner.JUnitCore NanoInjectTest

如此一来,每次跑一下run.sh就成啦,so easy!



3、编码是最艰难的步骤

真正麻烦的,其实是编码。离开Intellij IDEA,更加深刻地意识到她的强大。用Sublime Text遇到的困难包括且不限于:

  • new一个ArrayList之后,不能再Option+Enter,而是得到最顶上手写"import java.util.ArrayList;",不翻文档我真记不得这个包名...

  • 一个对象,点号后面我打了个getType,我去编译不过...哦哦,原来应该是getClass,跟C#记混了。

  • newInstance方法,有两个checked exception,idea能给自动补全try...catch...这里只能干瞪眼了

  • javac编译提示unchecked cast警告,却告诉我哪一行。加了参数重新跑下,终于找着了^_^


有个厉害的IDE可以用,真好。不过我会在闲暇时间继续体验没有IDE的Coding,很有意思。

Centos下配置Android编译环境

自己整持续集成环境,第一步就是要在Centos下面部署一个Android编译环境。当然也不是什么难事儿。


Java SDK

JDK那是逃不掉的,一般Centos选minimal安装的话,不会有Java环境,但是如果选Basic Server什么的,就会装上Open JDK(似乎谷歌官方也没说不能用Open JDK,但是稳妥起见,还是装Oracle JDK吧)。


去oracle官网上下个最新的JDK7,装上:

rpm -i jdk-7u79-linux-x64.rpm

java -version,如果是这样的话就成啦:

# java -version
java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)


Android SDK

我们都用过SDK Manager,那是个有界面的玩意儿,在开发Android之前得在上面勾选我们需要组件。而对于没有UI的Linux来说,这是个麻烦事儿。

在SDK Readme里,有提到命令行可以执行这个命令:

tools/android update sdk --no-ui

试过之后才知道这个模式有多坑——它会自动下载一堆你用不着的东西。以天朝连接谷歌的稳定性,那就不指望了。


为此,我特地装了一个Centos的虚拟机,有界面的那种,只下必需的组件:



完事儿了之后打个tar.gz包,只有800M左右,nice!



Gradle

这个去gradle官网下,在这里,我们尽量和IntelliJ IDEA或者Android Studio所用gradle版本一致。比如我用的2.2.1。



设置环境变量

ANDROID_HOME(必需);
PATH里加上gradle的bin目录(不是必需的,只为了方便)。

# nano ~/.bash_profile
      
PATH=$PATH:$HOME/bin:/usr/local/android/gradle/gradle-2.2.1/bin
export PATH
      
export ANDROID_HOME=/usr/local/android/android-sdk-linux



编译打包

# cd到你的项目的根目录下面
cd ~/LanjingApp
      
# 删掉local文件,采用本地的ANDROID_HOME
rm local.properties
      
gradle build

然后就开始直面各种小问题吧:


1、-bash: /usr/local/android/gradle/gradle-2.2.1/bin/gradle: 权限不够

这个问题就是gradle的文件没有可执行权限,加上即可。

chmod a+x gradle


2、A problem occurred starting process 'command '/usr/local/android/android-sdk-linux/build-tools/22.0.1/aapt''

单独执行下aapt,可以看到回显:“/lib/ld-linux.so.2: bad ELF interpreter: 没有那个文件或目录”

这个问题一搜一大把,缺失32位的glibc。装完之后接着尝试,发现还缺zlib和libstdc++。

yum install glibc.i686
yum install zlib.i686
yum install libstdc++.i686



Android开发拾遗之四——自定义View

自定义View,是Android开发必须掌握的技能。今儿就说这个吧。


一般来说,根据需求的不同,有两种路子:

1、在现有View之上添加一些功能:

通常需要继承已有的View类,然后override一些方法。例如要把ImageView做成圆角的,就是走这个路子。

2、组合多个View:

通常需要继承某个ViewGroup,把多个View包含在里面。这种需求更多见,例如ListView里自定义样式的子View,又如需要多个页面间重用的导航栏。今天我们就重点扯一下这一类需求。


简单实现

我们就简单实现一个可重用的导航栏,中间是标题,左边有个“返回”,右边有个“设置”。再容易不过了,上代码:

<?xml version="1.0" encoding="utf-8"?>
<!-- view_simple_title_bar.xml -->
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="50dp">
        
    <TextView
            android:id="@+id/tv_left"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            tools:text="返回"/>
        
    <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            tools:text="标题"
            />
        
    <TextView
            android:id="@+id/tv_right"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            tools:text="设置"/>
        
</LinearLayout>

布局文件略去一些样式上的细节,比如字体颜色和字体大小什么的。


public class SimpleTitleBar extends LinearLayout {
        
    public SimpleTitleBar(Context context) {
        super(context);
        initViews();
    }
        
    public SimpleTitleBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        initViews();
    }
        
    public SimpleTitleBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initViews();
    }
        
    TextView tvLeft;
    TextView tvTitle;
    TextView tvRight;
        
    private void initViews() {
        LayoutInflater.from(getContext()).inflate(R.layout.view_simple_title_bar, this);
        tvLeft = (TextView) findViewById(R.id.tv_left);
        tvTitle = (TextView) findViewById(R.id.tv_title);
        tvRight = (TextView) findViewById(R.id.tv_right);
    }
        
    public TextView getLeftView() {
        return tvLeft;
    }
        
    public TextView getTitleView() {
        return tvTitle;
    }
        
    public TextView getRightView() {
        return tvRight;
    }
}

Java代码文件也很简单,注意在构造方法中需要用LayoutInflater加载布局文件,第二个参数root传自己。


完成了之后,把它放到一个Activity的layout里,就能看效果啦

<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
       
    <com.kailun.customviewsample.views.SimpleTitleBar
            android:id="@+id/titlebar"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"/>
       
</RelativeLayout>



看到效果的同时,我们打开hierarchyviewer,可以看到这个Activity的整个UI树。


SimpleTitleBar本身是个LinearLayout,但是底下又嵌套了一个多余LinearLayout。从谷歌的指引上,我们知道,更深层级的UI树,会带来更多的渲染时间。


着手优化,使用merge

merge只存在于layout里,而不会出现在UI树中。它用于替换无用的ViewGroup,减少UI树的层级。

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:tools="http://schemas.android.com/tools">
      
    <TextView
            android:id="@+id/tv_left"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textColor="@android:color/white"
            tools:text="返回"/>
      
    <!- ... -->
      
</merge>


重新跑起来,再看hierarchyviewer,done!



然而新问题又出来了,由于LinearLayout被替换成了merge,idea的预览视图就无法正确的渲染这个layout(或者可以认为是当成FrameLayout渲染了)。图就不上了,太难看了。

这个对于我这样坚持WYSIWYG的强迫症来说,实在太难受了!!


解决预览问题,showIn帮你搞定

我们需要给merge加上属性tools:showIn:,再提供一个LinearLayout作为容纳它的容器,即可:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:tools="http://schemas.android.com/tools"
       tools:showIn="@layout/preview_simple_title_bar">
     
    <TextView
            android:id="@+id/tv_left"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textColor="@android:color/white"
            tools:text="返回"/>
     
    <!-- ... -->
     
</merge>


接着创建这个preview_simple_title_bar,用来提供一个LinearLayout作为容纳它的容器:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="horizontal"
              android:layout_width="match_parent"
              android:layout_height="50dp">
    <include layout="@layout/view_simple_title_bar" />
</LinearLayout>


回来看一下预览视图,是不是完美了?


参考链接:

Protip. Inflating layout for your custom view

Android Layout Tricks #3: Optimize by merging

Tools Attributes - Android Tools Project Site

VirtualBox下Centos虚拟机的网络配置

着手配置一个Android的持续集成服务器,所以尝试着在本地先搭一个。

网络这块,我的需求就是虚拟机能正常访问外网,宿主机能通过SSH访问虚拟机,通过iTerm来登录。

Linux的配置一直是我很头疼的事儿,这次也不例外。所以完工之后,我把操作步骤记录了下来。


一些环境

我的环境是OSX 10.10,VirtualBox 4.3.30,选择的Centos镜像是7.0 amd64版本。


虚拟机网络设置

VirtualBox的虚拟机可以设置4个网卡,我们需要两个:

  • 网卡1:连接方式采用“网络地址转换(NAT)”,虚拟机上网所需;

  • 网卡2:连接方式采用“仅主机(Host-Only)适配器”,宿主机连虚拟机所需;


安装Centos时设置网络连接

尽量在安装的时候配置好网络连接,是在选择配置软件包那个界面,最底下那个选项就是。

两个网卡都设置成自动启动,这样一开机就有网可以用了。


第一次启动

启动之后,打开终端,ifconfig,就能看到所有的网卡:

enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.2.15  netmask 255.255.255.0  broadcast 10.0.2.255
        ...
 
enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.56.101  netmask 255.255.255.0  broadcast 192.168.56.255
        ...
 
...

第一个网卡即enp0s3,NAT网络,有ip则访问外网ok;

第二个网卡即enp0s8,Host-Only,有ip则从宿主机访问ok。


如果enp0s8没有获得ip,那么就需要设置一下开机自启:


nano /etc/sysconfig/network-scripts/ifcfg-enp0s8
# 修改ONBOOT为yes即可


一切顺利的话,从宿主机ssh过来看看就成啦。

Gradle构建Android项目时libpng warning的问题

libpng warning: “iCCP: Not recognizing known sRGB profile that has been edited”


项目迁移到Gradle之后,build时候就会遇到上述的警告,每个png都会有。上网搜了下,据说原因是“新版本的libpng对关于ICCP采用了更严苛的约束”。

啥意思我也不懂,但总之比较烦人就是,解决方法也不难,装一个imagemagick,给所有png做一下convert处理就好。

Mac OSX下面安装就HomeBrew啦:

brew install imagemagick

接下来cd到每个drawable目录下面,对每个png文件做如下转换

convert xxx.png -strip xxx.png

注意哈,imagemagick本身不是一个命令,是一堆工具的集合,我们用的是其中的convert。


当然,如果你熟悉shell的话,应该能找到一些取巧的办法做批量处理:

ls -1 | grep .png | awk '{printf("convert %s -strip %s\n", $1, $1)}' | zsh


参考链接:

处理LIBPNG WARNING: ICCP: NOT RECOGNIZING

5.20

再一次进入自己博客的后台,发现今年已经快要过半,但是留下的记录好少...

每回想写点啥的时候,都不知道怎么组织句子...唉,真是对不住语文老师...


那咋办呢,图片流吧:


旧都之行,感谢曹博士和洪博士夫妇的招待^_^



回到家的第二天,睡了个大懒觉,然后急冲冲地赶到民政局...

结果第一回没办成,只好又去了一回。从此入赘大江苏...



绿油油的田野,让人心旷神怡。



扎辫子对我来说似乎不是一个易于掌握的技能,总之就是被岳母大人bs了...



宜兴人民广场舞跳的居然是江南Style,也真是醉了...


回来帝都之后,就下定买了游戏机,Xbox+小米电视,咩哈哈哈

不过确实是下了血本了哈



左边那位小伙伴要回旧都一阵子,五彩城小聚了一顿。



不知不觉的,和媳妇儿在一处五年了。
“我爱你,好媳妇儿”

试一次:U盘安装Mac OS X 10.10

想起来我的Pro买来也有两年了,系统升级过若干次,乱七八糟的软件装过一坨,各种设置也被我改了又改。这会儿,XCode动不动就出错,也不知道咋搞。痛定思痛,反正Bootcamp也不准备要了,干脆重装一下吧。


OS X的重装似乎比Windows容易不少:

1、从Appstore下载最新的Yosemate系统


2、去Application下面找到安装 OS X Yosemite



3、显示包内容,找到InstallESD.dmg,双击挂载



4、使用磁盘工具,抹掉U盘



5、使用磁盘工具,在恢复选项中,设置源磁盘(刚刚挂载的dmg)和目的磁盘(刚刚的U盘)



上面的步骤搞定,一个启动U盘就制作OK啦。下面在重启电脑后按住Option,选择从U盘启动,据说就成啦。


祝我好运吧~


果然实践才是王道啊,上面的路子居然失败了...

上网一查,这个路子原来在10.8还有效,10.10已经不行了...正确的路子是这样的:


5、找到createinstallmedia,并复制到你的用户目录下

6、执行命令,制作你的安装盘

sudo ./createinstallmedia --volume /Volumes/(U盘的卷的名字,这里是Install) --applicationpath (把那个"安装OS X Yosemite"拽进来得到它的完整路径) --nointeraction



这个命令会跑很久,请耐心等待它完成,安装U盘就可以搞定。

后面重启开始安装的时候,也得注意第一阶段复制文件时很慢,会在剩余时间大约1秒钟这儿卡很久(目测有半个小时),也请耐心等待。


大概就是酱紫


参考链接

如何制作MAC OS X 10.8美洲狮安装U盘

OSX Yosemite U盘安装教程:Mac OSX 10.10优胜美地u盘制作