GeoHash算法,实现快速查找附近的点

做任何跟地理位置相关的服务,位置如何表示及存储是绝对绕不开的问题之一。位置的表示倒是可以用经纬度,但是索引和检索的时候,经纬度这种二维表示法就比较麻烦了,这时我们就可以利用 GeoHash 进行『降维攻击』来解决这个问题了。

Geohash介绍

应用场景
  • POI(Point of Interest):查找某个坐标附近的支部,餐厅等常见需求
  • 热点分析:统计某个地图区域的热度;
    Geohash算法
    地图上一般是使用经度和纬度两个维度来唯一的确定一个点,而geohash采用经纬度二维值转为一维的值。

优点:

  • 只需要对一个字段进行索引,提高性能、降低复杂度
  • 可转成可排序,可比较的字符串,满足灵活的需求

具体详细的介绍参考 维基百科: https://en.wikipedia.org/wiki/Geohash

以下文章转自:http://www.cnblogs.com/LBSer/p/3310455.html

引子

机机是个好动又好学的孩子,平日里就喜欢拿着手机地图点点按按来查询一些好玩的东西。某一天机机到北海公园游玩,肚肚饿了,于是乎打开手机地图,搜索北海公园附近的餐馆,并选了其中一家用餐。

饭饱之后机机开始反思了,地图后台如何根据自己所在位置查询来查询附近餐馆的呢?苦思冥想了半天,机机想出了个方法:计算所在位置P与北京所有餐馆的距离,然后返回距离<=1000米的餐馆。小得意了一会儿,机机发现北京的餐馆何其多啊,这样计算不得了,于是想了,既然知道经纬度了,那它应该知道自己在西城区,那应该计算所在位置P与西城区所有餐馆的距离啊,机机运用了递归的思想,想到了西城区也很多餐馆啊,应该计算所在位置P与所在街道所有餐馆的距离,这样计算量又小了,效率也提升了。

查看更多

RecyclerView根据内容多少动态设置高度

在项目开发中我们经常会遇到需要RecyclerView和ScrollViewq嵌套的情况,一般我们都需要把RecyclerView中的内容全部显示,然后设置RV的高度为所有内容的高度+RV Padding+item 之间的Divider。
原理很简单,实现起来也很简单。

  • 创建RV ViewHolder

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public abstract class  MyViewHolder extends RecyclerView.ViewHolder {

    TextView tv_item;
    ImageView img_delete_item;

    public MyViewHolder(View itemView) {
    super(itemView);
    tv_item = (TextView) itemView.findViewById(R.id.tv_single);
    img_delete_item = (ImageView) itemView.findViewById(R.id.img_toprecycle_delete);
    }


    public abstract void setFixedHeight();
    }
  • 实现自定义VH

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public MyViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
    View view = mInflater.inflate(R.layout.item_useview, parent, false);
    MyViewHolder viewHolder = new MyViewHolder(view){
    @Override
    public void setFixedHeight() {
    // magic happening here
    ViewGroup.LayoutParams parentParams = parent.getLayoutParams();
    parentParams.height =
    ((RecyclerView) parent).computeVerticalScrollRange()
    + parent.getPaddingTop()
    + parent.getPaddingBottom();
    parent.setLayoutParams(parentParams);
    }
    };

    return viewHolder;
    }
  • 设置RV高度

    1
    2
    3
    4
    //绑定viewholder
    public void onBindViewHolder(MyViewHolder holder, final int position) {
    holder.setFixedHeight();
    }

Android P使用私有API弹出warning(转载)

转自:https://huangyu.github.io/archives/35fd3a81.html

最近手头的Mix2s接收到MIUI官方开发版的推送,可以升级为Android P,很开心,然后升级后,部分应用就打不开或者挂了,比如某奇艺(买了VIP看剧都看不了,强烈吐槽)。
于是顺手检查了一下公司的应用,发现都能正常启动和使用,但是最近在做SDK的接入Demo,一打开发现弹出了warning对话框

起初以为是targetAPI的问题,升级到28问题依旧,于是Google了一下,发现是从Android P开始,系统会限制非SDK的接口调用,也就是如果App通过反射使用系统隐藏的API,则会弹出提示。

具体的细节可参考:https://developer.android.com/about/versions/pie/restrictions-non-sdk-interfaces?hl=zh-cn

Android P中,将所有API分为以下几类:

  • 白名单:SDK的API,正常使用;
  • 浅灰名单(light greylist)会在logcat弹出提示:Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI);
  • 深灰名单(dark greylist)则debug版本在会弹出dialog提示框,在release版本会有Toast提示,均提示为”Detected problems with API compatibility”。;
  • 黑名单(darklist)则会引发异常。

看了logcat日志,项目中许多地方使用到了系统私有API,直接全部修改工作量比较大,于是寻找解决方案。

