常用 adb command 命令

一些常用的 adb 命令,包括 Android 录屏,及从电脑复制文件,从 Android 设备拉取文件等等。

adb 的全称是 Android Debug Bridge, 这个命令可以用来发送一系列指令给 Android 设备,包括但不限于基本的 Linux 指令。只要手机或者任何 Android 设备开启了 Debug 模式并且取得信任,adb 命令几乎可以用来做任何事情。因此网络上也存在使用 adb 来入侵同一局域网下的 Android 盒子的例子。

adb 使用的默认端口是 5037.

Ubuntu 系下安装

sudo apt-get install android-tools-adb
sudo apt-get install android-tools-fastboot

查看连接设备 adb devices

来查看设备是否已经连接

$ adb devices
List of devices attached
2dd11c6e	device

获取设备状态

adb get-state

返回的结果可能有三种

  • device: 设备正常连接
  • offline: 连接异常,设备无响应
  • unknown: 没有连接设备

杀掉 adb 进程

在连接异常时,而已杀死服务重启尝试

adb kill-server
adb start-server

打印日志

adb logcat

将文件推送到设备上 adb push

将文件复制到 Android 设备上:

adb push filename.txt /sdcard/Download/

从设备上拉取文件 adb pull

将文件从 Android 设备上拉回本地

adb pull /sdcard/Download/filename.txt ~/filename.txt

从设备上拉取多个文件

adb pull 命令不知道为什么用 *.apk 这样的正则无法使用,所以只能先过滤出来特定的文件后缀,然后再使用 adb pull

比如说要把 /sdcard/Download 文件夹中的 *.apk 文件都拉取到本地,那么可以:

adb shell ls /sdcard/Download/\*.apk | tr '\r' ' ' | xargs -n1 adb pull

如果想要了解更多 xargs 命令的使用,可以参考这一篇文章

检查设备是否已经 root

adb shell

然后进入之后输入 su 查看手机是否会弹出 root 授权窗口。

adb reboot

重启设备,在刷机时经常使用

进入 bootloader

使用

adb reboot bootloader

重启进去 bootloader

或者使用 adb reboot recovery 直接进入 recovery 模式

端口重定向

将宿主机上某端口重定向到设备端口

adb forward tcp:8008 tcp:8008

执行命令后所有发往宿主机 8008 端口的数据都会被转发到 Android 设备 8008 上。

adb shell 命令

直接使用该命令可以进入手机的 Shell。 adb 其他命令是 adb 自带的命令,而 adb shell 则是调用的 Android 设备上的命令。Android 设备的命令放在 /system/bin 目录下。

比如 adb shell which ls 返回的结果会告诉你找到的是 /system/bin/ls 这个命令。

修改权限

进入 Android Shell 之后就可以使用任何 Linux 命令来直接操作 Android 设备, 比如:

adb shell chmod 666 /data/filename.txt

screencap 屏幕截图

shell 中可以直接截取设备的屏幕

adb shell screencap -p /sdcard/screen.png
adb pull /sdcard/screen.png
adb shell rm /sdcard/screen.png

使用 screencap 截图保存到 sdcard 上,使用 pull 命令拉到本地并删除 sd 卡中文件。这种方式比较繁杂,需要三个步骤,如果查看 screencap -h 会发现,帮助中有一行,如果不加文件名,命令会将结果输出到标准输出。那么

adb shell screencap -p > screen.png
adb shell screencap -p | sed 's/\r$//' > screen.png

直接将结果输出到本地,之所以使用 sed 是因为需要将多余的 \r 删除。adb shell 在执行时会将 \n 转换为 \r\n

在本地添加 alias

alias and-screencap="adb shell screencap -p | sed 's/\r$//'"
and-screencap > screen.png

screenrecord 在 shell 中录制屏幕

在 shell 命令中可以使用 screenrecord 命令来录制屏幕。需要 Android 4.4 (API Level 19)及以上,该命令将屏幕保存成 MPEG-4 文件。不录制声音。

adb shell screenrecord /sdcard/Download/filename.mp4

使用 Ctrl + c 来停止录像,否则默认录制 3min 或者使用 --time-limit 参数来指定。

注意:

  • screenrecord 命令可以按照设备原始分辨率录屏,但是有些设备可能不支持原始分辨率,此时降低分辨率再尝试。
  • 不支持录屏中旋转屏幕

可选参数:

  • --help
  • --size <width*Height> 比如 1280*720.
  • --bit-rate <rate> 默认码率 4Mbps,6Mbps 可以设置 6000000.
  • --time-limit <TIME> 默认为 180 (3min) 设置时间,单位秒
  • --rotate 旋转输出
  • --verbose 显示 log 信息,如果不设置,不显示任何信息

列出 Android 设备上所有安装的应用

pm 可以记忆成 package manager ,使用 pm 命令可以获取设备上应用相关的信息。

使用如下命令 1:

adb shell 'pm list packages'

使用如下命令去除前面的 package:

adb shell pm list packages | awk -F ":" '{print $2}'
# 或者,-f 用来输出第二部分, -d 用来标示分割符号
adb shell pm list packages | cut -f 2 -d ":"

在使用 adb shell 进入 手机 Shell 之后可以使用, pm help 来获取更多关于 pm 命令的详情。关于 pm 的命令。

  • adb shell pm list packages
  • adb shell pm list packages -f 列出包名及对应的 apk 名及存放的位置
  • adb shell pm list packages -d Filter to only show disabled packages.
  • adb shell pm list packages -e Filter to only show enabled packages.
  • adb shell pm list packages -s Filter to only show system packages.
  • adb shell pm list packages -3 Filter to only show third party packages.
  • adb shell pm list packages -i 应用包名及其安装来源 com.android.vending 表示从官方 Play Store 安装
  • adb shell pm list packages -u Also include uninstalled packages.
  • adb shell pm list packages --user <KEYWORDS> The user space to query.

在命令后直接添加关键词能够根据关键字过滤结果。

adb shell pm path com.android.chrome

能够给出应用的 apk 路径。

adb shell pm dump com.android.chrome

能够列出指定应用的各种信息

如果 apk 文件在 Android 设备上,可以使用

adb shell pm install /path/to/apk
adb shell pm uninstall package_name
adb shell pm clear  # 清除应用数据

am

启动一个指定的 Activity

adb shell am start -n com.oneplus.camera/.OPCameraActivity

强制停止应用

adb shell am force-stop pacakge_name

input 模拟点击和滑动事件

命令格式

adb shell input text <string>
adb shell input keyevent <key code number or name>
adb shell input tap <x> <y>             # 点击事件
adb shell input swipe <x1> <y1> <x2> <y2> [duration(ms)]

模拟按键的,keycode 为 3 时表示 HOME 键,更多的可以参考后文的附录

adb shell input keyevent 3

模拟点击时,后面接的 x,y 都是真实屏幕分辨率,比如想要点击屏幕 (x,y)=(150,150) 像素的位置

adb shell input tap 150 150

模拟屏幕滑动和 tap 是一样的,只是需要给出滑动的起点和终点两个坐标值

adb shell input swipe 150 150 200 200

ime 列出输入法

列出设备上的输入法

adb shell ime list [-a] [-s]
adb shell ime enable ID
adb shell ime disable ID
adb shell ime set ID

wm

获取设备分辨率

adb shell wm size

安装及卸载应用程序 adb install

可以使用该命令安装应用程序,命令的通用模式:

adb install app.apk

可以使用 -r 命令覆盖安装应用 adb install -r apkfilename.apk

如果 adb 连接了多台设备可以使用 -s 来选择一台设备

adb -s DEVICE install -r app.apk

adb install 的其他参数

  • adb install -l app.apk forward lock application
  • adb install -r app.apk 替换存在的应用
  • adb install -t app.apk 允许测试包
  • adb install -s app.apk 在 sdcard 上安装
  • adb install -d app.apk 允许比现在安装版本更低的包 allow version code downgrade
  • adb install -p app.apk 增量更新 partial application install

同理可以使用 adb uninstall apkfilename.apk 来卸载应用。

使用 adb uninstall -k apkfilename.apk 可以卸载应用,但是保留数据。

adb connect

通过网络来使用 adb,可以通过该命令来连接网络上开放远程调试的设备(比如 Android 机顶盒之类)。

adb connect <host>[:<port>]

远程连接之后就可以使用上面的所有命令,也可卸载远程设备上的应用,也可以安装本地的 apk 到远程设备上,也可以通过 adb 命令来控制远程设备上的应用。

通过下面的命令向远程设备安装应用

adb -s <ip:port> install -r <app.apk>

向远程设备发送按键事件,比如下面向远程设备发送 Powerbutton 按键按下事件

