Autoprefixer: A Postprocessor for Dealing with Vendor Prefixes in the Best Possible Way

Autoprefixer parses CSS files and adds vendor prefixes to CSS rules using theCan I Use database to determine which prefixes are needed.

All you have to do is add it to your asset building tool (Grunt, for instance) and you can totally forget about CSS vendor prefixes. Just write regular CSS according to the latest W3C specifications without any prefixes. Like this:

a {
  transition: transform 1s
}

Autoprefixer uses a database with current browser popularity and properties support to apply prefixes for you:

a {
  -webkit-transition: -webkit-transform 1s;
  transition: -ms-transform 1s;
  transition: transform 1s
}

Autoprefixer logo by Anton Lovchikov

The Problem

We can, of course, write vendor CSS prefixes by hand, but it can be tedious and error-prone.

We can use services like Prefixr and text editor plugins, but it is still exhausting to work with big blocks of repeating code.

We can use mixin libraries with preproccesors like Compass for Sass or nib for Stylus. They solve a lot of problems, but create other problems instead. They force us to use a new syntax. They iterate much slower than modern browsers do, so a stable release can have a lot of unnecessary prefixes, and sometimes we need to create our own mixins.

And Compass does not really hide prefixes from you since you still need to decide on a lot of questions, for example: Do I need to write a mixin forborder-radius? Do I need to split arguments for +transition by comma?

Lea Verou’s -prefix-free came closest to solving this problem, but using client side libraries is not such a good idea when you take end-user perfomance into account. To avoid doing the same job again and again, it is better to build CSS once: during asset building or project deployment.

Under the Hood

Instead of being a preprocessor – such as Sass and Stylus – Autoprefixer is a postprocessor. It doesn’t use any specific syntax and works with common CSS. Autoprefixer can be easily integrated with Sass and Stylus, since it runs after CSS is already compiled.

Autoprefixer is based on Rework, a framework for writing your own CSS postproccesors. Rework parses CSS to useful JavaScript structure and exports it back to CSS after your manipulations.

Each version of Autoprefixer contains a copy of latest Can I Use data:

  • List of current browsers and their popularity.
  • List of prefixes required for new CSS properties, values and selectors.

By default, Autoprefixer will support 2 latest versions of major browsers, much like Google does. But you can choose, what browsers are supported in your project, by name (like “ff 21″) or by pattern:

  • Last 2 version of each major browsers using “last 2 versions”.
  • With more that 1 % of global usage statistics using “> 1%”.
  • Only newer versions by “ff > 20″ or “ff >= 20″.

Then Autoprefixer calculates which prefixes are required and which are outdated.

When Autoprefixer adds prefixes to your CSS, it doesn’t forget about fixing syntax differences. This way, CSS is produced according to the latest W3C specs:

a {
  background: linear-gradient(to top, black, white);
  display: flex
}
::placeholder {
  color: #ccc
}

compiles to:

a {
  background: -webkit-linear-gradient(bottom, black, white);
  background: linear-gradient(to top, black, white);
  display: -webkit-box;
  display: -webkit-flex;
  display: -moz-box;
  display: -ms-flexbox;
  display: flex
}
:-ms-input-placeholder {
  color: #ccc
}
::-moz-placeholder {
  color: #ccc
}
::-webkit-input-placeholder {
  color: #ccc
}
::placeholder {
  color: #ccc
}

Autoprefixer cleans outdated prefixes as well (from legacy code or CSS libraries like Bootstrap), so the following code:

a {
  -webkit-border-radius: 5px;
  border-radius: 5px
}

compiles to:

a {
  border-radius: 5px
}

So after Autoprefixer, CSS will contain only actual vendor prefixes. AfterFotorama switched from Compass to Autoprefixer, the CSS file size decreasedby almost 20%.

Demo

If you still don’t use any kind of tool to automate the building of your assets, be sure to check out Grunt. I highly recommend to start using build tools. This can open you a whole new world of “sugar” syntaxes, time-saving mixin libraries and useful image processing tools. All of developers’ productivity methods to save a lot of nerves and time (the freedom to choose languages, code re-use, the ability to use third-party libraries) are available now for front-end programmers.

Let’s create a project directory and write simple CSS in style.css:

a { }

For this example, we will use Grunt. First, we will need to install grunt-autoprefixer using npm:

npm install grunt-cli grunt-contrib-watch grunt-autoprefixer

Then we should create Gruntfile.js and enable Autoprefixer:

module.exports = function (grunt) {
    grunt.initConfig({
        autoprefixer: {
            dist: {
                files: {
                    'build/style.css': 'style.css'
                }
            }
        },
        watch: {
            styles: {
                files: ['style.css'],
                tasks: ['autoprefixer']
            }
        }
    });
    grunt.loadNpmTasks('grunt-autoprefixer');
    grunt.loadNpmTasks('grunt-contrib-watch');
};

This config enables the compilation of style.css to build/style.cssusing Autoprefixer. Also we will use grunt-contrib-watch to recompilebuild/style.css every time <code”>style.css changes.

Let’s start Grunt’s Watch:

./node_modules/.bin/grunt watch

Now, we’ll add a CSS3 expression to style.css and save the file:

a {
  width: calc(50% - 2em)
}

The magic has just happened and now we have a build/style.css file. Grunt detected the change in style.css and launched the Autoprefixer task. Autoprefixer did find the calc() value unit, that needs a vendor prefix for Safari 6.

a {
  width: -webkit-calc(50% - 2em);
  width: calc(50% - 2em)
}

Now we’ll add a little bit more complicated CSS3 to style.css and save the file:

a {
  width: calc(50% - 2em);
  transition: transform 1s
}

Autoprefixer already knows that Chrome, Safari 6 and Opera 15 need prefixes for transition and transform. But IE 9 also needs a prefix fortransform, which we used as value in transition.