最后发现原因是在SDK 28版本中Activity中的performStart有这个警告,只要在调用performStart前,ActivityThread的mHiddenApiWarningShown变量的值为true,产生这个警告的条件就无法满足。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
boolean isAppDebuggable = (mApplication.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
// This property is set for all non-user builds except final release
boolean isApiWarningEnabled = SystemProperties.getInt("ro.art.hiddenapi.warning", 0) == 1;
if (isAppDebuggable || isApiWarningEnabled) {
if (!mMainThread.mHiddenApiWarningShown && VMRuntime.getRuntime().hasUsedHiddenApi()) {
// Only show the warning once per process.
mMainThread.mHiddenApiWarningShown = true;
String appName = getApplicationInfo().loadLabel(getPackageManager())
.toString();
String warning = "Detected problems with API compatibility\n"
+ "(visit g.co/dev/appcompat for more info)";
if (isAppDebuggable) {
new AlertDialog.Builder(this)
.setTitle(appName)
.setMessage(warning)
.setPositiveButton(android.R.string.ok, null)
.setCancelable(false)
.show();
} else {
Toast.makeText(this, appName + "\n" + warning, Toast.LENGTH_LONG).show();
}
}
}

具体是在Application OnCreate加入以下代码:(通过反射私有变量来解决私有API的warning,也是有趣)

1
2
3
4
5
6
7
8
9
10
11
try {
Class<?> cls = Class.forName("android.app.ActivityThread");
Method declaredMethod = cls.getDeclaredMethod("currentActivityThread");
declaredMethod.setAccessible(true);
Object activityThread = declaredMethod.invoke(null);
Field mHiddenApiWarningShown = cls.getDeclaredField("mHiddenApiWarningShown");
mHiddenApiWarningShown.setAccessible(true);
mHiddenApiWarningShown.setBoolean(activityThread, true);
} catch (Exception e) {
e.printStackTrace();
}

最终warning框不再弹出,但是logcat依旧,在Logcat中看到,修改mHiddenApiWarningShown本身就是dark greylist,估计后续该办法会给官方禁用。

最好的解决方式是根据logcat的warning提示,对使用darklist的地方必须进行修改,尽量避免使用dark greylist方法。

Cookie 的路径以及 Cookie 域,设置同一域名下多个Tomcat共享Cookie

cookie 一般都是由于用户访问页面而被创建的,可是并不是只有在创建 cookie 的页面才可以访问这个cookie。在默认情况下,出于安全方面的考虑,只有与创建 cookie 的页面处于同一个目录或在创建cookie页面的子目录下的网页才可以访问。那么此时如果希望其父级或者整个网页都能够使用cookie,就需要进行路径的设置。

让这个设置的cookie 能被其他目录或者父级的目录访问的方法:

1
document.cookie = "userName = 独行冰海; path=/";

路径能解决在同一个域下访问 cookie 的问题,那么如何解决同一个主域下的访问问题呢?我们可以通过指定可访问cookie的主机名来进行设置。

document.cookie=”name=value; domain=cookieDomain”;

例如 “www.baidu.com" 与 “mp3.baidu.com” 公用一个关联的域名”baidu.com”,我们如果想让 “www.baidu.com" 下的cookie被 “mp3.baidu.com” 访问,我们就需要用到 cookie 的domain属性,并且需要把path属性设置为 “/“

这里需要注意一点:一定的是同域之间的访问,不能把domain的值设置成非主域的域名。

默认cookie的域是当前域名,默认的路径的设置cookie时的当前页面的目录路径。如果想要跨域或者在其他的路径下访问cookie就必须要重新设置这两个属性,domain和path。

www.baidu.com/content/example/3.jsp
则域domain为www.baidu.com, 路径path为/content/example

1
document.cookie = "username=独行冰海; path=/; domain=baidu.com"

在项目中的使用实战:
在一个系统例如www.jlspdj.cn/spzzb下 登录之后设置cookie,代码如下:

1
2
3
4
5
$.cookie("userName", userName, { expires: 7,path:'/',domain:'jlspdj.cn'  });
$.cookie("userType", lgUser.userType, { expires: 7,path:'/',domain:'jlspdj.cn' });
$.cookie("perName", lgUser.perName, { expires: 7,path:'/',domain:'jlspdj.cn' });
$.cookie("pwd", lgUser.pwd, { expires: 7,path:'/',domain:'jlspdj.cn' });
$.cookie("systemflag", "1", { expires: 7,path:'/',domain:'jlspdj.cn' });

在另一个系统www.jlspdj.cn/bigdata 中就使用下面代码获取cookie即可判断。

1
var strcookie = document.cookie;

使用百度地图API解决区县级以下行政区划轮廓问题

我们在项目开发中经常会遇到在地图上显示区划轮廓的情况,所幸现在各大地图服务商都提供了获取区划边界(县区以上)的功能(有时候边界不准,但总比没有强),例如百度提供的显示区划的边界。
百度地图demo例子: http://lbsyun.baidu.com/jsdemo.htm#c1_10

以及基于高德地图的区划显示:
http://webapi.amap.com/ui/1.0/ui/geo/DistrictExplorer/examples/index.html?guide=1

但是改功能只能展示区级及以上范围行政区划。

研究了一下百度地图行政区划的实现方式,其实就是存储了行政区划的边界点集合,显示时候其实是显示的点的多边形的集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function getBoundary(){       
var bdary = new BMap.Boundary();
bdary.get("北京市海淀区", function(rs){ //获取行政区域
map.clearOverlays(); //清除地图覆盖物
var count = rs.boundaries.length; //行政区域的点有多少个
if (count === 0) {
alert('未能获取当前输入行政区域');
return ;
}
var pointArray = [];
for (var i = 0; i < count; i++) {
var ply = new BMap.Polygon(rs.boundaries[i], {strokeWeight: 2, strokeColor: "#ff0000"}); //建立多边形覆盖物
map.addOverlay(ply); //添加覆盖物
pointArray = pointArray.concat(ply.getPath());
}
map.setViewport(pointArray); //调整视野
addlabel();
});
}

那我们自己也可以按照这个逻辑来实现乡镇行政区划的展示,那现在的问题就是如何来获取行政区边界的点集,从百度地图api V1.1版本,polygon类提供了enableEditing()功能,我们可以在地图上拖动编辑边界点,然后,再通过polygon类提供的getPath()方法返回边界点数组,将我们自己编辑过的边界点数组存储起来,就实现了行政区划的数字化工作。

整体实现见文件:
地图经纬度描点工具

关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 显示所有区划
function showMyArea() {
var areas = [];
//铁西区
var siping_tiexiqu = "124.394261,43.200225;124.363162,43.14644;124.359246,43.131221;124.342686,43.13835;124.327579,43.153527;124.299905,43.163609;124.292776,43.176343;124.281179,43.184908;124.286515,43.199605;124.2953,43.209288;124.28495,43.235119;124.301071,43.234313;124.30711,43.23646;124.309022,43.250198;124.313187,43.255563;124.320604,43.254237;124.328298,43.256463;124.332289,43.255015;124.335821,43.24598;124.349095,43.244098;124.356816,43.248521;124.369427,43.248006;124.374714,43.243444;124.390598,43.244364;124.399349,43.239998;124.410266,43.243724;124.415474,43.238964;124.425377,43.239837;124.423989,43.235824;124.42865,43.236023;124.43232,43.232498;124.438555,43.233977;124.438702,43.228258;124.452916,43.225358;124.446783,43.218094;124.435393,43.215793;124.423932,43.203086;124.403633,43.203134;124.394261,43.200225";
// var siping_lishu = "124.717877,43.184866;124.66483,43.180224;124.643888,43.1635;124.634191,43.151678;124.628805,43.15007;124.61549,43.165761;124.599496,43.167858;124.578632,43.183615;124.572448,43.184601;124.569519,43.182571;124.571261,43.188129;124.531548,43.186809;124.525446,43.196129;124.494772,43.206798;124.488282,43.215329;124.476199,43.222616;124.438702,43.228258;124.438555,43.233977;124.43232,43.232498;124.42865,43.236023;124.423989,43.235824;124.425377,43.239837;124.415474,43.238964;124.410266,43.243724;124.399349,43.239998;124.390598,43.244364;124.374714,43.243444;124.369427,43.248006;124.356816,43.248521;124.349095,43.244098;124.340037,43.245237;124.335821,43.24598;124.331431,43.255935;124.320604,43.254237;124.314992,43.256494;124.309022,43.250198;124.30853,43.237342;124.294073,43.234175;124.283577,43.235176;124.28107,43.239702;124.269164,43.237759;124.266655,43.245038;124.259782,43.238071;124.257449,43.24366;124.251762,43.243022;124.249587,43.239765;124.241543,43.242631;124.235055,43.241337;124.231932,43.248111;124.225847,43.247806;124.229393,43.251877;124.22312,43.252848;124.222754,43.262281;124.208845,43.25729;124.205514,43.252726;124.201234,43.253584;124.198692,43.24972;124.195014,43.252969;124.184336,43.249269;124.183183,43.25468;124.179313,43.255896;124.175818,43.250119;124.164593,43.257501;124.157209,43.254853;124.147483,43.258949;124.134178,43.254178;124.121673,43.255854;124.119411,43.2828;124.108225,43.299548;124.051683,43.295149;124.041668,43.289664;124.03353,43.289105;124.007062,43.311549;124.001684,43.312088;123.956243,43.356327;123.941881,43.359869;123.928481,43.366864;123.904879,43.363339;123.899711,43.377361;123.907784,43.386242;123.902929,43.392938;123.897976,43.396915;123.877266,43.40022;123.853355,43.425646;123.875252,43.444157;123.872991,43.459346;123.847204,43.476725;123.837107,43.474812;123.799451,43.498242;123.791182,43.483941;123.784907,43.483314;123.772711,43.495354;123.778534,43.513874;123.784022,43.516719;123.785764,43.522433;123.792631,43.522182;123.798089,43.525946;123.799205,43.542823;123.809716,43.552573;123.830938,43.55756;123.842469,43.55425;123.852855,43.557246;123.868418,43.555772;123.886379,43.56175;123.890321,43.565408;123.892139,43.586177;123.899506,43.590187;123.900722,43.594354;123.910263,43.597505;123.927287,43.597878;123.931247,43.604624;123.936321,43.601905;123.944076,43.602656;123.952315,43.619391;123.96463,43.620919;123.976669,43.63034;123.985999,43.628109;123.987383,43.632232;123.997123,43.635666;124.003678,43.650564;124.015595,43.649861;124.02115,43.662984;124.033997,43.665187;124.045211,43.662798;124.055615,43.667878;124.067245,43.693561;124.084376,43.70277;124.105542,43.707627;124.10714,43.714171;124.115462,43.711159;124.141787,43.715909;124.136447,43.710529;124.151598,43.709754;124.155335,43.713911;124.181124,43.713578;124.183484,43.719179;124.195015,43.718619;124.204286,43.736224;124.210587,43.7382;124.211798,43.748612;124.219898,43.751659;124.214398,43.761009;124.224091,43.764159;124.293399,43.768221;124.363848,43.776467;124.408229,43.767412;124.417658,43.770994;124.415863,43.766106;124.435428,43.762663;124.465599,43.765999;124.468391,43.768789;124.487087,43.756202;124.489668,43.758576;124.503633,43.754509;124.536092,43.759688;124.541466,43.752034;124.55246,43.749835;124.560309,43.737036;124.581525,43.727615;124.605665,43.695414;124.63191,43.687309;124.669728,43.666632;124.685555,43.634384;124.681797,43.610145;124.68706,43.595558;124.697827,43.582857;124.698733,43.560558;124.691779,43.5354;124.681796,43.520622;124.683705,43.512157;124.716562,43.448648;124.735872,43.428534;124.744233,43.400559;124.763803,43.386218;124.762479,43.372668;124.768286,43.361576;124.766678,43.354669;124.773502,43.350665;124.780015,43.332178;124.771381,43.320117;124.773549,43.314083;124.766845,43.311227;124.761519,43.303088;124.76599,43.284148;124.779824,43.279007;124.789961,43.262506;124.806291,43.249429;124.804008,43.245594;124.780772,43.238268;124.789005,43.225899;124.785969,43.220874;124.781806,43.221451;124.766526,43.214592;124.749493,43.214538;124.742382,43.211513;124.731807,43.203054;124.722254,43.187024;124.717877,43.184866";
var siping_shuangliao = "124.042873,43.790715;124.051277,43.786682;124.052739,43.778252;124.058225,43.772763;124.077469,43.763807;124.088468,43.748916;124.080787,43.7408;124.08685,43.736531;124.081597,43.725383;124.085757,43.719901;124.065518,43.700331;124.067245,43.693561;124.055615,43.667878;124.045211,43.662798;124.033997,43.665187;124.02115,43.662984;124.015595,43.649861;124.003678,43.650564;123.997123,43.635666;123.987383,43.632232;123.985999,43.628109;123.976669,43.63034;123.96463,43.620919;123.952315,43.619391;123.944076,43.602656;123.936321,43.601905;123.931247,43.604624;123.927287,43.597878;123.902776,43.59565;123.892139,43.586177;123.890321,43.565408;123.886379,43.56175;123.868418,43.555772;123.852855,43.557246;123.842469,43.55425;123.828592,43.557384;123.81302,43.554498;123.802316,43.547013;123.798628,43.54067;123.798672,43.526677;123.792631,43.522182;123.785764,43.522433;123.774051,43.504054;123.772711,43.495354;123.777268,43.489891;123.752252,43.476509;123.759203,43.469826;123.757652,43.459991;123.751933,43.457362;123.750169,43.452679;123.755332,43.445024;123.715638,43.422409;123.716833,43.418282;123.709243,43.409574;123.711438,43.402334;123.705994,43.396928;123.715997,43.391705;123.703311,43.388443;123.712092,43.382062;123.710983,43.377456;123.62133,43.373124;123.598131,43.379545;123.522193,43.444011;123.512911,43.45786;123.500463,43.465142;123.494028,43.477091;123.483002,43.473455;123.472841,43.462331;123.461033,43.456747;123.459223,43.446648;123.447419,43.42816;123.433992,43.428351;123.43603,43.439693;123.427419,43.450086;123.379298,43.47231;123.362449,43.485943;123.34236,43.493273;123.332469,43.503763;123.327763,43.511364;123.33164,43.523684;123.310911,43.540146;123.320041,43.557667;123.348534,43.561271;123.378195,43.570561;123.389297,43.568752;123.389387,43.571749;123.396067,43.572015;123.463797,43.557764;123.460202,43.566822;123.46838,43.577293;123.452914,43.587256;123.441784,43.587736;123.432856,43.605675;123.44679,43.60626;123.467166,43.599959;123.471532,43.591476;123.480863,43.597734;123.49698,43.594172;123.517193,43.599177;123.519948,43.610441;123.513174,43.617549;123.51985,43.621579;123.515464,43.626076;123.520978,43.628135;123.517585,43.631613;123.529982,43.635857;123.53534,43.631656;123.53848,43.632349;123.536687,43.635355;123.543916,43.640053;123.539329,43.647914;123.5444,43.652612;123.525798,43.687287;123.532239,43.696493;123.531603,43.702222;123.525847,43.706869;123.52596,43.718158;123.516232,43.722883;123.505241,43.739671;123.496774,43.737758;123.487567,43.745904;123.483789,43.77504;123.494444,43.776501;123.501559,43.78545;123.499407,43.803716;123.481611,43.840772;123.482287,43.856787;123.452258,43.893516;123.430365,43.93139;123.419071,43.936521;123.402802,43.96007;123.388592,43.958376;123.384068,43.964666;123.378099,43.962544;123.373771,43.965703;123.373577,43.969303;123.383861,43.979299;123.395591,43.981438;123.395565,43.984415;123.364993,44.016265;123.356972,44.0382;123.34751,44.051899;123.342118,44.073228;123.35943,44.077181;123.399417,44.077001;123.473648,44.087508;123.545063,44.082973;123.564344,44.089648;123.594181,44.095005;123.615549,44.087754;123.644973,44.083419;123.654037,44.093894;123.664629,44.097616;123.671401,44.111337;123.687419,44.112253;123.690863,44.110459;123.700227,44.083833;123.708153,44.082213;123.722556,44.071261;123.72776,44.061455;123.742862,44.052214;123.760658,44.047782;123.774276,44.035587;123.778164,44.027963;123.772542,44.021461;123.772747,44.016087;123.758252,44.00863;123.762618,44.006331;123.945659,43.99816;124.015917,44.00685;124.024136,44.011349;124.029537,43.97389;124.043655,43.935458;124.068589,43.904531;124.071157,43.896849;124.084317,43.887286;124.093782,43.873998;124.086758,43.860948;124.086314,43.847292;124.077004,43.81536;124.066149,43.803093;124.049835,43.798838;124.042873,43.790715";
// var siping_tiedong = "124.705676,43.066287;124.694906,43.059818;124.693465,43.043515;124.683754,43.033515;124.676525,43.008178;124.660432,42.99986;124.6669,42.98847;124.665799,42.981285;124.662003,42.979528;124.651681,42.981089;124.641698,42.97714;124.638527,42.965856;124.643466,42.960056;124.636521,42.955115;124.627582,42.953923;124.626315,42.947049;124.610895,42.942614;124.608754,42.929016;124.590857,42.911976;124.576528,42.905436;124.566758,42.896293;124.556547,42.896736;124.548121,42.892854;124.544029,42.874418;124.540939,42.873835;124.537413,42.877992;124.521088,42.879005;124.5037,42.868654;124.483558,42.863731;124.471792,42.85312;124.478175,42.837632;124.475871,42.830389;124.469195,42.828292;124.456918,42.832002;124.457914,42.858621;124.454402,42.869513;124.442829,42.874664;124.442411,42.885941;124.435814,42.886351;124.430807,42.880584;124.415876,42.886135;124.378219,42.886463;124.373799,42.888743;124.37172,42.90827;124.385047,42.918898;124.394206,42.916099;124.401643,42.921126;124.402872,42.926244;124.416546,42.927711;124.424387,42.935146;124.434587,42.936012;124.439488,42.944455;124.448357,42.948497;124.450013,42.952171;124.444542,42.958783;124.443686,42.969251;124.432745,42.974805;124.42448,42.983304;124.416905,42.983039;124.403225,42.976022;124.378916,42.976809;124.372223,42.981688;124.361067,42.98421;124.353018,42.995196;124.340034,43.005225;124.342607,43.009178;124.355846,43.014961;124.407003,43.057656;124.432235,43.085352;124.402742,43.098544;124.374603,43.125785;124.359246,43.131221;124.363162,43.14644;124.397274,43.202349;124.423932,43.203086;124.435393,43.215793;124.446783,43.218094;124.452916,43.225358;124.469939,43.224055;124.488282,43.215329;124.495937,43.2062;124.525446,43.196129;124.531548,43.186809;124.571261,43.188129;124.569519,43.182571;124.572448,43.184601;124.578632,43.183615;124.599496,43.167858;124.61549,43.165761;124.628805,43.15007;124.634191,43.151678;124.643888,43.1635;124.66483,43.180224;124.720662,43.185079;124.731807,43.203054;124.742382,43.211513;124.749493,43.214538;124.766526,43.214592;124.781806,43.221451;124.785969,43.220874;124.784753,43.215893;124.789283,43.207608;124.797406,43.202439;124.802947,43.202128;124.862577,43.152095;124.882296,43.146951;124.889477,43.140778;124.876792,43.130429;124.865065,43.125194;124.847178,43.132478;124.82973,43.13089;124.817815,43.124223;124.803436,43.128672;124.792125,43.123213;124.789136,43.109875;124.765721,43.093879;124.756951,43.077764;124.750536,43.075246;124.7301,43.075588;124.705676,43.066287";
// var siping_yitong = "125.69856,43.159455;125.71094,43.145087;125.715378,43.134793;125.725056,43.129752;125.763785,43.135943;125.789178,43.103631;125.761329,43.092263;125.738352,43.092162;125.744211,43.081889;125.738983,43.071085;125.715416,43.063864;125.688822,43.065002;125.668908,43.083287;125.666393,43.082402;125.665934,43.066856;125.670723,43.061151;125.641169,43.076562;125.615797,43.073263;125.591668,43.076397;125.565144,43.096881;125.549985,43.102503;125.500886,43.106488;125.485028,43.155521;125.485218,43.169048;125.496754,43.187553;125.490238,43.201717;125.485311,43.207353;125.470036,43.209848;125.463224,43.221474;125.453818,43.22573;125.453798,43.229729;125.443863,43.2291;125.440123,43.236465;125.427152,43.2392;125.416982,43.235636;125.408221,43.225987;125.379634,43.230478;125.374088,43.225064;125.379629,43.207776;125.377206,43.192683;125.362836,43.168997;125.3726,43.158892;125.407072,43.153791;125.408958,43.146091;125.395757,43.12682;125.395028,43.114575;125.391863,43.112176;125.381653,43.116718;125.371812,43.116471;125.344108,43.133751;125.319669,43.140772;125.276322,43.15996;125.272872,43.167332;125.253068,43.181747;125.239981,43.198245;125.21615,43.217362;125.211327,43.231127;125.205531,43.235003;125.16428,43.23562;125.150824,43.22971;125.107542,43.227699;125.099243,43.221878;125.090661,43.223222;125.076187,43.220885;125.065525,43.22201;125.05915,43.226496;125.048656,43.228347;125.025351,43.216848;125.018896,43.20533;125.011608,43.202528;124.976874,43.19961;124.971733,43.196297;124.969075,43.181092;124.918465,43.151707;124.901196,43.1482;124.889477,43.140778;124.877749,43.148854;124.870447,43.14839;124.862577,43.152095;124.842464,43.171445;124.852617,43.177367;124.853677,43.187482;124.843309,43.20094;124.831637,43.208099;124.820906,43.219624;124.83584,43.228332;124.877815,43.289829;124.886845,43.293606;124.888399,43.298172;124.897124,43.295972;124.904027,43.303007;124.912078,43.303009;124.919277,43.309617;124.919458,43.315532;124.923375,43.315199;124.92827,43.321344;124.936342,43.322942;124.935973,43.335351;124.925159,43.34244;124.925775,43.352215;124.922243,43.356684;124.890738,43.364948;124.885908,43.393101;124.87681,43.399868;124.873441,43.408268;124.866809,43.411792;124.876704,43.425816;124.892119,43.431498;124.899424,43.428337;124.904153,43.429498;124.921021,43.444102;124.918788,43.455558;124.921218,43.464337;124.914027,43.467923;124.900909,43.488901;124.928123,43.507321;124.969738,43.516282;124.992504,43.524596;125.009992,43.545531;125.010452,43.551151;125.018159,43.555221;125.041166,43.587235;125.071844,43.604529;125.078096,43.60678;125.083693,43.604684;125.100051,43.608944;125.103832,43.600358;125.126387,43.617435;125.161214,43.628554;125.208127,43.638346;125.232633,43.640178;125.242586,43.647241;125.255591,43.651664;125.288208,43.654382;125.305187,43.651056;125.307064,43.617477;125.298133,43.607041;125.300814,43.601659;125.297212,43.591908;125.278473,43.570437;125.288777,43.548713;125.286467,43.5447;125.27214,43.541249;125.265632,43.530071;125.275272,43.505202;125.266315,43.496397;125.252066,43.49415;125.244318,43.484082;125.251525,43.459845;125.257241,43.454801;125.293229,43.450238;125.306601,43.454738;125.315699,43.462179;125.331495,43.465616;125.360209,43.481921;125.371211,43.484638;125.383976,43.48194;125.391019,43.484364;125.389952,43.491028;125.383666,43.494887;125.385964,43.501981;125.375229,43.507436;125.375993,43.510695;125.372066,43.5125;125.374911,43.515425;125.365494,43.51901;125.377152,43.532367;125.3936,43.538578;125.383847,43.558537;125.395543,43.563925;125.407299,43.591817;125.435137,43.59508;125.455112,43.608795;125.459521,43.597839;125.456925,43.592145;125.459064,43.582223;125.465409,43.567734;125.462613,43.5485;125.453487,43.536558;125.463749,43.529068;125.469338,43.50595;125.48315,43.489724;125.493123,43.487568;125.498716,43.478667;125.494589,43.461839;125.508285,43.457884;125.514947,43.450957;125.522522,43.448737;125.50991,43.437685;125.504573,43.424785;125.512976,43.423616;125.519705,43.418771;125.540831,43.41729;125.55511,43.410032;125.58243,43.413632;125.589968,43.400564;125.588601,43.385648;125.587758,43.38331;125.568671,43.37834;125.553565,43.371051;125.551648,43.363071;125.560871,43.350705;125.577136,43.342083;125.602444,43.32027;125.614131,43.318399;125.626853,43.312196;125.634414,43.301839;125.679284,43.285987;125.689942,43.278994;125.694405,43.258222;125.682035,43.247226;125.675024,43.245323;125.668861,43.234842;125.660065,43.230828;125.655326,43.221257;125.660673,43.20562;125.672175,43.204562;125.677828,43.193732;125.693738,43.177593;125.691426,43.17047;125.69856,43.159455"

var shuangliao_maolin = "123.441047,43.91511;123.45657,43.913032;123.458869,43.91511;123.461169,43.91802;123.461744,43.919682;123.463469,43.923007;123.469793,43.925915;123.474392,43.926746;123.483016,43.929655;123.486465,43.931317;123.491065,43.932564;123.494514,43.935888;123.496814,43.937549;123.501413,43.939627;123.508312,43.94295;123.511761,43.944612;123.514061,43.946273;123.512911,43.942535;123.516936,43.941288;123.52096,43.940458;123.526134,43.942119;123.526134,43.944196;123.530159,43.94295;123.533033,43.946689;123.534758,43.945858;123.556605,43.952504;123.562929,43.955411;123.564079,43.959149;123.566953,43.95998;123.567528,43.962472;123.564079,43.962887;123.560629,43.966624;123.564079,43.967039;123.564654,43.971192;123.562929,43.974514;123.564654,43.986138;123.570403,43.985723;123.583051,43.986553;123.586501,43.986553;123.59225,43.986553;123.595124,43.986968;123.606048,43.988629;123.62272,43.990289;123.72678,43.99942;123.733679,43.999005;123.737703,43.99776;123.741728,43.9961;123.744027,43.9961;123.748627,43.996515;123.752076,43.99942;123.75725,44.002325;123.7584,44.00274;123.762425,44.0044;123.772198,44.00606;123.767671,44.006177;123.762658,44.00641;123.758328,44.008641;123.772755,44.016058;123.772576,44.021464;123.778181,44.027933;123.778199,44.027985;123.774309,44.035581;123.764365,44.044517;123.760673,44.047835;123.745087,44.051696;123.742887,44.052253;123.727786,44.061479;123.722603,44.071259;123.708185,44.08223;123.700253,44.083868;123.691926,44.107531;123.690893,44.110463;123.687443,44.112282;123.679987,44.111874;123.671372,44.111376;123.669962,44.108535;123.668112,44.10478;123.666908,44.102307;123.665264,44.098993;123.66459,44.097633;123.654008,44.093917;123.644962,44.083467;123.615561,44.087786;123.594181,44.09505;123.569118,44.090544;123.564375,44.089696;123.55294,44.085733;123.545053,44.083014;123.517942,44.08473;123.509516,44.085267;123.478192,44.087248;123.473664,44.087546;123.431435,44.08157;123.39941,44.077036;123.371428,44.077153;123.359409,44.077211;123.342081,44.073248;123.34747,44.051897;123.356948,44.038199;123.364969,44.016271;123.39553,43.984419;123.395548,43.981461;123.383834,43.979334;123.37353,43.96931;123.373737,43.965703;123.378076,43.962536;123.38404,43.964652;123.388586,43.958371;123.402779,43.960064;123.419047,43.936517;123.43033,43.931389;123.439601,43.915364;123.439646,43.915299;123.441038,43.91513"
// var shuangliao_bolishanzhen = "123.482297,43.856902;123.487471,43.85607;123.489771,43.857734;123.491496,43.85607;123.492646,43.859398;123.49782,43.85607;123.500119,43.856486;123.498395,43.861478;123.500694,43.862726;123.501844,43.862726;123.503569,43.863974;123.507593,43.865638;123.509893,43.868966;123.516792,43.871461;123.519667,43.871045;123.524266,43.872293;123.524841,43.873957;123.53289,43.875204;123.534614,43.873957;123.539214,43.876868;123.542663,43.877284;123.544388,43.878531;123.557036,43.875204;123.579458,43.86855;123.582907,43.868966;123.582907,43.866886;123.585782,43.866054;123.593256,43.867718;123.613378,43.852326;123.622576,43.845252;123.617977,43.843172;123.612228,43.83693;123.609353,43.837346;123.603604,43.833184;123.59728,43.831935;123.593831,43.832768;123.589231,43.829022;123.589806,43.826941;123.587507,43.82486;123.581757,43.826524;123.561635,43.821113;123.561635,43.817783;123.561061,43.815701;123.557611,43.815701;123.554736,43.811954;123.549562,43.810289;123.543238,43.80904;123.540938,43.80904;123.536339,43.812371;123.53174,43.811954;123.527715,43.80904;123.522541,43.807374;123.516217,43.805709;123.512768,43.805709;123.506444,43.801961;123.502994,43.800712;123.501269,43.801129;123.499545,43.801545;123.499257,43.803705;123.481399,43.840779;123.482189,43.856746;123.482297,43.856902"
var shuangliao_bolishanzhen = "123.482297,43.856902;123.487471,43.85607;123.489771,43.857734;123.491496,43.85607;123.492646,43.859398;123.49782,43.85607;123.500119,43.856486;123.498395,43.861478;123.500694,43.862726;123.501844,43.862726;123.503569,43.863974;123.507593,43.865638;123.509893,43.868966;123.516792,43.871461;123.519667,43.871045;123.524266,43.872293;123.524841,43.873957;123.53289,43.875204;123.534614,43.873957;123.539214,43.876868;123.542663,43.877284;123.544388,43.878531;123.557036,43.875204;123.579458,43.86855;123.582907,43.868966;123.582907,43.866886;123.585782,43.866054;123.593256,43.867718;123.613378,43.852326;123.622576,43.845252;123.617977,43.843172;123.612228,43.83693;123.609353,43.837346;123.603604,43.833184;123.59728,43.831935;123.593831,43.832768;123.589231,43.829022;123.589806,43.826941;123.587507,43.82486;123.581757,43.826524;123.582117,43.815345;123.561635,43.817783;123.561061,43.815701;123.557611,43.815701;123.554736,43.811954;123.549562,43.810289;123.543238,43.80904;123.540938,43.80904;123.536339,43.812371;123.53174,43.811954;123.527715,43.80904;123.522541,43.807374;123.516217,43.805709;123.512768,43.805709;123.506444,43.801961;123.502994,43.800712;123.501269,43.801129;123.499545,43.801545;123.499257,43.803705;123.481399,43.840779;123.482189,43.856746;123.482297,43.856902"

areas.push(siping_shuangliao);
areas.push(shuangliao_maolin);
areas.push(shuangliao_bolishanzhen);


// areas.push(siping_lishu);
// areas.push(siping_tiexiqu);
// areas.push(siping_shuangliao);
// areas.push(siping_tiedong);
// areas.push(siping_yitong);
colors = ["#fff492", "#fff492", "#98d057", "#e19ee6", "#1ad3da", "#ffc11d", "#98e800", "#b95817", "#03a9f4", "#39d0a4", "#8bbb53"];
var pointArray = [];
for (var i = 0; i < areas.length; i++) {

var ply = new BMap.Polygon(areas[i], {strokeWeight: 2, strokeColor: "#ff0000"}); //建立多边形覆盖物
ply.setFillColor(colors[i]) //设置多边形的填充颜色
ply.setFillOpacity(0.35);
// ply.enableEditing(); //范围可编辑 【编辑的时候开启】


if (i == 2) {
polygons.push(ply); //加入多边形数组,以之后获取多边形边界点集
ply.enableEditing(); //范围可编辑 【编辑的时候开启】
}

map.addOverlay(ply); //添加覆盖物
pointArray = pointArray.concat(ply.getPath());
}
map.setViewport(pointArray); //调整视野

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//获取编辑后的边界点集【编辑完后,可以通过 button 来触发这个方法】
function getpoints() {
for (var j = 0; j < polygons.length; j++) {
var polyline = polygons[j];
var pts = polyline.getPath();

var sss = "";
for (var i = 0; i < pts.length; i++) {
sss += pts[i].lng + "," + pts[i].lat + ";";
}
document.getElementById("info").innerHTML = "";
document.getElementById("info").innerHTML += sss; //输出数组里的经纬度

console.log(sss); //【将点集输出到控制台】
}

alert('已输出边界点集合!');
}
参考资料

Android修改全局Window窗口大小

在项目开发过程中,如果屏幕宽高比过于悬殊,例如电视大小的安卓设备(定制),在显示应用的时候需要居中显示,两边留白,按照手机大小显示应用。怎么实现呢?
思路是通过修改应用的window大小来实现。但是window是绑定activity的,所以需要修改所有界面的window大小。

实现效果如下:

实现方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 定义activity生命周期监听
private ActivityLifecycleCallbacks mActivityLifecycleCallbacks = new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (Build.MODEL.equalsIgnoreCase("3280")
&& Build.MANUFACTURER.equalsIgnoreCase("rockchip")) {
//特殊设备,设置宽度
WindowManager m = activity.getWindowManager();
Display d = m.getDefaultDisplay(); //为获取屏幕宽、高
android.view.WindowManager.LayoutParams p = activity.getWindow().getAttributes(); //获取对话框当前的参数值
p.height = (int) (d.getHeight() * 1); //高度设置为屏幕的0.6
p.width = (int) (d.getWidth() * 0.50); //宽度设置为屏幕的0.95
activity.getWindow().setAttributes(p); //设置生效
}
}

@Override
public void onActivityStarted(Activity activity) {

}

@Override
public void onActivityResumed(Activity activity) {

}

@Override
public void onActivityPaused(Activity activity) {

}

@Override
public void onActivityStopped(Activity activity) {

}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

}