adb -s <ip:port> shell input keyevent 26

或者启动远程设备上的应用 Start the App

adb -s <ip:port> shell monkey -p <package name> -c android.intent.category.LAUNCHER 1

adb 命令如何显示 package version

在电视盒子上安装 Youtube 应用的时候,遇到了几个版本,突然想到这个命令

adb shell dumpsys package com.google.android.youtube | grep version

可以用来查看当前这个 package 的版本号。

获取当前正在运行的应用 Activity Name

使用 adb shell dumpsys

adb shell dumpsys activity activities | grep 'Hist #'

  * Hist #0: ActivityRecord{b71f1a7 u0 com.oneplus.camera/.OPCameraActivity t4826}
  * Hist #0: ActivityRecord{5de041d u0 slide.cameraZoom/.CameraZoomActivity t4825}
  * Hist #0: ActivityRecord{1e2287c u0 mobi.acpm.inspeckage/.ui.MainActivity t4824}
  * Hist #0: ActivityRecord{e96e625 u0 com.google.android.calendar/.AllInOneCalendarActivity t4812}
  * Hist #0: ActivityRecord{cf38cb2 u0 com.google.android.googlequicksearchbox/com.google.android.apps.gsa.staticplugins.opa.OpaActivity t4711}
  * Hist #0: ActivityRecord{ff685a1 u0 com.android.launcher3/.Launcher t4639}
  * Hist #0: ActivityRecord{78edd61 u0 com.android.systemui/.recents.RecentsActivity t4652}

从 Android 中备份应用

有一个常见的场景就是在需要备份已经安装的 app,导出 apk 的时候,如果是手机的的话借助 ES Explorer 能快速搞定,如果是机顶盒那么就要 adb connect 之后用命令来备份了。用到的命令上文都有提及,首先要知道需要备份的应用的包名 package name

adb shell pm list packages

然后获取该应用的完整路径

adb shell pm path com.example.app

输出结果会是类似于

package:/data/app/com.example.app-2.apk

这样的路径,然后使用 pull 命令将文件备份到本地

adb pull /data/app/com.example.app-2.apk /path/to/local/destination/app.apk

然后在目标目录下就能看到备份的内容了。

附录 keyevent

KEYCODE_UNKNOWN=0;
KEYCODE_SOFT_LEFT=1;
KEYCODE_SOFT_RIGHT=2;
KEYCODE_HOME=3;
KEYCODE_BACK=4;
KEYCODE_CALL=5;
KEYCODE_ENDCALL=6;
KEYCODE_0=7;
KEYCODE_1=8;
KEYCODE_2=9;
KEYCODE_3=10;
KEYCODE_4=11;
KEYCODE_5=12;
KEYCODE_6=13;
KEYCODE_7=14;
KEYCODE_8=15;
KEYCODE_9=16;
KEYCODE_STAR=17;
KEYCODE_POUND=18;
KEYCODE_DPAD_UP=19;
KEYCODE_DPAD_DOWN=20;
KEYCODE_DPAD_LEFT=21;
KEYCODE_DPAD_RIGHT=22;
KEYCODE_DPAD_CENTER=23;
KEYCODE_VOLUME_UP=24;
KEYCODE_VOLUME_DOWN=25;
KEYCODE_POWER=26;
KEYCODE_CAMERA=27;
KEYCODE_CLEAR=28;
KEYCODE_A=29;
KEYCODE_B=30;
KEYCODE_C=31;
KEYCODE_D=32;
KEYCODE_E=33;
KEYCODE_F=34;
KEYCODE_G=35;
KEYCODE_H=36;
KEYCODE_I=37;
KEYCODE_J=38;
KEYCODE_K=39;
KEYCODE_L=40;
KEYCODE_M=41;
KEYCODE_N=42;
KEYCODE_O=43;
KEYCODE_P=44;
KEYCODE_Q=45;
KEYCODE_R=46;
KEYCODE_S=47;
KEYCODE_T=48;
KEYCODE_U=49;
KEYCODE_V=50;
KEYCODE_W=51;
KEYCODE_X=52;
KEYCODE_Y=53;
KEYCODE_Z=54;
KEYCODE_COMMA=55;
KEYCODE_PERIOD=56;
KEYCODE_ALT_LEFT=57;
KEYCODE_ALT_RIGHT=58;
KEYCODE_SHIFT_LEFT=59;
KEYCODE_SHIFT_RIGHT=60;
KEYCODE_TAB=61;
KEYCODE_SPACE=62;
KEYCODE_SYM=63;
KEYCODE_EXPLORER=64;
KEYCODE_ENVELOPE=65;
KEYCODE_ENTER=66;
KEYCODE_DEL=67;
KEYCODE_GRAVE=68;
KEYCODE_MINUS=69;
KEYCODE_EQUALS=70;
KEYCODE_LEFT_BRACKET=71;
KEYCODE_RIGHT_BRACKET=72;
KEYCODE_BACKSLASH=73;
KEYCODE_SEMICOLON=74;
KEYCODE_APOSTROPHE=75;
KEYCODE_SLASH=76;
KEYCODE_AT=77;
KEYCODE_NUM=78;
KEYCODE_HEADSETHOOK=79;
KEYCODE_FOCUS=80;//*Camera*focus
KEYCODE_PLUS=81;
KEYCODE_MENU=82;
KEYCODE_NOTIFICATION=83;
KEYCODE_SEARCH=84;
KEYCODE_MEDIA_PLAY_PAUSE=85;
KEYCODE_MEDIA_STOP=86;
KEYCODE_MEDIA_NEXT=87;
KEYCODE_MEDIA_PREVIOUS=88;
KEYCODE_MEDIA_REWIND=89;
KEYCODE_MEDIA_FAST_FORWARD=90;
KEYCODE_MUTE=91;

reference

  1. https://gist.github.com/davidnunez/1404789 


2016-09-08 android-dev , android , adb , dev , device

Android Animation Interpolator

动画插值器,用来描述动画的变化率,这里讨论的 Interpolator 指的是 android.animation 包下的 TimeInterpolator。 以下所有的插值器都继承自 Interpolator , 而 Interpolator 接口直接继承自 TimeInterpolator , 自身并没有添加任何方法。

TimeInterpolator 中有

abstract float getInterpolation(float input) 方法,参数 input:input 参数是一个 float 类型,它取值范围是 0 到 1,表示当前动画的进度,取 0 时表示动画刚开始,取 1 时表示动画结束,取 0.5 时表示动画中间的位置。返回值:表示当前实际想要显示的进度。取值可以超过 1 也可以小于 0,超过 1 表示已经超过目标值,小于 0 表示小于开始位置。

input 的值只与时间相关,和我们设定没有任何关系。线性插值器函数实现:

public float getInterpolation(float input) {  
    return input;  
}  

AccelerateInterpolator

加速插值器,开始很慢,不断加速

public AccelerateInterpolator(float factor)

将factor设置为1.0f会产生一条y=x^2的抛物线。增加factor到1.0f之后为加大这种渐入效果(也就是说它开头更加慢,结尾更加快)

当fractor不为1时,轨迹曲线是 y=x^(2*fractor)(0<x<=1) 的曲线

DecelerateInterpolator

减速插值器,开始比较快然后减速的插值器,动画的快慢度。将factor值设置为1.0f时将产生一条从上向下的y=x^2抛物线。增加factor到1.0f以上将使渐入的效果增强(也就是说,开头更快,结尾更慢)

AccelerateDecelerateInterpolator

加速减速插值器,开始慢从中间后开始变快

LinearInterpolator

线性插值器

BounceInterpolator

弹跳插值器

AnticipateInterpolator

回荡秋千插值器

AnticipateOvershootInterpolator

CycleInterpolator

正弦周期变化插值器

OvershootInterpolator

reference


2016-09-06 Android , AndroidDev

Android ImageView ScaleType

ImageView 的 ScaleType 属性决定了图片在 View 上显示时的样子,是比例缩放,还是显示图片的整体或者局部等等。对于一张图片,有其自身的大小,而 ImageView 也有其自身的大小,这两者如何完美的合作其结果很重要的设置便是 ScaleType 属性。

设置该属性的方式有两种:

  1. 在布局 Layout 中 ImageView 中定义 android:scaleType="center"
  2. 在代码中调用 imageView.setScaleType(ImageView.ScaleType.CENTER);

8 种 ScaleType

所有的 Scale 方式有 8 种,文档 中可以查看到,下文也会详细介绍。

ImageView.ScaleType.CENTER

按图片原大小居中显示,不缩放原图片。当原始图片长/宽超过View的长/宽,则截取图片的居中部分显示,当图片长/宽小于 View 长宽时,可能造成填充不完全, ImageView 周围有空白的情况。

