做Android开发的时候,经常会需要截取设备的屏幕甚至录制设备屏幕,来让同事知道提交的代码实现的功能,或者让他们知道产品中出现来什么问题。截取Android设备的屏幕有很多方法,比如在Nexus系列同时按下“电源”和“音量调低”按钮来截取,也可以用第三方程序来完成。通过另一部手机来拍照录像也是可行的方案。但是这些方法还需要想办法将文件传输到电脑上,又要多一步麻烦的操作。

Mac电脑上也有一些应用程序可以截屏Android设备,但是我更喜欢用终端指令,令人开心的是Android自带的 adb 已经提供了截取屏幕录制屏幕的指定:

  1. $ adb shell screencap /sdcard/screen.png
  2. $ adb shell screenrecord --verbose /sdcard/demo.mp4

不过生成的文件依然保存在设备中,还需要用adb pull指定来转到电脑上

为了让整个过程更加的便捷,我将这些指令包裹在bash方法中,然后一个指令来完成截取和传输的工作。

截取Android设备当前屏幕并保存到执行指令的电脑目录上:

  1. # capture screen of android device
  2. andrdroidScreenCapture() {
  3. curTime=`date +%Y-%m-%d-%H-%M-%S`
  4. tmpeName="$curTime.png"
  5. [[ -n $1 ]] && fileName=$1 || fileName=$tmpeName
  6. devicePath="/sdcard/$tmpeName"
  7. adb shell screencap -p $devicePath
  8. adb pull $devicePath $fileName
  9. adb shell rm $devicePath
  10. }

录取Android设备屏幕活动,结束后将视频文件保存到执行指令的电脑目录上:

  1. export ADB_SHELL_SCREENRECORD_ARGS='--verbose --bit-rate 2000000'
  2. # record screen of android device
  3. androidScreenRecord() {
  4. echo -e "\033[1m(press Ctrl-C to stop recording)\033[0m"
  5. curTime=`date +%Y-%m-%d-%H-%M-%S`
  6. tmpeName="$curTime.mp4"
  7. [[ -n $1 ]] && fileName=$1 || fileName=$tmpeName
  8. devicePath="/sdcard/$tmpeName"
  9. adb shell screenrecord $ADB_SHELL_SCREENRECORD_ARGS $devicePath
  10. sleep 1 # wait for video encoding finish
  11. adb pull $devicePath $fileName
  12. # Don't delete copy in device.
  13. # adb shell rm $devicePath
  14. open $fileName
  15. }

较短的指令别名,并通过提供的文件的扩展名来决定是截屏还是录制:

  1. function asc() {
  2. if [[ -z $1 ]]; then
  3. echo "Please provide a filename."
  4. echo "Provideing .png extension for capturing the device screen, and providing .mp4 for recording the device screen."
  5. return
  6. fi
  7.  
  8. if [[ $1 == *.png ]]; then
  9. andrdroidScreenCapture $1
  10. elif [[ $1 == *.mp4 ]]; then
  11. androidScreenRecord $1
  12. else
  13. echo "Filename with unknow extension, only .png and .mp4 are supported"
  14. fi
  15. }

完整脚本代码可以在Gist上找到,把它们加入到Mac电脑的~/.bash_profile中,连接上开启了“开发者模式”的Android设备就可以方便截图了。

 

References:
  1. Capture or record android screen, pull file to Mac when it’s completed
  2. ADB Shell Commands | Android Studio
  3. Android Debug Bridge | Android Studio
Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someone

常说“隔行如隔山”,每个行业都只有该业内人士才懂的术语。软件行业里也有很多别人听不懂的话,比如有个笑话就说:所有讲不清楚的过程都叫做“算法(Algorithm)”。但其实即使身处在软件行业里,也任然有好多模棱两可的的概念,常常被混淆使用。就让本文来梳理一下这些概念的细微区别。

function vs. method

函数(function)和方法(method)在编程中的概念就很相近,经常被互换着称呼,但它们的区别主要是在定义的位置上。

function是一段通过名字调用的代码,它可以接受数据进行操作,并可能会有返回值。
method是一段通过关联在对象上的名字调用的代码。

从这段解释上看函数和方法基本一致,只是方法是属于对象的一部分,所以在面向对象语言中才有概念。如果是C语句,就只有函数的说法。

方法是附属于对象的,相对于函数可以接受传入参数,对象本身也会作为隐性参数可以在方法中被调用。在返回值方面,方法不仅可以选择返回数据,还可以将数据赋予其所属的对象。