@Override
public void onActivityDestroyed(Activity activity) {

}
};

在application中注册

1
2
3
4
5
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
}

application注销的时候取消注册

1
2
3
4
5
@Override
public void onTerminate() {
super.onTerminate();
unregisterActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
}

使用 pyenv 管理 Python 版本

转自使用 pyenv 管理 Python 版本 ,略有修改。

前言

在使用python的时候,我们经常被python项目混乱的版本以及复杂的包依赖搞得头晕脑胀,但是自从有了pyenv和anaconda(python包虚拟环境,包依赖),一切都清晰明了了。

pyenv 是 Python 版本管理工具。 pyenv 可以改变全局的 Python 版本,安装多个版本的 Python, 设置目录级别的 Python 版本,还能创建和管理 virtual python environments 。所有的设置都是用户级别的操作,不需要 sudo 命令。

pyenv 主要用来管理 Python 的版本,比如一个项目需要 Python 2.x ,一个项目需要 Python 3.x 。 而 anaconda 主要用来管理 Python 包的依赖,不同项目需要依赖的包版本不同,则需要使用虚拟环境。

pyenv 通过系统修改环境变量来实现 Python 不同版本的切换。而 anaconda 通过将 Python 包安装到一个目录来作为Python 包虚拟环境,通过切换目录来实现不同包环境间的切换。