a {
  width: -webkit-calc(1% + 1em);
  width: calc(1% + 1em);
  -webkit-transition: -webkit-transform 1s;
  transition: -ms-transform 1s;
  transition: transform 1s
 }

Autoprefixer is designed to perform all the dirty work for you. It will check the Can I Use database, write all the prefixes needed and it does understand the difference between specifications as well. Welcome to the future of CSS3 — no more vendor prefixes!

发表在 Linux | 留下评论

微信商户页面mac下无法登陆

用Safari登陆

发表在 Linux | 留下评论

Sublime Text 多行编辑快捷键

鼠标选中多行,按下 Ctrl Shift L (Command Shift L) 即可同时编辑这些行;
鼠标选中文本,反复按 CTRL D (Command D) 即可继续向下同时选中下一个相同的文本进行同时编辑;
鼠标选中文本,按下 Alt F3 (Win) 或 Ctrl Command G(Mac) 即可一次性选择全部的相同文本进行同时编辑;
Shift 鼠标右键 (Win) 或 Option 鼠标左键 (Mac) 或使用鼠标中键可以用鼠标进行竖向多行选择;
Ctrl 鼠标左键(Win) 或 Command 鼠标左键(Mac) 可以手动选择同时要编辑的多处文本

发表在 前端技术 | 留下评论

sublime text php 代码格式化插件 phpfmt

https://packagecontrol.io/packages/phpfmt

发表在 php | 留下评论

iPhone屏幕尺寸、分辨率及适配

1.iPhone尺寸规格

设备

iPhone

Width

Height

对角线

Diagonal

逻辑分辨率(point)

Scale Factor

设备分辨率(pixel)

PPI

3GS

2.4 inches (62.1 mm)

4.5 inches (115.5 mm)

3.5-inch

320×480

@1x

320×480

163

4(s)

2.31 inches (58.6 mm)

4.5 inches (115.2 mm)

3.5-inch

320×480

@2x

640×960

326

5c

2.33 inches (59.2 mm)

4.90 inches (124.4 mm)

4-inch

320×568

@2x

640×1136

326

5(s)

2.31 inches (58.6 mm)

4.87 inches (123.8 mm)

4-inch

320×568

@2x

640×1136

326

6

2.64 inches (67.0 mm)

5.44 inches (138.1 mm)

4.7-inch

375×667

@2x

750×1334

326

6+

3.06 inches (77.8 mm)

6.22 inches (158.1 mm)

5.5-inch

414×736

@3x

(1242×2208->)

1080×1920

401

2.单位inch(英吋

    1 inch = 2.54cm = 25.4mm

3.iPhone手机宽高

    上表中的宽高(width/height)为手机的物理尺寸,包括显示屏和边框。

    以下为iPhone4s的宽高示意图:

4.屏幕尺寸

    我们通常所说的iPhone5屏幕尺寸为4英寸、iPhone6屏幕尺寸为4.7英寸,指的是显示屏对角线的长度(diagonal)。

    以下为iPhone5~6+的屏幕尺寸规格示意图:

5.像素密度PPI

    PPI(Pixel Per Inch by diagonal):表示沿着对角线,每英寸所拥有的像素(Pixel)数目。

    PPI数值越高,代表显示屏能够以越高的密度显示图像,即通常所说的分辨率越高、颗粒感越弱。

    根据勾股定理,可以得知iPhone4(s)的PPI计算公式为:

    计算结果稍有出入,这是因为像素的离散采样有锯齿效应。

6.缩放因子scale factor between logic point and device pixel)

    (1)Scale起源

    早期的iPhone3GS的屏幕分辨率是320*480(PPI=163),iOS绘制图形(CGPoint/CGSize/CGRect)均以point为单位(measured in points):

    1 point = 1 pixel(Point Per Inch=Pixel Per Inch=PPI)

    后来在iPhone4中,同样大小(3.5 inch)的屏幕采用了Retina显示技术,横、纵向方向像素密度都被放大到2倍,像素分辨率提高到(320×2)x(480×2)= 960×640(PPI=326), 显像分辨率提升至iPhone3GS的4倍(1个Point被渲染成1个2×2的像素矩阵)。

    但是对于开发者来说,iOS绘制图形的API依然沿袭point(pt,注意区分印刷行业的“磅”)为单位。在同样的逻辑坐标系下(320×480):

    1 point = scale*pixel(在iPhone4~6中,缩放因子scale=2;在iPhone6+中,缩放因子scale=3)。

    可以理解为:

    scale=绝对长度比point/pixel)=单位长度内的数量比pixel/point)

    (2)UIScreen.scale

    UIScreen.h中定义了该属性:

    // The natural scale factor associated with the screen.(read-only)

    @property(nonatomic,readonly) CGFloat scale  NS_AVAILABLE_IOS(4_0);

    ——————————————————————————–

    This value reflects the scale factor needed to convert from the default logical coordinate space into the device coordinate space of this screen.

    The default logical coordinate space is measured using points. For standard-resolution displays, the scale factor is 1.0 and one point equals one pixel. For Retina displays, the scale factor is 2.0 and one point is represented by four pixels.

    ——————————————————————————–

    为了自动适应分辨率,系统会根据设备实际分辨率,自动给UIScreen.scale赋值,该属性对开发者只读。

    (3)UIScreen.nativeScale

    iOS8新增了nativeScale属性:

    // Native scale factor of the physical screen

    @property(nonatomic,readonly) CGFloat nativeScale NS_AVAILABLE_IOS(8_0);

    以下是iPhone6+下的输出,初步看来nativeScale与scale没有太大区别

    ——————————————————————————–

        (lldb)p (CGFloat)[[UIScreen mainScreen] scale]