(在我看来,计算机函数的概念应该与数学的“函数”概念是相通的,只是数学上的函数必须要输入和输出。如果考虑到计算机函数的参数的类型,以及异常(Exception)的情况,则感觉与数学的“映射”更接近了。)

References:

parameter vs. argument

parameter和argument都被翻译成“参数”,它们也经常被混淆。其实很好更精准的翻译应该是“形参(formal parameter)”和“实参(actual argument)”。

parameters是定义函数时定义的那些变量,它们是函数类型识别(type signature)的一部分;
arguments是调用函数时传入到函数中的那些变量,它们是函数的输入值。

这称为也是相对的,比如说下边的例子:

  1. void foo(int x, int y) {
  2. bar(x, y);
  3. }

xy对于foo函数来说是形参(parameter),而对于bar来说是实参(argument)。因此也可以说argument是parameter的实例。

References:

declaration vs. definition

申明(declaration)和定义(definition)这两个概念应该缘起于C/C++,因为写C/C++程序会分头文件(header files)和实现文件(implementation files)。方法和变量的申明部分一般放在头文件,定义部分则在实现文件里。

所以很容易理解

declaration是描述变量、方法、类的类型标识,它没有具体的实现;编译器看到申明部分不会分配内存,而是从其它地方找到其实现的定义。
definition是真正实例化变量,实现方法和类。编译器看到定义部分时就知道如何分配内存,在运行时分配相应大小内存去存放内容。

比如下边这些代码都属于申明:

  1. extern int bar;
  2. extern int g(int, int);
  3. double f(int, double); // extern can be omitted for function declarations
  4. class foo; // no extern allowed for type declarations

而下边的代码是对上述“申明”的具体“定义”:

  1. int bar;
  2. int g(int lhs, int rhs) {return lhs*rhs;}
  3. double f(int i, double d) {return i+d;}
  4. class foo {};

不严格的说“定义”也包含了“申明”的部分,应该单看定义中变量,方法和类的名字部分,那些可以算是“申明”。

 

在Java中并没有“申明”的概念,所有内容都是“申明”即“定义”。如果实在要说细分“申明”,不严格的讲可以把接口(interface)和抽象方法(abstract method)看做“申明”。但实际中我们还是会说“定义接口”,“定义抽象函数”。

References:

Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someone

universal-principles-of-design

As a front-end engineer, or specifically, an Android UI Engineer, I have a lot of time to work with designers. But there are a lot of concepts in design I am not familiar with. So I asked them how I can learn design, but not being a designer. Then they recommended this book to me.

I read it, and feel it’s really helpful and useful. It’s not only about UI design, many principles can apply to other designs as well: software design, landscape design, product design, option design. After read this book, I learned the explanation on how certain design decisions are made.

I would definitely forgot most principles, so I wrote this post to transcribe all principles and short description from the book, later I can just come here to recall them.
Continue reading

Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someone

做移动开发总是离不开在移动设备上做测试,虽然说Android设备单机可能比iPhone便宜,但是真要各种设备集齐也是不小的开支,再加上要试验不同的Android版本那组合起来又有好多了。所以一般开发者往往只在特定几个设备中做检验。需要测试不同版本也会求助于模拟器(Emulator)

Google为Android提供的自带模拟器的效果以前实在惨不忍睹,随着Android Studio 2.0出的模拟器倒是提高了不少,但依然挺糟糕的。
android-avd

大多公司都会购买Genymotion使用,可以创建多种不同版本的模拟器,即使是最新的版本也能很快支持。虽说价格抵上一部真机,却得到好多不同版本和厂商的设备,而且效果优越、功能丰富,真实物超所值。
genymotion

由于某些纠纷,Genymotion默认没有Google Play Services,就无法使用Google的Play Store,Account等等。不过网上也有很多攻略教大家怎将其么安装到Genymotion上,于是都可以在上边打《炉石传说》了。

真机和模拟器各有下边这样那样的缺点:

真实设备 模拟器
  • 价格昂贵
  • 不易携带多个
  • 同一设备不支持多种版本
  • 容易没电
  • 老设备会断货
  • 无SIM卡
  • 无摄像头
  • 不支持推送
  • 多手势不方便
  • 使用习惯不一致

Continue reading

Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someone