pyenv 的美好之处在于,它并没有使用将不同的 PATH 植入不同的Shell这种高耦合的工作方式,而是简单的在 PATH 的最前面插入了一个垫片路径(shims):~/.pyenv/shims:/usr/local/bin:/usr/bin:/bin。所有对 Python 可执行文件的查找都会首先被这个 shims 路径截获,从而使后方的系统路径失效。

pyenv安装

具体的安装方式参照 官网

  • centos自动安装

    1
    2
    # curl https://pyenv.run | bash
    # exec $SHELL # Restart your shell so the path changes take effect
  • centos手动下载安装
    在服务器环境中,发现使用上一种方式经常不好使,网络无法链接,所以采用以下这种方式,

    1
    2
    3
    4
    5
    6
    $ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
    $ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
    $ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile

    $ echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init -)"\nfi' >> ~/.bash_profile
    $exec "$SHELL"

我这里是Mac,推荐使用brew安装。
安装成功之后,在.bashrc 或者 .bash_profile 中添加三行来开启自动补全.

1
2
3
export PATH="$HOME/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

pyenv 常用命令

使用 pyenv commands 显示所有可用命令

  • 查看本机安装 Python 版本

    1
    pyenv versions
  • 查看可安装 Python 版本

    1
    pyenv install -l
  • python 安装与卸载

    1
    2
    $ pyenv install 2.7.3   # 安装python
    $ pyenv uninstall 2.7.3 # 卸载python
  • python切换

    1
    2
    $ pyenv global 2.7.3  # 设置全局的 Python 版本,通过将版本号写入 ~/.pyenv/version 文件的方式。
    $ pyenv local 2.7.3 # 设置 Python 本地版本,通过将版本号写入当前目录下的 .python-version 文件的方式。通过这种方式设置的 Python 版本优先级较 global 高。

