-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearchData.json
More file actions
1 lines (1 loc) · 66.3 KB
/
Copy pathsearchData.json
File metadata and controls
1 lines (1 loc) · 66.3 KB
1
[{"title":"Golang 学习之旅 --- 代码实战","url":"/2019/03/27/Golang 学习之旅 --- 代码实战/","content":"\n代码实战会从零开始,开发一个对图片进行马赛克处理的Web应用。它接收用户上传的目标图片,并据此生成相应的马赛克图片。\n编程的过程中没有使用第三方库,用的都是Go自带的标准库来完成,可以更好让我们理解Go最基础的一些知识。\n\n这个Web应用的核心是一个Go语言的马赛克算法,下面是这个算法的构思:\n\n(1)通过扫描图片目录,并使用图片的文件名作为键,图片的平均颜色作为值,构建出一个由瓷砖图片组成的散列,也就是一个瓷砖图片数据库。通过计算图片中每个像素红、绿、黄3种颜色的总和,并将它们除以像素的总数量,我们就得到了一个三元组,这个三元组就是图片的平均颜色。\n(2)根据瓷砖图片的大小,将目标图片分割成一系列尺寸更小的子图片。\n(3)对于目标图片切割出的每张子图片,将它们位于左上方的第一个像素设定为该图片的平均颜色。\n(4)根据子图片的平均颜色,在瓷砖图片数据库中找到一张平均颜色与之最接近的瓷砖图片,然后在目标图片的相应位置上使用瓷砖图片去代替原有的子图片。为了找到最接近的平均颜色,程序需要将子图片的平均颜色以及瓷砖图片的平均颜色都转换成三维空间中的一个点,并计算这两个点之间的欧几里得距离。\n(5)当一张瓷砖图片被选中之后,程序就会把这张图片从瓷砖图片数据库中移除,以此来保证马赛克图片中的每张瓷砖图片都是独一无二的。\n\nmosaic.go文件实现了上面的马赛克算法,可以在Github上看到:\n\nhttps://github.com/JimmyLee05/MosaicGo-Web/blob/master/mosaic.go\n\n\n### 下面是mosaic.go文件里的各个函数\n\n计算平均颜色的averageColor函数:\n\n\n\naverageColor函数把给定图片的每个像素中的红、绿、蓝3种颜色相加起来,并将这些颜色的总和除以图片的像素数量,最后把计算的结果纪录在一个新创建的三元组里面(包含3个元素的数组)。\n\n\n之后是resize函数,把图片缩放至指定的宽度:\n\n\n\ntilesDB函数,通过扫描瓷砖图片所在目录来创建一个瓷砖图片数据库:\n\n\n\n瓷砖图片数据库是一个映射,这个映射的键为字符串,而值则为三元组(用包含3个元素的数组来表示)。\n\n通过 getNearestTile函数寻找与目标图片相匹配的瓷砖图片:\n\n\n\ngetNearestTile函数会把瓷砖图片数据库中的所有纪录与目标图片的平均颜色一一进行对比,而两者欧几里得距离最短的那一条记录,就是与目标图片平均颜色最为接近的瓷砖图片。\n\n\n### 马赛克Web应用的main文件\n\n这个文件可以在Github上看到:\n\nhttps://github.com/JimmyLee05/MosaicGo-Web/blob/master/main.go\n\nmain函数:\n\n\n\nfunc main()是 Go语言程序的默认入口函数,函数主体用 {}括号包裹。\nmain函数只能用于main包中,且只能定义一个。\n\nupload函数:\n\n\n\n\nmosaic函数:\n\n\n\n这是并发版的mosaic处理器函数,上传的目标图片被分割成4份来独立处理。在mosaic函数里,程序调用的都是普通函数而不是goroutine,这是因为程序的并发部分存在于被调用函数的内部。cut函数会在内部以goroutine方式执行一个匿名函数,而这个匿名函数则会返回一个通道作为结果。\n\ncut函数:\n\n\n\n首先在cut函数里创建一个通道,并启动一个匿名goroutine来计算将要被发送至该通道的马赛克处理结果,接着再把这个通道返回给cut函数的调用者。这样一来,cut函数创建的通道就会立即返回给 mosaic处理器函数,而通道对应的马赛克子图片则会在处理完毕后被发送至通道。\n\ncombine函数:\n\n\n\ncombine函数接受的输入包含了4个来自cut函数的通道,这些通道包含了马赛克图片的各个组成部分,并且程序不知道这些部分什么时候才会通过通道传输过来。为此程序采用select方法,以先到先服务的方式来接收这些通道发送的消息。\n\n\n\n\n\n\n\n\n\n\n\n\n","tags":["原创","读书"],"categories":["读书笔记"]},{"title":"Golang 学习之旅 --- 基础知识","url":"/2019/03/25/Golang 学习之旅 --- 基础知识/","content":"\n### 1.并发和并行的区别\n并发 (concurrency):指两个或多个任务在同一时间段内启动运行并结束,并且这些任务可能会互动。 \n以并发形式执行的多个任务会同时存在,这跟顺序执行每次只会存在一个任务的情况正好相反。 \n\n并行 (parallelism):在并行中,多个任务将同时启动并执行。并行通过把一个大任务分割成多个更小的任务,然后通过同时执行这些小任务来提高性能。并行通常需要独立的资源(如CPU),而并发则会使用和分享相同的资源。 \n\n并发指的是同时处理多项任务,而并行指的是同时执行多项任务。 \n\n### 2.goroutine\ngoroutine指的是那些独立于其它 goroutine运行的函数。这一个概念看上去和线程有些相似,但实际上 goroutine并不是线程,它只是对线程的多路复用。因为 goroutine都是轻量级的,所以goroutine的数量可以比线程的数量多很多。一个 goroutine在启动时只需要一个非常小的栈,并且这个栈可以按需扩展和缩小。当一个 goroutine被阻塞时,它也会阻塞所复用的操作系统线程,而运行时环境(runtime)则会把位于被阻塞线程上的其它 groutine移动到其它未阻塞的线程上继续运行。\n\n### 3.信道\n信道就像一个箱子,不同的 goroutine可以通过这个箱子与其它 goroutine通信。\n信道(channel)是一种带有类型的值,它可以让不同的goroutine互相通信。信道用make函数创建,该函数在被调用之后将返回一个指向底层数据结构的引用作为结果值。\n\n创建一个整数组成的信道:\nch := make(chan int)\n\n通过信道实现消息传递:\n\n\n\n### 4.对Web应用的理解\n一个程序只需要满足以下两个条件,就可以看作是一个 Web应用:\na.这个程序必须向发送命令请求的客户端返回HTML,而客户端则会向用户展示渲染后的HTML;\nb.这个程序向客户端传送数据时必须使用HTTP协议\n\n在这个定义的基础上,如果一个程序不是向用户渲染并展示HTML,而是向其它程序返回某种非HTML格式的数据,那么这个程序就是一个为其它程序提供服务的Web服务。\n\nWeb应用最基本的请求与相应结构:\n\n\n\n### 5.HTTP\nHTTP是万维网的应用层通讯协议,Web应用中的所有数据都是通过这个看似简单的文本协议进行传输的。\n\nHTTP是一种无状态协议,它唯一知道的就是客户端会向服务器发送请求,而服务器会向客户端返回响应,并且后续发生的请求对之前发生过的请求一无所知。\n\n请求 - 响应是两台计算机进行通信的基本方式,其中一台计算机会向另一台计算机发送请求,而接收到请求的计算机则会对请求进行响应。在客户端 - 服务器计算模型中,发送请求的一方(客户端) 负责向返回响应的一方 (服务器) 发起会话,而服务器则负责向客户端提供服务。在HTTP协议中,客户端也被称作用户代理(user-agent),而服务器通常会被称为Web服务器。在大多数情况下,HTTP客户端都是一个Web浏览器。\n\n### 6.HTTP请求\nHTTP是一种请求 - 响应协议,协议涉及的所有事情都以一个请求开始。HTTP请求和所有的HTTP报文一样,都由一系列文本行组成,并以以下顺序排列:\n(1)请求行 (request-line)\n(2)零个或多个请求首部(header)\n(3)一个空行\n(4)可选的报文主体(body)\n\nGET /Protocols/rfc2616/rfc2616.html HTTP/1.1\nHost: www.w3.org\nUser-Agent: Mozilla/5.0\n(empty line)\n\n### 7.各种HTTP方法\nGET\nHEAD\nPOST\nPUT\nDELETE\nTRACE\nOPTIONS\nCONNECT\nPATCH\n\n### 8.安全的请求方法\n如果一个 HTTP方法只要求服务器提供信息而不会对服务器的状态做任何修改,那么这个方法就是安全的。\n\n### 9.幂等的概念\n如果一个HTTP方法在使用相同的数据进行第二次调用的时候,不会对服务器的状态作出任何改变,那么这个方法就是幂等的。PUT和DELETE方法虽然不安全,但也是幂等的。\n\n### 10.请求首部\nHTTP请求方法定义了发送请求的客户端想要执行的操作,而 HTTP请求首部则记录了与请求本身以及客户端相关的信息。\n\n### 11.URL\nUniform Resource Identifier 统一资源标识符 URI\nUniform Resource Location 统一资源定位符 URL\n\n### 12.Web应用组成部分\nWeb应用可以被分为处理器(handler)和模版引擎(template engine)两个部分。\n\n### 13.处理器的作用\nWeb应用中的处理器除了要接收和处理客户端发来的请求,还需要调用模版引擎,然后由模版引擎生成 HTML并把数据填充至将要返回给客户端的响应报文当中。\n\n### 14.动态模版和静态模版\n动态模版里包含一些编程语言结构,如条件语句,迭代语句和变量。而静态模版是一些夹杂着占位符的HTML,通常不包含任何逻辑代码。\n\n### 15.服务器在 Web应用中的工作流程\n\n\n\n### 16.数据模型\n数据模型里包含一些数据结构,比如以下四种数据结构:\na.User\nb.Session\nc.Thread\nd.Post\n\n数据结构会被映射到关系数据库里,并与数据库交互\n\n### 17.请求的接收与处理\n请求的接收和处理是所有Web应用的核心。Web应用的工作流程如下:\n(1)客户端将请求发送到服务器的一个URL上\n(2)服务器的多路复用器将接收到的请求重定向到正确的处理器,然后由该处理器对请求进行处理。\n(3)处理器处理请求并执行必要的动作\n(4)处理器调用模版引擎,生成相应的HTML并将其返回给客户\n\n### 18.处理器函数的概念\n处理器函数是一种接收ResponseWriter和Request指针作为参数的 Go函数\n\nfunc index(w http.ResponseWriter, r *http.Request) {\n\tfiles := []string{“templates/layout.html”,\n\t\t\t\t “templates/navbar.html”,\n\t\t\t\t “templates/index.html”,}\n\ttemplates := template.Must(template.ParseFiles(files…))\n\tthreads, err := data.Threads(); if err == nil {\n\t\ttemplates.ExecuteTemplate(w, “layout”, threads)\n\t}\n}\n\n### 19.常用的处理器函数\nfunc handler(writer http.ResponseWriter, request *http.Request) {\n\tfmt.Fprintf(writer, “Hello World, %s!”, request.URL.Path[1:])\n}\n\nhandler在特定事件被触发后,负责对事件进行处理的回调函数。\n第一个参数是 ResponseWriter接口,第二个参数是指向 Request结构的指针。\nhandler函数会从 Request结构中提取相应的信息,然后创建一个HTTP响应,最后通过 ResponseWriter接口将响应返回给客户端。\n\n### 20.使用Cookie进行访问控制\n因为HTTP是无状态协议,所以需要Cookie来标记状态,进行访问控制。\n\n“以Cookie的方式在客户端实现数据持久化,并以会话的方式在服务器上实现数据持久化。”\n\n### 21.HTML\n超文本标记语言(Hypertext Marked Language),用来制作超文本文档的简单标记语言。\n\n用HTML编写的超文档称为HTML文档,超文本意味着可以加入图片,声音,动画等内容。\n事实上每一个HTML文档都是一种静态的网页文件,这个文件里包含了HTML指令代码,这些代码并不是一种程序语言,只是一种排版网页中资料显示位置的标记语言。\n\n### 22.Go切片\nGo的切片为处理同类型数据排列提供了一个方便且高效的方式,切片有些类似于其它语言中的数组。\nGo中的数组的长度不可改变,在特定场景中这样的集合就不太适用。切片和数组相比长度不固定,可以追加元素,在追加时可能使切片的容量增大。\n\n切片中有两个概念: 长度(len)和容量(cap)。长度指已经被赋过值的最大下标+1,可以通过内置函数len()获得。容量是指切片目前可容纳的最多元素个数,可以通过内置函数cap()获得。\n\n切片是引用类型,因此在传递切片时将引用同一指针,修改值将会影响其他的对象。\n\n### 23.空接口类型\n参数的类型为空接口类型,这说明参数可以接受任何类型的值作为输入。\n\n### 24.对数据库进行操作\nDb是一个全局变量,这个全局变量是一个指针,指向的是代表数据库连接池的sql.DB。后续的代码会使用这个Db变量来执行数据库查询操作。\n\nvar Db *sql.DB\nfunc init() {\n\tvar err error\n\tDb, err = sql.Open(“postgres”, “dbname=chitchat simone=disable”)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\treturn\n}\n\n### 25.数据层\n在处理器函数和数据库中间,需要一个数据层,来避免处理器函数直接对数据库进行访问。\n\n### 26.通过数据模型连接数据库和处理器\n\n\n\n### 27.Web应用的工作流程\n(1)客户端向服务器发送请求;\n(2)多路复用器接收到请求,将其重定向到正确的处理器;\n(3)处理器对请求进行处理;\n(4)在需要访问数据库的情况下,处理器会使用一个或多个数据结构,这些数据结构都是根据数据库中的数据建模而来的;\n(5)当处理器调用与数据结构有关的函数或方法时,这些数据结构背后的模型会与数据库进行连接,并执行相应的操作;\n(6)当请求处理完毕时,处理器会调用模版引擎,有时候还会向模版引擎传递一些通过模型获取到的数据;\n(7)模版引擎会对模版文件进行语法分析并创建相应的模版,而这些模版又会与处理器传递的数据一起合并生成最终的HTML;\n(8)生成的HTML会作为相应的一部分回传至客户端。\n\n### 28.HTTPS\n即SSL之上的 HTTP,实际上就是在 SSL/TSL连接的上层进行HTTP通信。\nSSL(Secure Sockets Layer 安全套接层)\nTLS (Transport Layer Security 传输层安全协议)\n\n\n### 29.服务器\n在 Go语言中,一个处理器就是一个拥有 ServeHTTP方法的接口,这个ServeHTTP方法需要接收两个参数: ResponseWriter接口和指向Request结构的指针。\n\n处理器的定义也可以理解为:任何一个接口只要拥有一个 ServeHTTP方法,并且该方法带有以下签名(signature),那么这个接口就是一个处理器。\n\n### 30.DefaultServeMux\n多路复用器 DefaultServeMux是 ServeMux结构的一个实例,同时也是 Handler的实例,因此它不仅是一个多路复用器,还是一个特殊的处理器。唯一要做的就是根据请求的URL将请求重定向到不同的处理器。\n\n### 31.处理器函数\n处理器函数其实就是和处理器拥有相同行为的函数,这些函数与 ServeHTTP方法拥有相同的签名,它们接收 ResponseWrite和指向 Request结构的指针作为参数。\n处理器函数就是创建处理器的一种更便捷的方法。\n\n\n### 32.管道管理\n串联起两个函数的方法称为管道管理,串联多个函数可以让程序执行更多的动作。\n函数式编程可以把一个函数传递给另外一个函数,将两个函数串联起来。\n\n### 33.ServeMux\n是一个HTTP请求多路复用器,负责接收 HTTP请求并根据请求中的 URL将请求重新定向到正确的处理器。当用户没有为 Serve结构指定处理器时,服务器就会使用 DefaultServeMux作为ServeMux的默认实例。\n\nServeMux是一个结构而不是接口,DefaultServeMux是 ServeMux的一个实例。\u001c\n\n\n### 34.HTTP报文\nHTTP报文是在客户端和服务器之间传递的消息,分为HTTP请求和HTTP响应两种类型,并且这两种类型的报文都拥有相同的结构。\n(1)请求行或者响应行\n(2)零个或多个首部\n(3)一个空行\n(4)一个可选的报文主体\n\nGET /Protocols/rfc2616/ftc2616.html HTTP/1.1\nHost: [www.w3.org](http://www.w3.org/) \nUser-Agent: Mozilla/5.0\n(empty line)\n\n### 35.Request结构\na.URL字段;\nb.Header字段;\nc.Body字段;\nd.Form字段 PostForm字段和MultipoarForm字段\n\n\n### 36.使用 Request结构的方法获取表单数据的一般步骤\na.调用ParseForm方法或者ParseForm方法,对请求进行语法分析\nb.根据步骤a调用的方法,访问相应的Form字段 PostForm字段或MultipartForm字段\n\n\n### 37.func index(w http.ResponseWriter, r *http.Request)里的 ResponseWriter\nResponseWriter是一个接口,处理器可以通过这个接口创建HTTP响应。ResponseWriter在创建响应时会用到http.response结构,因为该结构是一个非导出(nonexported)的结构,所以用户只能通过ResponseWriter来使用这个结构,而不能直接使用它\n\nResponseWriter接口拥有以下3个方法: Write WriteHeader和Header\n\n服务器通过向 ResponseWriter写入首部和主体来向客户端返回响应\n\n### 38.Cookie\ncookie是一种存储在客户端的,体积较小的信息,这些信息最初都是由服务器通过 HTTP响应报文发送的。每当客户端向服务器发送一个 HTTP请求时,cookie都会随着请求被一同发送至服务器。cookie的设计本意是要克服HTTP的无状态性,虽然cookie并不是完成这一目的的唯一方法,但却是最常用的流行方法之一。\n\n\n### 39.Go语言Cookie的结构\n\ntype Cookie struct {\nName\t\t\tstring\nValue\t\t\tstring\nPath\t\t\t\tstring\nDomain\t\t\tstring\nExpires\t\t\ttime.Time\nRawExpires\t\tstring\nMaxAge\t\t\tint\nSecure\t\t\t bool\nHttpOnly\t\tbool\nRaw\t\t\t\tstring\nUnparsed\t\t[]string\n}\n\n\n### 40.闪现消息\n闪现消息的意思是,当一个页面上的某个条件被满足的时候,页面上会显示一条临时出现的消息,这样用户在刷新页面之后就不会再看到相同的信息了。\n\n这种临时出现的消息称为闪现消息(flash message)\n\n实现闪现消息的方法有很多种,最常见的一种就是把这些消息存储在页面刷新时就会被移除的会话cookie里面。\n\n### 41.模版和模版引擎\nWeb模版就是一些预先设计好的 HTML页面,模版引擎的代码会通过重复使用这些模版页面来创建一个或多个HTML页面。\n\n模版引擎通过将数据和模版组合在一起来生成最终的HTML,而处理器负责调用模版引擎并将引擎生成的HTML返回给客户端\n\n\n### 42.处理器调用模版引擎的流程:\n\n处理器首先掉用模版引擎,接着以模版文件列表的方式向模版引擎传入一个或多个模版,然后再传入模版需要用到的动态数据;模版引擎在接收到这些参数之后会生成相应的HTML,并将这些文件写入到 ResponseWriter里面,然后由 ResponseWriter将 HTTP响应返回给客户端。\n\n\n\n\n### 43.ParseFiles函数\n可以对模版文件进行语法分析,并创建出一个经过语法分析的模版以供 Excute方法执行\n\n\n### 44.Go里面的Panic\n通过这种机制处理分析模版时可能产生的erorr:\nt := template.Must(template.ParseFiles(“tmp.html”))\nMust函数可以包裹起一个函数,被包裹的函数会返回一个指向模版的指针和一个错误,如果这个错误不为nil,那Must函数会产生一个panic。\nGo里面的panic会导致正常的执行流程会终止:如果panic是在函数内部产生的,那么这个函数会将这个panic返回给它的调用者。panic会一直向调用栈的上方传递,知道main函数为止,并且程序也会因此崩溃。\n\n### 45.Go模版里的动作\nGo模版的动作就是一些嵌入在模版里的命令,在模版中使用两个大括号进行包围。包括:\na.条件动作\nb.迭代动作\nc.设置动作\nd.包含动作\ne.定义动作\n\n(.)也是一个动作,并且是最重要的动作,代表的是传递给模版的数据,其它动作和函数基本都会对这个动作进行处理,以此达到格式化和内容展示的目的。\n\n### 46.Web应用常用的存储手段\na.程序运行时,将数据存储在内存里\nb.将数据存储在文件系统的文件里\nc.通过服务器程序前端,将数据存储在数据库里\n\n\n### 47.Go与SQL\n如果需要在一个健壮并且可扩展的环境里存储数据,就需要转向使用数据库服务器。数据库服务器是一种程序,它可以让其它程序通过客户端-服务器模型(client-server model)来访问数据,并且这种访问只能通过数据库服务器实现,其它形式的访问则会被拒绝。通常情况下,客户端与数据库服务器进行连接,然后通过结构化查询语言(Structured query language, SQL)对数据进行访问。\n\n\n### 48.handle 句柄\nvar Db *sql.DB\nsql.DB结构是一个数据库句柄(handle),它代表的是一个包含了零个或任意多个数据库连接的连接池(pool),这个连接池由sql包管理。程序可以通过调用Open函数,并将相应的数据库驱动名字 (driver name)以及数据源名字 (data source name)传递给该函数来创建与数据库的连接。Open函数在执行后会返回一个指向sql.DB结构的指针作为结果。\n\n句柄不是实际的连接,代表的是一个会自动对连接进行管理的连接池。\n\n### 49.预处理语句\n一条预处理语句就是一个SQL语句模版\n使用sql.DB结构的Prepare方法来创建预处理语句:\nstmt, err := db.Prepare(statement)\n这行代码创建了一个指向sql.stmt接口的引用,这个引用就是上面提高的预处理语句。sql.stmt接口的定义位于sql.Driver包当中,而具体的结构则有数据库驱动实现。\n\n\n### 50.关系型数据库\n关系型数据库之所以能够成为一种流行的数据存储手段,其中一个原因就是它可以在表与表之间建立关系,从而使不同的数据能够以一种一直且易于理解的方式互相进行关联。基本上,有4种方法可以把一项纪录与其它纪录关联起来:\na.一对一关联,例如一个用户对应一个个人简介\nb.一对多关联,例如一个用户可以拥有多篇论坛帖子\nc.多对一关联,例如多篇论坛帖子可以同属某一个用户\nd.多对多关联,例如一个用户可能参与多篇帖子的讨论,而一篇帖子里也会有多个用户发表评论\n\n\n### 51.CURD\nCreat \t增加\nRetrieve 读取查询\nUpdate 更新\nDelete 删除\n是DataBase层或者持久层的基本操作功能\n\n\n### 52.ORM\n对象 - 关系映射器 (object-relational mapper)\n\n\n### 53.并发和并行\n并发和并行是两个相辅相成的概念,但它们并不相同。并发指的是两个或多个任务在同一时间段内启动,运行和结束,并且这些任务可能会彼此互动。而并行则是单纯的同时运行多个任务。\n\nGo通过goroutine和通道这两个重要的特性直接支持并发,但Go并不直接支持并行。\nGo Web服务器本身是并发的,服务器会把接收到的每条请求都放到独立的goroutine里运行。\n\n\n### 54.select\nselect语句可以以先到先服务的方式,从多个通道里选出一个已经准备好执行接收操作的通道。\n它允许用户从多个通道中选择一个通道来执行接收或者发送操作,select关键字就像专门为通道而设的switch语句。\n\n\n\n### 55.等待goroutine完成\n\n等待组(WaitGroup)可以解决程序比goroutine更早结束的问题:\na.声明一个等待组\nb.使用Add方法为等待组的计数器设置值\nc.当一个goroutine完成它的工作时,使用Done方法对等待组的计数器执行减一操作\nd.调用Wait方法,该方法将一直阻塞,知道等待组计数器的值变为0\n\n\n\n### 56.有缓冲通道\n有缓冲通道就像是一个能容纳多个同类信息的大箱子: 一个goroutine可以持续地向箱子里面推入信息,并且在箱子被填满之前,推入信息的goroutine都不会被阻塞;同样的,另一个goroutine可以按照信息被推入的顺序,持续地从箱子里取出信息,并且在箱子被掏空之前,取出信息的goroutine都不会被阻塞。\n\n\n### 57.基于REST的Web服务\nREST(Representation State Transfer,具像状态传输)是一种设计理念,用于设计那些通过标准的几个动作来操纵资源,并以此来进行相互交流的程序。\n\n在面向对象编程(OOP)范型中,通过创建称为对象(object)的模型来表示事物,然后定义称为方法(method)的函数并将它们附着到模型之上。REST是这种编程思想的进化版,但它并不是把函数暴露(expose)为可调用的服务,而是以资源(resource)的名义把模型暴露出来,并允许人们通过少数几个称为动词(verb)的动作来操纵这些资源。\n\n在使用HTTP协议事项REST服务时,URL将用于表示资源,而HTTP方法则会用作操纵资源的动词,例如:\n\n\n基于REST的Web服务通过HTTP协议向外界公开自己拥有的资源,并允许外界通过HTTP协议对这些资源执行指定的动作。\n\n\n\n### 57.分析JSON\na.创建出用于存储JSON数据的结构\nb.通过json.Unmarshal函数,把JSON数据解封道结构里面\n\n将结构映射至JSON的规则: 对于名字为<name>的JSON键,用户只需要在结构里创建一个任意名字的字段,并将该字段的结构标签设置为`json:\"name\"`,就可以把JSON键<name>的值存储到这个字段里面。\n\n\n### 58.基于SOAP的Web服务\nSOAP是一种协议,能否对定义在XML中的结构化数据进行交换。SOAP的WSDL报文很复杂。\n\n创建和分析XML已经JSON的步骤都是相似的,用户要么根据指定的结构去生成XML或者JSON,要么从指定的结构里面提取数据到XML或者JSON里面。前一种称为封装,后一种操作称为解封。\n\n\n\n\n\n\n\n\n\n\n\n\n\n","tags":["原创","读书"],"categories":["读书笔记"]},{"title":"Golang 学习之旅 --- 前言","url":"/2019/03/22/Golang 学习之旅 --- 前言/","content":"\n作为一个 iOS开发工程师,看到一个 App时会很自然的分析出用到哪些组件,背后的代码是怎样实现的,整个项目的架构应该是什么样。\n\n在公司里有专门的后端工程师,有现成的API接口可以调用,但后端代码的逻辑和实现的原理自己就不清楚了,让人有点遗憾。感觉如果 iOS端和后端代码都会写,对自己的成长应该更有帮助,工作上和后端的开发工程师也能更好的配合。\n\n决定掌握一门后端语言后,开始在 Java,Python和 Golang间做选择。\n最后决定选择 Golang。\n\n不太想用Java,现在工作中写 Android时用,感觉有点繁琐。用 Python写过自动化测试系统的代码,用来写后端感觉不是 Python精通的事。同样是高并发的代码,用Python也能实现,但难度更高一点,用 Go能更简单的写出相同性能的代码。\n\n找了一圈资料,发现 [Go 语言之旅](https://tour.go-zh.org/welcome/1)这个网站的 Go基础概念还行。花了半天时间看了一遍,了解了 Go的语法和基础知识,看到了很多编程语言的影子。\n\nGo 语言之旅看完后,买了本《Go web 编程》来系统性学习 Go并实战,新加坡的一位工程师写的。书写的不错,基础概念讲的很通透,在自己看的书里可以排前三。瑕疵是 Git上的示例代码有两个Bug,项目跑起来后有问题,其中一个Bug比较难读了好几遍源码才找到问题。==\n\n这本书反复看了三个月时间,总结了一些重要的知识点。看的很慢,慢慢培养了自己的后端思维,打牢基础。这篇文章记录自己学习的成长和总结,复盘 Go知识的同时,也给看这篇文章的人打开一扇 Go学习的大门,希望能对别人有一些启发。\n\n准备分三步写这篇文章: 1.前言 2.基础知识 3.实战部分。可以看成是 Go语言之旅的增强版,《Go Web 编程》的简化版。太简单的知识不会写出来,写的都是自己认为重要的知识点。\n\n\n\n","tags":["原创","读书"],"categories":["读书笔记"]},{"title":"换个角度思考,升级我们的程序员思维","url":"/2019/02/18/换个角度思考,升级你的程序员思维/","content":"\n曾经很好奇那些日入上万的 App成功的原因, 觉得肯定是因为代码写的好,所以才这么多人下载。看到一个 App成功,第一个想法是用什么编程语言写的,这个页面是怎么实现,有没有用到后台的功能。\n\n现在来看,这是典型的程序员思维。\n\n编程技术好,不一定代表产品做的好。如果一个应用不做运营和推广的工作,那这个应用就是个人项目,而不能称之为一个产品。能称之为产品的应用,是面向用户和市场的,而个人的项目只是面向了开发者。一款商业级App的成功,产品、设计、编程、运营的工作缺一不可。\n\n\n当一款应用一直稳定在苹果商店的榜单前列,原因可能是 ASO做的好,占据了好多搜索量高的关键词,大家一搜索出来的都是这个App。一款应用突然下载量暴增,有可能是有外力推荐: 比如媒体推荐、公众号推荐、抖音上突然流行带来大量下载的原因。\n\n我们看到抖音在国外大街小巷地铁站上做广告,就是为了获取流量。互联网时代,酒香也怕箱子深,代码写的再好,别人不知道这个 App也没办法。当我们看到 App下载量不好时,不应该光打开IDE来优化代码,而应该从产品和运营的角度来思考哪里出了问题。\n\n\n当我们能跳出程序员的思维,来看这个世界,也会发现很多有意思的事。锤子的子弹短信曾经登顶 App Store下载榜第一,原因就是老罗发布会热度带来的流量,发布会结束后慢慢也就没有声音了。如果我们从技术的角度思考子弹短信的成功并企图复制,那结果应该并不理想。\n\n\n如果想做成功的独立开发者,那运营的工作必须会做。会做 ASO、写文章、推广和做视频。产品经理的工作也要能拿下来,思考产品的核心竞争力,画产品流程图,熟悉 App一些常用的产品套路。一份工作80%的内容,花20%的时间就能掌握。所以学习掌握产品、运营和设计的技能不是一件特别难的事,只要想做。\n\n做到顶级的水准可能有点难,但做到平均线往上一点的水准,以程序员的学习能力,应该不难。\n","tags":["原创","生活"],"categories":["开发"]},{"title":"从测试萌新到独立开发的成长之路","url":"/2019/01/03/从测试萌新到独立开发的成长之路/","content":"\n今天随手打开了TesterHome论坛, 想起了三年前自己还是测试萌新时的日子。\n\n从最初的测试工程师,成长为开发测试和开发工程师,再到现在的独立开发者。\n\n一路走来,慢慢有了些收获。\n\n写下这些文字,给曾经迷茫过的我们\n\n希望能给大家的职业规划提供一些参考\n\n\n##### 3年前还是测试新人时\n\n\n毕业后被内推到了华为下面的公司工作,成为一名测试工程师,被派去了华为南研所\n\n工作不算太辛苦,工资也不低\n\n但大公司里面每个人做的事都是螺丝钉,而且华为的技术不通用,我看到很多前辈10年时间里\n\n就在华为和中兴下面的公司跳来跳去,能选择的路很少。\n\n不想一直做通讯相关的测试,也不想天天加班,想以后有小孩了能晚上回去陪陪小孩\n\n所以工作的第一个月里,定下了目标,要去互联网公司。同时去房价低一点的城市,能安家立业。\n\n当时买了 MacBook,自学编程,准备写一个简单的 iOS App出来去面试新公司\n\n花了3个月时间,第一个简单的App上线了\n\n现在看半天时间能写好的一个 App\n\n拿着这个App 成功找到一份测试开发的工作,也顺利换了城市\n\n\n\n\n##### 开发测试的那段时间 \n\n\n在新公司做开发测试的工作,薪水涨了一点。\n\n在这期间围绕 Appium写了一整套的自动化测试系统。\n\n算是入门了开发测试。\n\nTesterHome上的大神给了很多帮助,好多教程这上面的最好。\n\n后面这套系统公司卖给了老外,赚了不少钱。\n\n工作时间长了对测试工作慢慢有了自己的观点\n\n有人认为测试往上走,就是开发测试了: 写UI自动化,接口测试,性能测试,写测试工具\n\n我觉得这种观点不能说错,我见过很多人走这条路,走的都挺好。\n\n但觉得应该更进一步。\n\n移动端测试的目标是一个App,从代码最底层测试一个App的效果应该是最好的。\n\n换句话说,开发人员是最好的测试人员 (自己写的代码哪里不好心里没点数吗 -_-)\n\n所以测试应该慢慢往开发的方向发展。\n\n写一个应用的业务逻辑没有那么复杂,写多了就会了,熟能生巧而已。\n\n\n##### 成为了一名iOS开发\n\n\n上家公司做开发测试一年时间后,跳槽到一家互联网公司。刚毕业时的愿望达成 ^—^\n\n在这家公司工作到现在,有两年时间。\n\n当初以测试开发的身份进来的,花了6个月时间把现在公司的 App代码过了两遍,转岗成了一名 iOS工程师\n\n最开始分配的都是些简单的工作,慢慢就上手了\n\n很感谢现在公司遇到的同事和Boss,给了我很多帮助\n\n同是90后的技术总监,写过iOS和Android,现在写C++。做事专注,做人厚道\n\n公司移动端的负责人,会 iOS,Android和PHP,学什么都很快,区块链刚出来的时候很快就上线到\n\n业务上。乐于助人,代码上的问题一般都能解决\n\n他们提高了我对自己的期待,也让我知道了成长的方向\n\n\n##### 成为独立开发者\n\n\n产品,设计,运营,编程和测试的工作都要独立开发者自己做\n\n平时还是要上班的,回家后做自己的项目\n\n最开始是想多写一点代码,提高的快一点 (写代码够1万个小时,足够成长为大牛了)\n\n在公司只是写某个模块的代码,没有从头完整的写过一个App\n\n写了一个月后上线了自己独立开发的App,反响不错\n\n最好成绩是中国区工具付费榜第20名\n\n培养了自己产品和运营的思维\n\n想到一个好的创意 --> 完成 UI设计 --> 完成编程和测试 --> 日常运营\n\n上面的流程完整的走了一遍\n\n其实大部分工作80%的内容,花20%的时间就能掌握。\n\n开发者有产品和运营思维后,对开发的理解也能更深一点\n\n这期间认识了很多做独立开发的大佬,知道了他们精彩的人生\n\n有做独立开发后融资开公司的\n\n有做独立开发收入让工资变成零花钱的\n\n这让我对生活有了更多的期待,别人可以的\n\n我也可以\n\n看文章的你,也能做到\n\n\n\n##### 结束语\n\n分享自己走过的路,希望能给看到的你一些信心\n\n这条路有人走过了,如果你想的话,也可以的\n\n做测试对代码保持好奇心,保持学习的习惯\n\n能持续不断的进步\n\n那自己测试的路一定能越走越宽\n\n我是城南花已开\n\n微信 JeremyLee051226,想交流的同学可以加我微信\n\n我可以分享我学习的资料,好的自学编程的网站,好的开源代码给你\n\n愿 TesterHome 上的同学变得越来越好\n","tags":["原创","开发"],"categories":["开发"]},{"title":"iOS面试之道 - 读书笔记","url":"/2018/12/03/iOS面试之道/","content":"\niOS面试之道是在小专栏这个网站上看到的,比较流行的一本关于 iOS面试的书。读这本书的目的在于了解下当下最新的 iOS面试的信息,\n同时通过面试题,来回顾整理下 iOS编程的基础知识。毕竟问的比较多的面试题,一般都比较经典,弄懂了平时工作中提升也比较快。\n\n同时养成看书的习惯,在知识碎片化的时代,系统的学习知识效率反而是最高的。\n\n这篇文章记录了我读这本书的一些感想,笔记和重点的整理。首先看的是第二部分的算法和第三部分的 iOS专业问答。第一部分的面试准备\n这一章,以后换工作的时候再看。\n\n##### 1.基本的数据结构\n\n数组\n\n数组是最基本的数据结构。Swift中的 Array数组的实现方式有三种: ContiguousArray, Array和 ArraySlice。\n\n数组的一些基本应用:\n\n // 声明一个不可修改的数组\n let nums = [1, 2, 3]\n let nums = [Int](repeating: 0, count: 5)\n\n // 声明一个可以修改的数组\n var nums = [3, 1, 2]\n // 增加一个元素\n nums.append(4)\n // 对原数组进行升序排序\n nums.sort()\n // 对原数组进行降序排序\n nums.sort(by: >)\n // 将原数组除了最后一个以外的所有元素赋值给另一个数组\n let anotherNums = Array(nums[0 ..< nums.count - 1])\n\n用数组实现栈:\n\n // 用数组实现栈\n class Stack {\n var stack: [AnyObject]\n var isEmpty: Bool { return stack.isEmpty }\n var peek: AnyObject? { return stack.last }\n\n init() {\n stack = [AnyObject]()\n }\n\n func push(object: AnyObject) {\n stack.append(object)\n }\n\n func pop() -> AnyObject? {\n if (!isEmpty) {\n return stack.removeLast()\n } else {\n return nil\n }\n }\n }\n\n\n字典和集合\n\n let primeNums: Set = [3, 5, 7, 11, 13]\n let oddNums: Set = [1, 3, 5, 7, 9]\n\n // 交集、并集、差集\n let primeAndOddNum = primeNums.intersection(oddNums)\n let primeOrOddNum = primeNums.union(oddNums)\n let oddNotPrimNum = oddNums.subtracting(primeNums)\n\n // 用字典和高阶函数计算字符串中每个字符的出现频率,结果 [“h”:1, “e”:1, “l”:2, “o”:1]\n Dictionary(\"hello\".map { ($0, 1) }, uniquingKeysWith: +)\n\n\n字符串\n\nSwift中的字符串是值类型而不是引用类型\n\n // 字符串和数字之间的转换\n let str = \"3\"\n let num = Int(str)\n let number = 3\n let string = String(num)\n\n // 字符串长度\n let len = str.count\n\n // 访问字符串中的单个字符,时间复杂度为O(1)\n let char = str[str.index(str.startIndex, offsetBy: n)]\n\n // 修改字符串\n str.remove(at: n)\n str.append(\"c\")\n str += \"hello world\"\n\n // 检测字符串是否是由数字构成\n func isStrNum(str: String) -> Bool {\n return Int(str) != nil\n }\n\n // 将字符串按字母排序(不考虑大小写)\n func sortStr(str: String) -> String {\n return String(str.sorted())\n }","tags":["原创","读书"],"categories":["读书笔记"]},{"title":"小专栏App架构 - 读书笔记","url":"/2018/11/20/读书笔记/","content":"\n##### 1.交互逻辑: view action 转变为 model action的操作,我们一般称为交互逻辑\n\n##### 2.表现逻辑: 将 model 的通知和数据转变为 view更改的操作,称为表现逻辑\n\n##### 3.键值观察(KVO): 可以将某个对象上属性的改变通知给另外一个对象\n\n##### 4.响应式编程可以理解为: 建立了一个动态的数据流关系(当 b或者 c的值发生变化时,a的值自动发生变化)\na = b + c\n赋值之后 b 或者 c 的值变化后,a 的值不会跟着变化\n响应式编程,目标就是,如果 b 或者 c 的数值发生变化,a 的数值会同时发生变化\n\n##### 5.常用的两种 App设计模式: MVC(Model-View-Controller) 和 MVVM(Model-View-ViewModel-协调器)。(都是基于场景进行的架构)\n\nMVC:\n\n\n第二种是将 Controller里的负责 view-model(视图模型)的 Controller和管理其它 view controller 的 Controller 单独区分开来,本质上是MVC模式的细分版本。\n\nMVVM:\n\n\n在 MVC中 view controller的角色由 View层级的一部分和协调 view和 model之间的交互,减少到了只是 view层级的一部分。view controller 不需要负责处理展示其它的 view controller,协调器(coordinate) 来进行 view controller层级的管理。\n\n注:View-Model 实际扮演的是类似 Controller的角色, 是 Controller的一个子集。负责 View页面对应的那部分 Controller的功能,MVVM中在每个场景中使用 view-model来描述场景中的表现逻辑和交互逻辑。View-Model为每个 view获取和准备数据。\n\n##### 6.MVC中最常见的两个问题:失效的观察者模式和臃肿的 ViewController\n\n##### 7.Model-View-ViewModel + 协调器 (MVVM) 是一种基于 MVC进行改进的模式,它将所有 Model相关的任务(更新 model,观察变更,将 model显示出来)从 Controller层抽离出来,放到view-model这一层中。\n\n##### 8.Model层持有网络比 Controller层持有网络更好,数据更容易被共享。\n","tags":["原创","读书"],"categories":["读书笔记"]},{"title":"推荐我在Mac上常用的软件和网站","url":"/2018/11/16/推荐我在Mac上常用的软件和网站/","content":"\n上个周末入手了一台新的 iMac,花了半天时间把自己常用的软件装了上去,正好趁这个机会写一篇关于Mac常用软件分享的文章,希望对大家能有所帮助。\n\n## 生活类软件:\n\n### 1.Flux\n\n\n\n保护视力的一个App,能自动调节屏幕的亮度和色彩,用了很多年。平时打开,关闭这个App的时候眼睛感觉挺明显。\n\n### 2.Pap.er\n\n\n\n一款壁纸App,里面的壁纸挺漂亮。交互也比较简单,是我用过的几款相似软件中比较出众的。\n\n### 3.iTranslate\n\n\n\n\n\n一款比较好用的翻译App。推荐它的原因是在Mac顶部状态栏里打开就能用,平时遇到单词可以顺手查一下,比单独打开谷歌翻译的速度要快一点。\n\n### 4.印象笔记 Evernote\n\n\n\n\n\n每天都会用到的App,有什么想法和灵感都会记录在这上面。养成每日记录和写文字的习惯,支持 iPhone和 Mac上的内容同步。\n\n### 5.Things3\n\n\n\n\n\n一款To-Do类应用,非常推荐。每天早上都会想好今天每个时间段做什么事情,并在上面记录下来,晚上的时候给完成的事打勾,可以让我们每天过的明白一点,知道什么时候做什么事情。\n\n### 6.YoMail\n\n\n\n一款好用的邮件客户端,比开网页邮件端要好。\n\n### 7.Google Chrome Canary\n\n\n\n谷歌浏览器的开发版本,用起来比普通版本的感觉要好。之前用了很长时间Mac自带的浏览器Safari,现在在用这个浏览器。两个各有优点,更推荐Google Chrome Canary。\n\n### 8. QQ & 微信\n\n\n\n\n\n必备的App。\n\n### 9.SpechtLite\n\n\n\n这个可以自己搜索了解一下。\n\n### 10.Dr Cleaner\n\n\n\n好用的免费Mac清理软件,平时使用足够了。(收费版的CleanMyMac之前有一次把电脑清崩溃了…)\n\n\n## 开发类软件: \n\n### 1.Xcode\n\n\n\n用 Xcode和 Android Studio来开发 iOS和 Android应用,总感觉 Xcode用起来更优雅一点。\n\n### 2.Android Studio\n\n\n\n开发Android App首选。\n\n### 3.iTerm\n\n\n\n比 Mac自带的终端更好用。\n\n### 4.SourceTree\n\n\n\nGit 的客户端管理工具,属于开发必备的工具,代码管理的神器。\n\n### 5.图标构建\n\n\n\n很好用的Appicon 自动生成所有分辨率类型的App,开发必备。","tags":["原创","生活"],"categories":["生活"]},{"title":"RosJava环境搭建","url":"/2018/11/12/ROSJava环境搭建/","content":"\nROS (Robot Operating System) 是在2010年发布的开源机器人操作系统。\n\nRosJava 是用 Java实现的 ROS,它提供了一个客户端库用于快速调用 ROS的 Topics, Services 和Parameters,我们可以用它在 Android 上进行 RosJava的开发。\n\n资料分享: Ros官网: http://wiki.ros.org/\n\t\tRosJava官方文档: http://wiki.ros.org/rosjava\n\n\n## 1.ROS环境搭建\nROS Kinetic支持 Xenial (16.04)和 Wily(15.10),其它 Linux 系统需要安装其它的 ROS版本。\n下面开始在 Ubuntu 16.0.4上搭建 ROS Kinetic开发环境。\n\n参考资料: http://wiki.ros.org/kinetic\n\t\t http://wiki.ros.org/kinetic/Installation/Ubuntu\n\n### 使用下面命令:\n\n ~$ sudo sh -c 'echo \"deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main\" > /etc/apt/sources.list.d/ros-latest.list'\n \n\n\n2.2 设置keys\n\n ~$ sudo apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-key 0xB01FA116\n\n\n\n2.3 安装\n\n ~$ sudo apt-get update\n\n ~$ sudo apt-get install ros-kinetic-desktop-full\n\n安装功能包:\n\n ~$ sudo apt-get install ros-kinetic-slam-gmapping\n\n查找在kinetic中可以使用的功能包:\n\n ~$ apt-cache search ros-kinetic\n\n2.4 初始化\n\n ~$ sudo rosdep init\n\n ~$ rosdep update\n\n2.5 环境配置\n\n ~$ echo \"source /opt/ros/kinetic/setup.bash\" >> ~/.bashrc\n\n ~$ source ~/.bashrc\n\n注意当安装多个ROS发行版,使用kinetic需要用到下面命令:\n\n ~$ source /opt/ros/kinetic/setup.bash\n\n2.6 安装rosinstall\n\n ~$ sudo apt-get install python-rosinstall\n\n2.7 测试roscore\n\n ~$ roscore\n\n\n\n\n安装ROS成功后,在Beginner Tutorials中有一个简单的示例程序.\n\n\n\n\n在Terminal中运行以下命令: \n\n $ roscore\n\n新开一个terminal,运行以下命令,打开小乌龟窗口: \n\n $ rosrun turtlesim turtlesim_node\n\n\n新开一个terminal,运行以下命令,打开乌龟控制窗口,可使用方向键控制乌龟运动: \n\n\n $ rosrun turtlesim turtle_teleop_key\n\n\n选中控制窗口,按方向键,可看到小乌龟窗口中乌龟在运动。\n新开一个terminal,运行以下命令,可以看到ROS的图形化界面,展示结点的关系: \n\n\n $ rosrun rqt_graph rqt_graph\n\n至此,测试完成,ROS成功\n\n\n\n2.RosJava环境搭建 \n\n在 Ros环境搭建完成后搭建 RosJava 环境。\n\n参考资料: http://wiki.ros.org/rosjava/Tutorials/kinetic/Source%20Installation\n\t\t https://blog.csdn.net/F_season/article/details/9166133\n\n使用下面命令:\n1.安装依赖\n$ sudo apt-get install ros-kinetic-catkin ros-kinetic-rospack python-wstool openjdk-8-jdk\n\n2.核心功能\n$ mkdir -p ~/rosjava/src\n$ wstool init -j4 ~/rosjava/src https://raw.githubusercontent.com/rosjava/rosjava/kinetic/rosjava.rosinstall\n$ source /opt/ros/kinetic/setup.bash\n$ cd ~/rosjava\n$ rosdep update\n$ rosdep install --from-paths src -i -y\n$ catkin_make\n\n3.配置 .bashrc文件\n在.bashrc 文件里设置 Ros的环境变量,不设置的话 ROS和 RosJava Android\n间的通讯不能连通。\n\nLinux系统里打开终端,输入命令:\n$ gedit ~/.bashrc\n\n\n\n在最后一行添加三个 export语句\nexport ROS_HOSTNAME=192.168.199.141\nexport ROS_IP=192.168.199.141\nexport ROS_MASTER_URL=http://192.168.199.141:11311\n\n把IP地址改成自己本机连接网络的IP地址,之后手机和安装Linux的ROS系统需要连接在同一个网络\n\n\n3.在 Android中使用ROS\n在Mac上搭建好 Android开发环境,安装好Android Studio,配置好翻墙网络。\n通过给Android App添加上ROS的依赖库,可以在没有ROS环境的Mac上开发ROS客户端。\n\n参考资料:\nhttp://community.bwbot.org/topic/627/%E5%9C%A8android%E4%B8%AD%E4%BD%BF%E7%94%A8ros\n\n1.创建一个 Android App项目\n\n\n\n\n\n然后点击Finish\n\n等待项目同步完成。一般这里出问题就是网络没有配置好,需要开翻墙软件。\n\n\n2.修改 build.gradle文件\n\n项目同步完成之后,在项目左侧的文件列表内会有两个build.gradle文件。其中一个是Project的,另一个是Module的。\n\n\n\n首先修改Project的build.gradle文件\n\n把文件中的\n\nbuildscript {\n repositories {\n jcenter()\n }\n dependencies {\n classpath 'com.android.tools.build:gradle:2.2.3'\n\n // NOTE: Do not place your application dependencies here; they belong\n // in the individual module build.gradle files\n }\n}\n修改为\n\nbuildscript {\n apply from: \"https://github.com/rosjava/android_core/raw/kinetic/buildscript.gradle\"\n}\n然后在文件中添加\n\nsubprojects {\n apply plugin: 'ros-android'\nDo not place your applica\n afterEvaluate { project ->\n android {\n // Exclude a few files that are duplicated across our dependencies and\n // prevent packaging Android applications.\n packagingOptions {\n exclude \"META-INF/LICENSE.txt\"\n exclude \"META-INF/NOTICE.txt\"\n }\n }\n }\n}\n然后修改Module的build.gradle,在dependencies 中添加ros依赖\n\n...\ndependencies {\n ...\n // You now now add any rosjava dependencies, like so:\n compile 'org.ros.android_core:android_10:[0.3,0.4)'\n}\n...\n同时把dependencies 中的 全部implementation修改为compile。注意修改时的大小写。\n\n把文件中的compileSdkVersion版本设置为25\ntargetSdkVersion也设置为25\n把 com.android.support:appcompat-v7:27.1.1也修改成25的版本\n\n最后修改完成的文件如下面所示\n\napply plugin: 'com.android.application'\n\nandroid {\n compileSdkVersion 25\n defaultConfig {\n applicationId \"org.bwbot.rostest\"\n minSdkVersion 15\n targetSdkVersion 25\n versionCode 1\n versionName \"1.0\"\n testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n }\n buildTypes {\n release {\n minifyEnabled false\n proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n }\n }\n}\n\ndependencies {\n compile fileTree(dir: 'libs', include: ['*.jar'])\n compile 'com.android.support:appcompat-v7:25.4.0'\n compile 'com.android.support.constraint:constraint-layout:1.1.3'\n testCompile 'junit:junit:4.12'\n androidTestCompile 'com.android.support.test:runner:1.0.2'\n androidTestCompile 'com.android.support.test.espresso:espresso-core:3.0.2'\n compile 'org.ros.android_core:android_10:[0.3,0.4)'\n}\n\n3.修改AndroidManifest.xml文件\n此时如果编译项目会出现下面的错误\n\nManifest merger failed : Attribute application@icon value=(@mipmap/ic_launcher) from AndroidManifest.xml:7:9-43\n\tis also present at [org.ros.android_core:android_10:0.3.3] AndroidManifest.xml:19:9-36 value=(@mipmap/icon).\n\tSuggestion: add 'tools:replace=\"android:icon\"' to <application> element at AndroidManifest.xml:5:5-19:19 to override.\n此时需要修改AndroidManifest.xml文件在application项目中做如下修改\n\n<application xmlns:tools=\"http://schemas.android.com/tools\"\n tools:replace=\"android:icon\"\n ...\n为了能够正常使用还需要给app添加网络权限。在AndroidManifest.xml文件中添加\n\n<uses-permission android:name=\"android.permission.INTERNET\"/>\n最后的AndroidManifest.xml文件如下\n\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"org.bwbot.rostest\">\n <uses-permission android:name=\"android.permission.INTERNET\"/>\n <application\n xmlns:tools=\"http://schemas.android.com/tools\"\n tools:replace=\"android:icon\"\n android:allowBackup=\"true\"\n android:icon=\"@mipmap/ic_launcher\"\n android:label=\"@string/app_name\"\n android:roundIcon=\"@mipmap/ic_launcher_round\"\n android:supportsRtl=\"true\"\n android:theme=\"@style/AppTheme\">\n <activity android:name=\".MainActivity\">\n <intent-filter>\n <action android:name=\"android.intent.action.MAIN\" />\n\n <category android:name=\"android.intent.category.LAUNCHER\" />\n </intent-filter>\n </activity>\n </application>\n</manifest>\n此时项目已经可以成功编译了。\n\n2. 写一个简单的消息发布程序\nMainActivity.java内容如下\n\npackage org.bwbot.rostest;\n\nimport android.support.v7.app.AppCompatActivity;\nimport android.os.Bundle;\n\nimport org.ros.android.RosActivity;\nimport org.ros.concurrent.CancellableLoop;\nimport org.ros.namespace.GraphName;\nimport org.ros.node.ConnectedNode;\nimport org.ros.node.Node;\nimport org.ros.node.NodeConfiguration;\nimport org.ros.node.NodeMain;\nimport org.ros.node.NodeMainExecutor;\nimport org.ros.node.topic.Publisher;\n\nimport java.net.URI;\n\nimport std_msgs.String;\n\npublic class MainActivity extends RosActivity {\n\n protected MainActivity() {\n super(\"ros_test\", \"ros_test\", URI.create(\"http://192.168.0.23:11311\")); // 这里是ROS_MASTER_URI\n }\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_main);\n }\n\n @Override\n protected void init(NodeMainExecutor nodeMainExecutor) {\n NodeConfiguration nodeConfiguration = NodeConfiguration.newPublic(getRosHostname());\n nodeConfiguration.setMasterUri(getMasterUri());\n nodeMainExecutor.execute(new NodeMain() {\n @Override\n public GraphName getDefaultNodeName() {\n return GraphName.of(\"ros_test\");\n }\n\n @Override\n public void onStart(ConnectedNode connectedNode) {\n final Publisher<std_msgs.String> pub = connectedNode.newPublisher(\"/test\", String._TYPE);\n connectedNode.executeCancellableLoop(new CancellableLoop() {\n @Override\n protected void loop() throws InterruptedException {\n std_msgs.String msg = pub.newMessage();\n msg.setData(\"hello world\");\n pub.publish(msg);\n Thread.sleep(1000);\n }\n });\n }\n\n @Override\n public void onShutdown(Node node) {\n\n }\n\n @Override\n public void onShutdownComplete(Node node) {\n\n }\n\n @Override\n public void onError(Node node, Throwable throwable) {\n\n }\n }, nodeConfiguration);\n }\n}\n\n编译后,在手机上运行App,在运行的ROS的主机上打印/test话题。\n\n手机要和Linux机器连接在同一个 Wi-Fi下,Linux上已修改 .bashrc文件,添加本机 IP。\nAndroid程序中已配置ROS_MASTER_URL 地址。\n\n在 Linux 上新开一个 Terminal中运行以下命令: \n$ roscore\n新开一个terminal,cd 到 rosjava文件夹中,运行命令:\n$ ros topic echo /test\n\n\n\n可以看到消息已经成功发送出来了。\n到这Anroid端和Linux端的ROS通讯成功。\n","tags":["原创","技术"],"categories":["开发"]},{"title":"Some feature of Objective-C","url":"/2018/08/12/SomefeatureofObjective-C/","content":"\n## 1.AppDelegate.h and AppDelegate.m\nWhen the application is created there are AppDelegate.h files and AppDelegate.m files by default. AppDelegateis a proxy for the whole application, it provides a monitoring interface to setting the program when it’s in the start, background, exit and active.\n\n## 2.Class methods and instance methods\nThere are two types of methods in Objective-C: class methods and instance methods. Like “ - (void) setnumber: (int) n; “ that begin with symbol “-“ means that the\nmethod is an instance methods. Instance methods are some of the operations performed on a particular instance of a class. For example, there is a white rabbit, it’s weight has increasted. We can define a instanse methods of weught to reflect the weight change of the white rabbit.\n\nClass methods are some of the operations performed on the class itself. For example, we create a new calss, this method is class methods.\n\n\n\n\n\n\n\n","tags":["原创","技术"],"categories":["开发"]},{"title":"Android 入门教程","url":"/2018/07/23/入门教程/","content":"\n这篇文章总结了我在学习 Android过程中获得的经验、资源和代码。\n用这篇文章作为 Android学习结束的技术文档,自己总结一遍的同时,\n希望能帮到看这篇文档的人。\n\n## 1.基础知识入门\n\n这里首推谷歌的开发者文档,其中部分文档已经有中文版的,读起来很方便。\nhttps://developer.android.com/guide/\n\n也有中文版的 Android官方培训课程,可以对照着谷歌的文档一起看。\nhttp://hukai.me/android-training-course-in-chinese/index.html\n\n看官方文档推荐挑着看,如可以先读\n1.1 建立第一个App ;\n1.2添加ActionBar; \n1.4管理Activity的生命周期;\n1.5 使用Fragment建立动态UI; \n1.6数据保存; \n1.7与其它应用的交互等基础\n这些基础章节的内容看完后,整个Android App的运行机制大概就有了解了,剩下几个大章的文档\n就是各种细节的实现。等需要做哪部分功能的时候,再看相应的文档。\n\n其它的一些资源推荐:\na. 开发Android用的 Java文档\nhttp://www.runoob.com/java/java-methods.html\n现在Android开发可以使用 Java和 Kotlin,考虑到Java使用的广泛性,推荐使用Java来入门Android和\n开发公司的App,Kotlin可以之后开发个人App的时候使用。\n\nb.Android Fragment使用详解\nhttps://juejin.im/post/5a926630f265da4e8c45264e\n通过这篇文章可以了解下 Fragment和它的生命周期内容\n\nc.Android 的 mvp分层架构\nhttp://www.androidchina.net/8195.html\n了解下 Android的mvp架构,先有个印象。\n\nd.Android的按键消息分发机制\nhttps://www.jianshu.com/p/0c80cdb37af8\n通过这篇文章可以了解Android的按键消息分发机制,了解App在按键过程中的运行流程。\n\ne.记7月份Android面试感想\nhttps://juejin.im/post/5b67ca1c518825625529b2fb\n常见的Android面试题,面试中能问到的Android问题都是比较经典的,通过面试的问题\n来学习Android 中一些重要的知识点。\n\n### 说明:\n在上面分享 Android基础知识的资源,官方文档挑着看完之后就应该写代码了,\n首先根据官方文档先写出一个简单的App,之后再写稍微复杂一点的App。\n其它推荐的资源是边写代码有疑问的时候看的。\n不要等全部资料看完了再写代码,需要注意这个顺序。\n\n### 注意:\n\n上面的一些教程默认是翻墙的环境,并安装好了 Android Studio\n\n同步Android Studio过程中的大部分问题都与网络有关,搞定网络问题(翻墙)就解决了一大半问题。\n\n关于Android Studio的资料:\nhttp://wiki.jikexueyuan.com/project/android-studio-guide/\n\n## 2.Android代码实践\n完成了上面的基础教程,下一步就准备写代码。\n(看别人的源码理解太浅,要自己写才能发现实际的问题)\n\n这里给出一个实际的很简单的需求:\n\n新开封的盒装牛奶和放在咖啡机里的牛奶容易过期,现在做\n一个Android App来记录开牛奶和往咖啡机里加牛奶的时间,\n并显示当前的日期,能让看的人知道当前的牛奶是否过期了。\n\n分析:\n1.需要两个Button,点击后可以再两个对应的Label上显示当前\n的日期和时间,精确到秒。\n2.还有一个Label来显示当前的实时时间。\n3.UI自己设计,能做的美观大方最好。\n\n通过分享可以知道这是一个很简单的AndroidA pp,尝试自己来完成。\n\n这里有开源的App,可以参考着写:\nhttp://gitlab.mynt.com/NanJun/MilkCoffee","tags":["原创","技术"],"categories":["开发"]},{"title":"Vysor破解过程记录","url":"/2018/05/12/Vysor破解过程记录/","content":"\n最近需要用到到 Vysor,走 Hacker的路线准备破解。大家有能力请支持正版软件。\n\n1.首先要有 Chrome浏览器 && 这是 Mac下的破解记录\n\n\n2.打开 https://www.crx4chrome.com/crx/109271/ 下载 Vysor的历史版本 1.9.3\n\n\n3.下载下来的是 crx格式的文件,把它从下载文件夹移动到另外放文件的地方。\n\n后缀改成 .zip格式,再解压下来成文件夹的格式\n\n\n4.Chrome浏览器打开输入 chrome://extensions/ 回车,打开开发者选项。选择\n\n加载已解压的扩展程序,将刚才解压的文件夹加载进来。\n\n\n5.\b打开 Vysor文件夹,搜索 uglify.js 文件。打开复制内容。打开 JS格式化代码网站\n\nhttp://tool.oschina.net/codeformat/js,复制代码转换,将转换好的代码复制进 uglify.js 文件。\n\n\n6.搜索 Account Management 关键词,将上面两行代码改成这样:\n\n\n\n\n\n\n7.搜索关键词 e.contentWindow._lm,将下面一行代码改成这样\n\n\n\n\n8.找到manifest.json这个文件\n\n \"version\": \"1.7.2\" 改为 \"version\": \"99\"\n\n9.保存文件,Chrome里重启扩展程序。Launchpad里打开 Vysor发现已经变成专业版了。","tags":["原创","Hacker"],"categories":["开发"]},{"title":"What is unittest and how to write unit testing","url":"/2016/05/29/Unittest/","content":"\nUnit test is used for a module, a function or a class to test their validity.\nWe can write some test case for function abs() to test the validity of it:\n\n\n1.Input a positive number: 1, 3.2 or 0.6. We expect the return value to be the same as the input.\n\n2.Input a negative number: -1, -2,3 or -0.99. We expect the return value to be the opposite of the input.\n\n3.Input 0. We expect to return 0.\n\n<!-- more --> \n\n4.Input non-numeric types, such as None, [], {}. We expect a TypeError to be thrown.\n\nWe put the above test cases into a test module, it's a complete unit test.\nIf the unit test can be passed, which means that we tested this function to work properly.If the unit test does not pass, either the function has a bug, or the test condition is not entered correctly, we need fixes it to make unit tests pass.\n\nWhat are the benefits of unit testing? Imagine if we modified the abs () source code, we only need to run the unit tests again, if the test passed it means our changes to the abs () function does not affect the original behavior. If the test does not pass, it means that our changes have problems, we need to modify the code or modify the test.\n\nNext, we actually write code to illustrate the unit test.\n\n\n## abs.py\n\n\tdef abs(x):\n\n\t\tif not isinstance(x, (int, float)):\n\t\traise TypeError('bad operand type')\n\n\t\tif x >= 0:\n\t\t\treturn x\n\t\telse:\n\t\t\treturn -x\n\nThis is a python file, which defines an abs () function that takes an absolute value. \n\nOpen Terminal, cd into the folder where the abs.py file is located. Input python and press Enter to enter python's interactive programming environment:\n\n\t$ cd /Users/hisoft/Desktop/Unittest\n\t$ python\n\tPython 2.7.11 (default, Jan 22 2016, 08:29:18) \n\t[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin\n\tType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n\nNow imput the 'import abs' on the Termanal and press to enters the module name abs there.\nWe use abs.abs (-5) to invoking the function in the module to find the absolute value of -5.\nThis part of the python can refer to the [official documents](https://docs.python.org/2.7/tutorial/modules.html).\n\n\t>>> import abs\n\t>>> abs.abs(-5)\n\t5\t\n\n\n## abs_test.py\n\nNow let's write the unit test for the abs function. We use the Python's unittest module, abs_test.py is like this:\n\n\timport unittest\n\n\tfrom abs import abs\n\n\tclass UnitTestDemo(unittest.TestCase):\n\n\t\tdef setup(self):\n\t\t\tprint('setUp...')\n\n\t\tdef tearDown(self):\n\t\t\tprint ('tearDoen...')\n\n\t\tdef test_case1(self):\n\n\t\t\tself.assertEqual(abs(1), 1)\n\t\t\tself.assertEqual(abs(-1), 1)\n\t\t\tself.assertEqual(abs(0), 0)\n\n\t\tdef test_case2(self):\n\t\t\n\t\t\twith self.assertRaises(TypeError):\n\t\t\t\tabs('1')\n\t\t\t\n\tif __name__ == '__main__':\n \tunittest.main()\n\nWe need to write a test class When we write unit tests, This is inherited from 'unittest.TestCase'.\nThe test method begins with test, There are test_case1 and test_case2. A method that does not start with test is not considered a test method and will not be executed when tested.\n\n\n##Run the unit test\nNow we can run unit tests. To the end of the abs_test.py with two lines of code:\n\n\tif __name__ == '__main__':\n \tunittest.main()\n\nThe two lines of code means that this script has been completed,we can run it directly:\n\n\tpython abs_test.py\n\n\nThere are two methods in the unit test script setUp() and tearDown(), They will invoking before and after each test method execute.\n\n\n\n\n\n","tags":["原创","技术"],"categories":["开发"]}]