已经看过了万能的final关键字在Java中的不同用法和效果,其实static关键字的的用处也很广泛。它的用法更多涉及到面向对象设计(Object-Oriented Design)的思想,有时更加让人迷惑。许多是否应该使用static关键字的决定也常常基于工作经验中形成的约束和规范。本文就来帮助大家解锁static的正确使用姿势,写出更加清晰的代码。

为了更好的了解Java程序,就需要了解代码被编译后的样子,所以下边会涉及到javacjavap这些指令的使用,不用担心都是比较简单直接的用法。也需要借助一些反编译(decompile)工具来查看.class文件,这里用到的是JD-GUI

Static Variable

静态变量(Static Variable)在Java中只有静态成员变量,不同于C++、PHP等,它不允许在方法内定义局部静态变量。静态成员变量也称为类变量,相对的是对象成员变量。这些变量是属于定义的类,而非某个对象实例,但是该类的所有实例都可分享该变量。

用个简单的例子会比较直观一些:

  1. package com.iderzheng.statickeyword;
  2.  
  3. public class StaticField {
  4. public static int sField;
  5. public int mField;
  6.  
  7. public void print() {
  8. System.out.println(
  9. String.format("%s{sField: %d, mField: %d}", this, sField, mField)
  10. );
  11. }
  12.  
  13. public static void main(String[] args) {
  14. StaticField one = new StaticField();
  15. StaticField two = new StaticField();
  16.  
  17. one.sField = 7;
  18. one.mField = 7;
  19.  
  20. two.sField = 21;
  21. two.mField = 21;
  22.  
  23. one.print();
  24. two.print();
  25.  
  26. StaticField.sField = 49;
  27. // Error: non-static variable mField cannot be referenced from a static context
  28. // StaticField.mField = 49;
  29. one.print();
  30. two.print();
  31. }
  32. }

编译运行这段代码得到如下结果:

com.iderzheng.statickeyword.StaticField@34469729{sField: 21, mField: 7}
com.iderzheng.statickeyword.StaticField@6d172f8f{sField: 21, mField: 21}
com.iderzheng.statickeyword.StaticField@34469729{sField: 49, mField: 7}
com.iderzheng.statickeyword.StaticField@6d172f8f{sField: 49, mField: 21}

很清楚的看到变量twosField的修改影响了one中的sField值。而mField在两个对象中是独立互补影响的。也看到可以用类的名字来引用静态变量,这是提倡调用方法,不应该直接用对象变量来引用静态变量,这样做不明确调用的是静态变量,很容易带来副作用。

Static Final Variable

一般在程序中不常定义静态变量,因为静态变量的可被访问和使用的地方比局部变量和对象变量要多,维护起来更加困难。更多时候是跟final结合起来定义常量,对于无法修改的只读变量,就不用担心维护问题了。

对象变量也可以用final修饰定义为常量,只不过每个对象的常量只属于对象自己,而且每个对象都需要分配内存给这些常量。而属于类的静态变量则只需要一份内存甚至无需存储变量的值。所以如果对象变量在每个对象中都是一致的,那么就可以考虑转为静态常量。转成静态常量也能方便地用类名直接进行访问,省去创建多余的中间对象。

Java编译器对于常量会做优化,在引用它们的地方会将它们的值直接嵌入,这样就不需要内存来长期存储这些值,更不要从内存地址中寻找,所以效率要高很多。

还是做一个简单的例子:

  1. package com.iderzheng.statickeyword;
  2.  
  3. public class StaticFinalFields {
  4.  
  5. static final int CONSTANT_INT = 7;
  6. static final String CONSTANT_STRING = "iderzheng.com";
  7. static final int[] CONSTANT_ARRAY = { 7, 21, 31 };
  8.  
  9. final String name;
  10. final int constantInt = 21;
  11. final boolean constantBoolean;
  12.  
  13. {
  14. constantBoolean = true;
  15. }
  16.  
  17. public StaticFinalFields(String name) {
  18. this.name = name;
  19. }
  20.  
  21. public static void main(String[] args) {
  22. StaticFinalFields finalFields = new StaticFinalFields("Ider");
  23. System.out.println(finalFields.name);
  24. System.out.println(finalFields.constantInt);
  25. System.out.println(finalFields.constantBoolean);
  26.  
  27. System.out.println(CONSTANT_INT);
  28. System.out.println(CONSTANT_STRING);
  29. System.out.println(CONSTANT_ARRAY);
  30. }
  31. }