python优先级

shell > local > global

pyenv 会从当前目录开始向上逐级查找 .python-version 文件,直到根目录为止。若找不到,就用 global 版本。

1
2
3
4
$ pyenv shell 2.7.3 # 设置面向 shell 的 Python 版本,通过设置当前 shell 的 PYENV_VERSION 环境变量的方式。这个版本的优先级比 local 和 global 都要高。–unset 参数可以用于取消当前 shell 设定的版本。
$ pyenv shell --unset

$ pyenv rehash # 创建垫片路径(为所有已安装的可执行文件创建 shims,如:~/.pyenv/versions/*/bin/*,因此,每当你增删了 Python 版本或带有可执行文件的包(如 pip)以后,都应该执行一次本命令)

使用Pandas Merge替代Excel Vlookup



事情起因是媳妇儿在处理excel数据的时候,用VLookup查找查找数据的时候,发现经常出现查找错误,折腾了好几天,也没找到规律,所以我决定用Python Pandas替换Excel的VLookup函数。

假设我们有这样一个Excel文件:
Sheet1为:

Sheet2为:

其中Sheet2数据比Sheet1数据多一条,我们要实现的目标是把Sheet1中人员对应的年龄放到Sheet2对应的人员上,如果人员在Sheet1中不存在,则不用处理。
预期结果如下图所示:

正常我们可以用Excel的VLookup实现这个功能,但是不知道为啥,VLookup在数量大的时候经常查询错乱,所以这里用Pandas的merge功能实现VLookup。

1
What is vlookup? Vlookup is essentially combining two different tables using a shared column.

查看更多

Android项目在Gitlab-CI上的使用

Setting up GitLab CI for Android projects

搭建Android环境
下载JDK
下载Android Sdk

由于我们安装的Runner环境一般都是在服务器上,是没有界面操作的,所有需要我们通过无ui 的方式下载更新安卓sdk,
先下载android sdk tools

1
2
- wget --quiet --output-document=android-sdk.zip https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
- unzip -q android-sdk.zip -d android-sdk-linux

要使用sdk tools下载sdk首先需要接受安卓sdk的协议

1
2
3
- mkdir android-sdk-linux/licenses
- printf "8933bad161af4178b1185d1a37fbf41ea5269c55\nd56f5187479451eabf01fb78af6dfcb131a6481e" > android-sdk-linux/licenses/android-sdk-license
- printf "84831b9409646a918e30573bab4c9c91346d8abd" > android-sdk-linux/licenses/android-sdk-preview-license

下载sdk
1
2
3
4
5
6
/home/gitlab-runner/CI_runner/android-sdk-linux/tools/bin/sdkmanager --update > update.log
# SDK检查更新
/home/gitlab-runner/CI_runner/android-sdk-linux/tools/bin/sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" "build-tools;${ANDROID_BUILD_TOOLS}" "extras;google;m2repository" "extras;android;m2repository" > installPlatform.log
#下载sdk,support包等
/home/gitlab-runner/CI_runner/android-sdk-linux/tools/bin/sdkmanager "extras;m2repository;com;android;support;constraint;constraint-layout;1.0.2" > installPlatform.log
# 下载constrain包