在 xml 中使用定义: android:scaleType="center"

ImageView.ScaleType.CENTER_CROP

按比例扩大图片的size居中显示,不改变图片宽高比,使得图片长(宽)等于或大于View的长(宽),图片填充满 ImageView,可能产生裁剪,此时能保证 View 被完整填充。也就意味着这种裁剪方式会使 ImageView 被完整填充,对于高大于宽的图片,高部分就会被裁剪。

在 xml 中使用定义: android:scaleType="centerCrop"

ImageView.ScaleType.CENTER_INSIDE

将图片的内容完整居中显示,通过按比例缩小或原来的size使得图片长/宽小于或等于View的长/宽,ImageView 可能产生空白部分。对于高度大于宽度图片,则高度填充整个 ImageView 高度,宽度则小于 ImageView 宽度,则宽度两边产生空白。

在 xml 中使用定义: android:scaleType="centerInside"

ImageView.ScaleType.FIT_CENTER

把图片按比例扩大/缩小到 ImageView 的高度或者宽度,使用 CENTER 方式居中显示,也就是使用该方式,使用图片长的一部分适应。

FIT_START, FIT_END 在图片缩放效果上与 FIT_CENTER 一样,只是显示位置不同,FIT_START 是置于顶部,FIT_CENTER 居中,FIT_END 置于底部。

在 xml 中使用定义: android:scaleType="fitCenter"android:scaleType="fitEnd"android:scaleType="fitStart"

ImageView.ScaleType.FIT_XY

不按比例缩放图片,把图片塞满整个 View ,如果 View 与 原图片长宽不对应,可能造成图片长宽比改变,以及图片变形。

在 xml 中使用定义: android:scaleType="fitXY"

ImageView.ScaleType.MATRIX

通过变化矩阵来设置 ImageView 大小。可以通过设置 matrix 然后手动利用 matrix 来调整 ImageView 可以实现 ImageView 的双指缩放和旋转。不过似乎用的很少。

具体细节可以继续再讨论。

如果要看效果图,这里 很不错,一目了然。

在 xml 中使用定义: android:scaleType="matrix"

其他问题

ImageView src 与 background 属性区别

每一种 View 都可以包含背景图片,而 ImageView 的 src 可以设置 ScaleType ,adjustViewBounds 等属性。

保证 ImageView 长宽比

center, center_crop, center_inside, fit_center, fit_start, fit_end 等方式都会改变图片的比例,但是会有一定程度的裁剪。

保持图片比例填满宽度

使用如下 Layout

<ImageView
    android:id="@id/img"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:scaleType="fitCenter" />

使用 fitCenter 来使图片填充整个 ImageView 宽度,并且进行等比例放缩。

答案来自 StackOverflow

reference


2016-09-05 Android , AndroidDev

Android Animation

Android 的动画实现有不同的方式,在 3.0 以前 Android 的动画很简单只能在 View 层做,在后期版本中不断的加入动画实现,至今已经有这非常完善的动画系统了。Android 系统提供了 Property animation 和 View animation 两大动画系统。除去这两大动画系统之外,还有一类 Drawable Animation, 允许加载 drawable 并且一帧一帧播放:

  • View Animation 视图动画
  • Property Animation 属性动画
  • Drawable Animation 帧动画

另外官方文档也有另外一种分类方法 将 Tween Animation 补间动画 和 Frame Animation 逐帧动画归纳为 View Animation。 见文档

View Animation

旧的动画系统,只能用来对 View 进行动画。View Animation 很简单,不过只能支持简单的缩放、平移、旋转、透明度基本的动画,且有一定的局限性。

Android中提供的 4种 View 动画:

  • AlphaAnimation 透明度动画效果,可以实现渐隐渐现
  • ScaleAnimation 缩放动画效果
  • TranslateAnimation 位移动画效果
  • RotateAnimation 旋转动画效果

以及 AnimationSet 动画集合,可以混合使用多种动画。 View Animation 都在 view.animation 包名下。

Android 系统在 3.0 之后推出了 Property Animation (属性动画)框架,属性动画可以用于任何对象,相比属性动画,View Animation (视图动画)的一个非常大的缺陷就是不具备交互性,当某个元素发生视图动画后,其响应事件的位置还依然是在动画前的地方,所以视图动画只能做普通的动画效果,避免交互的发生。但它的优点也非常明显,即效率比较高且使用方便。

视图动画使用非常简单,不仅可以通过XML文件来描述一个动画过程,同样可以使用代码来控制整个动画过程。

通用方法

animation.setDuration(long durationMillis);           // 设置动画时长
animation.setRepeatCount(int repeatCount);        // 设置动画重复次数
animation.setFillAfter(boolean);           // 动画执行完后是否停留在执行完的状态
animation.setFillBefore(boolean);                 // 动画执行前是否应用状态
animation.setStartOffset(long startOffset);          // 动画执行前的等待时间
animation.setInterpolator(Interpolator i);         // 设置动画插值
// 设置重复模式, 这个模式只有在 repeat count 大于0 或者 INFINITE 时才生效,有 RESTART 和 REVERSE 两种,RESTART 为重头播放,REVERSE  为逆向播放动画。
animation.setRepeatMode(int repeatMode);
animation.setStartTime(long startTimeMillis);      // 设置动画开始时间
animation.start();            // 开始动画
animation.startNow();           // 立即开始动画

启动动画的两种方式:

view.startAnimation(animation);

//或者设置动画之后延后开始动画

view.setAnimation(animation);
animation.start();

使用 xml 定义动画

当使用 xml 配置动画时,需要放到 res 目录下 res/anim/ 。 xml 定义的 root 元素可以是 :

  • <alpha>
  • <scale>
  • <translate>
  • <rotate>
  • interpolator 元素
  • <set> 或者 set 中包含其他 set

举例:

<set android:shareInterpolator="false">
    <scale
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:fromXScale="1.0"
        android:toXScale="1.4"
        android:fromYScale="1.0"
        android:toYScale="0.6"
        android:pivotX="50%"
        android:pivotY="50%"
        android:fillAfter="false"
        android:duration="700" />
    <set android:interpolator="@android:anim/decelerate_interpolator">
        <scale
           android:fromXScale="1.4"
           android:toXScale="0.0"
           android:fromYScale="0.6"
           android:toYScale="0.0"
           android:pivotX="50%"
           android:pivotY="50%"
           android:startOffset="700"
           android:duration="400"
           android:fillBefore="false" />
        <rotate
           android:fromDegrees="0"
           android:toDegrees="-45"
           android:toYScale="0.0"
           android:pivotX="50%"
           android:pivotY="50%"
           android:startOffset="700"
           android:duration="400" />
    </set>
</set>

可以使用如下代码,从 XML 中加载动画:

ImageView spaceshipImage = (ImageView) findViewById(R.id.spaceshipImage);
Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump);
spaceshipImage.startAnimation(hyperspaceJumpAnimation);

接下来就分别看一下不同 View Animation 的使用。

AlphaAnimation

AlphaAnimation 动画,可实现淡入淡出。

AlphaAnimation(float fromAlpha, float toAlpha)

参数说明:float 设置透明度渐变动画, 1.0 完全不透明, 0.0 是完全透明

然后利用 View 的 startAnimation 方法就可以使用。

RotateAnimation

旋转动画,可以在 X-Y 平面设置旋转的动画,可以指定旋转中心, (0,0) 表示 top left 坐点,如果不指定,默认为左上角为旋转中心。

有四个构造函数:

RotateAnimation(Context context, AttributeSet attrs)

// 默认旋转中心为 (0,0)
RotateAnimation(float fromDegrees, float toDegrees)

RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY)

RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)

参数说明:

  • float fromDegrees 旋转的开始角度。默认为角度, 0为原始角度, 90为顺时针个旋转90度。
  • float toDegrees 旋转的结束角度。
  • float pivotX X方向旋转中心,默认为0,左
  • float pivotY Y方向旋转中心,默认为0, 上边缘
  • int pivotXType X 轴的伸缩模式,可以取值为 Animation.ABSOLUTERELATIVE_TO_SELFRELATIVE_TO_PARENT。决定了 pivotXValue 的方式。
  • float pivotXValue X轴坐标的伸缩值,当方式为 ABSOLUTE 时,可以为固定值,也可以为百分数,1.0 为 100%。
  • int pivotYType:Y轴的伸缩模式,可以取值为 ABSOLUTERELATIVE_TO_SELFRELATIVE_TO_PARENT
  • float pivotYValue:Y轴坐标的伸缩值。同 X 轴

ScaleAnimation

放缩动画,可指定缩放中心。

ScaleAnimation(Context context, AttributeSet attrs)