对于上边的代码,通过javac进行编译后得到StaticFinalFields.class,再讲其通过JD-UI打开,会看到编译后代码的
static-final-inline

  1. package com.iderzheng.statickeyword;
  2.  
  3. import java.io.PrintStream;
  4.  
  5. public class StaticFinalFields
  6. {
  7. static final int CONSTANT_INT = 7;
  8. static final String CONSTANT_STRING = "iderzheng.com";
  9. static final int[] CONSTANT_ARRAY = { 7, 21, 31 };
  10. final String name;
  11. final int constantInt = 21;
  12.  
  13. final boolean constantBoolean = true;
  14.  
  15. public StaticFinalFields(String paramString)
  16. {
  17. this.name = paramString;
  18. }
  19.  
  20. public static void main(String[] paramArrayOfString) {
  21. StaticFinalFields localStaticFinalFields = new StaticFinalFields("Ider");
  22. System.out.println(localStaticFinalFields.name);
  23. localStaticFinalFields.getClass(); System.out.println(21);
  24. System.out.println(localStaticFinalFields.constantBoolean);
  25.  
  26. System.out.println(7);
  27. System.out.println("iderzheng.com");
  28. System.out.println(CONSTANT_ARRAY);
  29. }
  30. }

从编译后的代码可以看出对于原始类型的变量,在用final进行修饰并内嵌初始化后,使用这些变量的地方就直接用这些值进行了替换。虽然也有非static修饰的对象常量被智能优化后直接内嵌,但是为了代码的维护性,依然应该显示地申明成static

至于对象变量,它们并非真正意义上的常量,对象的创建后的地址也总会不同,而且对象的内容依然可以被修改,所以这些变量加上static final也无法用其值在使用的地方进行替换。

另外还看到通过block进行初始化的对象变量虽然也是常量,但是因为放入了block,编译器觉得它的初始化比较复杂,所以就没有将其引用直接替换。

直接内嵌常量的可以提高运行的效率,但是也有一些弊端:比如项目A中使用了项目B中的静态常量,成功编译后的A会直接使用那些常量;要是B的常量值被人修改了之后只编译了B而没有重新编译A,那A就不会得到新的值。好在现代的编译器都很智能,可以发现这些依赖关系并正确编译。
Continue reading

Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someone

前阵子更新了一下系统和软件,打开Chrome浏览器发现插件(Chrome Extension)的显示位置变了,原来显示在Omnibox里的Page Action突然跑到了Toolbar上跟Browser Action厮混在了一起。

于是开始在网上搜索原因才发现原来Chrome对插件的按钮显示做了改版(见”Introducing Chrome 48! “,” Upcoming UI Change“,”What happened with pageAction “),将它们都归到了Toolbar上并且可以隐藏到Menu中。

chrome-extension-menu

我以前也一直觉得Browser Action和Page Action是那么的相似,在Browser Action的监听事件中也能获得当前页面的信息,何必要分成两种让开发者做选择呢。现在Google也是有意要将两者归并,甚至可能在未来淘汰其中一个来简化冗余界面对用户造成的困惑。因为在新的设计下,对Page Action进行hide不会像以前那样将图标真的隐藏,而转为变灰表示其不可用。这在但这API显然不能真实反映其效果,很有可能会为了统一而淘汰它们的使用。

另一些变化是即使插件不用Browser Action和Page Action,也会在Toolbar上显示灰色的图标已表示其被安装使用着。这样可以更明显的提醒用户在浏览器上现在都有哪些插件,来防止恶意插件被用户忽略。

Chrome的插件也只能使用Browser Action和Page Action之一,如果在manifest里同时定义了两者,就会得到“Only one of ‘browser_action’, ‘page_action’, and ‘app’ can be specified.”的错误。

哎,以前写的关于Chrome插件开发的文章就这么被过时了,好在基本的结构和方式没有大的变化。

Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someone

实用Mac的Dictionary应用程序》已经帮助我们翻译英文单词,提高了阅读的能力,但是“听”和“说”还是大问题。发音要是不标准,念出来给别人听可能还是得不到理解。Mac也想到了这个问题,所以自带了发音程序来,当然机器念出来的英语还是会有不流畅的感觉,有些发音也可能不到位。

在许多原生Mac应用程序中,都很好的集成了发音选项,只要选取想要电脑阅读的段落,然后选择“Start Speaking”就可以听到朗读开始了。
speech-on-page