(CGFloat) $1 = 3
(lldb) p(CGFloat)[[UIScreen mainScreen] nativeScale]
(CGFloat) $2 = 3

    ——————————————————————————–

    (4)机型判别

    在同样的逻辑分辨率下,可以通过scale参数识别是iPhone3GS还是iPhone4(s)。以下基于nativeScale参数,定义了探测机型是否为iPhone6+的宏:

    ——————————————————————————–

    // not UIUserInterfaceIdiomPad
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
// detect iPhone6 Plus based on its native scale
#define IS_IPHONE_6PLUS (IS_IPHONE && [[UIScreenmainScreen] nativeScale] == 3.0f)

    ——————————————————————————–

    那么,同样的分辨率和scale,如何区分机型iPhone4与4s、iPhone5与5s呢?通过[[UIDevice currentDevice] model]只能判别iPhone、iPad、iPod大类,要判断iPhone具体机型型号,则需要通过sysctlbyname(“hw.machine”)获取详细的设备参数信息予以甄别。

7.Resolutions &Rendering

8.@2x/@3x以及高倍图适配

    (1)@2x

    @2x means the same “double”retina resolution that we’veseen on all iOS devices with retina displays to date, where each virtual pointin the user interface is represented by two physical pixels on thedisplay in each dimension, horizontal and vertical.

    iPhone3GS时代,我们为一个应用提供图标(或按钮提供贴图),只需要icon.png。针对现在的iPhone4~6 Retina显示屏,需要制作额外的@2x高分辨率版本。

    例如在iPhone3GS中,scale=1,用的图标是50x50pixel(logicalimage.size=50x50point);在iPhone4~6中,scale=2,则需要100×100pixel(logical image.size=50x50point,乘以image.scale=dimensions in pixels),并且命名为icon@2x.png。

    如果APP要同时兼容iPhone3GS~iPhone6,则需要提供icon.png/icon@2x.png两种分辨率的图片。

    (2)@3x

    @3x means a new “triple” retina resolution, where eachuser interface point is represented by three display pixels. A single @2x pointis a 2 × 2 square of 4 pixels; an @3x point is a 3 × 3 square of 9 pixels.”

    iPhone6+在实际渲染时,downsampling/1.15(1242×2208->1080×1920),准确的讲,应该是@2.46x。苹果为方便开发者用的是@3x的素材,然后再缩放到@2.46x上。

    参考:《为什么iPhone 6 Plus要将3x渲染的2208×1242分辨率缩小到1080p屏幕上?》《详解 iPhone 6 Plus 的奇葩分辨率》《iPhone 6 Plus屏幕分辨率

    如果APP要同时兼容iPhone3GS~iPhone6+,则需要提供icon.png/icon@2x.png/icon@3x.png三种分辨率的图片。

    需要注意的是,iOS APP图标的尺寸命名都需要遵守相关规范。

    (3)高倍图文件命名

    对于iPhone3、4/5/6、6+三类机型,需要按分辨率提供相应的高倍图并且文件名添加相应后缀,否则会拉伸(stretchable/resizable)失真(模糊或边角出现锯齿)。
以下基于UIImage的两类初始化API简介高倍图的适配:
<1>+imageNamed:该方法使用系统缓存,适合表视图重复加载图像的情形。同时该API根据UIScreen的scale,自动查找包含对应高倍图后缀名(@2x)的文件,如果没找到设置默认image.scale=1.0。因此,使用该方法,无需特意指定高倍图后缀。在实际运行时,系统如果发现当前设备是Retina屏(scale=2),会自动寻找”*@2x.png”命名格式的图片,加载针对Retina屏的图片素材,否则会失真。
<2>+imageWithContentsOfFile/+imageWithData:(scale:)/-initWithContentsOfFile:/-initWithData:(scale:)
这组方法创建的UIImage对象没有使用系统缓存,并且指定文件名必须包含明确的高倍图后缀。如果文件名包含@2x后缀,则image.scale=2.0;否则默认image.scale=1.0,同样对于Retina屏将会失真。
<3>目前,适配iPhone6+时,除了一些铺满全屏的大图(LogoIcon、LaunchImage)需提供三倍图,其他的小图仍可沿用原有的二倍图自适应拉伸。

9.Screen Bounds Application Frame

    (1)UIScreen.bounds

    // Bounds of entire screen in points(本地坐标系,起点为[0,0])

    @property(nonatomic,readonly) CGRect bounds;

    ——————————————————————————–

    //考虑转屏的影响,按照实际屏幕方向(UIDeviceOrientation)的宽高

    #define SCREEN_WIDTH ([UIScreenmainScreen].bounds.size.width)

    #define SCREEN_HEIGHT ([UIScreenmainScreen].bounds.size.height)

    #define STATUSBAR_HEIGHT ([UIApplicationsharedApplication].statusBarFrame.size.height)

    //不考虑转屏的影响,只取竖屏(UIDeviceOrientationPortrait)的宽高

    #define SCREEN_WIDTH MIN([UIScreenmainScreen].bounds.size.width, [UIScreenmainScreen].bounds.size.height)

    #define SCREEN_HEIGHT MAX([UIScreenmainScreen].bounds.size.height, [UIScreenmainScreen].bounds.size.width)

    #define STATUSBAR_HEIGHT MIN([UIApplicationsharedApplication].statusBarFrame.size.width, [UIApplicationsharedApplication].statusBarFrame.size.height)

    ——————————————————————————–

     (2)UIScreen.nativeBounds

    iOS8新增了nativeBounds属性,输出竖屏像素级分辨率:

    // The bounding rectangle of the physical screen,measured in pixels. (read-only)