但是由于众所周知的GFW的问题,在国内下载安卓sdk的速度着实让人崩溃,所以一般需要配置上国内镜像,如下:

1
sdkmanager --proxy=http --proxy_host=mirrors.neusoft.edu.cn --proxy_port=80 --update

具体镜像可以从网上找一下。

配置环境变量
1
2
3
4
5
6
7
# vim /home/gitlab-runner/.bash_profile

export JAVA_HOME=/home/gitlab-runner/CI_runner/jdk1.8.0_181
export ANDROID_HOME=/home/gitlab-runner/CI_runner/android-sdk-linux
export PATH=$PATH:$JAVA_HOME/bin
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools/
编写Gitlab CI脚本

配置完环境之后,就需要编写CI脚本完成自动打包,测试,部署等工作。

简介

配置好 Runner 之后,我们要做的事情就是在项目根目录中添加 .gitlab-ci.yml 文件了。
当我们添加了 .gitlab-ci.yml 文件后,每次提交代码或者合并 MR 都会自动运行构建任务了。

还记得 Pipeline 是怎么触发的吗?Pipeline 也是通过提交代码或者合并 MR 来触发的!
那么 Pipeline 和 .gitlab-ci.yml 有什么关系呢?
其实 .gitlab-ci.yml 就是在定义 Pipeline 而已拉!

基本写法

我们先来看看 .gitlab-ci.yml 是怎么写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 定义 stages
stages:
- build
- test
# 定义 job
job1:
stage: test
script:
- echo "I am job1"
- echo "I am in test stage"
# 定义 job
job2:
stage: build
script:
- echo "I am job2"
- echo "I am in build stage"

写起来很简单吧!用 stages 关键字来定义 Pipeline 中的各个构建阶段,然后用一些非关键字来定义 jobs。
每个 job 中可以可以再用 stage 关键字来指定该 job 对应哪个 stage。
job 里面的 script 关键字是最关键的地方了,也是每个 job 中必须要包含的,它表示每个 job 要执行的命令。

回想一下我们之前提到的 Stages 和 Jobs 的关系,然后猜猜上面例子的运行结果?

1
2
3
4
I am job2
I am in build stage
I am job1
I am in test stage

根据我们在 stages 中的定义,build 阶段要在 test 阶段之前运行,所以 stage:build 的 jobs 会先运行,之后才会运行 stage:test 的 jobs。

常用的关键字

下面介绍一些常用的关键字,想要更加详尽的内容请前往 官方文档

  • stages
    定义 Stages,默认有三个 Stages,分别是 build, test, deploy。

  • before_script
    定义任何 Jobs 运行前都会执行的命令。

  • after_script
    定义任何 Jobs 运行完后都会执行的命令。

  • variables && Job.variables
    定义环境变量。
    如果定义了 Job 级别的环境变量的话,该 Job 会优先使用 Job 级别的环境变量。

我自己在项目中用到的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
image: openjdk:8-jdk

cache:
paths:
- .m2/
- .gradle/

variables:
ANDROID_COMPILE_SDK: "27"
ANDROID_BUILD_TOOLS: "27.0.3"
ANDROID_SDK_TOOLS: "27.1.1"
APP_NAME: "长白山先锋"


before_script:
# - apt-get --quiet update --yes
# - apt-get --quiet install --yes wget tar unzip lib32stdc++6 lib32z1
#
# - wget --quiet --output-document=android-sdk.zip https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
# - unzip -q android-sdk.zip -d android-sdk-linux

# - mkdir android-sdk-linux/licenses
# - printf "8933bad161af4178b1185d1a37fbf41ea5269c55\nd56f5187479451eabf01fb78af6dfcb131a6481e" > android-sdk-linux/licenses/android-sdk-license
# - printf "84831b9409646a918e30573bab4c9c91346d8abd" > android-sdk-linux/licenses/android-sdk-preview-license
- echo $PATH
- whoami
- echo $PWD
- /home/gitlab-runner/CI_runner/android-sdk-linux/tools/bin/sdkmanager --update > update.log
- /home/gitlab-runner/CI_runner/android-sdk-linux/tools/bin/sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" "build-tools;${ANDROID_BUILD_TOOLS}" "extras;google;m2repository" "extras;android;m2repository" > installPlatform.log
- /home/gitlab-runner/CI_runner/android-sdk-linux/tools/bin/sdkmanager "extras;m2repository;com;android;support;constraint;constraint-layout;1.0.2" > installPlatform.log

# - export ANDROID_HOME=$PWD/android-sdk-linux
# - export PATH=$PATH:$PWD/android-sdk-linux/platform-tools/
- chmod +x ./gradlew

stages:
- build
# - test
- deploy

build:
stage: build
only:
- v3_0502
script:
# - ./gradlew assembleRelease
- ./gradlew assembleRelease
artifacts:
paths:
- $PWD/apk/

#unitTests:
# stage: test
# script:
# - ./gradlew test