ScaleAnimation(float fromX, float toX, float fromY, float toY)

ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY)

ScaleAnimation(float fromX, float toX, float fromY, float toY, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)

参数说明:

  • float fromX 动画起始时 X坐标上的伸缩尺寸
  • float toX 动画结束时 X坐标上的伸缩尺寸
  • float fromY 动画起始时Y坐标上的伸缩尺寸
  • float toY 动画结束时Y坐标上的伸缩尺寸
  • float pivotX 缩放中心X坐标
  • float pivotY 缩放中心Y坐标
  • int pivotXType 动画在X轴 pivotValue 如何伸缩,有 Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF,Animation.RELATIVE_TO_PARENT..
  • float pivotXValue 动画相对于物件的X坐标的开始位置
  • int pivotYType 动画在Y轴相对于物件位置类型
  • float pivotYValue 动画相对于物件的Y坐标的开始位置

TranslateAnimation

位移动画,可以控制位置的动画

TranslateAnimation(Context context, AttributeSet attrs)

TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)

TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue, int fromYType, float fromYValue, int toYType, float toYValue)

参数说明:

  • float fromXDelta 动画开始的点离当前View X坐标上的差值
  • float toXDelta 动画结束的点离当前View X坐标上的差值
  • float fromYDelta 动画开始的点离当前View Y坐标上的差值
  • float toYDelta 动画开始的点离当前View Y坐标上的差值
  • int fromType 决定 fromXValue 如何被调用,Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or Animation.RELATIVE_TO_PARENT
  • int toXType 参数同上
  • int fromYType 参数同上
  • int toYType 参数同上

Property Animation

Android 3.0, API 11 及以上,可以对任意类型进行动画。 属性动画在系统的 android.animation 包名下。总的来说,属性动画就是,动画的执行类来设置动画操作的对象的属性、持续时间,开始和结束的属性值,时间差值等,然后系统会根据设置的参数动态的变化对象的属性。

可定义如下参数:

  • Duration 动画的持续时间,默认300ms
  • Time interpolation 动画插值,LinearInterpolator 等等,定义动画变化率,可单独展开讲
  • Repeat count 重复动作,无限播放,或者到达时间之后停止,或者反转播放动画
  • Animator set 动画集合
  • Frame refresh delay 动画刷新时间,默认为 10ms, 最终依赖系统状态,不一定为 10ms。

常用方法

addListener(Animator.AnimatorListener listener);      // 添加监听器,可以手动设置监听,手动设置属性
isRunning();     // 当前动画是否在执行
isStarted(); 	 // 动画是否开始
isPaused();      // 暂停状态

setDuration(long duration);    // 设置动画时长,单位为毫秒
setInterpolator(TimeInterpolator value);  // 设置动画插值
setStartDelay(long startDelay);     // start() 方法被调用之后延时
setTarget(Object target);          // 设置动画主体
start();   // 开始动画
cancel();   // 取消动画

static ValueAnimator ofArgb(int... values);   // 颜色动画
static ValueAnimator	ofFloat(float... values)
static ValueAnimator	ofInt(int... values)
static ValueAnimator	ofObject(TypeEvaluator evaluator, Object... values)

setRepeatCount(int value);      // 可选值 INFINITE 无限循环动画
setRepeatMode(int value);     //  RESTART,REVERSE

Object	getAnimatedValue();    // 运动时,当前运动点的值

// 监听动画变化时的实时值 
public static interface AnimatorUpdateListener {  
    void onAnimationUpdate(ValueAnimator animation);  
}
// 添加方法为:public void addUpdateListener(AnimatorUpdateListener listener)  

// 监听动画变化时四个状态
public static interface AnimatorListener {  
    void onAnimationStart(Animator animation);		// 动画开始
    void onAnimationEnd(Animator animation);  		// 动画结束
    void onAnimationCancel(Animator animation);  	// 动画 cancel
    void onAnimationRepeat(Animator animation);  	// 动画重复
}
//添加方法为:public void addListener(AnimatorListener listener)  

removeAllListeners();
removeListener(Animator.AnimatorListener listener);
removePauseListener(Animator.AnimatorPauseListener listener);

默认的 ValueAnimator 使用非线性的插值器 interpolation,加速进入减速退出,可以通过 setInterpolator(TimeInterpolator) 来改变默认的行为。

xml 定义动画

可以类似 View Animation 定义 xml 来创建动画, 动画资源文件需要保存在 res/animator/ 目录下。Animator 动画支持:

  • ValueAnimator <animator>
  • ObjectAnimator <objectAnimator
  • AnimatorSet <set>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueFrom="1"
    android:valueTo="0"
    android:valueType="floatType"
    android:repeatCount="1"
    android:repeatMode="reverse"/>

<!-- 同样可以定义动画集合,顺序执行动画 -->
<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>

定义动画资源之后可以使用如下代码使用动画:

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.animator.property_animator);
set.setTarget(myObject);
set.start();

更多XML定义的语法可以参考官方 文档

ObjectAnimator 动画的执行类,后面详细介绍 ValueAnimator 动画的执行类 AnimatorSet 用于控制一组动画的执行:线性,一起,每个动画的先后执行等。 AnimatorInflater 用户加载属性动画的xml文件 TypeEvaluator 类型估值,主要用于设置动画操作属性的值。 TimeInterpolator 时间插值,有很多子类,可以具体展开再讲,AccelerateDecelerateInterpolator, AccelerateInterpolator, AnticipateInterpolator, AnticipateOvershootInterpolator, BaseInterpolator, BounceInterpolator, CycleInterpolator, DecelerateInterpolator, FastOutLinearInInterpolator, FastOutSlowInInterpolator, Interpolator, LinearInterpolator, LinearOutSlowInInterpolator, OvershootInterpolator, PathInterpolator

ValueAnimator

ValueAnimator animator = ValueAnimator.ofInt(0,1000);  
animator.setDuration(1000);  

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        int curValue = (int)animation.getAnimatedValue();  
        Log.d(TAG,"curValue: "+curValue);  
    }  
});  
animator.start();

同样也可以通过 ofFloat, ofObject 等等静态方法构造不同类型的 ValueAnimator。然后通过监听器动态地更新属性。

ObjectAnimator

ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();

ObjectAnimator 使用范围更广,但是同样也有很多约束条件。

Drawable Animation

可以在 res/drawable/ 目录下定义 XML 文件:

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">
    <item android:drawable="@drawable/rocket_thrust1" android:duration="200" />
    <item android:drawable="@drawable/rocket_thrust2" android:duration="200" />
    <item android:drawable="@drawable/rocket_thrust3" android:duration="200" />
</animation-list>

用如下代码,将 Drawable 附加到 ImageView 中:

AnimationDrawable rocketAnimation;

public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
  rocketImage.setBackgroundResource(R.drawable.rocket_thrust);
  rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
}

public boolean onTouchEvent(MotionEvent event) {
  if (event.getAction() == MotionEvent.ACTION_DOWN) {
    rocketAnimation.start();
    return true;
  }
  return super.onTouchEvent(event);
}

start() 方法不能在 onCreate() 方法中调用, AnimationDrawable 此时还没有附加到window 上,如果需要动画自动播放,可以在 onWindowFocusChanged() 中调用。

更多的 XML 语法可以参考官方文档

区别

  • View Animation 只能作用于 View, 而 Property Animation 因为修改属性,可以作用于任何对象。
  • 命名方式 View Animation 都以 Animation 结束,在 view.animation 包下,而 Property Animator 属性动画都是 Animator 结尾,并在 android.animation 包下。
  • View Animation 的动画实现是通过 Parent View 实现,实际在被 draw 时改变其位置,因此真实的属性并没有改变。而 Property Animator 属性动画正是相反,改变的属性值。

reference


2016-09-04 Android , AndroidDev , Animation

《Effective Java》读书笔记

Java 语言中存在四种类型:

  • 接口 interface
  • 类 class
  • 数组 array
  • 基本类型 primitive type

前三种是引用类型,类实例和数组是对象,基本类型不是对象。

在 Java 中一共有 8 种基本数据类型,其中有 4 种整型,2 种浮点类型,1 种用于表示 Unicode 编码的字符单元的字符类型和 1 种用于表示真值的 boolean 类型。(一个字节等于 8 个 bit)

  1. 整型
类型 存储需求 bit 数 取值范围 备注
byte 1 字节 1*8 -128~127  
short 2 字节 2*8 -32768~32767  
int 4 字节 4*8 -2^31 ~ 2^31-1  
long 8 字节 8*8 -2^63 ~ 2^63-1  

Integer.MAX_VALUE = 2147483647 Integer.MIN_VALUE = -2147483648