// This rectangle is based on the device in a portrait-up orientation. This value does not change as the device rotates.

    @property(nonatomic,readonly) CGRect nativeBounds NS_AVAILABLE_IOS(8_0);

    以下是iPhone6+下的输出:

    ——————————————————————————–

       (lldb) poNSStringFromCGRect([(UIScreen*)[UIScreen mainScreen] bounds])
{{0, 0}, {414, 736}}
(lldb) poNSStringFromCGRect([(UIScreen*)[UIScreen mainScreen] nativeBounds])
{{0, 0}, {1242, 2208}}

    ——————————————————————————–

    (3)UIScreen.applicationFrame

    // Frame of application screen area in points (i.e.entire screen minus status bar if visible)

    // bounds除去系统状态栏

    @property(nonatomic,readonly) CGRect applicationFrame;

    ——————————————————————————–

    // APPFRAME_WIDTH=SCREEN_WIDTH

    #define APPFRAME_WIDTH ([UIScreen mainScreen].applicationFrame.size.width)

    // APPFRAME_HEIGHT=SCREEN_HEIGHT-STATUSBAR_HEIGHT

    //注意:横屏(UIDeviceOrientationLandscape)时,iOS8默认隐藏状态栏,此时APPFRAME_HEIGHT=SCREEN_HEIGHT

    #define APPFRAME_HEIGHT ([UIScreen mainScreen].applicationFrame.size.height)

    ——————————————————————————–

    (4)bounds和frame的区别

    下图展示了bounds和frame的区别

10.机型尺寸适配Screen Scale Adaption

    从iPhone3GS/iPhone4(s)过渡到iPhone5(s)时,在逻辑上宽度不变高度稍高,之前旧的素材和布局通过AutoresizingFlexible简单适配即可运行得很好,但由于高宽比增大,上下两端出现黑粗边(典型如LaunchImage)。从分辨率的角度来看,除了需要提供LaunchImage这种满屏图,其他基本沿用二倍图(@2x);从屏幕尺寸角度来看,需要对纵向排版略加调整。

    从iPhone5(s)发展到iPhone6(+),由于高宽比保持不变,iOS对图标、图片、字体进行等比放大自适应,清晰度会有所降低。同时,绝对坐标布局会导致在大屏下出现偏左偏上的问题。从分辨率的角度来看,iPhone6沿用二倍图(@2x),但需为iPhone6+提供更高的三倍图(@3x);从屏幕尺寸角度来看,需要重新对UI元素尺寸和布局进行适配,以期视觉协调。

    (1)按宽度适配

    我们先来看一下iPhone4~6(+)的屏幕高宽比:

       iPhone4(s):分辨率960*640,高宽比1.5
iPhone5(s):分辨率1136*640,高宽比1.775
iPhone6:分辨率1334*750,高宽比1.779
iPhone6+:分辨率1920*1080,高宽比1.778
可粗略认为iPhone5(s)、6(+)的高宽比是一致的(16:9),即可以等比例缩放。因此可以按宽度适配
fitScreenWidth= width*(SCREEN_WIDTH/320)
这样,共有iPhone3/4/5、6、6+三组宽度,在iPhone6、6+下将按比例横向放大。

    (2)按高度适配

    在同样的宽度下,iPhone4(s)的屏高比iPhone5(s)低,若纵向排版紧张,可以iPhone5(s)为基准,按高度适配
fitScreenHeight= height*(SCREEN_HEIGHT/568)
共有iPhone3/4、5、6、6+四组高度,在iPhone3/4下将按比例纵向缩小,在iPhone6、6+下将按比例纵向放大。

    这里需要注意iPhone/iOS双环上网的热点栏对纵向布局的影响:iPhone作为个人热点且有连接时,系统状态栏下面会多一行热点连接提示栏”Personal Hotspot: * Connection”,纵向会下压20pt,[UIApplication sharedApplication].statusBarFrame高度变为40pt;当所有连接都断开时,热点栏消失,纵向高度恢复正常为20pt。详情可参考《iPhone/iOS开启个人热点的纵向适配小结》。

    (3)按字体适配

    另外,iPhone的【设置】【通用】【辅助功能】中可以设置调节【更大字体】,APP也可以按字号适配
例如适配表视图(UITableView:UIScrollView),无法左右滑动,因此无论字号缩放比例多大,横向都不应超过SCREEN_WIDTH。注意限定控件元素内容区域宽度以及间距,并设置适当的LineBreakMode。表视图支持上下滑动,因此纵向上的表格行高和内容区域高度可按字号缩放。

    对于纵向也不支持滑动的视图,在屏幕可见视区内排版时,最好不要随字号缩放,否则可能超出既定宽高。

11.UI相对布局

    考虑到iPhone机型的多样性,不可能针对iPhone4(s)、5(s)、6、6+四种屏幕尺寸出四套视觉交互稿,也不要基于某一机型从上往下、从左往右给绝对标注,而应该关注subView在superView中的相对位置(EdgeInsets/Frame/Center)以及siblingView之间的偏移(Offset),尽量给出适合Autolayout的相对布局比例(理想情况是只给百分比)。假如交互按照iPhone5(s)下绝对标注,则在iPhone4(s)上可能挤出屏幕底部,而在iPhone6(+)上则可能横向偏左或纵向偏上。

    开发人员基于与屏幕边缘的间距(Margin/EdgeInsets),定位边缘处的控件(钉钉子)作为参照,然后基于控件尺寸和间隙进行相对计算排版。这样,若钉子移动,相邻控件将顺向偏移,不会因为局部调整而出现凌乱

    苹果在WWDC2012 iOS6中就已提出了Auto Layout的概念,即使用约束条件来定义视图的位置和尺寸,以适应不同尺寸和分辨率的屏幕。