deploy:
stage: deploy
script:
- echo $PATH
- rm -rfv /data/apps/${APP_NAME}
- mkdir /data/apps/${APP_NAME}
- cp $PWD/apk/*.apk /data/apps/${APP_NAME}
- lftp -u ftpuser_name,passwd -p port -e "mirror -R --delete --only-newer /data/apps/${APP_NAME} /ftp/apk ;quit;" sftp://xxx.xxx.xxx.xxx

# 把本地/data/apps/${APP_NAME}目录的文件拷贝到服务器/ftp/apk目录下,并且删除服务器/ftp/apk目录下的其他文件

问题 1
如果在代码提交之后,CI在下载代码的时候,报以下错误:

1
2
Peer's certificate issuer has been marked as not trusted by the user.
ERROR: Job failed: exit status 1

只需要在runner服务器上修改如下就行

1
2
3
4
# vim /etc/profile
#在最后一行加入

export GIT_SSL_NO_VERIFY=1

问题 2
在build结束的时候,runner会把生成的包当成artifacts上传到CI服务器上,如下所示文件

1
2
3
artifacts:
paths:
- $PWD/apk/

但是gitlba 默认只给每个项目100M的附件空间,所以如果你项目生成的文件比较多,有可能会报一下错误

1
2
ERROR: Uploading artifacts to coordinator... too large archive  id=1004 responseStatus=413 Request Entity Too Large status=413 Request Entity Too Large token=y9oZhbeX
FATAL: Too large

这时候只需调整如图所示图片即可

问题 3
修改runner 任务artifacts上传存储路径

1
2
3
4
5
6
vim /etc/gitlab/gitlab.rb

### For storing GitLab application uploads, eg. LFS objects, build artifacts
###! Docs: https://docs.gitlab.com/ce/development/shared_files.html
# gitlab_rails['shared_path'] = '/var/opt/gitlab/gitlab-rails/shared'
gitlab_rails['shared_path'] = '/data/gitlab_shared'

然后执行

1
然后执行sudo gitlab-ctl reconfigure

参考资料

搭建GitLab CI服务器

写在前面

之前已经用Bitnami-gitlab一体包已经在虚拟机上搭建了Git服务器,在项目开发过程中,经常面临频繁打包的问题,搞得软件研发人员很疲惫,之前就听说过可以使用“持续集成”的思路解决这样的问题,并且能够提高工作效率,所在才有了今天在持续集成上的尝试。

什么是“持续集成”

持续集成(Continuous Integration)指的是,频繁地(一天多次)将代码集成到主干。
持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。
它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。

使用GitLab CI进行持续集成

我这里因为已经使用了Gitlab作为代码服务器,而Gitlab本身已经支持了CI,所以我这里就选择了使用Gitlab CI进行持续集成。

在开始之前先了解一些持续集成的相关概念。

Pipeline

一次 Pipeline 其实相当于一次构建任务,里面可以包含多个流程,如安装依赖、运行测试、编译、部署测试服务器、部署生产服务器等流程。
任何提交或者 Merge Request 的合并都可以触发 Pipeline,如下图所示:

1
2
3
4
5
+------------------+           +----------------+
| | trigger | |
| Commit / MR +---------->+ Pipeline |
| | | |
+------------------+ +----------------+

Stages

Stages 表示构建阶段,说白了就是上面提到的流程。
我们可以在一次 Pipeline 中定义多个 Stages,这些 Stages 会有以下特点:

  1. 所有 Stages 会按照顺序运行,即当一个 Stage 完成后,下一个 Stage 才会开始
  2. 只有当所有 Stages 完成后,该构建任务 (Pipeline) 才会成功
  3. 如果任何一个 Stage 失败,那么后面的 Stages 不会执行,该构建任务 (Pipeline) 失败

因此,Stages 和 Pipeline 的关系就是:

1
2
3
4
5
6
7
8
9
+--------------------------------------------------------+
| |
| Pipeline |
| |
| +-----------+ +------------+ +------------+ |
| | Stage 1 |---->| Stage 2 |----->| Stage 3 | |
| +-----------+ +------------+ +------------+ |
| |
+--------------------------------------------------------+

Jobs

Jobs 表示构建工作,表示某个 Stage 里面执行的工作。
我们可以在 Stages 里面定义多个 Jobs,这些 Jobs 会有以下特点:

  1. 相同 Stage 中的 Jobs 会并行执行
  2. 相同 Stage 中的 Jobs 都执行成功时,该 Stage 才会成功
  3. 如果任何一个 Job 失败,那么该 Stage 失败,即该构建任务 (Pipeline) 失败

所以,Jobs 和 Stage 的关系图就是:

1
2
3
4
5
6
7
8
9
+------------------------------------------+
| |
| Stage 1 |
| |
| +---------+ +---------+ +---------+ |
| | Job 1 | | Job 2 | | Job 3 | |
| +---------+ +---------+ +---------+ |
| |
+------------------------------------------+

GitLab Runner

简介
理解了上面的基本概念之后,有没有觉得少了些什么东西 —— 由谁来执行这些构建任务呢?
答案就是 GitLab Runner 了!

想问为什么不是 GitLab CI 来运行那些构建任务?
一般来说,构建任务都会占用很多的系统资源 (譬如编译代码),而 GitLab CI 又是 GitLab 的一部分,如果由 GitLab CI 来运行构建任务的话,在执行构建任务的时候,GitLab 的性能会大幅下降。

GitLab CI 最大的作用是管理各个项目的构建状态,因此,运行构建任务这种浪费资源的事情就交给 GitLab Runner 来做拉!
因为 GitLab Runner 可以安装到不同的机器上 ,所以在构建任务运行期间并不会影响到 GitLab 的性能.

我这里采用的是:
git服务器地址为:192.168.1.113,
Gitlab runner地址为192.168.1.120

安装GitLab Runner

参照官网,选择一种方式进行安装:

1
2
3
4
5
# For RHEL/CentOS/Fedora
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash

# For RHEL/CentOS/Fedora
sudo yum install gitlab-runner

注册 Runner

安装好 GitLab Runner 之后,我们只要启动 Runner 然后和 CI 绑定就可以了。

  • 打开你 GitLab 中的项目页面,在项目设置中找到 runners
  • 运行 sudo gitlab-ci-multi-runner register
  • 输入 CI URL
  • 输入 Token
  • 输入 Runner 的名字
  • 输入 gitlab-ci tags
  • 完成

当注册好 Runner 之后,可以用 sudo gitlab-runner list 命令来查看各个 Runner 的状态:

1
2
3
# sudo gitlab-runner list
Listing configured runners ConfigFile=/etc/gitlab-runner/config.toml
localhost.localdomain Executor=shell Token=2a627041123ff25c490629d80f0834 URL=https://192.168.1.113/

Register问题1
这一步比较坑,因为bitnami-gitlab一体包采用的是https的访问方式,所以在注册Runner的过程,一直报如下错误:

1
2
ERROR: Registering runner... failed                 runner=5nuRqxMY status=couldn't execute POST against https://192.168.1.113/api/v4/runners: Post https://192.168.1.113/api/v4/runners: x509: cannot validate certificate for 192.168.1.113 because it doesn't contain any IP SANs
PANIC: Failed to register this runner. Perhaps you are having network problems

解决方式参考如下地址,gitlab-ci-runner-ignore-self-signed-certificate

  • First edit ssl configuration on the GitLab server (not the runner)

    1
    2
    3
    4
    vim /opt/gitlab/embedded/ssl/openssl.cnf

    [ v3_ca ]
    subjectAltName=IP:192.168.1.1 <---- Add this line. 192.168.1.1 is your GitLab server IP.
  • Re-generate self-signed certificate

    1
    2
    3
    4
    cd /etc/gitlab/ssl
    sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /etc/gitlab/ssl/192.168.1.1.key -out /etc/gitlab/ssl/192.168.1.1.crt
    sudo openssl dhparam -out /etc/gitlab/ssl/dhparam.pem 2048
    sudo gitlab-ctl restart
  • Copy the new CA to the GitLab CI runner

    1
    scp /etc/gitlab/ssl/192.168.1.113.crt root@192.168.1.120:/etc/gitlab-runner/certs

这样就解决Https证书问题了。

Register问题2
在mac端配置Runner的时候,会遇到如下问题:

1
x509: certificate signed by unknown authority

主要是mac对git代码服务器的证书不信任,在注册的时候加上ca证书就行了。

1
sudo gitlab-runner register --tls-ca-file 192.168.1.113.crt

unregister Runner
  • 所有runners

    1
    gitlab-runner unregister --all-runners
  • By URL and token:

    1
    gitlab-runner unregister --url http://gitlab.example.com/ --token t0k3n
  • By name:

    1
    gitlab-runner unregister --name test-runner

完毕!

如果想知道如何用Gitlab-CI设置Android项目,请参考我下一篇文章Android项目在Gitlab-CI上的使用;

参考资料