Long.MAX_VALUE = 9223372036854775807 Long.MIN_VALUE = -9223372036854775808

  1. 浮点型
类型 存储需求 bit 数 取值范围 备注
float 4 字节 4*8 2^-149 ~ (2-2^-23)·2^127 float 类型的数值有一个后缀 F(例如:3.14F)
double 8 字节 8*8 2^-1074 ~ (2-2^-52)·2^1023 没有后缀 F 的浮点数值(如 3.14) 默认为 double 类型, Double 有静态变量 MIN_VALUEMAX_VALUE

3.char 类型

类型 存储需求 bit 数 取值范围 备注
char 2 字节 2*8 0 ~ 65,535  

4.boolean 类型

类型 存储需求 bit 数 取值范围 备注
boolean 1 字节 1*8 false、true  

补充:Java 有一个能够表示任意精度的算术包,通常称为“大数值”(big number)。虽然被称为大数值,但它并不是一种 Java 类型,而是一个 Java 对象。 如果基本的整数和浮点数精度不能够满足需求,那么可以使用 java.math 包中的两个很有用的类:BigIntegerBigDecimal (Android SDK 中也包含了java.math包以及这两个类)这两个类可以处理包含任意长度数字序列的数值。BigInteger 类实现了任意精度的整数运算,BigDecimal 实现了任意精度的浮点数运算。具体的用法可以参见 Java API。

BigInteger 和 BigDecimal 都是不可变 immutable ,类似于 String, 在使用

BigInteger sum = new BigInteger.valueOf(0);
sum.add(BigInteger.valueOf(10));    // wrong way, sum is still 0
sum = sum.add(BigInteger.valueOf(10));   // right way, add() return a BigInteger Object.

第 1 章:创建和销毁对象

1. 静态工厂方法代替构造器

优点:

  1. 静态工厂方法有名字;
  2. 不必要每次调用的时候都创建一个新的对象。
  3. 返回原返回类型的任何子类型对象。
  4. 创建参数化类型实例的时候使代码变得更加简洁。

2. 遇到多个构造器参数时要考虑使用构造器

Builder 模式的优势:

  • 代码易读,模拟了具名的可选参数
  • build 方法可检验约束条件
  • builder 可以自动填充某些域,比如每次创建对象的时自动增加序列号

何时使用 Builder 模式:

  • 类的构造器或者静态工厂方法中具有多个参数时,设计这种类构造方式时可以考虑 Builder 模式,尤其是当大多数参数都是可选的时候。
  • 当类的参数初始化时有相互关联时使用 Builder 模式。

3. 用私有构造器或者枚举类型强化 Singleton 属性

共有静态成员 final 域

public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() { … }
    public void leaveTheBuilding() {…}
}

静态方法所有调用,都会返回同一个对象引用,不会创建其他 Elvis 实例。

公有的成员是静态工厂方法:

public class Elvis {
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() { … }
    public static Elvis getInstance() { return INSTANCE; }

    public void leaveTheBuilding() {…}
}

4. 通过私有构造器强化不可实例化的能力

希望一个类不能被实例化,则私有化构造方法

5. 避免创建不必要的对象

String s1 = "string";

String s2 = new String("string");  // don‘t do this

当心无意识的基本类型自动装箱。

6. 清除过期的对象引用

Java 内存泄露可能发生在:

  • 类自我管理内存

    比如在实现 Stack 栈时,弹出栈时,消除对象引用,结束变量的生命周期。

  • 缓存
  • 监听器或者其他回调。

7. 避免使用终结方法

提供显式的终止方法,要求类客户端在每个实例不再有用的时候调用这个方法。Java 中 FileInputStream,FileOutputStream,Timer 和 Connection 都具有终结方法。

第 3 章

所有对象都通用的方法,非 final 方法(equals、hashCode、toString、clone 和 finalize)都有明确的通用约定,被设计成被覆盖。

8. 覆盖 equals 时请遵守通用约定

类的每个实例都只与它自身相等:

  • 类的每个实例本质都是 唯一
  • 不关心类是否提供“逻辑相等”的测试功能
  • 超类已经覆盖 equals,从超类继承过来的行为对于子类也合适
  • 类是私有的或者包级私有,可以确定 equals 方法永远不会被调用

什么时候覆盖 Object.equals ?

  • 类具有特定“逻辑相等”概念,不等同于对象等同的概念。
  • 并且超类没有覆盖 equals 实现。

equals 等价关系:自反,对称,传递,一致

里氏替换原则 Liskov substitution principle ,类型的任何重要属性也将适用于它的子类型。

9. 覆盖 equals() 时一定要覆盖 hashCode()

10. 始终覆盖 toString

将类的基本信息放入。IDE 中一般可以自动生成该方法。

11. 谨慎地覆盖 clone

浅拷贝指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。

12. 考虑实现 Comparable 接口

实现对象实例之间的比较。

第 4 章

13. 类和成员的可访问性最小化

encapsulation 封装,模块化

原因:有效地解除组成系统各个模块之间的耦合关系,使得模块之间可以独立开发、测试、优化、使用、理解和修改。

  • 尽可能使每个类或者成员不被外界访问
  • 实例域决不能是拥有的
  • 类具有公有的静态 final 数组域,或者返回这种域的访问方法,总是错误的,客户端能够轻易的修改数组中的内容

类成员的公开级别:

  • 私有 private,只有声明该类的内部才能访问
  • 包级私有 package-private 缺省访问级别,声明该成员的包内部任何类都能访问
  • 受保护 protected,声明该类的子类可以访问,包内部其他类可以访问
  • 公有 public,任何地方都可以访问,如果做成公有接口,则需要考虑并有责任永远支持该方法,以保持兼容性

公有的静态 final 域来暴露常量,惯例,使用 大写字母 加 下划线,这些域要么包含基本类型,要么包含指向不可变对象的引用。

14. 公有类中使用访问方法而非公有域

未来改变类的内部表示时非常有效,如果类是包级私有,或者私有嵌套类,直接暴露数据域并没有本质的错误。

15. 使可变性最小化

不可变类 实例不能被修改,实例包含的信息必须在创建时提供,并且在对象生命周期内固定不变。 String 基本类型包装类, BigInteger 和 BigDecimal 是不可变类。

原因:不可变类易于设计、实现和使用,不容易出错,更加安全

类不可变类要遵循一下规则:

  • 不要提供任何会修改对象状态的方法
  • 保证类不会被扩展, 将类定义成 final 类
  • 使所有域都是 final
  • 使所有域都是私有的
  • 确保任何可变组件互斥访问,

优点:1. 不可变对象本质上是线程安全的,他们不要求同步。2. 不可变对象可以被自由地共享,将频繁使用的值提供公有的静态 final 常量,将频繁被请求的实例缓存起来,当现有实例可以符合请求时,不用创建新的实例。基本类型的包装类和 BigInteger 都有这样的静态工厂。3. 不可变对象为其他对象提供了大量的构建。

缺点:不可变类唯一的缺点就是,对于每个不同的值都需要一个单独的对象。

16. 复合优先于继承

不扩展现有的类,而是在新的类中增加一个私有域引用一个类的实例,这种设计叫做“复合” Composition。

优点:与方法调用不用,继承打破了封装性,子类依赖于父类中特定功能的实现细节,因此复合被提出,不扩展现有的类,而是在新的类中增加一个私有域来组成新的类。

缺点:包装类没什么缺点,但需要注意,包装类不适合用在回调框架 callback framework。

17. 为继承而设计,提供文档说明,否则就禁止继承

类必须有文档说明它可覆盖的方法的自用性。在发布类之前,编写子类进行测试。

为了允许继承,类需要遵守的约束条件:

  • 构造函数绝不能调用可被覆盖的方法
  • clone 和 readObject 都不可以调用可覆盖的方法,不管是以直接还是间接的方式。
  • 如果决定在一个为了继承而设计的类中实现 Serializable ,并且该类有 readResolve 或者 writeReplace 方法,则必须使 readResolve 或者 writeReplace 成为受保护的方法,而不是私有方法。

两种方法禁止子类化:

  • 声明类 final class
  • 所有构造函数变成私有,或者包级私有

18. 接口优于抽象类

Java 提供两种机制,来允许多实现,接口与抽象类。重要区别在于:

  • 抽象类 允许包含某些方法的实现 ,接口则不允许
  • 为了实现抽象类定义的类型,类必须成为抽象类的子类,Java 只允许单继承,抽象类受到一些限制

优点:

  • 现有的类可以很容易被更新,实现新的接口
  • 接口是定义 mixin (混合类型)的理想选择
  • 接口允许我们构造非层次结构的类型框架

19. 接口只用于定义类型