12.DEPRECATED API适配

    最后,除了对屏幕尺寸和分辨率进行适配之外,还需对iOS SDK中相关的DEPRECATED API进行适配。典型的如:

    (1)UILineBreakMode->NSLineBreakMode
(2)UITextAlignment->NSTextAlignment
(3)sizeWithFont:->boundingRectWithSize:
(4)stretchableImageWithLeftCapWidth:topCapHeight:->resizableImageWithCapInsets:

    (5)…

参考:

iOS设备的屏幕尺寸、分辨率及其屏幕边长比例
iOS判断设备屏幕尺寸、分辨率
iOS8中的UIScreen
Detecting iPhone 6/6+ screen sizes in point values
iOS8已出,@3x图让我们何去何从?
在Xcode 6中用矢量化PDF(vectorized PDF)来支持各种尺寸的iPhone
iOS8适配须知
适配iOS8备忘录
《iOS界面适配()()()》
iPhone 6/6+适配心得
iOS8/Xcode6/iPhone6(+)适配
APP适配iOS8,iPhone6(+)截图简要说明
按比例快速兼容适配iPhone6/6 Plus
iOS的APP如何适应iPhone 5s/6/6+三种屏幕的尺寸?

发表在 Linux | 留下评论

OkHttp使用进阶 译自OkHttp Github官方教程

没有使用过OkHttp的,可以先看OkHttp使用介绍

英文版原版地址

Recipes · square/okhttp Wiki

同步get

下载一个文件,打印他的响应头,以string形式打印响应体。
响应体的 string() 方法对于小文档来说十分方便、高效。但是如果响应体太大(超过1MB),应避免适应 string()方法 ,因为他会将把整个文档加载到内存中。
对于超过1MB的响应body,应使用流的方式来处理body。

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
	Request request = new Request.Builder()
	    .url("http://publicobject.com/helloworld.txt")
	    .build();

	Response response = client.newCall(request).execute();
	if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

	Headers responseHeaders = response.headers();
	for (int i = 0; i < responseHeaders.size(); i++) {
	  System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
	}

	System.out.println(response.body().string());
}

异步get

在一个工作线程中下载文件,当响应可读时回调Callback接口。读取响应时会阻塞当前线程。OkHttp现阶段不提供异步api来接收响应体。

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
	Request request = new Request.Builder()
	    .url("http://publicobject.com/helloworld.txt")
	    .build();

	client.newCall(request).enqueue(new Callback() {
	  @Override public void onFailure(Request request, Throwable throwable) {
	    throwable.printStackTrace();
	  }

	  @Override public void onResponse(Response response) throws IOException {
	    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

	    Headers responseHeaders = response.headers();
	    for (int i = 0; i < responseHeaders.size(); i++) {
	      System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
	    }

	    System.out.println(response.body().string());
	  }
	});
}

提取响应头

典型的HTTP头 像是一个 Map<String, String> :每个字段都有一个或没有值。但是一些头允许多个值,像Guava的Multimap。例如:HTTP响应里面提供的Vary响应头,就是多值的。OkHttp的api试图让这些情况都适用。
当写请求头的时候,使用header(name, value)可以设置唯一的name、value。如果已经有值,旧的将被移除,然后添加新的。使用addHeader(name, value)可以添加多值(添加,不移除已有的)。
当读取响应头时,使用header(name)返回最后出现的name、value。通常情况这也是唯一的name、value。如果没有值,那么header(name)将返回null。如果想读取字段对应的所有值,使用headers(name)会返回一个list。
为了获取所有的Header,Headers类支持按index访问。

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
	Request request = new Request.Builder()
	    .url("https://api.github.com/repos/square/okhttp/issues")
	    .header("User-Agent", "OkHttp Headers.java")
	    .addHeader("Accept", "application/json; q=0.5")
	    .addHeader("Accept", "application/vnd.github.v3+json")
	    .build();

	Response response = client.newCall(request).execute();
	if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

	System.out.println("Server: " + response.header("Server"));
	System.out.println("Date: " + response.header("Date"));
	System.out.println("Vary: " + response.headers("Vary"));
}

Post方式提交String

使用HTTP POST提交请求到服务。这个例子提交了一个markdown文档到web服务,以HTML方式渲染markdown。因为整个请求体都在内存中,因此避免使用此api提交大文档(大于1MB)。

public static final MediaType MEDIA_TYPE_MARKDOWN
  = MediaType.parse("text/x-markdown; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
	String postBody = ""
	    + "Releases\n"
	    + "--------\n"
	    + "\n"
	    + " * _1.0_ May 6, 2013\n"
	    + " * _1.1_ June 15, 2013\n"
	    + " * _1.2_ August 11, 2013\n";

	Request request = new Request.Builder()
	    .url("https://api.github.com/markdown/raw")
	    .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
	    .build();

	Response response = client.newCall(request).execute();
	if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

	System.out.println(response.body().string());
}

Post方式提交流

以流的方式POST提交请求体。请求体的内容由流写入产生。这个例子是流直接写入Okio的BufferedSink。你的程序可能会使用OutputStream,你可以使用BufferedSink.outputStream()来获取。

public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse("text/x-markdown; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
	RequestBody requestBody = new RequestBody() {
	  @Override public MediaType contentType() {
	    return MEDIA_TYPE_MARKDOWN;
	  }

	  @Override public void writeTo(BufferedSink sink) throws IOException {
	    sink.writeUtf8("Numbers\n");
	    sink.writeUtf8("-------\n");
	    for (int i = 2; i <= 997; i++) {
	      sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
	    }
	  }

	  private String factor(int n) {
	    for (int i = 2; i < n; i++) {
	      int x = n / i;
	      if (x * i == n) return factor(x) + " × " + i;
	    }
	    return Integer.toString(n);
	  }
	};

	Request request = new Request.Builder()
	    .url("https://api.github.com/markdown/raw")
	    .post(requestBody)
	    .build();

	Response response = client.newCall(request).execute();
	if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

	System.out.println(response.body().string());
}