在System Preference里也可以为Speech设置快捷键,这样无论在哪里选中文字都可以让电脑来阅读:
speech-shortcut
Continue reading

Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someone

dictionary学习英文最需要的是一本字典来查看单词的意思。在电脑上虽然可以上网来搜单词,但是被网速约束总是会让人烦躁,让英文阅读变得不流畅,所以就更希望能够有电子字典可以直接使用。以前使用Window PC的时候有很多英汉汉英字典可以选用,但到了Mac电脑上就没有什么功能和界面都比较完善的应用。

其实Mac自带的Dictionary应用就已经非常强大:庞大词库,准确释义;离线查询,在线Wiki;屏幕选词,原生结合;清爽界面,稳定流畅。而且在最新的系统中,字典库还包含了牛津英汉和牛津汉英字典,就不用在网上到处找中英字典安装。
Continue reading

Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someone

现在每个公司都为会员工配备工作电脑,但是对电脑的使用也会有诸多的限制和保护,以免公司的重要资料被泄露。其中最基础的一条就是电脑必须要有登录密码,并且当人离开电脑的时候必须要锁定电脑(Lock Screen)防止被别人获取到电脑上的内容。本文就来分享一些快速让电脑进入屏幕锁定状态的技巧和操作。

首先来分一下“关闭”的几个等级,它们从高到低的等级如下:

  • 关闭电脑(Shut down):这个功能只在系统或者软件更新,或者死机时才会用到,其它时候电脑基本不关闭。
  • 登出用户(Log out):公司电脑大多只用一个账户,除了少数应用需要登出后重新登录才能初始化好。
  • 进入睡眠(Sleep):这个功能基本是取代关机功能,大多数情况是因为电脑在一定时间没有操作自动进入了该状态。
  • 关闭屏幕(Display off):只是屏幕没有显示,电脑还在运行,启动屏保也可以算是其中一种。

之后我们主要也是针对最后两种等级来讨论。

让Mac笔记本电脑进入睡眠方式最直接的方法自然是合上电脑。而其他关闭状态则可以从Mac的苹果菜单栏( Menu)下选择相应的选项。
menu

当按住Option键时选项中的会消失,此时点中选项系统不会弹出确认框,直接执行选定的操作。
Continue reading

Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someone

在《Java关键字分类解析》里介绍了所有Java关键字和保留字的基本用途,到《Java修饰符及其作用对象》又介绍了所有Java修饰符的应用。通过修饰符的表格看到final是唯一一个可以在类(Class)、方法Method、变量(Variable)上都能应用的,但前文也提到final在这些作用对象上的效果是不太一样的,本文就详细展开介绍一下final的具体效果。

final class

当一个类被定义成final class,表示该类的不能被其他类继承,即不能用在extends之后。否则在编译期间就会得到错误。

  1. package com.iderzheng.finalkeyword;
  2.  
  3. public final class FinalClass {
  4. }
  5.  
  6. // Error: cannot inherit from final
  7. class PackageClass extends FinalClass {
  8. }

Java支持把class定义成final,似乎违背了面向对象编程的基本原则,但在另一方面,封闭的类也保证了该类的所有方法都是固定不变的,不会有子类的覆盖方法需要去动态加载。这给编译器做优化时提供了更多的可能,最好的例子是String,它就是final类,Java编译器就可以把字符串常量(那些包含在双引号中的内容)直接变成String对象,同时对运算符+的操作直接优化成新的常量,因为final修饰保证了不会有子类对拼接操作返回不同的值。

对于所有不同的类定义—顶层类(全局或包可见)、嵌套类(内部类或静态嵌套类)都可以用final来修饰。但是一般来说final多用来修饰在被定义成全局(public)的类上,因为对于非全局类,访问修饰符已经将他们限制了它们的也可见性,想要继承这些类已经很困难,就不用再加一层final限制。

另外要提到的是匿名类(Anonymous Class)虽然说同样不能被继承,但它们并没有被编译器限制成final

  1. import java.lang.reflect.Modifier;
  2.  
  3. public class Main {
  4.  
  5. public static void main(String[] args) {
  6. Runnable anonymous = new Runnable() {
  7. @Override
  8. public void run() {
  9. }
  10. };
  11.  
  12. System.out.println(Modifier.isFinal(anonymous.getClass().getModifiers()));
  13. }
  14. }
  15.  
  16. // Output:
  17. // false

Continue reading

Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someone