常量接口 constant interface ,不包含任何方法,只包含静态 final 域,每个域都是一个常量。反面,不值得使用。

导出常量可选合理方案,将常量添加到 相关类 或者接口中,尽量使用枚举类型 enum type,否则应该使用不可实例化的工具类 utility class 来导出常量。

接口只应该被用来定义类型,不应该被用来导出常量。

20. 类层次优于标签类

标签类指在类中用标签区别类的行为模式,比如使用枚举变量,更加枚举不同显示不同内容。尽量不使用标签类,使用子类抽象,将标签类中每个方法都定义成一个包含抽象方法的抽象类。

21. 用函数对象表示策略

允许程序把“调用特殊函数的能力”存储起来并且传递这种能力,这种机制允许函数调用者通过传递第二个函数,来指定行为。

执行对象上的某项操作,能够实现函数指针。

Comparator

22. 优先考虑静态成员类

嵌套类 nested class 指被定义在另一个类内部的类。

嵌套类有四种:

  • 静态成员类 static member class
  • 非静态成员类 nonstatic member class
  • 匿名类 anonymous class
  • 局部类 local class

除去第一种之外,其他三类都被称为内部类 inner class。

静态成员类,最简单的嵌套类,普通类,静态成员类可以访问外围类的所有成员,包括私有。访问性原则同类中其他成员。

静态成员类,常见用法作为公有辅助类,与外部类一起使用时才有意义。

非静态成员类,语法层面只有一个 static 修饰符的不同,但是两者区别很大。非静态成员类的每一个实例都隐含着与外围类的一个外围实例 enclosing instance。嵌套的实例需要外围类的实例。

非静态成员类的一种常见用法是定义 adapter,它允许外部类的实例被看做是另一个不相关的类的实例。

私有静态成员类 常见用法是代表外围类所代表的对象的组件。

匿名类没有名字,在使用的同时被声明和实例化。匿名类可以出现在代码中任何允许存在表达式的地方。

匿名类的一种常见用法是动态地创建函数对象 function object。 另一种常见用法是创建过程对象 process object ,比如 Runnable , Thread 或者 TimeTask 实例。第三种常见用法是在静态工厂方法的内部。

局部类是嵌套类中用得最少的类。任何可以声明局部变量的地方都可以声明局部类。

成员类的每个实例都需要一个指向外围实例的引用,就要把成员类做成非静态的,否则做成静态的。

嵌套类属于方法内部,只需要一个地方创建实例,匿名类,否则就是局部类

23. 不在新代码中使用原生态类型

尽早发现错误,最好是编译时就发现。 在 Java 1.5 之前,运行时,集合,才会发现错误。

24. 消除非受检警告

非受检强制转化警告 unchecked cast warnings

非受检方法调用警告

非受检普通数组创建警告

非受检转换警告 unchecked conversation warnings

尽可能小的使用 SuppressWarnings(“unchecked”) 注解

25. 列表优先 于数组

数组是协变 covariant 的,泛型是不可变的 invariant。

数组在运行时才知道并检查元素类型。泛型在编译时就能检查类型。[[Java 泛型的协变与逆变]]

26. 优先考虑泛型

27. 优先考虑泛型方法

编写泛型方法和编写泛型类相似

泛型方法的特性是,无需明确指定类型参数的值,编译器通过检查方法参数的类型来计算类型参数的值。类型推导。

28. 利用有限制通配符来提升 API 的灵活性

参数化类型是不可变的

Interface Iterable<T> 接口,实现这个接口可以让对象使用 foreach 循环,或者使用

// If you have Iterable<String> , you can do:
for (String str : myIterable) {
    ...
}

大部分的容器类都实现了这个接口。

http://docs.oracle.com/javase/6/docs/api/java/lang/Iterable.html

Java 提供了特殊的参数化类型,有限制的通配符类型 bounded wildcard type

29. 优先考虑类型安全的异构容器

第 5 章

第六章

枚举和注解, Java 1.5 版本中增加两个引用类型家族:1. 新的类枚举类型(enum type) 2. 新的接口注解类型(annotation type)。

30. 用枚举类型 enum 代替 int 常量

枚举类型是指由一组固定常量组成合法值的类型。

优点:

  • 采用 int 枚举模式,程序非常脆弱
  • 将 int 枚举打印成字符串没有便利方法
  • 使用 String 常量无效

31. 用实例域代替序数

所有枚举都有 ordinal 方法,返回枚举常量在类型中的位置,不要使用该方法。永远不要根据枚举的序数导出与它关联的值,而是应该将它保存到实例域中。

32. 用 EnumSet 代替位域

33. 用 EnumMap 代替序数索引

34. 用接口模拟可伸缩的枚举

35. 注解优先于命名模式

不必定义注解类型,但是都应该使用 java 平台所提供的预定义注解类型。

36. 坚持使用 Override 注解

37. 用标记接口定义类型

标记接口 marker interface 没有包含任何方法声明的接口,指明一个类实现的某种属性的接口。

第 7 章

设计方法的参数,返回值

38. 检查参数的有效性

39. 必要时进行保护性拷贝

在传入可变参数时需要特别注意,外部可能会通过改变传入参数而间接的影响到类内部的实现。因此需要在初始化的时候进行保护性拷贝。

40. 谨慎设计方法签名

  • 谨慎地选择方法名称,遵循命名习惯

  • 不过于追求提供便利的方法

  • 避免过长的参数列表

有三种方法可以缩短长参数列表

  • 将方法拆解成多个方法

  • 创建辅助类 helper class , 保存参数分组,一般作为静态成员类

  • 从对象构建到方法调用都采用 Builder 模式,多次 setter

对于参数类型,要优先使用接口而非类, Map 接口作为参数,可以传入 Hashtable, HashMap,TreeMap,TreeMap 子映射表 submap 等等

对于 boolean 参数,优先使用两个元素的枚举类型

41. 慎用重载

永远不要导出两个具有相同参数数目的重载方法。

42. 慎用可变参数

Java 1.5 版本中增加了 可变参数 varargs 方法,一般称为 variable arity method 。可匹配不同长度的变量的方法。

可变参数方法接受 0 个或者多个指定类型的参数,先创建一个数组,数组大小为在调用位置所传递的参数数量,然后将参数值传到数组中,最后将数组传递给方法。

43. 返回零长度的数组或者集合,而不是 null

44. 为所有导出的 API 元素编写文档注释

方法的文档应该简洁地描述出它和客户端之间的约定。

第八章

45. 将局部变量的作用域最小化

几乎每一个局部变量的声明都应该包含一个初始化表达式。

46. for-each 循环优于传统 for 循环

有三种情况无法使用 for-each 循环

  • 过滤 —- 需要遍历集合,使用 remove 方法
  • 转换 —- 需要低缓其中部分或者全部数据
  • 平行迭代 —- 并行遍历多个集合

47. 了解使用类库

使用标准类库,可以充分利用这些编写标准类库的专家知识,以及在你之前其他人的使用经验。

将时间花在应用程序上,而不是底层细节。

都应该熟悉

  • java.lang
  • java.util
  • java.io 中的内容。
  • java.util.concurrent 并发工具

48. 需要精确的答案,避免使用 float 和 double

使用 BigDecimal 做精确计算。

使用 int 或者 long 数值范围没有超过 9 位十进制数,可以使用 int,没有超过 18 位,可用 long ,超过 18 位,就必须使用 BigDecimal.

49. 基本类型优先于装箱基本类型

基本类型 primitive int double boolean

对应的引用类型 reference type ,称为装箱基本类型 boxed primitive ,对应为 Integer、Double、Boolean

何时使用装箱基本类型:

  • 作为集合中的元素、键、值

50. 如果其他类型更适合,则尽量避免使用字符串

  • 字符串不适合代替其他的值类型
  • 字符串不适合代替枚举类型
  • 字符串不适合代替聚集类型,如果需要用 String 来描述实体,通常不建议这样做,通常应该用一个私有静态成员类来描述。
  • 字符串不适合代替能力表 capabilities

如果可以使用更加合适的数据类型,或者可以编写更加合适的数据类型,就应该避免使用字符串来表示对象。

51. 小心字符串连接的性能

字符串连接操作符 “+”

获得可接受的性能,使用 StringBuilder 代替 String append 方法

52. 通过接口引用对象

使用接口作为类型,程序会更加灵活

53. 接口优先于反射机制

核心反射机制 core reflection facility java.lang.reflect 通过程序来访问关于已装载的类信息的能力。给定 Class 实例,可以获得 Constructor Method 和 Field 实例。

丧失了编译时类型检查的好处

执行反射访问所需要的代码笨拙冗长

性能损失

反射功能只是在设计时被用到,通常普通应用程序在运行时不应该以反射方式访问对象。