Post方式提交文件

以文件作为请求体是十分简单的。

public static final MediaType MEDIA_TYPE_MARKDOWN
  = MediaType.parse("text/x-markdown; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
	File file = new File("README.md");

	Request request = new Request.Builder()
	    .url("https://api.github.com/markdown/raw")
	    .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
	    .build();

	Response response = client.newCall(request).execute();
	if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

	System.out.println(response.body().string());
}

Post方式提交表单

使用FormEncodingBuilder来构建和HTML<form>标签相同效果的请求体。键值对将使用一种HTML兼容形式的URL编码来进行编码。

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
	RequestBody formBody = new FormEncodingBuilder()
	    .add("search", "Jurassic Park")
	    .build();
	Request request = new Request.Builder()
	    .url("https://en.wikipedia.org/w/index.php")
	    .post(formBody)
	    .build();

	Response response = client.newCall(request).execute();
	if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

	System.out.println(response.body().string());
}

Post方式提交分块请求

MultipartBuilder可以构建复杂的请求体,与HTML文件上传形式兼容。多块请求体中每块请求都是一个请求体,可以定义自己的请求头。这些请求头可以用来描述这块请求,例如他的Content-Disposition。如果Content-LengthContent-Type可用的话,他们会被自动添加到请求头中。

private static final String IMGUR_CLIENT_ID = "...";
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
	// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
	RequestBody requestBody = new MultipartBuilder()
	    .type(MultipartBuilder.FORM)
	    .addPart(
	        Headers.of("Content-Disposition", "form-data; name=\"title\""),
	        RequestBody.create(null, "Square Logo"))
	    .addPart(
	        Headers.of("Content-Disposition", "form-data; name=\"image\""),
	        RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
	    .build();

	Request request = new Request.Builder()
	    .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
	    .url("https://api.imgur.com/3/image")
	    .post(requestBody)
	    .build();

	Response response = client.newCall(request).execute();
	if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

	System.out.println(response.body().string());
}

使用Gson来解析JSON响应

Gson是一个在JSON和Java对象之间转换非常方便的api。这里我们用Gson来解析Github API的JSON响应。
注意:ResponseBody.charStream()使用响应头Content-Type指定的字符集来解析响应体。默认是UTF-8。

private final OkHttpClient client = new OkHttpClient();
private final Gson gson = new Gson();

public void run() throws Exception {
	Request request = new Request.Builder()
	    .url("https://api.github.com/gists/c2a7c39532239ff261be")
	    .build();
	Response response = client.newCall(request).execute();
	if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

	Gist gist = gson.fromJson(response.body().charStream(), Gist.class);
	for (Map.Entry<String, GistFile> entry : gist.files.entrySet()) {
	  System.out.println(entry.getKey());
	  System.out.println(entry.getValue().content);
	}
}

static class Gist {
	Map<String, GistFile> files;
}

static class GistFile {
	String content;
}

响应缓存

为了缓存响应,你需要一个你可以读写的缓存目录,和缓存大小的限制。这个缓存目录应该是私有的,不信任的程序应不能读取缓存内容。
一个缓存目录同时拥有多个缓存访问是错误的。大多数程序只需要调用一次new OkHttp(),在第一次调用时配置好缓存,然后其他地方只需要调用这个实例就可以了。否则两个缓存示例互相干扰,破坏响应缓存,而且有可能会导致程序崩溃。
响应缓存使用HTTP头作为配置。你可以在请求头中添加Cache-Control: max-stale=3600 ,OkHttp缓存会支持。你的服务通过响应头确定响应缓存多长时间,例如使用Cache-Control: max-age=9600

private final OkHttpClient client;

public CacheResponse(File cacheDirectory) throws Exception {
	int cacheSize = 10 * 1024 * 1024; // 10 MiB
	Cache cache = new Cache(cacheDirectory, cacheSize);

	client = new OkHttpClient();
	client.setCache(cache);
}

public void run() throws Exception {
	Request request = new Request.Builder()
	    .url("http://publicobject.com/helloworld.txt")
	    .build();

	Response response1 = client.newCall(request).execute();
	if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);

	String response1Body = response1.body().string();
	System.out.println("Response 1 response:          " + response1);
	System.out.println("Response 1 cache response:    " + response1.cacheResponse());
	System.out.println("Response 1 network response:  " + response1.networkResponse());

	Response response2 = client.newCall(request).execute();
	if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);

	String response2Body = response2.body().string();
	System.out.println("Response 2 response:          " + response2);
	System.out.println("Response 2 cache response:    " + response2.cacheResponse());
	System.out.println("Response 2 network response:  " + response2.networkResponse());

	System.out.println("Response 2 equals Response 1? " + response1Body.equals(response2Body));
}

扩展

在这一节还提到了下面一句:
There are cache headers to force a cached response, force a network response, or force the network response to be validated with a conditional GET.

我不是很懂cache,平时用到的也不多,所以把Google在Android Developers一段相关的解析放到这里吧。

Force a Network Response

In some situations, such as after a user clicks a ‘refresh’ button, it may be necessary to skip the cache, and fetch data directly from the server. To force a full refresh, add the no-cache directive:

connection.addRequestProperty("Cache-Control", "no-cache");

If it is only necessary to force a cached response to be validated by the server, use the more efficient max-age=0 instead:

connection.addRequestProperty("Cache-Control", "max-age=0");

Force a Cache Response

Sometimes you’ll want to show resources if they are available immediately, but not otherwise. This can be used so your application can show something while waiting for the latest data to be downloaded. To restrict a request to locally-cached resources, add the only-if-cached directive:

try {
     connection.addRequestProperty("Cache-Control", "only-if-cached");
     InputStream cached = connection.getInputStream();
     // the resource was cached! show it
  catch (FileNotFoundException e) {
     // the resource was not cached
 }
}

This technique works even better in situations where a stale response is better than no response. To permit stale cached responses, use the max-stale directive with the maximum staleness in seconds:

int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
connection.addRequestProperty("Cache-Control", "max-stale=" + maxStale);

以上信息来自:HttpResponseCache – Android SDK | Android Developers

取消一个Call

使用Call.cancel()可以立即停止掉一个正在执行的call。如果一个线程正在写请求或者读响应,将会引发IOException。当call没有必要的时候,使用这个api可以节约网络资源。例如当用户离开一个应用时。不管同步还是异步的call都可以取消。
你可以通过tags来同时取消多个请求。当你构建一请求时,使用RequestBuilder.tag(tag)来分配一个标签。之后你就可以用OkHttpClient.cancel(tag)来取消所有带有这个tag的call。

private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
	Request request = new Request.Builder()
	    .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
	    .build();

	final long startNanos = System.nanoTime();
	final Call call = client.newCall(request);

	// Schedule a job to cancel the call in 1 second.
	executor.schedule(new Runnable() {
	  @Override public void run() {
	    System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f);
	    call.cancel();
	    System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f);
	  }
	}, 1, TimeUnit.SECONDS);

	try {
	  System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f);
	  Response response = call.execute();
	  System.out.printf("%.2f Call was expected to fail, but completed: %s%n",
	      (System.nanoTime() - startNanos) / 1e9f, response);
	} catch (IOException e) {
	  System.out.printf("%.2f Call failed as expected: %s%n",
	      (System.nanoTime() - startNanos) / 1e9f, e);
	}
}

超时

没有响应时使用超时结束call。没有响应的原因可能是客户点链接问题、服务器可用性问题或者这之间的其他东西。OkHttp支持连接,读取和写入超时。

private final OkHttpClient client;

public ConfigureTimeouts() throws Exception {
	client = new OkHttpClient();
	client.setConnectTimeout(10, TimeUnit.SECONDS);
	client.setWriteTimeout(10, TimeUnit.SECONDS);
	client.setReadTimeout(30, TimeUnit.SECONDS);
}

public void run() throws Exception {
	Request request = new Request.Builder()
	    .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
	    .build();

	Response response = client.newCall(request).execute();
	System.out.println("Response completed: " + response);
}

每个call的配置

使用OkHttpClient,所有的HTTP Client配置包括代理设置、超时设置、缓存设置。当你需要为单个call改变配置的时候,clone 一个 OkHttpClient。这个api将会返回一个浅拷贝(shallow copy),你可以用来单独自定义。下面的例子中,我们让一个请求是500ms的超时、另一个是3000ms的超时。

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
	Request request = new Request.Builder()
	    .url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay.
	    .build();

	try {
	  Response response = client.clone() // Clone to make a customized OkHttp for this request.
	      .setReadTimeout(500, TimeUnit.MILLISECONDS)
	      .newCall(request)
	      .execute();
	  System.out.println("Response 1 succeeded: " + response);
	} catch (IOException e) {
	  System.out.println("Response 1 failed: " + e);
	}

	try {
	  Response response = client.clone() // Clone to make a customized OkHttp for this request.
	      .setReadTimeout(3000, TimeUnit.MILLISECONDS)
	      .newCall(request)
	      .execute();
	  System.out.println("Response 2 succeeded: " + response);
	} catch (IOException e) {
	  System.out.println("Response 2 failed: " + e);
	}
}

处理验证

这部分和HTTP AUTH有关。
相关资料:HTTP AUTH 那些事 – 王绍全的博客 – 博客频道 – CSDN.NET

OkHttp会自动重试未验证的请求。当响应是401 Not Authorized时,Authenticator会被要求提供证书。Authenticator的实现中需要建立一个新的包含证书的请求。如果没有证书可用,返回null来跳过尝试。

public List<Challenge> challenges()
Returns the authorization challenges appropriate for this response's code. If the response code is 401 unauthorized, this returns the "WWW-Authenticate" challenges. If the response code is 407 proxy unauthorized, this returns the "Proxy-Authenticate" challenges. Otherwise this returns an empty list of challenges.

当需要实现一个Basic challenge, 使用Credentials.basic(username, password)来编码请求头。

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
	client.setAuthenticator(new Authenticator() {
	  @Override public Request authenticate(Proxy proxy, Response response) {
	    System.out.println("Authenticating for response: " + response);
	    System.out.println("Challenges: " + response.challenges());
	    String credential = Credentials.basic("jesse", "password1");
	    return response.request().newBuilder()
	        .header("Authorization", credential)
	        .build();
	  }

	  @Override public Request authenticateProxy(Proxy proxy, Response response) {
	    return null; // Null indicates no attempt to authenticate.
	  }
	});

	Request request = new Request.Builder()
	    .url("http://publicobject.com/secrets/hellosecret.txt")
	    .build();

	Response response = client.newCall(request).execute();
	if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

	System.out.println(response.body().string());
}

声明

欢迎转载,但请保留文章原始出处
作者:GavinCT
出处:http://www.cnblogs.com/ct2011/

发表在 Linux | 留下评论

微信支付流程

交互流程:

1.服务端生成二维码/消息链接(支付页面)
2.用户点击消息/扫描二维码打开网页
3.网页请求服务端生成订单
4.服务端调用微信支付系统统一下单api
5.微信支付系统生成预付单
6.返回预付单信息给服务端
7.生成JSAPI页面调用的支付参数并签名
8.返回支付参数给客户端
9.客户端点击发起支付请求
10.微信支付系统检查参数合法性和授权域权限
11.返回验证结果, 并要求支付授权
12.客户端提示用户输入密码
13.用户输入密码, 提交支付授权到微信支付系统
14.微信支付系统验证授权