54. 谨慎使用本地方法

JNI 方法调用本地 Native method

55. 谨慎地进行优化

  • 规则 1:不要进行优化
  • 规则 2:(仅针对专家)还是不要进行优化,再没有绝对清晰的未优化方案之前不要进行优化

不要试图去编写快速的程序 —- 应该努力编写好的程序,速度随之而来。再设计系统时,设计 API、线路层协议和永久数据格式时候,一定要考虑性能因素。

56. 遵守普遍接受的命名惯例

包名,层次,句号分割,小写字母和数字(少使用) 不以 java 和 javax 开头

包名其余部分通常不超过 8 个字符

第九章 异常

57. 只针对异常情况才使用异常

58. 对可恢复的情况使用受检异常,对编程错误使用运行异常

Java 提供三种可抛出结构:

  • 受检的异常 checked exception

  • 运行时异常 run-time exception

  • 错误 error

期望调用者能够适当地恢复,使用受检异常。

用运行时异常来表明编程错误。

runtimeException 子类

1、 java.lang.ArrayIndexOutOfBoundsException 数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。 2、java.lang.ArithmeticException 算术条件异常。譬如:整数除零等。 3、java.lang.NullPointerException 空指针异常。当应用试图在要求使用对象的地方使用了 null 时,抛出该异常。譬如:调用 null 对象的实例方法、访问 null 对象的属性、计算 null 对象的长度、使用 throw 语句抛出 null 等等 4、java.lang.ClassNotFoundException 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历 CLASSPAH 之后找不到对应名称的 class 文件时,抛出该异常。

5、java.lang.NegativeArraySizeException 数组长度为负异常 6、java.lang.ArrayStoreException 数组中包含不兼容的值抛出的异常 7、java.lang.SecurityException 安全性异常 8、java.lang.IllegalArgumentException 非法参数异常

2.IOException IOException:操作输入流和输出流时可能出现的异常。 EOFException 文件已结束异常 FileNotFoundException 文件未找到异常

  1. 其他 ClassCastException 类型转换异常类 ArrayStoreException 数组中包含不兼容的值抛出的异常 SQLException 操作数据库异常类 NoSuchFieldException 字段未找到异常 NoSuchMethodException 方法未找到抛出的异常 NumberFormatException 字符串转换为数字抛出的异常 StringIndexOutOfBoundsException 字符串索引超出范围抛出的异常 IllegalAccessException 不允许访问某类异常 InstantiationException 当应用程序试图使用 Class 类中的 newInstance() 方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常

59. 避免不必要地使用受检的异常

60. 优先使用标准异常

61. 抛出与抽象相对应的异常

62. 每个方法抛出的异常都要有文档

63. 在细节消息中包含能捕获失败的信息

64. 努力使失败保持原子性

65. 不要忽略异常

第十章

并发

66. 同步访问共享的可变数据

synchronized 保证同一个时刻只有一个线程执行某一段代码块

什么时候使用锁:

  • 当多个线程共享可变数据时,每个读或者写数据的线程都必须执行同步。

67. 避免过度同步

68. executor 和 task 优先于线程

69. 并发工具优先于 wait 和 notify

70. 线程安全性的文档化

文档说明类是否可以被多个线程安全使用

  • 不可变 immutable 类实例不可变,不需要外部同步,String Long BigInteger

  • 无条件的线程安全 unconditionally thread-safe 实例可变,但是有足够的内部同步 ,实例可被并发使用,无需外部同步, Random ConcurrentHashMap

  • 有条件的线程安全 conditionally thread-safe Collections.synchronized

  • 非线程安全 not thread-safe 类实例可变,需要外部同步 ArrayList HashMap

  • 线程对立 thread-hostile 类不能安全地被多个线程并发使用

71. 慎用延迟初始化

72. 不要依赖于线程调度器

依赖于线程调度器的程序,很有可能都是不可移植的。

73. 避免使用线程组

第 11 章

序列化

将对象编码成字节流,对象序列化

74. 谨慎地实现 Serializable 接口

序列化成功需要:

  • 实现 java.io.Serializable
  • 类的属性必须是可序列化的

Externalizable 接口继承了 java 的序列化接口,并增加了

void writeExternal(ObjectOutput out) throws IOException;
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;

为继承而设计的类,尽量避免去实现 Serializable 接口。

内部类不应该实现 Serializable, 静态成员类可以

75. 考虑使用自定义的序列化形式

76. 保护性编写 readObject 方法

77. 对于实例控制,枚举类型优先于 readResolve

78. 考虑用序列化代理代替序列化实例


2016-09-02 android , java , design-pattern

画图工具收集

画图工具整理,平时少许的画图需求,所以找了这些网站,Draw.io 还是不错的,简单的流程图都能搞定。

所见即所得

Draw.io

国外的免费画图网站,支持 Google Drive,Dropbox 等等

gliffy

所见即所得,有 Chrome 扩展,可离线,和 Google Drive 有结合

ProcessOn

国内的画图网站,可以实时写作

Visio

微软出的绘图软件,但是 Windows only

编程类

Matplotlib

Matplotlib 是 Python 下绘图的首选,可以用来绘制高质量的图像。

ggplot2

R 下绘图标配

MATLAB

MATLAB 是一个优秀的商业软件,它的绘图能力同样强大。

D3

D3 是一个前端的绘图框架。有些情况下,网页是很好的图像呈现方式。因为可以开发一些可互动的可视化效果。

tikz

tikz 是 LaTex 下非常强大的画图宏包。不过感觉只适合极客 / 科研党,因为本身用 LaTex 写作的人就少。

SVG

SVG 意为可缩放矢量图形(Scalable Vector Graphics)。SVG 使用 XML 格式定义图像。

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<rect width="300" height="100"
style="fill:rgb(0,0,255);stroke-width:1;
stroke:rgb(0,0,0)"/>
</svg>

2016-08-28 draw , grach , collection

Grub2 bootloader 主题

之前把 Linux Mint Grub2 bootloader 的背景图片更换了一下,直接用的 gnome-lookAnonymous Hope 主题 ,这里就记录一下,以便快速恢复。

Gnome-look 这个网站上有非常多人分享的内容,可以到这个网站自行选择自己喜欢的内容。

Usage

一般 grub 的主题需要在 /boot/grub/themes 目录下。如果没有这个目录需要手动创建

sudo mkdir -p /boot/grub/themes

然后将下载的文件内容拷贝到该目录下。完成拷贝之后需要修改一下 /etc/default/grub 配置文件,增加如下配置:

GRUB_THEME="/boot/grub/themes/Anonymous-Hope/theme.txt"

然后更新 grub.cfg

sudo update-grub

如果一切都按照步骤,应该可以看到日志显示

Generating grub configuration file ...
Found theme: /boot/grub/themes/Anonymous-Hope/theme.txt

然后一切 OK。

reference


2016-08-26 grub2 , bootloader , theme , grub-theme

让网站在分享时预览更美观

在最近用 Telegram 分享网页的时候,Telegram 会预先抓取网页形成一个缩略预览,但是如果是自己的网站没有适配一些 meta 信息,抓取出来的信息就非常不堪入目。所以优化一下网站在分享的时候的预览图,这个术语叫做 Rich Previews,想查看网站是否已经优化好,可以查看下面的网站:

想要好看的预览需要如下的标签

  • Title 最长 65 个字符,否则 Google 结果就会被截断
  • Meta description 最好小于 155 字符
  • og:title 最好不超过 35 个字符,否则预览会截断
  • og:description 65 字符
  • og:url 页面 url
  • og: image JPG 或者 PNG,最小分辨率 300 × 200 像素
  • favicon 网站 logo, 32 × 32 像素

上面的网站目前支持很多聊天工具,比如 WhatsApp, Telegram, Skype ,还有社交网站 Facebook,Twitter 等等。

举例

<title>Rich Link Preview</title>
<meta name="description" content="Also want these pretty website previews?" />
<meta property="og:title" content="Rich Link Preview" />
<meta property="og:description" content="Also want these pretty website previews?" />
<meta property="og:url" content="http://richpreview.com/" />
<meta property="og:image" content="http://richpreview.com/richpreview.jpg" />
<link rel="shortcut icon" href="http://richpreview.com/favicon.ico" type="image/x-icon" />

这些标签的定义都在 http://ogp.me/ 这个网站上,只要遵循这个 The Open Graph protocol 协议基本上能搞定大部分的网站。

reference


2016-08-25 website , html , meta , share , preview

Python 经典项目整理:阅读好的代码

阅读经典的项目是学习的起点。

  • Howdoi Howdoi 是代码搜寻工具,使用 Python 编写。
  • Flask Flask 是基于 Werkzeug 和 Jinja2,使用 Python 的微框架。它能够快速启动,并且开发意图良好。
  • Diamond Diamond 是 python 的守护进程,它收集指标,并且将他们发布至 Graphite 或其它后端。 它能够收集 cpu, 内存,网络,i/o,负载和硬盘指标。除此,它拥有实现自定义收集器的 API,该 API 几乎能 从任何资源中获取指标。
  • Werkzeug Werkzeug 起初只是一个 WSGI 应用多种工具的集成,现在它已经变成非常重要的 WSGI 实用模型。 它包括强大的调试器,功能齐全的请求和响应对象,处理 entity tags 的 HTTP 工具,缓存控制标头,HTTP 数据,cookie 处理,文件上传,强大的 URL 路由系统和一些社区提供的插件模块。
  • Requests Requests 是 Apache2 许可的 HTTP 库,使用 Python 编写。
  • Tablib Tablib 是无格式的表格数据集库,使用 Python 编写。

reference


2016-08-16 python , project , structure , reading , flask

Python 项目的结构

之前和朋友聊天,说到 Python 项目的结构问题,Python 确实是一门比较灵活的语言,你可以单独执行某个文件,把这些文件扔到一起看起来像个工程,但是外人来看其实就是一个个独立的小文件,小模块,对于小型项目来说可能并不会造成什么问题,但是对于大型项目,尤其是如果需要多人合作,有一个关于工程结构的基本认识是非常必要的。

Python 没有强制的结构规范,但是有一个大家的共识

README.rst
LICENSE
setup.py
requirements.txt
sample/__init__.py
sample/core.py
sample/helpers.py
docs/conf.py
docs/index.rst
tests/test_basic.py
tests/test_advanced.py

项目的结构是项目的门面,简单易懂的项目结构能够让介入的第三人快速的熟悉项目,Python 项目结构没有太多的约束和限制,Python 提供的导入和模块管理使得结构化 Python 相对简单,但也要注意一些比如循环依赖的问题。

模块

为了解决项目结构的问题就不得不提到模块(module),这是 Python 最主要的抽象层,模块允许将代码分为不同的部分,每个部分包含相关的数据和功能。

模块名尽量要短,使用小写,并且避免使用特殊符号,比如(.)或者 (?) 等。不推荐在模块名中使用下划线。

# ok
import library.plugin.foo
# not ok
import library.foo_plugin

import 的工作原理,比如 import modu 将会寻找合适的文件,调用目录下的 modu.py 文件,如果没有找到,Python 解释器会递归地在 PYTHONPATH 环境变量中查找该文件,如果没有则抛出 ImportError 异常。一旦找到 modu.py Python 解释器将在隔离的作用域内执行模块。所有顶层语句都会被执行,包括其他的引用。方法和类的定义会存储到模块字典中。这个模块的变量、方法和类通过命名空间暴露给调用方。

import 语句也可以为 from modu import * ,使用 from modu import func 能够精确定位想要导入的方法,并将其放入到全局命名空间。

# 较差的写法
[...]
from modu import *
[...]
x = sqrt(4)  # sqrt 是模块 modu 的一部分么?或是内建函数么?上文定义了么?

# 稍好
from modu import sqrt
[...]
x = sqrt(4)  # 如果在 import 语句与这条语句之间,sqrt 没有被重复定义,它也许是模块 modu 的一部分。

# 最好的做法
import modu
[...]
x = modu.sqrt(4)  # sqrt 显然是属于模块 modu 的。

Python 提供非常简单的包管理系统,简单地将模块管理机制扩展到一个目录上,就是包。任意包含 __init__.py 文件的目录都被认为是一个 Python 包。如果包内的模块和子包没有代码共享的需求,使用空白 __init__.py 是正常的做法。

导入深层嵌套包可以使用 import very.very.deep.module as mod 可以简化导入冗长子包。

对象

使用无状态的函数是一种更好的编程范式。

把有隐式上下文和副作用的函数与仅包含逻辑的函数(纯函数)谨慎地区分开来,会带来 以下好处:

  • 纯函数的结果是确定的:给定一个输入,输出总是固定相同。
  • 当需要重构或优化时,纯函数更易于更改或替换。
  • 纯函数更容易做单元测试:很少需要复杂的上下文配置和之后的数据清除工作。
  • 纯函数更容易操作、修饰和分发。

装饰器

装饰器本质上是一个 Python 函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

def use_logging(func):

    def wrapper():
        logging.warn("%s is running" % func.__name__)
        return func()   # 把 foo 当做参数传递进来时,执行 func() 就相当于执行 foo()
    return wrapper

def foo():
    print('i am foo')

foo = use_logging(foo)  # 因为装饰器 use_logging(foo) 返回的时函数对象 wrapper,这条语句相当于  foo = wrapper
foo()                   # 执行 foo() 就相当于执行 wrapper()

使用 @ 符号,装饰器的语法糖

def use_logging(func):

    def wrapper():
        logging.warn("%s is running" % func.__name__)
        return func()
    return wrapper

@use_logging
def foo():
    print("i am foo")

foo()

这样就可以省略掉 foo = use_logging(foo) 这句赋值。

带参数的装饰器

def use_logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "warn":
                logging.warn("%s is running" % func.__name__)
            elif level == "info":
                logging.info("%s is running" % func.__name__)
            return func(*args)
        return wrapper

    return decorator

@use_logging(level="warn")
def foo(name='foo'):
    print("i am %s" % name)

foo()

类装饰器

class Foo(object):
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print ('class decorator runing')
        self._func()
        print ('class decorator ending')

@Foo
def bar():
    print ('bar')

bar()

通过 functools.wrap 来找回原函数的元信息

from functools import wraps
def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print func.__name__      # 输出 'f'
        print func.__doc__       # 输出 'does some math'
        return func(*args, **kwargs)
    return with_logging

@logged
def f(x):
   """does some math"""
   return x + x * x

Python 类相关的内置装饰器

@staticmathod、@classmethod、@property

上下文管理器

上下文管理器是一个 Python 对象,为操作提供了额外的上下文信息。with 语句初始化上下文

with open('file.txt') as f:
    contents = f.read()

实现这个功能有两种简单的方法:使用类或使用生成器。 让我们自己实现上面的功能,以使用类方式开始:

class CustomOpen(object):
    def __init__(self, filename):
        self.file = open(filename)

    def __enter__(self):
        return self.file

    def __exit__(self, ctx_type, ctx_value, ctx_traceback):
        self.file.close()

with CustomOpen('file') as f:
    contents = f.read()

生成器使用 Python 自带的 contextlib

from contextlib import contextmanager

@contextmanager
def custom_open(filename):
    f = open(filename)
    try:
        yield f
    finally:
        f.close()

with custom_open('file') as f:
    contents = f.read()

由于这两种方法都是一样的,所以我们应该遵循 Python 之禅来决定何时使用哪种。 如果封装的逻辑量很大,则类的方法可能会更好。 而对于处理简单操作的情况,函数方法可能会更好。

reference


2016-08-13 python , project , structure , management

电子书

本站提供服务

最近文章

  • AI Shell 让 AI 在命令行下提供 Shell 命令 AI Shell 是一款在命令行下的 AI 自动补全工具,当你想要实现一个功能,敲一大段命令又记不住的时候,使用自然语言让 AI 给你生成一个可执行的命令,然后确认之后执行。
  • 最棒的 Navidrome 音乐客户端 Sonixd(Feishin) Sonixd 是一款跨平台的音乐播放器,可以使用 [[Subsonic API]],兼容 Jellyfin,[[Navidrome]],Airsonic,Airsonic-Advanced,Gonic,Astiga 等等服务端。 Sonixd 是一款跨平台的音乐播放器,可以使用 [[Subsonic API]],兼容 Jellyfin,[[Navidrome]],Airsonic,Airsonic-Advanced,Gonic,Astiga 等等服务端。
  • 中心化加密货币交易所 Gate 注册以及认证 Gate.io 是一个中心化的加密货币交易所。Gate 中文通常被称为「芝麻开门」,Gate 创立于 2013 年,前身是比特儿,是一家致力于安全、稳定的数字货币交易所,支持超过 1600 种数字货币的交易,提供超过 2700 个交易对。
  • 不重启的情况下重新加载 rTorrent 配置文件 因为我在 Screen 下使用 rTorrent,最近经常调试修改 rtorrent.rc 配置文件,所以想要找一个方法可以在不重启 rTorrent 的情况重新加载配置文件,网上调查了一下之后发现原来挺简单的。
  • Go 语言编写的网络穿透工具 chisel chisel 是一个在 HTTP 协议上的 TCP/UDP 隧道,使用 Go 语言编写,10.9 K 星星。