15.异步通知商户支付结果
16.告知微信通知处理结果

17.返回支付结果, 并发微信消息提示给客户端, 公众号下収消息,提示収货成功。该步骤可选。
18.展示支付消息给用户
19.跳转回商户页面, 该页面由商户自定义

20.客户端可以查询服务端支付结果
21.服务端调用查询api, 向微信支付平台查询支付结果
22.微信支付平台查询到结果后返回给服务端
23.服务端把结果返回给客户端
24.客户端根据结果展示提示页面

交互细节:

1)用户打开商户网页选购商品, 収起支付, 在网页通过JavaScript调用
getBrandWCPayRequest接口, 収起微信支付请求, 用户进入支付流程。
2)用户成功支付点击完成按钮后, 商户的前端会收到JavaScript的返回值。商户可
直接跳转到支付成功的静态页面进行展示。
3)商户后台收到来自微信开放平台的支付成功回调通知,标志该笔订单支付成功。

(2)和(3)的触发不保证遵循严格的时序。JS API 返回值作为触収商户网页跳转的标
志,但商户后台应该只在收到微信后台的支付成功回调通知后,才做真正的支付成功的处理。
JS API 返回值目前只在支付成功时返回,后续版本将扩展返回值,以便商户做更多个
性化的展示。

发表在 Linux | 2条评论

HTTPS服务器配置

一.SSL证书申请

 

  • 1.确认需要申请证书的域名
  • 2.生成私钥和csr文件

 

在linux机器上执行以下命令生成私钥
#openssl genrsa -out server.key 2048
在linux机器上执行以下命令生成csr文件
#openssl req -new -key server.key -out certreq.csr
以下黑色标识文字仅供参考,请根据商户自己实际情况进行填写
Country Name: CN                      //您所在国家的ISO标准代号,中国为CN
State or Province Name:guandong       //您单位所在地省/自治区/直辖市
Locality Name:shenzhen                 //您单位所在地的市/县/区
Organization Name: Tencent Technology (Shenzhen) Company Limited                 //您单位/机构/企业合法的名称
Organizational Unit Name: R&D         //部门名称
Common Name: www.example.com     //通用名,例如:www.itrus.com.cn。此项必须与您访问提供SSL服务的服务器时所应用的域名完全匹配。
Email Address:                          //您的邮件地址,不必输入,直接回车跳过
“extra”attributes                        //以下信息不必输入,回车跳过直到命令执行完毕。
执行上面的命令后,在当前目录下即可生成私钥文件server.keycertreq.csr csr文件

 

  • 3.将生成的csr文件提交给第三方证书颁发机构申请对应域名的服务器证书,同时将私钥文件保存好,以免丢失。
  • 4.证书申请后,证书颁发机构会提供服务器证书内容和两张中级CA证书,请按证书颁发机器说明生成服务器证书,此处假设服务器证书文件名称为server.pem
  • 5.将生成的私钥文件server.key和服务器证书server.pem拷贝至服务器指定的目录即可进行HTTPS服务器配置

 

 

二.HTTPS服务器配置

1. Nginx配置
server {
listen       443;   #指定ssl监听端口
        server_name  www.example.com;
        ssl on;    #开启ssl支持
        ssl_certificate      /etc/nginx/server.pem;    #指定服务器证书路径
        ssl_certificate_key  /etc/nginx/server.key;    #指定私钥证书路径
        ssl_session_timeout  5m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;     #指定SSL服务器端支持的协议版本
        ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;    #指定加密算法
        ssl_prefer_server_ciphers   on;    #在使用SSLv3和TLS协议时指定服务器的加密算法要优先于客户端的加密算法
#以下内容请按域名需要进行配置,此处仅供参考
location / {
return 444;
}
}

2.其它web服务器配置
请参考文档:http://www.itrus.cn/html/fuwuyuzhichi/fuwuqizhengshuanzhuangpeizhizhinan《服务器证书配置指南》

 

三.相关事项

1.证书颁发机构

推荐天威诚信,具体请见:http://www.itrus.com.cn

2. 参考文档
http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_prefer_server_ciphers 《ngx_http_ssl_module》
http://nginx.org/cn/docs/http/configuring_https_servers.html《nginx配置HTTPS服务器》
http://www.itrus.cn/html/fuwuyuzhichi/fuwuqizhengshuanzhuangpeizhizhinan《服务器证书配置指南》

  1. 3.常见问题
  2. 1)证书受信任的问题
    部分国内签发的SSL证书,在Android上不受信任,推荐GeoTrust;
  3. 2)如果页面有动静分离,静态资源使用独立域名的话,也需要为该域名申请证书;
  4. 3)android低版本不支持SNI扩展,受此限制,一台服务器只能部署一个数字证书;
发表在 Linux | 留下评论

JSAPI 支付时序图

QQ20150718-1@2x

发表在 Linux | 留下评论

Sublime Text3 安装ftp插件

环境说明:
mac os 10.9、 sublime text3 (Unregistered)

1、首先调出命令面板(cmd+shift+p)

输入install 调出 Install Package
(要等几秒)

2、出现插件面板

然后键入sftp,然后按回车。
当出现一个sftp/ftp的untitled说明文件时意味着安装成功了.

3、然后点击『Project』 添加文件夹『Add Folder to Project…』,
选择项目的文件夹。在左侧文件夹上右键,选择『SFTP/FTP』- 『Map to Remote』,
之后会出来一个配置文件sftp-config.json,修改配置文件,

然后在要ftp的文件或文件夹上右键乃至在编辑窗口中右键都可以上传下载了。

发表在 Linux | 留下评论