SmartBeijing Day06

Day06 01.昨天内容总结 ##

对昨天内容进行一下小结,昨天主要做了这么些功能,首先是新闻中心页面的一个轮播条,头条新闻自动轮播的一个效果其实是用Handler去发消息,这个我们已经实现过一次了



然后我们是实现了一下侧边栏的组图模块,那组图这个模块的话,我们是可以实现一个GridView和ListView的切换的,其实我们是用一个帧布局然后提前把ListView和GrirdView写好,一个显示一个隐藏,而这些数据的话,我们是从网络去加载的一个组图的数据,把它展现在了我们的这样一个ListView上,然后我们实现了一个卡片式的listView,其实它整体还是一个ListView,只不过我们在这个布局的时候,稍微做了一些处理,那比如说我们是给它呢去写了一个这样的线性布局,而这个线性布局的布景是一个卡片的背景,那这个线性布局就离它的根布局设置Margin的外边距,这样的话,它就能实现一个卡片的效果,那同时呢,我们也可以去掉它这个两个Item之间的分割线,即在布局文件中去掉它的divider属性,这是我们的这样一个组图模块



然后组图模块的话,我们最开始是使用XUtils提供的BitmapUtils去加载,那后来我们自己写了一个我们这样的图片加载的工具类MyBitmapUtils,那我们怎么去写的呢?



我们是分析了一下三级缓存的机制,那首先的话,我们是从内存里边去加载这个缓存,因为内存速度比较快,而且不耗费流量,所以我们最优先从内存里面去取,如果内存没有的话,我们再看看本地有没有,那本地有的话,我们直接从本地去取,也不用浪费流量,那如果本地和内存都没有的情况下,这时候最后才会考虑到从网络上去加载我们的图片,这是我们的这样一个三级缓存的机制



然后呢我们就单独的写了一个MyBitmapUtils.java,负责对我们的这个缓存全局的进行一个调用



而这个我们单独写的三个类,一个是网络缓存NetCacheUtils.java,一个是本地缓存LocalCacheUtils.java,一个是内存缓存MemoryCacheUtils.java,分别去实现他们的缓存的功能,那关于内存缓存的话,我们是使用到了一个LruCache,因为这个LruCache它是类似于一个HashMap,但是它呢是可以控制我们的这个内存的容量不要超过一定的范围



那这个怎么去控制呢?我们先拿到我们模拟器分配给我们每个app这样一个最大内存,一般是16MB



然后呢,我们给它算一个1/8的容量,填充给我们的LruCache,这样的话,它就能控制它的容量不超过这个1/8的容量,大概是2MB的这样一个范围,那用法呢,其实和我们的HashMap一样,get或者put

这是我们所讲的一个三级缓存



那三级缓存完了之后呢,我们讲了一下,关于内存溢出我们详细讲了一下软引用,弱引用



我们默认声明一个变量,我们java里边都是强引用,只要这个强引用还在,那么垃圾回收器就不会回收



但是有时候我们可以用一个软引用SoftRefrence,或者弱引用WeakRefrence,把一个引用给包装起来,那包装起来之后呢,垃圾回收器会在内存不够的时候,它会考虑回收,只不过这个在2.3之后,可能会导致更倾向于回收,所以的话,我们尽量用LruCahce来解决我们内存溢出的问题



然后呢,我们去讲了另外一个大的模块,屏幕适配,那屏幕适配的话



我们首先是要养成一个良好的屏幕适配习惯,比如说多用dp,不用px,多用线性布局和相对布局,不用绝对布局,或者说如果我们必须要使用px的话,需要将dp转成	px之后在使用,



那在我们这种良好的习惯的基础上,这时候开发出来的项目基本上屏幕适配是可以的,那如果实在不行的话,我们又有5种手段去解决这个屏幕适配,比如图片适配,布局适配,尺寸适配,权重适配,和代码适配,那么关于图片,布局和尺寸,其实都是新建一些文件夹



比如说布局适配的话,我们要适配的话,会新建layout-800x480的文件夹,在这个文件夹下,单独的去写	480x800的布局文件



那尺寸适配的话,也一样,那尺寸的话,我们全都可以声明在一个Values-800x480的文件夹下的dimens.xml文件中



权重适配的话,就是专门针对线性布局LinearLayout



代码适配的话,是在代码中动态的拿到屏幕的宽高,计算之后呢,在动态的设置给我们的布局



这是我们讲的一个屏幕适配



最后的话,我们讲了一下推送



那推送的话,我们实际上是使用到了一个极光推送,那极光推送的话,我们也是简单解释了下它的使用原理,我们也使用它的sdk,然后简单写了一个demo,这是我们昨天讲的一个推送



这就是我们昨天讲的内容的总结 

Day06 02.推送原理介绍 ##

那这个关于昨天推送的知识,我们再稍稍的补充一些知识点,比如说这个推送它到底是该怎么去实现的呢



我们现在只是使用sdk去调它的api对吧,非常的简单,但是呢,如果你实际上自己去开发一个推送模块的话,它要涉及到哪些知识点,这些我们是属于一个了解的范畴



我简绍这个知识点,并不是非要你们把这个东西完完整整的开发出来,说这些知识点是要你在面试的时候,就有的和人家说对吧,真的和人家面试官扯到这个推送的时候,我们至少和面试官有些沟通的语言



不要说我就知道一个极光推送对吧,然后去调用一下sdk的api就出来了,就像上次的那个同学,刚毕业之后呢,它去面试的时候,往简历里边写了一个了解消息推送,然后这个面试官就问他,那你说这个消息推送是怎么个实现啊,你怎么去实现的啊,它说我就是用了一个sdk,然后去调了一下它的api,然后就实现了,他就这么一句就完了,那面试官就问,那具体是哪个sdk的哪个api啊,它又说不清楚,你这样的话,你写这个知识点就说这么一句,那面试官就肯定想你肯定不会啊,不然最起码可以说一天,你不会还写简历里边,那水分得是有多大啊,还了解消息推送,我一问你就不会啊,那你别的知识点是不是也不会啊,那这样的话,就感觉你的简历水分很大,肯定就觉得你这个人不行



所以我们在这里还是稍微了解下,首先我们去了解下推送原理



推送原理是这样的,服务器,手机端,然后服务器端给手机端发送消息,那要发送消息的话,我们客户端是不是需要实时的和服务器保持连接啊,如果你和服务器没有保持连接,服务器到时候有消息的话,那怎么找你啊,找不着吧



客户端找服务器好找嘛,你只要服务器给我一个链接,域名,我客户端去请求是不是就能找到服务器,那找到服务器之后就请求,但是服务器怎么找到客户端呢?你怎么找?



茫茫客户端,你怎么找到你要推送的那个人啊对吧,除非你是知道那个客户端当前的ip地址



因为我们平时手机端是不是要联3G联Wife对吧,你要联我是不是都要分配一个ip啊,但这个ip是动态的,一会是这个,一会儿是那个吧,一会儿是加载个wife一会儿又是3G对吧,一会儿又是别的啊对吧,那所以的话,你这个ip又是动态的,那服务器很难找到你



那我们为了让服务器找到你,就是在客户端请求服务器端的那一刻,那客户端首先是不是要请求下服务器端,那请求完之后呢,服务器端就和客户端要保持住这个连接,要把这个连接要一直保持住,千万不要断点,一旦一请求我们就连上,不要断掉对吧,那这种连接呢,我们叫做长连接,就和什么似得,你们在学基础的时候,应该都了解过socket吧,socket的话,你们会有一个客户端的socket,还会有一个服务端的socket,然后你客户端给服务端去提交数据,服务器是不是还给客户端去写数据啊,那服务器去给客户端写数据,客户端是不是一直打开一个端口,一直在那等着呢。一旦服务器写过来,那个地方是不是一下就打印出来了,那这种简单的一个demo,其实它就是一个连接,因为它这个连接是保持住的



因为我们没有手动的把这个连接去disConnect的断掉对吧,那这个连接时保持住的,保持住的话,那我们客户端是不是就可以实时的和服务器端进行一个相互的沟通了,这就是我们所说的长连接,而长连接的这个是我们在http1.1之后,都会支持这个长连接,在1.0版本的http的时候,只要一响应,这个连接就断了,那Http1.1之后,它支持长连接,它其实有一个字段叫做keep-alive,就说我们的请求头,还记不记得Http的请求头啊,响应头啊请求码啊,什么乱七八糟的



keep-alive这是一个请求头,请求头里边的字段叫keep-alive,那这个keep-alive如果你传的是true的,就是我们要求一个长连接,那这时候呢,http它就不会自动把这个链接给断开了,这是我们所谓的一个长连接,只有保持长连接,我们的客户端才能实时的和服务器保持一个连接,这就是我们第一个关键字长连接



 那除了这个长连接之外呢,还有什么东西呢?就说服务器和客户端要相互的去发消息嘛,那发消息的话,我们的消息很短,就一句话,或者某一个字符串对吧,通知给我们这样的一个客户端,要发消息,

这种消息我们如果走的是http协议的话,http协议,大家知道是什么吧,整天用Http对吧,如果面试的时候,面试官问你,你了解http协议吗?



你们现在在座的,肯定第一反应是什么,你了解吗?谁了解给我解释下什么是http协议,你怎么去给面试官解释什么是http协议, 你这不就是Htppurl嘛,http://	www.baidu.com,这不就是http协议嘛,在讲http协议的时候,我觉得在我们前边学的那个javaWeb里边是吧,肯定老师讲过Htpp协议,而我觉得那个是属于javaweb里边非常重要的一个部分,你可以忘掉jsp,servlet,你可以忘掉javasrcript,html,因为那些东西是服务器的对吧,但我客户端要了解的话,你http协议是不是要了解,http协议你怎么讲,就是get,post对吧,get的话,参数传在后边,post的话,我们参数传在body体里边对吧



而在发数据的时候呢,会有一个响应头,我们会有一个请求头,请求头里边会有一些比如语言啦,别的一些请求头,这个你也不用太了解它所有的请求头,你就知道它有一个请求头,可以请求服务器,然后服务器响应的时候,它会有一个响应码,200啦,302啦,404啦,500啦,分别代表什么意思,你们应该知道吧,这个也是属于http协议的一部分,同时在响应完了之后,它会有一个响应头,响应头里边会告诉你这个里边的一些你现在相关返回的一些数据的编码格式啦,语言啦之类的,当然还有一个响应体,响应体里边就是我们具体获取的内容,以响应体的形式返回回来,比如你要获取一串json数据,那他也就在这个响应体里边,这是一个标准的http协议,一般你就这样想,想请求头啦,响应头啦,get啦,post啦,你就朝这个方向去讲,这些东西都是属于http协议的一个范畴,那我们为什么要讲协议呢?



那这些其实在网络访问的时候,我们都知道有一个用socket是不是可以访问啊,是不是用socket就可以请求服务器,返回回去,那当时在用socket的时候,我们是不是随随便便搞一些乱七八糟的数据就发送过去了吧,服务器是不是也随随便便搞一堆乱七八糟的数据就发回来了吧



这种是指的是没有任何http协议的前提下,我们是搞一个纯的数据流,来交互交互,但是我们为什么要讲http协议呢?是因为http协议就相当于是对原来的这种非常杂乱的数据进行了一个规范性的操作,就说以后客户端在请求服务器的时候,你不能瞎搞一段数据就仍过去了对吧,我们符合一定的规范,那什么规范呢?那这个规范就是get,post,请求头对吧,你要是按照我这个固定的格式,然后把这个请求的这串数据封装起来,然后再去发给我们的服务器,那服务器在返回数据的时候,你也要遵循一定的规范给我返回回来,你不能乱七八糟的就给我返回回来,你要尊重响应头,响应码,响应体,你要基于这种规范框架结构,基于这种http协议结构,这个就是我们所说的http协议存在的一种架势,因为只要大家都遵循这种协议的话,那以后客户端和服务端沟通就很方便了,因为大家都遵循这种框架结构嘛,那怎么解析数据啦,怎么去请求啦,我们都可以知道,都在基于这个http协议,所以说,这是一种协议,那么我们有没有别的协议呢?也是有的



那我们之所以扯这么多http协议是因为这个协议在消息推送的时候,它是有另外一种协议,也就是另外一种框架结构,它叫做xmpp,这也是一种协议,那这种协议,就像http也是一种协议,来规范我们消息推送时候的数据结构,这是我们消息推送常用的协议,那消息推送有些哥们儿说那消息推送我们为什么不用http协议呢?



因为消息推送,我们都是一个很短的数据,你就发一个字对吧,你就还放在http协议里边,请求头什么的一大堆数据咔嚓搞过去,那边又一大堆数据给我搞过来,我其实只传了一个字嘛,对吧,你说有这个必要吗?没有嘛对吧,消息都是一个很短的消息嘛,你和人聊天对吧,你写一个500字的作文,然后给人家发过去,那不可能嘛,我们是一句话,一句话发过去了,都是很短的一个数据,所以的话,这时候我们没有必要用http这种比较重的协议了,我们用的是xmpp协议,xmpp协议具体是什么一种协议呢?



这个你不用了解太多,你就知道xmpp是一种消息推送的协议就可以了,其实它底层的数据格式是一种xml的数据格式,他会把这个数据流封装成xml,然后发给对方,它是这样的一种协议,那这时候如果真的让你基于xmpp协议去做这样一个请求的话,就相当于是你自己去写这样一个底层的消息推送的客户端的连接,那这时候其实我们就不能用传统的那种方式去封装了,因为传统去请求服务器链接获取响应的时候,我们都走的是http协议,走的http协议的话,这时候是不是又是httpurlconnection,包括httpClicent都能请求吧,那这时候如果你再用xmpp协议的话,你还能用httpclient吗?



还能用httpurlConnection吗?不能了吧,两种协议,人家是基于http协议去搞的一种封装对吧,其实我们的httpClient和我们的httpurlconnection其实全都是对最底层的访问网络数据用什么去访问的啊,最底层访问网络是用socekt吧,最底层访问的话,都是用socket去访问的,那只不过我们为什么用httpClient去访问呢?是因为httpClient,httpurlConnection是对这个socket进行了一些封装,封装完了之后,然后我们调用起来是不是就更容易一些



其实我们直接就用httpClient请求,它就是基于http这种协议然后去请求的,对吧,我们不需要自己去拼一些请求头啦,什么乱七八糟的对吧,httpClient在底层是不是帮我们拼好了吧,httpClient,httpUrlConnection其实就是对socket的封装



那这时候如果我们要用xmpp协议的话,那这时候呢,我们就不能用httpClient了,那这时候你是不是就要自己基于这个socket去封装一套xmpp协议啊,你需要自己纯手工用socket去打造,所以的话,如果你真的要自己去写消息推送的话,有些面试官会问你了不了解socket,问一下这个socket怎么去使用,可能会问这些东西,如果你真的是用xmpp去搞的话



刚才这些东西你没有理解哈,那现在我说一个面试题,当然这个不称为什么面试题,面试官可能会问,或者有些学生会问,老师呀,socket和http有啥区别啊?



你告诉我socket和http有啥区别,	他两没有任何的关系,两个东西



http是一种协议,规范,是一种数据框架,格式对吧

socket是什么,访问数据,访问网络的一种工具而已对吧,它是一种工具,它是在实现网络连接的时候,我们要用socekt来实现一个网络的连接对吧,但是在这种连接已经建立起来,socket已经将这条通道已经打通之后呢,然后我们在上边去传递数据的时候,用http协议对他进行一个封装,再传递过去吧,



所以本质上,socket和http没有啥关系,socket只是一种工具,那http它是一种协议对吧



就跟什么似的,你举个例子,比如说socket你就认为他是一部电话对吧,那通过这部电话的话,我们可以和远在他乡的人,进行一个通话,是不是可以通话啊,那这个东西是不是就相当于一款工具对吧,但是我们的http协议的话,它相当于一个什么呀,就说我们在通话的时候,你用什么语言呢?你用中国话呢?还是用英文啊,对吧,是这种语言吧,它只是对声音进行传递,那你找一条狗对吧,对着电话叫一声,那个声音能传到对边吗?



也能吧,但是我们要正常工作的话,是不是要用语言啊,这个语言就是一种规范嘛,我们照着这个语言去通话是不是才能达到一定的意义啊,所以说这个我们就多扯了一些



那最后的话,除了这种协议之外,我们还有一个东西,叫做心跳包,什么东西呢?



那我们在去请求的时候,我们客户端要和服务器要保持长连接对吧,但你觉得他两能够永远的保证这个连接不会断吗?



不可能对吧,那比如你的客户端突然进到电梯里边了对吧,或者你进到地铁里边了,或怎么着的时候,你的客户端就没网了吧,你没网怎么和服务器进行连接啊,连接不上吧,网络肯定就断了吧,即使你怎么长连接,它都联不上嘛,那这时候就断了吧,断了之后我们的服务器要给这个客户端去发消息的时候,客户端收不到消息吧,因为它没网了,那这时候,我们服务器怎么知道这个客户端是属于一个连接的状态还是未连接的状态呢?



这时候就需要服务器去监听这个客户端有没有心跳,这个是个很形象的词,你要看一个人是活着还是死了,看看它有没有心跳是不是就行了,那现在服务器要看这个客户端是活着还是死了,就看客户端有没有心跳,那这个心跳是个什么东西呢?



 它是一段极短的数据,那客户端每隔一段时间都会向服务器发送数据,来证明自己还活着对吧



比如说每隔一段时间,比如说一分钟对吧,我们就向服务器发送一段非常短的数据,比如这个是个心跳对吧,之所以非常短是因为我们这个地方这个数据没有什么意义,我们搞短是不是就能帮用户省流量



那我们这个数据只是用来证明我还和你连接着呢,那一旦服务器每隔一分钟都能收到这种心跳的话,那服务器就会认为你这个客户端还活着,因为它能收到客户端发送过来的心跳对吧,那一旦服务器比如隔了好几分钟了,比如说隔了5分钟了,看这个客户端还没有心跳的话,那这时候服务器就认为客户端死掉了



那下次发消息的话,服务器是不是就会自动的把,本来服务器是不是还专门要给这个客户端要开一个端口去连接,但现在服务器为了节省资源,发现这个已经死了,我还干嘛要给你留一个位置啊,那这时候它是不是就把这个位置让给别人了,它就把这个客户端从它服务器移除掉了,这是我们发送心跳包的这样一个意义,那这时候就会有一个问题,就说那如果我的客户端死了,那我难道就收不到消息了吗?



比如说你正在和一个女神去聊天对吧,正聊到火热的时候,女神他给你发了一句话对吧,说今天晚上某某宾馆312房间见对吧,发了这段话,而你当时刚好在电梯里边,对吧,没信号嘛对吧,你是不是不知道啊,但是这时候服务器就认为这个哥们儿已经死了对吧,那那么一段重要的信息是不是就丢失掉了对吧,



那丢失掉了之后,那你怎么办?你肯定就懵了吧对吧,但是我们在实际上,使用微信或者qq的时候,是不是没有任何影响啊,就说我可能进到电梯里边了没有收到消息,但是我从电梯出来之后,马上这个消息是不是就过来了吧



所以的话,它是这样的,就说一旦我们失去心跳,服务器会将那段消息零时先保存起来,然后等客户端活过来之后,重新发送消息吧,避免消息丢失,客户端什么时候活过来,比如说它有网了对吧,它有信号了这时候客户端是不是就一有信号就马上赶紧请求一下服务器,给服务器发一个心跳啊,说我又活了对吧,



那这时候服务器就说你活了,那你还有好多消息对吧,昨天晚上的那个消息,我赶紧给你发过来吧,发过来已经晚了对吧,这个就是我们所说的消息推送,所以的话,消息推送的话,它大概就是这么一个逻辑



就说包括长连接,包括我们的socket,xmpp,包括心跳包对吧,这些东西我们都可以去了解下



那这个就是我们消息推送的一个简单的原理



当然有些同学可能对聊天更感兴趣一些,他可能会希望说,老师那这个聊天它到底怎么聊啊对吧,它的那个底层原理是什么对吧,那么在讲这个之前,我们先讲一下消息推送,我们目前要开发的话,可能要做这么多哈,那我们消息推送的这个工作量,它是服务器和客户端都要有工作量对吧



服务器首先得暴露很多的接口,比如说如何保持长连接,如何发送心跳,什么格式都需要做好,而且这个对服务器更加的一个考验是什么呢?



就是服务器如何能保证他和成千上万的客户端同时保持这个长连接,那如果服务器要和客户端成千上万的客户端都能保持长连接的话,这就是对服务器的要求是极高的,非常高的



 所以说我们并不是说随随便便的一个小公司对吧,它就能马上做一个非常稳定的消息推送系统,不可能,这也就是说,为什么极光推送它有市场的原因,就在于如果大家都能随随便便搞一个消息推送的服务器,客户端的话,那极光推送这个公司还怎么活啊



它肯定就没有是市场了吧,所以说极光推送它就专门去做消息推送,而且很多应用,就为了避开那些繁杂的一些消息推送的实现,所以它就用了第三方的平台了,比如说之前我在新浪的时候,它就会有这样一个需求,新浪他会发私信,那这个私信它其实也是一段消息,但是由于客户端当时非常的多对吧,成千上万,因为活跃率很高嘛对吧,它如何能保证在高并发的情况下也能够准确无误的将这个消息发送给每个客户端呢?



这个是很考验技术的,所以说最开始的时候,新浪微博还用的是第三方的技术,它自己还没有实现它自己的,使用的是第三方的平台,后来的时候,因为它第三方平台不好维护,因为改需求的话,第三方平台爱搭理不爱搭理的对吧,所以它就干脆自己去开发一套这样的消息推送的架构,而开发这个东西它是至少花了半年以上的时间,去把这个东西搞好的,而且搞出来之后,还各种不稳定,调来调去对吧,最后终于搞稳定了还到处吹牛逼开讲座,我这个怎么怎么去搞的对吧,开讲座给人家吹牛逼对吧,这个是当时它的这样一个情况对吧,所以说消息推送对服务器的考验是非常大的,如果你只是联几千台设备的话,服务器很容易hold住,现在是几十万设备的话,你怎么hold住?



就像微信,像qq对吧,几亿的设备有时候都和你连着对吧,同时连接对吧,你如果说保证这个几亿用户的消息都不丢失对吧,而且都能够精准的去送达,这个就太考验技术了,所以说我们不能不佩服这个qq和微信对吧,你会发现任何人在使用qq和微信的时候,几乎都是非常稳定的吧,一发消息马上就能收到吧

你可能觉得你自己的很稳定,但是它要保证所有人的都一样稳定,这个就特别难了,当然也不能说它太牛逼,因为你看,人家qq做这种消息推送,它做了多少年啊,它至少做了十几年吧,qq啥时候出来的,九几年出来的吧,那现在都快二十年了,它一直在做这个消息嘛,qq是不是一直在做,那qq这个是不是已经做的很稳定了,而微信发消息是不是和qq很类似,它直接把原来qq的那个框架直接搞过来不就得了嘛,所以说微信一下子为什么就发展这么壮大是吧,一定原因首先是因为它技术很牛逼,已经积累这么多了吧,已经很有经验了,其次还因为庞大的qq用户是不是一下子转到微信啊,所以导致微信一下子爆火起来



我们用过小米手机的应该知道小米手机的米聊对吧,米聊完全是比微信还要早的一款设备,很有可能微信就是抄的米聊的对吧,很有可能对吧,不确定哈,反正后来微信发展起来了,但是米聊没有发展起来,最根本的原因也不在于技术积累了,小米公司技术还可以哈,主要是因为用户对吧,用户太多了,太吓人了,qq6,7亿的用户量对吧,只要平移过来对吧,用户是不是就不用注册,直接就可以上手用了吧,再经过腾讯一推广对吧,一下子就火起来了,就是因为它有用户,所以一般腾讯为什么搞一款产品成功一款,因为搞一款都有qq用户,再搞一款是不是还是qq用户,这些用户你不用搞是不是就过来了吧,所以的话,微信发展起来了,你像这个小公司对吧,好多人想模仿微信对吧,像易信,阿里还有那个来往,还有什么旺旺对吧,那个东西你想模仿人家qq,微信,你即使做的和人家微信一模一样,也是没有用户吧,支付宝上次更新了一个版本,它给这个版本里边加了一个发现的模块,还是好友的模块,里边抄的是和微信几乎一模一样,专家评价属于业内像素级抄袭对吧,连图标乱七八糟的都一样对吧,但是它还是没有用户啊



你做的完全一样,用户是不是还是人家微信的用户啊,所以腾讯公司它在中国互联网来讲的话,它是属于一个对用户进行垄断的,中国互联网是有两大垄断,第一大垄断是腾讯对用户的一个垄断,用户垄断这个很吓人的,随随便便把用户平移过去就一下就火起来了,另外一个垄断是百度公司对流量进行垄断,有些哥们儿说流量怎么垄断,什么叫流量垄断,就是说大家一打开浏览器,第一反应是www.baidu.com吧,是不是第一反应都是这个,就算测个有没有网是不是都写的百度啊



所以的话,你在这个地方随便去搜个东西的话,是不是都在百度的平台上进行搜的啊,所以你走的所有的一个网络数据的流量,是不是全走的是百度的啊,它对流量进行垄断,流量垄断有什么好处呢?



因为它都是以它为入口嘛,以它为入口,它是不是很容易发广告啊,那这个就是通过广告赚了好多钱,这个是我们随便扯了一些东西,实际上我们刚才要讲的东西是聊天系统哈





聊天系统是这样的,比如说我们这个是一个客户端,然后这个是你的那个女神对吧,你们两个客户端聊天嘛,这时候不要以为是点对点,你给我发,我给你发对吧,没这么夸张,肯定不是这样发的,它怎样发的呢?他中间肯定有个服务器,那比如说我们有一个第三方的服务器,比如说这个是我们极光推送的服务器,那我们是由极光推送去发送,但这时候如果你去客户端去请求极光推送的时候,极光推送就可以给你发哈,因为极光推送它是作为一个第三方的公司,它只是负责推送,它不负责维护什么张三李四的用户吧,这些用户是不是你们自己公司要维护这波用户啊,所以你一定要有一台自己公司的服务器,这是我们自己公司的服务器,那我们服务器的一个交互是怎么交互的呢?



我们是这样的,比如说这是我们的张三,这是小丽,这时候张三要给小丽发一句话,说“今晚在哪里吃饭呢?”,比如说它发这么一句话,“约不约?”,它肯定不是直接发给了小丽,它是先发到自己的服务器,它发个“约吗?”,约吗之后它自己的服务器也不能直接发给小丽,因为我们现在要推送对吧,自己的服务器是不是无法实现一个长连接啊,无法去推,我们需要委托极光推送去推吧, 所以这时候呢,自己的服务器要先去委托极光推送对吧,然后她把这个“约吗?”消息,发给极光推送,委托极光推送这时候把消息发送给我们的小丽吧,它是这么一个操作,就说我们自己的服务器会调极光推送的接口,会请求极光推送,让你把这个“约吗?”发给小丽,因为只有极光推送才可以直接将数据推给我们的客户端,那这时候小丽比如说回一句话对吧,比如她回一句“500块”对吧,那这时候小丽回的时候,也不是找这个极光推送,它也是找到我们自己的服务器,服务器要对这些数据进行一个维护,然后服务器再去委托极光推送,把这个“500块”再发送给我们的张三对吧,这个就是我们的一个聊天流程



就说自己的服务器如果要去使用极光推送来去实现一个聊天模块的话,其实它就这么一个流程,就我们的客户端永远和自己的服务器打交道,但是我们自己的服务器要和极光推送打交道,告诉极光推送,该把这个消息推送给张三呢还是李四,我们自己的服务器去维护这个数据,这就是我们简简单单的一个聊天模块,这些东西包括我讲的消息推送的原理,全都是我们需要了解的一个部分



其实有时候你在面试的时候,我们不需要对某一块了解的特别精对吧,因为我们术业有专攻对吧,每一个人在android领域它所面向的那个点,因为android太大了对吧,有些人可能就专注于搞这个Android游戏对吧,有些人就专注于搞这个云储存对吧,我之前就搞这个云存储,上传下载对吧,那有些人可能就专注的搞视频播放对吧,在线视频播放什么乱七八糟的对吧,那有些有可能专注于搞浏览器开发,有些人又可能专注于搞其他的,每个人都有领域,那如果你让一个专注于搞浏览器开发的人员突然切入到让它开发游戏,它肯定会很吃力的吧,所以每个人都有他专业的地方,当然我们专业的地方就是我们讲的这几个项目哈,这是我们的一个专长的地方,但是我们在把专长的地方搞定了之后呢,在面试的时候,还是可以和对方去扯一些比较大的一些范围,我可以没做过,但是我不能不了解,我只要都了解了的话,比如面试官问你,有没有做过这个聊天啊,当然前边已经聊的很愉快了吧,那你有没有做过聊天啊,这个我没做过,但是我还是有点了解啊,我简单给你说下,什么乱七八糟的,刷刷刷的去说对吧,这时候这个面试官就会觉得,这哥们儿不错啊,这个没做过还这么了解对吧,那做了还得了,所以的话,他要是问你有没有做过推送啊,你就说搞过一点,当时用极光推送搞一下,收一下消息,那他问那你了解它的一些机制吗?



这时候你卡卡卡的把什么长连接啊,xmpp什么的乱七八糟的说了,但是你说的这个东西,我们没做过对吧,那个做起来确实很费劲,但是你要把它的心跳包啊,长连接啊,socket啊什么乱起八糟的,都给它一说对吧,那他是不是就觉得这哥们儿好牛逼啊,这个知识面好广啊,所以这些都是我们了解的,所以面试的时候,把一些大框架,大思路给它说清楚,那他是不是就很嗨,所以在黑马那边有一个个哥们儿,拿到27k,它怎么拿到那么高呢?



就是刚毕业之后,它在面试的时候,那个高度就和你们下边拿10k的那个高度不一样了,拿10k的就很紧张,人家问生命周期,我怎么给人家回答啊,这个是拿10k的一个标准,那人家拿27k的面试的时候,是站在一个全局架构师,包括项目经理的一个高度去和对方去聊天的,直接就和人家技术总监去沟通,那这帮人在聊天的时候,它聊什么呢?它会聊activity声明周期吗?绝对不会聊这个东西吧,它聊什么,聊的是大框架,大思路对吧,就和国家领导人讲东西,它绝对不会讲下边的小县城怎么怎么的,它就问下全局的gdp提高多少,我们的国防怎么怎么着,要裁几十万对吧,下边你爱怎么裁怎么裁对吧,反正我说30万,你就去裁呗,它都是大方向对吧,你大方向和人家沟通的话,其实你细节全都不知道对吧,但是项目经理就说,我靠,这个思路很清晰,它的这个框架啦,结构啦,原理啦,它还都很了解,这时候就愿意给它开27k,而且一去就是技术总监,所以这个就是这样的,当然我不说要求大家下去就朝这个大方向去,但是就担心你到时候没的说的,朝大方向去说,就扯得人家觉得你很幼稚对吧,人家就觉得你扯得是什么啊对吧,还大方向呢



我们就踏踏实实的先拿到个10k对吧,先工作对吧,以后等真的了解了,真的入门了,再给人家去扯大方向对吧,这个就是我们随便扯了这么多,那这节课就到这里,我们休息下

推送原理介绍
XMPP协议

			一种基于TCP/IP的应用层协议, 专门用于消息推送, 数据格式为xml

长连接

			使用socket请求时, 服务端和客户端都不主动关闭输入输出流, 从而实现长连接

心跳包

			客户端每隔一段时间(比如1分钟)向服务器发送一段极短的数据,称为心跳包. 服务器收到数据, 就证明客户端还活着, 就会保持连接,向客户端推送消息. 否则断开连接,节省服务器性能. 客户端重连后,服务器将未发送成功的消息重新发出.

Day06 03.语音听写 ##

科大讯飞语音云

http://open.voicecloud.cn/



下载SDK, 阅读SDK文档(MSC开发手册), 按照文档,实现如下三个功能

语音识别
语音识别弹窗
语音朗诵

接下来我们讲一个比较高大上的知识点,其实这两天都是高大上的知识点,那这个是语音识别,语音识别大家肯定听说过,就说比如说我说段话,它就能够识别出来我说的是什么,这个就是语音识别,那语音识别的话,它也要用到第方法的开放平台,它叫科大讯飞,这个是一个老牌子,它一直在专注于做语音识别方面的一些技术,而且呢,它也将这个技术开放了出来,让我们第三方开发者,也可以非常迅速的来接入它的服务,它开放了一个平台,科大讯飞语音云,我们可以去百度一下,上他的官网看下





 在官网上,我们有些同学可能会用到讯飞输入法,这个也是科大讯飞公司出的一款东西,什么录音宝,灵犀之类的玩意,我们不知道



我们就看一下它的开放平台,我们作为一个开发者,主要是想看下它的开放平台,都做了一些什么事情,我们可以把这个开放平台打开



那我们其实在进入它的开放平台的时候,其实这个逻辑和讲极光推送的时候,比较像对吧,我们也需要在它上边去下载它的sdk,当然你的应用要去使用人家的sdk的话,你也需要使用到人家的一个appkey对吧,它也会给你分配一个appkey,我们使用这个appkey然后再进行一个下载哈,这个就是他的一个开放平台



而他的开放平台里边的话,有很多功能,比如叫做语音听写,什么叫做听写呢?就说你说句话,数据就能识别出来,它就能给你写成文字



还有一个在线语音合成,那在线语音合成指的是什么呢?指的是你写一段文字,它能给你念出来,你搞一段小说,它就能给你刷刷的往下念,这个就是语音合成



开放语义:这个什么意思呢? 因为有时候,你要和这个计算机进行沟通嘛,你说段话,它一听之后,要给你回段话吧,语音机器人嘛, 你比如说苹果的那个mengmeng,你和他去讲的话,它就能听出来是什么意思,它怎么能知道是什么意思呢?这个就叫做开放语义



比如说中文,说话是不是很复杂啊,同一个意思我们可能有很多不同表达方式对吧,比如说你要从西安去北京对吧,你要坐火车从西安去北京,那这时候呢,你就可以问mengmeng,我想从西安去北京怎么走呢?



这是一种问法吧,从西安去北京的列车都有哪些呢?这也是一种问法吧,从我们的西安去首都,该坐哪趟火车呢?是不是还是一种说法,但是这三种话是不是说的都是一个意思啊,那这时候emngmeng知道这个意思之后,它就把西安到北京的所有最近的火车给你列出来吧,它这个就要听懂你的意思,这个就是所谓的开放语义,所以说你要机器听懂你说话是什么意思,这个还是比较高端的,这个就是开放语义



人脸识别:识别人脸,刷脸嘛



声纹识别:微信这两年也推出来了一个声纹登陆,你说一段话,它就能听出来你是谁,而且你的语音作为你的人的一种识别方式,就刷声音对吧,我说段话,它就知道是我,然后把门就自动打开了对吧,这个就是所谓的声纹识别



语音唤醒:那语音唤醒指的是什么呢?无需触碰,直接进去,让应用具备全语音交互能力,这个是什么意思?就是说这个进入智能家居时代,你可能一进门,说给我开灯,然后灯就开了, 你说看电视,电视就开了,给我烧热水,我要洗澡对吧,这个热水就开始烧了对吧,这个就是全语音时代全是语音唤醒,它一听就知道你要干什么,它就给你搞了,这个不是未来的科幻小说,就在眼前,现在我们这些技术其实都可以实现,只不过还没有普及出来,但是这个迟早都会普及的,就是所谓的智能家居时代,这几年是智能硬件大爆照,到处都是智能硬件,当然我们搞android开发也是和智能硬件息息相关的,因为我们智能硬件往往需要一个控制端,控制端在哪里?就在android手机上,通过android手机去控制你家电的各种东西,你可以用手机当做电视的遥控器,小米就能做到吧,因为小米手机就可以当做遥控器去控制小米的电视,空调,等等,当然这个手机端的软件就是我们去开发的对吧,这个是随便说了下语音的功能



然后我们主要用的是它的语音听写和在线语音合成,别的开放语义,人脸识别,声纹识别,语音唤醒等暂时用不到



我们可以看下这个语音听写的平台,然后我们是不是可以使用这个服务啊,点使用服务,他会让你登陆,登陆上去之后,它会让你创建应用,当然我也可以创建一个应用嘛,下边是我之前创建的应用,我们可以重新创建一个新的应用



创建一个新的应用需要输入:



应用名称:zhxa02



应用分类(比如我们随便写个助理,智能家居,交通对吧)



应用功能描述:随便写



应用平台:选择android平台





你创建完应用之后,点一下你应用的名称,你点创建,它就给你创建这么一个应用,就能够使用语音听写的服务了,然后呢,再去下载它的sdk,它会有“单个服务sdk下载”,“组合服务sdk下载”,那我们就选组合服务sdk下载,选择里边免费的语音听写,在线语音合成,然后选择“Android平台”,选择应用是不是选择我们刚刚创建的zhxa02, 然后他会马上下载sdk



下载完了之后,我们看下刚才这个应用的appkey,我们在哪里看他的appkey呢?



我们找下它的appkey,在我的应用的应用名称右侧,有一个appid:55ea507f,我们把它复制保存好,



我们刚才把sdk下载下来了,现在就是使用下它的sdk,名称是"Android_voice_1081_55ea507f"



它有这么几个目录,我们首先看他的doc文件夹下的文档,看“MSC Develop Manual for Android”这个文档



直接看里边的第二步预备工作的第一步:导入sdk,将libs包拷贝到我们自己项目的目录下,那我们单独写一个语音识别的项目“语音识别02”,将刚才sdk“"Android_voice_1081_55ea507f"”中libs下的东西全都拷贝到我们写的“语音识别02”项目的libs文件夹下,拷贝过来之后,我们看到它有好多C代码写的库文件,当然他支持很多平台,比如arm的处理器,mips的处理器,还有一个x86的处理器,它都支持,拷贝过来之后呢,我们看下后边它做什么



“MSC Develop Manual for Android”这个文档中又说,要添加用户权限,那我们把这个文档中的权限全都拷贝到“语音识别02”的清单文件中



然后第三步是:初始化即创建语音配置对象,只有初始化后才可以使用MSC的各项服务,建议将初始化放在程序入口处(如Application、Activity的onCreate方法),那我们就在activity的onCreate方法中初始化吧



那初始化的话,需要调用这样一个方法,然后再进行一个初始化操作,那首先来到我们自己的MainActivity中的onCreate方法中,我们在这个地方调一下这个方法,即:



@Override

protected void onCreate(Bundle savedInstanceState) {

	super.onCreate(savedInstanceState);

	setContentView(R.layout.activity_main);

	SpeechUtility.createUtility(this, SpeechConstant.APPID + "=55ea507f");

}



并将appid替换成我们刚才保存的appid



然后我们再往下看,文档中下边的一些东西感觉用不上,我们先开发个语音听写,比如说我说一段话,它能够给我解析识别出来



那我们先在activity_main.xml中增加一个Button按钮,然后给他添加onClick属性值为“startListen”,就可以在MainActivity中findViewById找到它,即:



/**
  • 语音听写
     *

  • @param view
     */
     public void startListen(View view) {
     // 1.创建SpeechRecognizer对象,第二个参数:本地听写时传InitListener
     SpeechRecognizer mIat = SpeechRecognizer.createRecognizer(this, null);

      // 2.设置听写参数,详见《科大讯飞MSC API手册(Android)》SpeechConstant类
    
      mIat.setParameter(SpeechConstant.DOMAIN, "iat");
    
      mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
    
      mIat.setParameter(SpeechConstant.ACCENT, "mandarin ");// 普通话
    
    
    
      // 3.开始听写
    
      mIat.startListening(mRecoListener);
    

    }

    语音听写怎么听写呢?我们看下文档是怎么去写的,把文档中的这段代码抄过来就行了,即创建SpeechRecognizer对象,然后设置听写参数,最后开始听写

    这时候我们需要重写它几个方法了,第一个方法叫做onResult方法,它是听写结果的一个回调接口,

    就说我们将来的听写结果都会在onResult方法中打印出来,我们把这个整体抄一下,这些方法是什么意思我们都在下边添加了注释,即:

    // 听写监听器

    private RecognizerListener mRecoListener = new RecognizerListener() {

      // 听写结果回调接口(返回Json格式结果,用户可参见附录13.1);
    
      // 一般情况下会通过onResults接口多次返回结果,完整的识别内容是多次结果的累加;
    
      // 关于解析Json的代码可参见Demo中JsonParser类;
    
      // isLast等于true时会话结束。
    
      public void onResult(RecognizerResult results, boolean isLast) {
    
      	System.out.println("识别结果:" + results.getResultString());
    
      	System.out.println("isLast:" + isLast);
    
      }
    
    
    
      // 开始录音
    
      @Override
    
      public void onBeginOfSpeech() {
    
    
    
      }
    
    
    
      // 结束录音
    
      @Override
    
      public void onEndOfSpeech() {
    
    
    
      }
    
    
    
      // 会话发生错误回调接口
    
      @Override
    
      public void onError(SpeechError arg0) {
    
    
    
      }
    
    
    
      // 扩展用接口
    
      @Override
    
      public void onEvent(int arg0, int arg1, int arg2, Bundle arg3) {
    
    
    
      }
    
    
    
      // volume音量值0~30,data音频数据
    
      @Override
    
      public void onVolumeChanged(int arg0, byte[] arg1) {
    
    
    
      }
    

    };

    我们主要关注的是第一个方法onResult,我们讲完话之后,它能把这个结果正常的给我打印出来,我们关注的是这么一个结果,这样的话,我们就把语音听写这样一个功能已经写完了

    那现在呢,我们就可以把这个程序运行一下,看看它的效果怎么样,我们按一下语音听写的按钮,就可以说话了,我们说:“大家早上好”,这时候在控制台就会输出“大家早上好”

    isLast打印的是false,那是因为我话还没说完,当打印isLast是true的时候,就说明话说完了

    这个就是我们这样一个语音识别的功能

Day06 04.语音合成&语音听写弹窗 ##

那我们再来一个语音合成,语音合成怎么去合成呢?



还是在“MSC Develop Manual for Android”这个文档的下边



我们看到有“6.语音合成”,它说与语音听写相反,合成是将文字信息转化为可听的声音信息,让机器像人一样开口说话。合成的调用方法也给我们都写好了



那我们现在再在activity_main.xml中去写一个按钮叫做“语音合成”,给它添加onClick属性值“startSpeak”,然后我们就可以来到MainActivity中去写一个startSpeak方法,把参数View传进去,这是我们的语音合成,我们看下它具体是怎么去写的,我们照着它的api抄下,即:



/**
  • 语音合成
     *

  • @param view
     */
     public void startSpeak(View view) {
     // 1.创建SpeechSynthesizer对象, 第二个参数:本地合成时传InitListener
     SpeechSynthesizer mTts = SpeechSynthesizer
     .createSynthesizer(this, null);

      // 2.合成参数设置,详见《科大讯飞MSC API手册(Android)》SpeechSynthesizer 类
    
      // 设置发音人(更多在线发音人,用户可参见 附录12.2
    
      mTts.setParameter(SpeechConstant.VOICE_NAME, "vixying"); // 设置发音人
    
      mTts.setParameter(SpeechConstant.SPEED, "50");// 设置语速
    
      mTts.setParameter(SpeechConstant.VOLUME, "80");// 设置音量,范围0~100
    
      mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD); // 设置云端
    
      // 设置合成音频保存位置(可自定义保存位置),保存在“./sdcard/iflytek.pcm”
    
      // //保存在SD卡需要在AndroidManifest.xml添加写SD卡权限
    
      // //仅支持保存为pcm和wav格式,如果不需要保存合成音频,注释该行代码
    
      mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, "./sdcard/iflytek.pcm");
    
    
    
      // 3.开始合成
    
      mTts.startSpeaking("床前明月光,地上鞋两双, 一对狗男女,其中就有你!", null);
    

    }

    它首先是需要创建一个SpeechSynthesizer对象,并且把context对象换成我们的this

    然后设置发音人,就说你想让谁去开口说话呢?它有好几个机器人,我们这里是一个“xiaoyan”,语速是“50”,音量是“80”,还有一个语音引擎,就说你是要用什么语音引擎,我们一般都是在线合成,即TYPE_CLOND,云端的这样一个云引擎,那下边还有一个TTS_AUDIO_PATH, 这个地方是一个sd卡的路径,这指的是你在合成之后,要不要把刚刚合成的那段话,保存在本地呢?那保存的格式是一个TTS的语音格式,

    最后第3步是开始合成,在它里边传一个说话的监听mSynListener,监听里边我们可以拿到他说话有没有结束的一个进度,它开始说话什么乱七八糟的api,我们不监听了,传一个null,我们这里开始合成的时候,可以自己设置一句话,让它合成,我们写一首诗,“床前明月光,地上鞋两双, 一对狗男女,其中就有你!”

    那现在运行程序,让这个机器人去帮我们朗诵一下,我们可以更改发音人,还有河南话,陕西话,中文,英文等等,我们更改下,然后再运行一下,还行是吧

    这是我们发音的一个功能,当然我们发音还有一个功能,就说我们在聊天的时候,或者说话的时候,它会在手机屏幕中间弹出一个对话框,对话框中有个声波,我说句话它就刷刷刷的转,大家都用过这种语音识别的软件吧,你随便语音录入,它是不是最后说话完了之后,它就转几圈,然后把你说话的结果就传上去了吧,有这么一个UI效果,那我们也可以加这样一个语音的UI效果,那这个UI效果其实科大讯飞已经帮我们搞好了,在文档中搜索“语音输入UI”,找到它,那这个语音输入UI怎么去弄呢?

    它说首先需要将SDK目录下的assets拷贝到我们自己的项目中的assets目录下,那放进来之后,我们再在activity_main.xml布局文件中添加“语音输入UI”按钮,然后我们再给它加一个点击事件onClick属性值为“listenUI”,然后我们在MainActivity.java中写个listenUI方法,

    /**

  • 语音听写UI
     *

  • @param view
     */
     public void listenUI(View view) {
     // 1.创建RecognizerDialog对象
     RecognizerDialog mDialog = new RecognizerDialog(this, null);

      // 2.设置accent、language等参数
    
      mDialog.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
    
      mDialog.setParameter(SpeechConstant.ACCENT, "mandarin");
    
    
    
      // 3.设置回调接口
    
      mDialog.setListener(new RecognizerDialogListener() {
    
    
    
      	@Override
    
      	public void onError(SpeechError arg0) {
    
    
    
      	}
    
    
    
      	@Override
    
      	public void onResult(RecognizerResult results, boolean isLast) {
    
      		System.out.println("识别结果:" + results.getResultString());
    
      		System.out.println("isLast:" + isLast);
    
      	}
    
    
    
      });
    
      // 4.显示dialog,接收语音输入
    
      mDialog.show();
    

    }

    我们在创建RecognizerDialog对象的时候,初始化监听的参数mInitListener不需要传递,我们传一个null就可以了

    第二步设置accent、language等参数,第三步是设置回调接口,那这个回调的话,我们需要监听它的读的结果,那这时候我们需要设置一个回调接口,它需要一个什么回调接口呢?叫做RecognizerDialogListener,那我们可以new一个,然后在它里边重写几个方法 ,我们主要是在onResult方法中打印它的输出结果,这个args0其实就是之前的results,我们也可以把这些值打印一下

    运行程序,看他能不能通过界面传参的形式把它这个展示出来,我们说个“今天中午吃什么啊?”,它打印出来的识别结果就是这个吧,最后再打印了一下“islast为true”结束对吧

    这个就是我们语音弹框的这样一个效果

Day06 05.聊天机器人01 ##

项目演示

美女
你好
天王盖地虎
你是谁

布局搭建

ListView + LinearLayout

语音识别

参照科大讯飞sdk相关文档

那刚才讲了一下语音识别,那接下来就利用语音识别的这样一个功能框架,来去开发一个小项目,我们要开发一个什么项目呢?我们要开发一个聊天机器人,就有点类似于Siri这样的一个效果,我们先把这个项目简单的去演示一下,效果是这样的,下边是一个大大的按钮“点击开始语音识别”,然后呢,我们点击之后呢,就可以去说话了,比如说我说一句话,“你好啊”,Siri机器人就会回一句“你好啊”,而且这些交谈会以文字的形式展示在按钮上边,然后你说“你是谁呀!”,Siri机器人就说“我是你的小助手!”,同样会以聊天文字的形式展示,你说“还有没有美女?”,它就给你展示一张美女图片,问“这样怎么样?”

你问“天王盖地虎”,它就回答你“小鸡炖蘑菇”,这个功能还是挺智能,那我们现在把这个项目去开发一下



我们先分析下这个页面的结构,这个是一个典型的聊天页面,下边是这样一个大大的按钮对吧,那这个布局我们到底应该怎么去写呢?



其实这个布局写起来也非常简单,这个是不是可以上下滑,你一看到上下滑,我们第一次就会联想到listview,只不过对listview进行了稍微的修改就可以,这是我们这样的一个listView,但是这个listview的话,有时候在左边,有时候在右边,其实这个布局我们是怎么去写的呢?



我们是这样去写的,这是我们的这样一个页面,这个页面我们写起来是这样去写的,我就写一个左边,一个右边的布局就行了,那当我展示他的提问的时候呢,我就把下边回答的这个布局给隐藏掉,把上边这个提问的布局展示出来,反之亦然



我们创建一个新的项目MySiri02,activity_main布局文件为下:



activity_main.xml



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" >



    <ListView

        android:id="@+id/lv_list"

        android:layout_width="match_parent"

        android:divider="@null"

        android:layout_height="0dp"

        android:layout_weight="1" />



    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:background="@drawable/bottom_bar"

        android:gravity="center"

        android:orientation="vertical" >



        <Button

            android:id="@+id/btn_start"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:background="@drawable/btn_selector"

            android:text="点击开始语音识别" />

    </LinearLayout>



</LinearLayout>

			

这个Button有一个灰色的背景,我们这里用LinearLayout去写下,并写按钮的状态选择器



布局写完之后,就可以来到我们的MainActivity中,找到我们的listview和Button,并给listview设置adapter为MyAdapter,然后在MyAdapter中的getView方法中,判断convertView是不是为null,如果它等于null的情况下,我们是不是需要添加一个布局文件,这时候呢,需要在layout文件夹下新建

list_item.xml



<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent" >



    <TextView

        android:id="@+id/tv_ask"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_alignParentRight="true"

        android:layout_alignParentTop="true"

        android:layout_marginTop="5dp"

        android:background="@drawable/asker_bubble"

        android:gravity="center"

        android:paddingLeft="5dp"

        android:paddingRight="10dp"

        android:text="提问"

        android:textColor="#000"

        android:textSize="16sp" />



    <LinearLayout

        android:id="@+id/ll_answer"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_below="@id/tv_ask"

        android:layout_marginTop="5dp"

        android:background="@drawable/answer_bubble"

        android:orientation="vertical" >



        <TextView

            android:id="@+id/tv_answer"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:gravity="center"

            android:padding="5dp"

            android:text="回答"

            android:textColor="#000"

            android:textSize="16sp" />



        <ImageView

            android:id="@+id/iv_pic"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content" />

    </LinearLayout>



</RelativeLayout>



我们用相对布局来表示,那他的布局一个在左边,一个在右边,用相对布局比较好控制



那这时候我们就可以用一个textview来表示出来,它是提问的textView,id我们设成tv_ask



然后我们有了提问,是不是还应该有个回答啊,那回答的话,是“你好呀”,那回答的时候他这个比较复杂,因为有时候回答的话,会回答一张图片,所以的话,我们不能用一个简单的textView去表示,应该用一个线性布局LinearLayout把它包裹起来,然后在它里边再写个回答的TextView,然后回答的时候,有时候会是图片,所以在它里边再增加一个ImageView控件,那这样我们就把布局文件基本上写完了



那这个布局文件写完了之后,我们就可以在getView方法中初始化了,即:



@Override

	public View getView(int position, View convertView, ViewGroup parent) {

		ViewHolder holder;

		if (convertView == null) {

			convertView = View.inflate(getApplicationContext(),

					R.layout.list_item, null);

			holder = new ViewHolder();

			holder.tvAnswer = (TextView) convertView

					.findViewById(R.id.tv_answer);

			holder.tvAsk = (TextView) convertView.findViewById(R.id.tv_ask);

			holder.ivPic = (ImageView) convertView

					.findViewById(R.id.iv_pic);

			holder.llAnswer = (LinearLayout) convertView

					.findViewById(R.id.ll_answer);



			convertView.setTag(holder);

		} else {

			holder = (ViewHolder) convertView.getTag();

		}



		TalkBean item = getItem(position);



		if (item.isAsker) {// 提问布局

			holder.tvAsk.setVisibility(View.VISIBLE);

			holder.llAnswer.setVisibility(View.GONE);

			holder.tvAsk.setText(item.content);

		} else {// 回答布局

			holder.tvAsk.setVisibility(View.GONE);

			holder.llAnswer.setVisibility(View.VISIBLE);

			holder.tvAnswer.setText(item.content);



			if (item.imageId > 0) {

				// 有图片

				holder.ivPic.setVisibility(View.VISIBLE);

				holder.ivPic.setImageResource(item.imageId);

			} else {

				holder.ivPic.setVisibility(View.GONE);

			}

		}

		return convertView;

	}

}



static class ViewHolder {

	public TextView tvAsk;

	public TextView tvAnswer;

	public ImageView ivPic;

	public LinearLayout llAnswer;

}



这个是我们这样一个简单的getView,我们现在还没有更新数据,那这个数据的话,我们需要在getCount方法里边返回它数据的对象,返回集合哈,即它需要一个ArrayList去维护我们listView的数据

一般listview都会维护一个Arraylist,那这个ArrayList里边我们到底要存什么东西呢?



肯定是要存一个一个的对象,我们要封装成一个对象,我们在这里封装一个对象叫做TalkBean.java

即:



public class TalkBean {

	

	public String content;//谈话内容

	public int imageId;//图片id



	public boolean isAsker;// true表示提问者,false表示回答者



	public TalkBean(String content, int imageId, boolean isAsker) {

		this.content = content;

		this.imageId = imageId;

		this.isAsker = isAsker;

	}

}



那这个TalkBean.java中都有什么字段呢?谈话内容,有时候是不是还有图片,图片的话直接把id搞过来,但是我们这个谈话是不是也分提问者和回答者啊,到底你是提问者还是回答者呢?我们要通过一个boolean值表示它是提问还是回答对吧,这个boolean值我们用isAsker表示,true表示是提问者,false表示是回答者



TalkBean写完之后,那在MainActivity中是不是应该有个ArrayList去维护我们的TalkBean对象对吧,即:



private ArrayList<TalkBean> mTalkList = new ArrayList<TalkBean>();



直接在这new一个ArrayList,后边我们就不用初始化了,直接在这初始化一个ArrayList,那初始化完了之后呢,这时候在getCount方法中,我们就可以返回mTalkList.size();



getItem方法的话,应该是返回mTalkList.get(position);它返回的是一个TaskBean,所以我们把它的返回值改为TalkBean



getItemId方法返回的是position;



然后在getView方法中就可以首先判断是提问者还是回答者,如果是提问者,那就把回答者的布局隐藏掉,反之亦然,如果item.imageId>0,那说明我们有图片对吧,默认是0没有图片嘛,那有图片的话,这时候我们是不是应该让,因为我们这个布局中有时候有图片,有时候没有图片,当然有图片的话,你要将这个imageview显示出来,没有的话隐藏掉对吧,有的话,还要给ivPic去setImageResource



这个就是我们listView的布局,我们就写完了,写完了之后,就要给这个mTalkList集合添加对象对吧,我要一个一个的添加,那这个对象怎么去添加呢?



是这样的,我说一段话,它一识别出来就马上把它添加成一个对象了吧,所以的话,我们首先应该说话对吧,那我们就给这个btnStart按钮设置点击事件,一点击之后,我们就说话,即:

	

btnStart.setOnClickListener(new OnClickListener() {



		@Override

		public void onClick(View v) {

			// 开始听写

			listenUI();

		}

	});



那在这个地方我们怎么去做呢,一旦一点击,我们就开始说话,而且说话的时候,我们屏幕中间会有一个弹框来,就说科大讯飞那个语音输入的UI,就我们刚刚写的那个,要让它弹出来,那所以的话,要把科大讯飞的所有东西都抄到我们的目录下



首先是libs目录下的包,要全部抄到我们的libs目录下,那这些包抄过来之后,还要抄它的权限



抄完权限我们抄代码,首先是在MainActivity.java中的onCreate方法中,初始化语音识别的api,

所以我们需要把这个调用一下,首先在onCreate的时候,初始化我们api,其次的话,我们是有一个语音听写的ui,叫listenUI,我们把它抄过来,放在我们自己的MainActivity.java中,当然我们是不用传这个View对象的,因为我们是直接设置一个onClickListener,这是语音听写的UI,那一旦识别出来之后呢,我们这个地方会打印,而这个方法在什么地方调用呢?



当然是在我们onClick的时候,需要马上去开始听写了吧,开始听写之后,它就会马上弹出这样一个弹框,来不断的去识别我的结果



运行项目,看下效果,我们说“中午吃什么呀?”它就会打印这个,那我们肯定要把这个结果添加到我们的列表里边去展现,那这个结果现在是好复杂的这样一个数据格式,它其实不就是个json嘛,所以说我在讲json的时候,说json很重要,好多地方都要用到json,所以的话,json怎么去解析,这个你们也是要掌握的,我们用HiJson把它去格式化一下,看看它是什么样的格式



这是我们json的一个格式吧,而我们现在要做什么呢?



我们现在要把中午吃什么呀这么几个字拼成一句话,所以我们需要解析这个json,我们专门写个方法叫parseJson,去解析我们的json,那这时候我们要把我们的json传过来,那传过来之后呢,我们要解析,解析时候用Gson解析是不是比较方便,所以我们从智慧西安项目里把Gson包先拷贝过来吧,即:

数据解析

/**
  • 解析语音json数据
     */
     private String parseJson(String json) {
     Gson gson = new Gson();
     VoiceBean bean = gson.fromJson(json, VoiceBean.class);
     ArrayList ws = bean.ws;

      StringBuffer sb = new StringBuffer();
    
      for (WsBean wsBean : ws) {
    
      	ArrayList<CwBean> cw = wsBean.cw;
    
      	for (CwBean cwBean : cw) {
    
      		String w = cwBean.w;
    
      		sb.append(w);
    
      	}
    
      }
    
    
    
      System.out.println("解析数据为:" + sb.toString());
    
      return sb.toString();
    

    }

    用Gson解析json时,需要写一个对象,那我们就再写一个对象,我们叫做VoiceBean,那声音这个对象的话,我们要照着声音的json串去写吧,我们只关心ws这个字段,它是一个数组,所以的话,我们在这写一个ArrayList,叫做ws,它的范型是一个对象WS,我们单独写一个类,那WS对象里边是什么东西呢?

    它首先是一个bg,我们用不上,还有个cw,这个我们要用吧,它是一个数组,所以又是一个ArrayList,那这个ArrayList的对象是什么呢?我们再用一个CW对象去表示它,而CW对象中又是什么东西呢?是sc,还有个w吧,我只关心这个w,所以我们在CW对象中,再搞一个w,即:

      public class VoiceBean {
    
    
    
      public ArrayList<WS> ws;
    
    
    
      public class WS {
    
      	public ArrayList<CW> cw;
    
      }
    
    
    
      public class CW {
    
      	public String w;
    
      }
    

    }

    写完了这个数据之后,就可以回到我们的MainActivity中的processData方法中去解析我们的数据了,

    从voice中先拿到他的ws吧,它本身就是ArrayList,那这时候我们需要遍历一下我们的ArrayList,那遍历的话,这个w每个对象里边是不是又有cw这样的字段啊,所以通过w可以找到它的cw,而cw又是一个集合吧,你没发现这个集合中永远都是一个对象吧, 那我们找她第一个对象是不是就可以了吧,所以你直接从cw中get一个0,拿到他第一个对象,第一个对象里边的什么字段呢?

    w字段吧,那这个就是我们所谓的一个字,我们把它叫做word,而我们是希望把所有的字都拼在一起,所以的话,我们可以搞一个StringBuffer或者StringBuilder都可以,即:

    StringBuffer sb = new StringBuffer();

    那一旦拿到这个word之后呢,我们让这个sb去append一下我们这个word吧,最后sb.toString(),把这个字符串返回回去

    这就是我们解析数据的一个逻辑,那现在我们把它打印一下嘛,即在OnResult方法中写:

      System.out.println("解析结果:" + result);
    

    那我们这时候就调用processData,给它传给results.getResultString(),把它这个数据是不是可以解析出来啊,即:

    @Override

    public void onResult(RecognizerResult results, boolean isLast) {

      System.out.println("识别结果:" + results.getResultString());
    
      System.out.println("isLast:" + isLast);
    
      String result = processData(results.getResultString());
    
      System.out.println("解析结果:"+ result);
    

    }

    它返回的就是这样一个result,我们把它这个result打印下

    运行程序,看它解析的结果怎么样,我们看下这个语音识别,比如说“今天中午吃什么呀?我好饿呀”,然后它就会这样打印:

    isLast:false

    解析结果:今天中午吃什么呀我好饿呀

    isLast:true

    解析结果:!

    isLast中间的这段话是一段json对吧,那后边是不是又来了一段json,一个感叹号对吧,那我们是不是需要将这个感叹号追加在这段话的后边啊,不然的话,因为我们这段话没说完嘛对吧,话说完了就是isLast:true, 不是true的话,是不是都需要将我们上一段对话都要追加在一起,那么我们现在就这样去做嘛

    那我们还得搞个StringBuffer,把onResult方法中解析之后返回的result是不是还要追加在一起啊,这个result是不是就是我们刚才打印出来的东西,我们搞一个StringBuffer,是不是就可以把这段话和感叹号追加到一起

    所以我们再搞一个StringBuffer,那这时候这个StringBuffer我们就可以搞一个全局的,在这个地方,我们要放在外边,全局的把控我们的StringBuffer,那这时候我们就在上边成员变量处声明一个StringBuffer了,即:

      private StringBuffer mTalkBuffer; //聊天的文字缓存区
    

    这是我们的这样一个谈话的对象, 那现在的话,我们在哪里去初始化呢?就是在它开始语音识别的时候,我们可以初始化一下我们的这个StringBuffer了,即在listenUI方法中,我们添加如下代码:

       mTalkBuffer = new StringBuffer();
    

    当然你也可以在上边成员变量处声明的时候,去new一下来初始化也可以

    那这时候,可以在onResult方法中,去用mTalkBuffer去append一下result,即:

      	mTalkBuffer.append(result);
    

    只有当isLast等于true的时候,话是不是才说完了,即在onResult方法中判断,如下:

      if(isLast){
    
      	//话已经说完了
    
      	String finalResult =  mTalkBuffer.toString();
    
      	System.out.println("最终的结果:"+ finalResult);		
    
      }
    

    这是它的一个最终的结果吧,我们可以把它打印出来吧

    这个表示我们话已经说完了,我们现在把这个程序去运行下,看下它的效果,我们说“今天中午吃什么呀”

    它打印:

      isLast:false
    
      解析结果:今天中午吃什么呀
    
      isLast:true
    
      解析结果:!
    
      最终结果:今天中午吃什么呀!
    

    那这个就是我们现在说的这样一个结果,那有了它之后,我们是不是可以把现在解析的数据结果展示在我们的最终的这样一个文字上吧, 那展示的话,我们就需要给这个listView去添加了,那在最终结果打印出来之后呢,我们这时候就可以将我们提问的这个对象添加到集合上,首先我们初始化一个集合的对象,那集合的对象,我们叫做TalkBean对吧,即在onResult方法的判断中,添加如下代码:

      if(isLast){
    
      	//话已经说完了
    
      	String finalResult =  mTalkBuffer.toString();
    
      	System.out.println("最终的结果:"+ finalResult);
    
      	TalkBean ask = new TalkBean();		
    
      }
    

    那我们需要在这个TalkBean.java中把它的内容,图片id,包括相关的信息,都给他传过去对吧,所以的话,我们可以直接在TalkBean.java中用几个参数搞一个构造方法,把这个参数直接传过来,即:

      public TalkBean(String content, int imageId,boolean isAsker){
    
      	super();
    
      	this.content = content;
    
      	this.imageId = imageId;
    
      	this.isAsker = isAsker;
    
      }
    

    那这时候回到我们的MainActivity中onResult方法的判断中,我们这时候如果要去声明的话,那就如果要去new的话,是不是要把这个构造方法传过来啊,即:

      if(isLast){
    
      	//话已经说完了
    
      	String finalResult =  mTalkBuffer.toString();
    
      	System.out.println("最终的结果:"+ finalResult);
    
      	TalkBean ask = new TalkBean(content,imageId,isAsker);		
    
      }
    
    分别传它的内容,内容content是finalResult,id这个提问是不是没有图片呀,可以传个-1,表示它没有这个值,isAsker是不是提问人员,是啊,那就写true,即:
    
    
    
      if(isLast){
    
      	//话已经说完了
    
      	String finalResult =  mTalkBuffer.toString();
    
      	System.out.println("最终的结果:"+ finalResult);
    
      	TalkBean ask = new TalkBean(finalResult,-1,true);		
    
      }
    

    这是ask的这样一个对象吧,然后呢,我把这个对象添加给谁呢?添加给我们的集合吧,叫做mTalkList点add一个对象吧,这个对象叫做ask吧,即:

      if(isLast){
    
      	//话已经说完了
    
      	String finalResult =  mTalkBuffer.toString();
    
      	System.out.println("最终的结果:"+ finalResult);
    
      	TalkBean ask = new TalkBean(finalResult,-1,true);	
    
      	mTalkList.add(ask);
    
      }
    

    完了之后,我们去把这个界面是不是可以刷新一下啊,是不是可以去刷新一下Adapter啊,所以的话,我们这个地方可以写一个adapter,现在ListView还没有写adapter,即在MainActivity中的onCreate方法中,添加如下代码:

      MyAdapter mAdapter = new MyAdapter();
    

    然后把它搞成一个全局的变量:

      private MyAdapter mAdapter;
    
      	mAdapter = new MyAdapter();
    

    然后给lvList去set一个Adapter,并把这个mAdapter传进来,即:

      lvList.setAdapter(mAdapter);
    

    那这时候,一旦数据有更新的话,我们直接需要在这个地方去刷新一下就可以了,即在onResult方法中,增加如下代码:

      // 刷新列表
    
      mAdapter.notifyDataSetChanged();
    

    那现在我们把这个程序去运行下,看看它有没有把我们提问的这句话,刷新在我们的界面上呢?

    那现在我说一句话,“天王盖地虎”,这时候它在聊天框中会输出“提问”二字,这是因为我们没有给提问的内容赋值,那我们提问的布局这块,给setText,即:

      if (item.isAsker) {// 提问布局
    
      		holder.tvAsk.setVisibility(View.VISIBLE);
    
      		holder.llAnswer.setVisibility(View.GONE);
    
      		holder.tvAsk.setText(item.content);
    
      	}
    

    回答的是不是也一样啊,也需要给他写内容啊,即:

      if (item.isAsker) {// 提问布局
    
      		holder.tvAsk.setVisibility(View.VISIBLE);
    
      		holder.llAnswer.setVisibility(View.GONE);
    
      		holder.tvAsk.setText(item.content);
    
      	} else {// 回答布局
    
      		holder.tvAsk.setVisibility(View.GONE);
    
      		holder.llAnswer.setVisibility(View.VISIBLE);
    
      		holder.tvAnswer.setText(item.content);
    
      }
    

    那这个就是我们这样的一个处理,同时我们发现一些别的小细节的问题,比如说,我们发现现在这个ListView底下有一个分割线,我们仔细看一下,是不是可以去掉啊,那去掉的话,我们就来到activity_main.xml中,给listview添加属性为:

      android:divider="@null"
    

    就可以去掉分割线吧,我们现在可以再去运行一下,说“天王盖地虎”,这几个字是不是就在聊天框中出现了,这就是我们的一个提问,那回答怎么去回答呢?

    这个地方,我们怎么去回答呢?

    我们这个地方要回答,是写死的,就说比如说,我们会根据你提问的内容,来去确定我们的回答对象,

    如果我发现你提问的内容里边,包含一个“你好”这样的字,那我这时候回答的话,比如说我们这写一个回答的内容“没听清”,如果他是说你好的话,这时候,我们就去回答一个“你好呀!”,那这时候其实你会发现,只要我的文字里边包含“你好”,这两个字,它都会回答“你好呀!”,比如说我说“你好呀!”,或者说“你好不好”,或者说“你好呀!”,它是不是都会回答“你好呀!”,因为我的那段话里边,包含“你好”这两个字,我们再搞一个else if,那如果它问,finalResult.contains(“你是谁”),那我们去回答的话,我们写个“我是你的小助手,么么哒!”,这个是我们这样一个回答对吧,当然还有一个elseif,

    那如果是“天王盖地虎”,那这时候我们回答一个“小鸡炖蘑菇”,即:

      // 根据提问内容,确定回答对象
    
      String answerContent = "没听清";
    
      int imageId = -1;
    
      if (finalResult.contains("你好")) {
    
      	answerContent = "你好呀!";
    
      } else if (finalResult.contains("你是谁")) {
    
      	answerContent = "我是你的小助手,么么哒!";
    
      } else if (finalResult.contains("天王盖地虎")) {
    
      	answerContent = "小鸡炖蘑菇";
    
      	imageId = R.drawable.m;
    
      } else if (finalResult.contains("美女")) {
    
      	// 生成随机数
    
      	Random random = new Random();
    
      	int rStr = random.nextInt(mAnswers.length);
    
    
    
      	answerContent = mAnswers[rStr];
    
    
    
      	int rImage = random.nextInt(mImageIds.length);
    
      	imageId = mImageIds[rImage];
    
      }
    

    那这个文字弄好之后,我们这时候就可以封装这样一个新的对象了,内容的话,就是我们的answerContent,id的话,我们先写一个-1,isAsker是否回答者,我们先写个false,即:

    TalkBean answer = new TalkBean(answerContent, imageId,

      					false);
    
      			mTalkList.add(answer);
    

    然后把它添加到集合中,即mTalkList.add(answer);

    然后刷新一下就可以了,即:

    // 刷新列表

    mAdapter.notifyDataSetChanged();

    我们去运行下,有些同学说,老师你这个好业余啊, 你看人家那个siri好高大上,你说啥,人家都能给你挺清,你现在这么写死,你得写到什么时候,说话那么多内容,其实实际上是这样的,就说我们在实际开发语音助手的时候,这些东西肯定不是在本地写死的,那它怎么做的呢?

    就说这样的,比如说我说一个你好呀,这样简单的话,它会先把“你好呀!”这个字传给服务器,由服务器进行一个强大的语义分析,来分析你现在说的是什么话,它分析的是你在打招呼,那么服务器就把应该回答的那段话,传给客户端,所以它都是在服务器端进行的一个交互,绝对不对写死在本地的,所以的话,今天siri可能回答“你好”,但明天服务器里边把这个数据改了一下,那siri可能回答的就不是“你好”,而是“你烦不烦啊,整天说你好”,它可能会根据你用户的一个行为,去进行一个语义的分析,然后让服务器把这个结果返回给我们,肯定不是在本地写死的,那这个语义分析用服务器的逻辑,它有复杂的算法,判断这个对方到底说的是啥对吧,我到底应该怎么听清它的回答对吧,都是服务器的逻辑,所以如果是语音小助手,你在提问的时候,你会发现它反应比较慢,如果网络比较慢的话,它可能转个半天才给你回答一句,那个时候,它是在调网络,问服务器应该怎么回答,都是要问服务器的,我们现在先看我们这个小小效果吧,看下运行起来怎么样

    这时候我们去问下,“你好呀!”,这时候,它在回答的时候,也会在聊天框中回答“你好呀!”,比如我们问,“你是谁呀?”那这时候它就会回答,“我是你的小助手,么么哒!”

    那这就是我们目前的这样一个情况,我们这个因为是写死的,所以你刚一问,它就会马上把这个结果返回给你,这就是我们讲的这样一个简单的效果,下午再把这个东西简单的完善下

Day06 06.聊天机器人02 ##

今天上午,对推送的原理进行了一下简单的简绍,其实推送的话,我们是有一些长链接啊,心跳,xmpp协议,socket,我们简单了解一下,那回头如果真的去面试,真的聊到这个地方的时候,我们就比较低调些,就说我对这块虽然没有做过,但是稍微有一点点的了解,然后把我讲的那些东西给它说下,这样的话,其实也是可以的,不要一上来就说全部都知道,你问吧,那他到时候一问,真的问到一些细节的东西,咱又没弄过对吧,你万一说错了,那肯定不好了对吧,所以有些知识点,我们可以说确实弄过对吧,但是有些地方,我们可以是了解,那这样的话,会显的我们的知识面比较广,所以说我们问什么要介绍这个,简绍那个呢?



推送简绍,语音识别也介绍,其实就是希望你们在毕业之前,能够多了解一些android相关的东西,那这样的话,就是你在外边搞一些东西,你将来就业或者找工作,这个知识面就会越来越宽广,那包括这个语音,当然我们不一定非得搞个像siri那样的软件,当然现在android端的话,也有公司在搞比如说,出门问问,这样一个客户端,它就是一个语音识别,就是类似于android版的siri,好多这种软件,当然比如说,导航,百度地图说“前方左转,右转”,那个都是机器它给你读的声音对吧,那它从读出来,我们就把文字输给它,它就能够朗诵出来,或者我们说段话,你给我拨打什么什么电话,它听到这段话之后,它就会识别出来之后,马上给你进行相应的操作



那我们接着去看我们的语音识别,这个东西我们把它八成的东西已经做完了,我们接下来再去看一下



细节我们优化一下,那我们今天中午的话,写到哪里了呢?



有一个提问,提问完了之后呢?



我们就可以回答,这时候回答的时候,我们是分情况,看你问的内容去回答



 比如说“你好”,我们就回答“你好呀!”,“你是谁?”,“我是你的小助手”对吧,“天王盖地虎”,“小鸡炖蘑菇”,当然我们也有另外一个,比如说,我说一个“美女”,如果它的结果里边包含一个美女的话,那这时候我在问美女的时候,它这个回答还不是一个确定的值,它是比较随机的值,一会是“约吗?”,一会儿是“这样怎么样?”,我们是一个随机的值,所以的话,这时候我们就可以把这些随机的值,写在一个数组里边,我们从这个数组里边随机的去取它相应的值就可以了



那这时候我们就可以取一下,我们搞一个String[],即:



private String[] mAnswers = new String[]{};



提前把这些东西都写好,比如我们写个“约吗?”即:



private String[] mAnswers = new String[] { "约吗?", "这张怎么样?", "美不美呀?",

		"不要再要美女了!", "这是最后一张了!!!" };



 这是我们随机回答的这么几个文字,当然也有我们随机回答的图片,每次它给的图片都是随机的,即:



private int[] mImageIds = new int[] { R.drawable.p1, R.drawable.p2,

		R.drawable.p3, R.drawable.p4 };



这是我们这样的图片,那首先我们先搞一个随机数对吧,用Random生成一个随机数,即:



else if (finalResult.contains("美女")) {

	// 生成随机数

	Random random = new Random();

	int rStr = random.nextInt(mAnswers.length);



	answerContent = mAnswers[rStr];



	int rImage = random.nextInt(mImageIds.length);

	imageId = mImageIds[rImage];

}





那首先,先生成我们的文字,文字的话,我们用random.nextInt,那他应该是多少呢?



那就看它数组是多长,我就多长吧,即:mAnswers.length,它有多长,我就从它这个长度里边去取,比如说它现在是1,2,3,4,5的话,那他随机数是0-5吧,它返回的是0,1,2,3,4,然后这时候拿到这个rStr文字的位置,那这时候我们的answerContent就应该等于我们mAnswers[rStr]



同时我们再随机生成我们的图片,那这个int的值应该是我们图片数组的长度length,即:

	int rImage = random.nextInt(mImageIds.length);

然后这时候呢,我们就给图片设置一个id,那这时候呢,我们可以在上边声明一个imageIds,比如说它默认是-1,表示没有图片,那这时候生成完了之后,要去添加这个回答对象的时候,我们图片的id应该是刚刚声明的图片的id,默认它是-1,那一旦走到美女这个地方来的话,它会把这个图片赋值,那通过“小鸡炖蘑菇”的时候,我们也可以给他去赋值,比如说它的值我们可以写死,即:

	imageId = R.drawable.m;



这是小鸡炖蘑菇的这样一个图片,这样的话,我们就能够随机的去回答它的这样一个情况了,剩下的情况就是没有走到这里边来的话,剩下的情况就都是“没听清楚”



运行程序,问“又没有美女?”,它就在聊天框中回复“这张怎么样?”,然后下边显示一张美女图片,然后我们现在没有对图片做处理,所以的话,这个imageview上边有一大截是空白的,是因为这个图片太大了



再问“还有没有美女啊!”,它就给你发“约吗?”,然后又给你展示一张美女图片,但是这时候你只有手动的去下拉这个listview,才能展示最新的这个聊天记录,但是我们是希望每次刷完之后,它就要展示到我的最后一个地方吧,所以这时候你是不是可以强制让这个listview设置一下他的位置,即:



	// 让listview展示在最后一个item上

	lvList.setSelection(mTalkList.size() - 1);



那同时,在我和小助手沟通的时候,希望小助手也能够把它回答的内容给我念出来,那这时候呢,就用到我们上午写的这个demo了,那在demo中的话,其实就有一个语音合成开始说话startSpeak方法,在这个地方,可以把这个startSpeak抄过来,这是语音合成的方法,当然这时候,它所有的方法肯定不能在这写死了吧,我们肯定要动态的去传过来,比如我们的内容content,要传过来,即:



/**
  • 语音合成
     *

  • @param view
     */
     public void startSpeak(String content) {
     // 1.创建SpeechSynthesizer对象, 第二个参数:本地合成时传InitListener
     SpeechSynthesizer mTts = SpeechSynthesizer
     .createSynthesizer(this, null);

      // 2.合成参数设置,详见《科大讯飞MSC API手册(Android)》SpeechSynthesizer 类
    
      // 设置发音人(更多在线发音人,用户可参见 附录12.2
    
      mTts.setParameter(SpeechConstant.VOICE_NAME, "vixying"); // 设置发音人
    
      mTts.setParameter(SpeechConstant.SPEED, "50");// 设置语速
    
      mTts.setParameter(SpeechConstant.VOLUME, "80");// 设置音量,范围0~100
    
      mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD); // 设置云端
    
      // 设置合成音频保存位置(可自定义保存位置),保存在“./sdcard/iflytek.pcm”
    
      // //保存在SD卡需要在AndroidManifest.xml添加写SD卡权限
    
      // //仅支持保存为pcm和wav格式,如果不需要保存合成音频,注释该行代码
    
      mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, "./sdcard/iflytek.pcm");
    
    
    
      // 3.开始合成
    
      mTts.startSpeaking(content, null);
    

    }

    那这时候,在什么地方startSpeak呢?在让listview展示在最后一个item上之后,去startSpeak,即:

    // 让listview展示在最后一个item上

    lvList.setSelection(mTalkList.size() - 1);

    //开始朗诵

    startSpeak(answerContent);

    运行程序,你问“你好呀!”,它就语音回答你,“你好呀!”,然后在聊天框中是不是也会记录聊天内容,你问“你是陕西人吗?”,它就回答“没听清”,你问“你是谁啊?”它就回答“我是你的小助手,么么哒!”

    这个就是简单版的手机助手,我们已经完全开发完了

Day06 07.友盟统计指数分析 ##

简绍一下“统计“这个知识点,什么叫做“统计”呢?



有一个叫做“友盟统计”的东西,这个是我们外边的开发者和公司,经常会用它来做应用的数据的统计



那首先我们先讲下为什么要对数据进行统计,比如说,你的应用上线了之后,我们是需要去接听一下,看看我这个软件到底有多少的下载量,到底有多少的活跃量,用户喜欢按什么按钮对吧,其实就是在后台对用户进行一个调研,看看它到底是一个什么行为对吧,或者是他的一个活跃的情况,那有了这些数据之后,这时候产品经理才能够决策它下一个版本应该新增什么功能,或者是别的一些战略性的调整,这些都是要依赖于数据,没有数据,你光把应用开发完了放出去对吧,你连它是什么情况都不知道,那完全就是蒙着眼睛去过河,所以我们需要这些数据去分析,这就是我们的友盟统计



那关于我们友盟统计的话,其实我们在网站上,也可以看到它是一个第三方的平台,我们把它打开,我们以后要和很多个开放平台打交道,因为每个开放平台,其实它都是专注于做某一件事情,那我们就可以把精力从这个地方分散开,让它来帮我们去做这样的事情,那我们就能省很多的时间



那友盟作为一个专业的平台,其实它呢,也经常会发布一些目前来讲,android的一些行情,以及android手机的使用情况,它这个平台进来之后,右上角有个指数,这个指数其实就是在汇报一下我们目前的android和ios的行情



那我们先看下android的设备使用情况,那从目前全国来讲的话,小米手机M3它占2.5%的市场份额,红米手机1s是占2.2%的份额,它如何去统计到这些数据呢?



我每一个手机它内置了友盟的这样一个sdk之后呢,它就会拿到这个手机的版本信息,以及设备型号,都可以去通过手机api去拿到手机这个情况,然后再进行一个汇总,就能统计出现在全国来讲,我们android的使用情况,所以你看,前十的话,基本已经被小米和三星瓜分掉了,偶尔这个vivo也有0.6%的市场份额,华为,魅族这些还是比较靠后的,那这个是这样一个情况,而这个确实是一个比较真实的情况



有些同学说,老师小米手机这么垃圾,怎么还能排行第一对吧,其实我后来发现个规律,就说骂声越强的那个东西,反倒是很好,我上次在应用市场上搜了一下“微信”,看看大家怎么评论它,全是在骂微信,垃圾,奔溃什么什么的,但是这玩意用的都很好啊,大家老在骂它,是因为用的人太多了,所以偶尔有那么百分之一的人骂,你就感觉骂声好多,人家几亿用户,百分之一算个啥,如果一款软件,根本就没人用,那也就没人骂,说明它很不活跃,都没人懒得去搭理你,所以骂声越高,说明这个东西越火,就像这个凤姐,大家都在骂她,人家就在骂声中去美国了对吧,这几年好像又回来了对吧,美国混不下去,那这个是我们的这样一个情况



你去北京上班,在地铁上,你会发现你周围的几乎所有女性,全都在用苹果手机,因为我是从北京回到陕西,然后去对比对吧,北京遍地人都在用苹果手机,尤其是女生,但是在陕西的话,你偶尔才能看见一个人在用苹果手机,那苹果手机在北京已经成为一个常态了,那这个是他的这么一个情况



我们再看下android的系统,是什么情况的一个分部,目前看来,4.4是android排行老大,是40.60%的份额,5.0和2.3比较少,所以说我们现在还在考虑2.3的版本,但是市面上98%的设备现在一般都是4.0之上的手机了,所以以后如果你的老板非得让你去兼容2.几的版本的话,你可以拿这个友盟统计的数据给它看,你告诉它,拿到为了这百分之2的用户,让这个项目再延期一个月发布吗?

那老板想一下,还是算了吧,我还是4.0以上吧



分辨率我们也看下吧,因为我们要做屏幕适配,所以需要知道目前市场上的主流的分辨率到底是多大,我们看到目前主流的分辨率是1280x720,它是占28%的份额,其实如果是两年前的话,1280x720还是比较少的,那会儿主流分辨率是480x800,目前我们看到480x800已经退居二线了,然后是480x854,然后是1920x1080,android屏幕分辨率是越来越高的,这个是我们分辨率的这样一个情况,以后我们尽量去按照1280x720去适配我们的屏幕



再看下联网,69%都是通过wifi联网,因为流量资费还是比较高的



我们看到android手机还是在北京和广东用的比较多,而这个是他设备的排行,比如说北京的话,用的最多的是红米1s,所以并不是说,越有钱的地方,它用的手机就越贵对吧,不是这个道理



android手机一般1,2年换一次就可以,而且这个手机的话,因为现在android手机都走的是千元机的这样一个路线,都主打的是低端机的手机嘛,而且低端手机它配置又很好,所以很多人愿意去花几百块钱去买这个红米手机,大不了用一年扔了再换一个对吧



而且我个人也建议,在买android手机的时候,如果你想买一款android手机,不要买太高端的,3000以上的,不要考虑,两千以上就可以了对吧,就很不错了,1000块钱的话,也没太大问题,即使你拿一个红米手机去面试,面试官会对你嘲笑吗?绝对不会



越高端的手机,尤其是三星,手机越贵越垃圾,买回来之后,用个一段时间就越来越卡,其实再贵的android手机,都有可能会出现死机,卡顿,你如果手机想体验好的话,那我建议你买苹果手机,因为苹果手机体验的话,确实非常的好,至少它很流畅,而且至少在关键时刻不会卡壳,那比如android手机,赶紧拍张照片,赶紧拿出手机,按相机,但是死机了对吧,或者突然想打个电话,突然死机了,这个在苹果手机上是绝对不会出现的,这个就是我们这样一个情况,android现在只能和苹果拼硬件,系统其实原生来讲的话,已经落后于苹果了,为什么呢?



因为android系统,它的上层是java层,但底层是不是还有个c层啊,所以它java层还得调c层的话,本身就是一个跨语言的东西,可能在这个跨度的中间,本身就会浪费一些速度,性能



但苹果的话,它原生是用ObjectC,而且原生的去支持C代码,那这样的话,它就不需要跨平台,虚拟机之类的,它直接就去运行了,所以它原生的速度就比较快一些,这个是苹果在系统上的一些优势



而且苹果手机因为有苹果公司来保证质量,所以不会像android,杂七杂八的,各个公司在给你瞎改rom,改来改去的,导致每款手机的体验可能不太一样



小米手机从全球份额来说的话,也是比较高的,全球的话,三星是打头阵,那小米手机是中国大部分人都在用小米,因为中国人又多,所以导致小米手机在中国,包括全球市场上的份额也是比较大的,好像不到百分之十,但是也很高了,这是我们的设备指数



我们看下社交指数,讲这些东西有什么用呢?这些东西已经和编码没有任何的关系,但是我们是在扩展知识面对吧,具体怎么去和面试官发挥,这个就看你们自己的一个表达能力了



社交指数中,目前来讲的话,微信朋友圈,是不是大家用的比较多



instagram这个软件知道是什么吗?

	这个当时是被Facebook收购了,那个公司的话,它只有11名员工,然后她在收购的时候,是花了多少钱呢?花了7千万美元,所以11个人一平分的话,随随便便大家都是千万富翁了



所以说这个创业公司,有时候他的投资回报率是非常大的,有时候小的创业公司去了之后,可能你会发现这个地方好累啊,啥都让我干,但是万一这个公司真的做起来了,那你作为一个创始人,那你投资回报率是特别高的,外边现在有很多的风投,那这些风投的话,它是干什么的?风险投资嘛,有风险才投资,没风险肯定不投资,这就是所谓的风险投资,那风险投资它就是手里握着一堆钱,钱放在银行里嫌利息太慢,这时候它就把它投出去对吧,投给谁呢?

它就看下小创业公司,你这个软件做的不错,我给你投个100万,作为你的天使投资,那这时候因为那个小公司刚开始,它没有钱嘛,100万对于小公司是不是很多啊,那100万是不是就可以拿到它百分之四五十的股份了,我就甚至可以作为它的一个股东了



那这时候,小公司在它的金额下,不断的发展壮大,最后一估值,是1个亿对吧



那当时投资的那个100万是不是就已经成倍的上涨了,1个亿的40%,是不是好多啊,那这个是不是就能够赚很多啊,那这个就是他的一个所谓的风险投资



但是它的风险也是极大的,风险越大,投资回报率也越高,风险越低,放在银行风险最低,那这个利息才是多少啊,但是我们风险越大,投资也越高



所以风投会怎么做呢? 它是会拿一堆钱,这个公司100万,那个公司100万,投个十几二十家公司,然后就和赌博押注似的,看谁能成长起来,只要有一两家公司能够成长起来,那他就赚了



所以你们如果将来想搞一个创业公司的话,你作为公司的老板,肯定不会用自己的钱去创业嘛,谁会傻到拿自己的存款去搞一个公司,然后往里面烧钱啊,钱你根本烧不得对吧,我们肯定是想一款产品之后,拿这款产品去勾一些风险投资,你看我这个多有意思啊,将来要是发展起来,怎么怎么着的对吧,然后将来纳斯达克上市对吧,就吹的很牛逼,那些公司都是这样的,上市之后怎么怎么样的,估值多少亿对吧,这时候这个公司就过来,这些投资公司就给你投个100万,200万,这是第一次投资,叫做天使投资,那第一次就跟个天使降临了一样,给你100万,去玩吧对吧



然后你就拿这个100万开始经营,经营个1,2年之后,你发现这个做的还不错对吧,那这时候就要开始第一轮融资了,那这个就是所谓的A轮融资,A轮融资什么意思呢?



就是刚才那个天使投资的钱已经不够我花了,那这时候我就再招一些投资公司,你看我这软件已经发展这么多了,用户是多少多少,前景不可限量,A轮融资这时候融的就比较多了,融个1000万



那融个1000万之后呢,我再发展,再烧钱,永远是烧的投资人的钱嘛,我自己肯定不花一分钱嘛,你傻呀你自己花自己的钱



你那个钱也不够烧,你最多也就几百万,一烧就没了对吧,你看这个程序员现在一个月啥不干,工资都1万多块呢,肯定会烧钱,那这时候当这个发展又可以了之后,再来个B轮融资,B轮融资这时候再来个二三千万,有时候甚至来一个亿对吧,最后再来一个C轮融资,这个钱就更多,最后呢,也就没有那么多融资了,最后直接就纳克达斯上市了对吧,这是一个正常的,就说如果发展确实非常好的一款产品,它都是这样去成功的,当然真正有几个软件能够到最后纳斯达克上市的,这个一千个公司里边能有1个上市就不错了



比如说100家公司,大概有百分之10的公司,能够撑两年,也就说剩下百分之90的公司,会在两年之内倒闭掉,而这个能撑两年的100家公司中,又有十家能够撑5年,那剩下的公司也在5年之内,都会挂掉,然后撑了5年的这100家公司,可能只有10家能够上市,所以它是这样一个概率



所以创业的风险也是很大的,当然了,反正公司倒闭就倒闭吧,反正又不是我的钱,烧的是投资公司的钱



当然这些投资公司呢,它会心疼这点钱嘛?它有时候会心疼,但有时候他也不心疼,它本来就是风险投资嘛,没有风险我投资什么,那这个风险它肯定是已经考虑到了是吧,那这个就是所谓的创业



所以以后要是创业,就照着这个路子走,最后如果真的上市了,那你就发了对吧,当然还有一些小公司,它在创业的时候,不会这样创业,它不是搞产品,它是搞外包,就说我搞一个小作坊,小公司,成天各种各样的软件开发,那这时候比如说一个大公司,它想开发一个软件,它就找到你,给你个10万,20万,你给我开发一款软件,这个就不涉及到我们刚才所说的融资之类的



融资要有产品,你没有产品,只是纯粹的一个小作坊给人家外包的话,和融资就没有关系



其实创业的话,这个风险是很大的,因为很多巨头,比如你盯着这块蛋糕,巨头它会不会盯着这块蛋糕啊,它也会盯着啊,比如说我们之前的打车软件有好多,但是你们不知道吧,因为没起来就被人家给干死了,那会儿滴滴打车和快的打车,你赔10块,我赔5块,你赔个10几,我赔个10几对吧



再给出租车司机再补填个7,8块对吧,在那个厮杀的非常火热的时候,就腾讯和阿里两家公司对拼嘛,滴滴是腾讯的,快滴是阿里的,两个有钱的公司在那烧钱对吧,旁边那些创业公司好不容易刚刚做起来,app下个礼拜好不容易上线了对吧,滴滴和快滴开始杀起来了,你看谁还理你啊,谁还理你的破软件啊



你上线之后还有人用嘛?肯定没人用啊,除非你也像他们那样烧钱,你有钱烧嘛?



你根本就没钱烧啊,一个用户你赔个10几块,你赔得起嘛,你根本就赔不起嘛,所以你还没起来就被人家干死了,所以这些创业公司,稍微市场上有点赚钱的地方,他们就会盯着,所以我们要做什么事情呢?



所以在互联网创业,它要稳,要准,而且要快,一定要快,我如果赶在大公司超我之前,我一下子把用户来个100万,瞬间在2,3个月之内,你大公司不可能说今天一看,明天就抄出来吧,你是不是还得等个1,2个月去酝酿啊,还没等你酝酿起来,我就马上去占领这个市场了吧



就像新浪微博,新浪怎么去和腾讯拼啊,它拼不过腾讯的,你新浪就是人家腾讯的一个小指头对吧



腾讯是很有钱的,我们互联网的3大巨头BAT,分别是百度,阿里,腾讯



其他公司,网易,新浪,	搜狗,搜狐,这些公司,就是人家的小指头,你营业额能达到人家的1/10就不错了,但是当时为什么新浪微博比腾讯微博强呢?



就是因为马化腾还没反应过来,新浪微博就火起来了,那等马化腾回过神来,人家新浪微博已经占领了市场,用户已经达到10几个亿了,这时候腾讯微博还怎么玩啊?



你即使起来也没办法玩了吧,因为用户这个东西,就是抢占一个先机,所以我在你能力不强的情况下,只要我足够快,用户一下子能积累起来,那其他后来的就不行了,现在把腾讯微博搞的已经停止服务了



所以的话,这个就是一个抢占先机,那一个小公司如果能抢占先机的话,等别人再抄你,你已经先入为主了,那大公司这时候怎么办呢?那这时候它就考虑超不过你了,就会考虑把你买过来呗,收购嘛对吧,你看那个滴滴打车,快的打车,是不是都被人家收购过去的,只要一被收购,那就说明你赚了,你都不用上市



我们互联网创业,现在就是这样的,你们下去之后,可以了解下这样一个创业的情况



我们继续看我们移动设备用户年龄分布,现在前三是85,90,85后,这个说明,现在这个时代是90后的时代了,我们这些80后的人, 已经快out了,所以之前也和互联网的一些老大聊过,他说想招一些产品经理或者程序员,它是希望这些全都是90后,为什么呢?



90后没有经验啊,刚出社会,干嘛要招90后呢?



因为它是一个什么思路,它想的是因为这个智能手机市场,它是90后的市场,90后占了70%的份额吧



那说明现在智能手机是给90后玩的,80后是不是很少啊,那既然是90后玩的,你不是90后,你是80后,怎么去体会人家90后的思维呢?



你怎么知道人家90后喜欢什么?不喜欢什么呢?你不知道吧,而且很有可能人家90后喜欢的东西,你觉得是垃圾对吧,小时代,那么火,垃圾电影对吧,你85后肯定在骂,什么TFboy,efo,这种东西,当然我不知道你喜欢不喜欢,反正80后整天在网上骂,	但是这些是90后喜欢的东西,所以90后喜欢的东西,你还整天骂,那如果真的在搞一款软件的时候,你老是按照你自己的思路去设计一款软件,你会是人家90后的菜吗?



肯定不会吧,所以的话,90后在找工作的时候,因为它就是这个群体,它了解这个群体喜欢什么,不喜欢什么,包括看电影时候的那个弹幕,我反正是理解不了,好端端的一个电影,那么好的一个屏幕,满屏的文字飞过来了吧,但就是很多人喜欢,所以的话,这个市场是90后的市场,所以身为80后的一些同学,这时候在遇到90后喜欢的东西时,千万不要骂对吧,一定要想清楚人家为什么要喜欢,而且自己要尝试的去喜欢人家这个东西对吧,因为人家90后是走在风口浪尖的地方,所以你需要跟随,不断的去接受新的东西,那现在你会发现我们很多的父辈,他们就接受不了智能手机,都不会用对吧,那这就是因为他们不愿意学习,那我也见到很多比较上年龄的父母,他们就喜欢年轻人,年轻人喜欢玩这个智能手机,它也和大家微信聊天,那这种就是他在不断学习,但是大部分的父母不愿意,我用个诺基亚就行了,给我买个诺基亚,但是诺基亚已经破产了,上哪给你买个诺基亚,所以慢慢的就会被这个时代淘汰



移动用户的兴趣分布,电影电视,音乐,美发,美容护肤,电子游戏,饮食,汽车,理财等占大头

移动分享活跃时段分布,线越长,表示越活跃,中午,12.00--13.00,比较活跃,17.00--22.00比较活跃,这是他社交的一个分布



应用指数,其实指的是应用软件的安装量,它这个是安装量的一个排名,排名靠前的不用看,肯定是微信和qq,这个是友盟统计的简单简绍

Day06 08.友盟统计sdk的使用 ##

刚才看了一下友盟的数据,它作为一个第三方的平台,对数据进行了一些分析,而那个数据呢,可靠性还是比较高的,因为目前友盟作为一个数据分析行业的龙头老大,它的数据还是比较可靠的,而且友盟作为一个第三方的公司,也被阿里给收购了,这个也是一个创业公司,后来被马云给收购了,多少钱这个我不知道,这个就是我们的这样一个友盟统计,但是我们现在只是做了一个全局的分析,从整个市场行情来讲的话,我们有时候可能只专注于我们自己的软件到底有多少人在用,那我怎么知道呢?



我们就可以让个人的这样一款软件也可以接入友盟统计, 这样就可以统计软件的相关数量了,那接下来我就去展示下友盟统计



首先需要注册人家的网站,然后再进行一个登陆,那我现在已经用我的账号去登陆进来了,那登陆进来之后,得创建一个应用



那现在如果要给我们的zhxa02增加友盟统计的话,那其实也是可以加的,这时候你要写应用名称为zhxa02, 平台选择android,应用类型随便选择“阅读资讯”,下边的应用描述随便写



最后提交并获取Appkey,将AppKey保存好



这时候怎么去使用友盟统计呢?

点击下载sdk,下载好了之后,解压



其中的libs不用说,肯定要抄过来嘛,放在我们自己的libs文件夹下



我们打开“友盟统计分析SDK集成文档Android”,其实友盟统计接入到我们的项目中非常简单,我们看到首先后去AppKey,我们已经获取过了吧,再往下,下载sdk下载了吧,导入sdk我们已经导入了吧,



接下来配置manifest清单文件,它是不是需要这些权限,我们把它全部抄到zhxa02



然后有一个meta-data数据需要配置下,我们在这个数据里边是一个meta-data,我们需要在清单文件的application下配置下,并且把UMENG_APPKEY的value值改为自己的appkey



UMENG_CHANNEL的value值改为itcast,就说这个项目的渠道是itcast



然后下边需要做session统计,怎么去集成这个统计呢?就说在每一个Activity的onResume方法中调用友盟的MobclickAgent.onResume(Context),在onPause方法中调用友盟的MobclickAgent.onPause(Context)就可以了,我们直接把下边的onResume方法,onPause方法抄过来,放在我们的每一个Activity中,那我们现在是不是有4个activity,每个activity中抄这么一份即可



友盟统计现在就已经集成进来了,这样的话,你的activity只要一运行,它的数据就会在友盟的后台给统计过来了



运行程序,我们看下统计的效果,后边还有一些高级的功能,我们现在只是实现了友盟统计的基本功能



高级功能的话,我们暂时就不说了,它甚至会统计到按钮的点击次数,比如说某个标签按钮被点击了多少次



我们现在是把基本的数据全都被统计下来,就说他百分之八十以上的数据我们现在都有,刚才已经添加进来了



打开之后,我随便运行几下,看下友盟后台的统计分析这个模块,我看看有没有数据,现在还是0,说明这个数据还是没有同步上去,比如我们把这个应用有时候可以退出下,然后再启动下,要统计上去的话,需要我们的模拟器联网,也可以运行在真机上跑一下,数据是不是就同步到友盟统计的后台了



而且可以看很多东西,比如说整体趋势,它目前的活跃情况,累积用户只有一个对吧,我们再看下渠道分析的渠道列表,我们的渠道现在是一个itcast这样的渠道



有时候渠道有什么用呢?比如说可能要把这个包发给一个谷歌市场,那你在清单文件的meta-data渠道的value值这个地方就写一个goglemarket,那如果你要发布到豌豆荚,那就在这个地方写个豌豆荚



那这样的话,你在友盟统计后台的列表里边是不是可以看到每个渠道用户分布的情况,这是我们的渠道列表,还有终端属性,我们看下我们的设备终端,设备终端就是能统计出你现在用的是什么手机



还有一些比如说错误分析,那这个错误分析是什么呢?错误分析就指的是,我们有时候在用软件的时候,是不是有可能会奔溃,奔溃的话,我们之前是不是讲过,奔溃可以将这个日志统计出来,那友盟统计已经帮我们把奔溃日志已经收集起来了,收集起来在哪里能看到呢?



就在友盟统计的后台,只要这个软件发出去,以后你想知道这个软件用户用的有没有奔溃,我直接在后台的错误列表中,就可以看到所有的奔溃日志,而且logcat中是什么红色日志,它就是什么日志,从奔溃日志里边,我们就可以看出它哪一行代码有问题吧



这样的话,我下一个版本更新的时候,是不是就可以去直接更新了吧,把这个错误就修复掉了,这个就是我们的错误列表,这个对我们开发者非常重要,别的统计数据开发者可能不需要关心,但是这个统计数据关系到我们版本的稳定性,目前我们看到这个错误列表中没有错误,所以它的错误数是0,当然还有别的东西



它还有社会化分享功能,我能分享到微信啊,qq啊什么的,这个在讲sharesdk的时候讲过,但我们发现友盟统计也有个社会化分享,而且友盟统计是不是也有消息推送啊



当然友盟统计的社会化分享和消息推送刚起来,没有人家做的那么专业,稳定,所以我们当时主要介绍的是比较有名的一些推送方式,这个就是我们所说的友盟统计



那以后,你们可能在开发的时候,项目经理或老板可能就找你,我们应用发布之后,能不能记一下某个东西的统计啊,比如某个按钮的点击次数啊,我们页面展现的活跃啊,能不能统计下,它可能的意思是要不要你自己写一些东西,或者代码,或者去和服务器端沟通下,有没有什么统计的接口,我们自己去开发一套统计,那这时候你就可以非常干脆的回答,这个统计做起来非常的费劲,我们直接接入友盟统计,就可以一次性搞定所有的东西,这时候老板很有可能就会用友盟统计,因为友盟统计是非常专业的,它后台界面看起来是非常人性化的,所以这个是友盟统计的情况



当然有时候老板可能会考虑到这个数据会不会泄露掉,因为你的数据是不是都被友盟拿到了吧,那数据是不是就被友盟知道了,当然友盟它肯定知道你的数据,就像我之前在开发那个问问客户端的时候,这时候它的产品经理就死活不让我接入友盟统计,我说接入吧,这个很简单的,我一个小时就卡卡全搞定了,他说不行,这个要我们自己写,那自己写,你写1,2周都写不完对吧,而且写出来那个东西又老又丑的,但是他说啥,他说用友盟统计,担心这个数据被泄露,被友盟知道,所以它死活就不行,那所以的话,这个友盟统计它一般在中小型公司用,中小型公司就你那点数据,能干个啥对吧,人家把数据窃取了又能怎么着对吧,不能怎么着对吧,所以的话,这个一般中小型公司用





而且友盟也不可能说把你的数据分享给所有的厂商对吧,它只是自己知道就行了,它知道这些数据能干嘛啊,它对数据是不是可以进行一些整合啊,刚刚我们看了上节课的友盟指数是不是就是通过这些数据分析出来的嘛,它对数据进行整合之后,生成一些专业的文档,是不是就可以将这些数据进行出售啊,卖给一些第三方的公司吧,那这些数据肯定很值钱啊,21世纪什么最贵啊,数据最贵,主要是你有一个非常专业的数据,那很多公司会抢着要你这些数据,只要这些数据足够的精确,那对于产品的决策,走向,确实是有一个至关重要的作用



这就是友盟统计的一种方式,所以用友盟统计的话,不需要花一分钱,但是友盟统计照样可以赚钱,就是因为它有这么多数据在手里边,这就是我们讲的一个友盟统计

Day06 09.扫描二维码 ##

最后在讲一个东西,扫描二维码,我可以非常负责任的告诉大家,你在传智学的任何的东西,没有说没有用的,尤其是从android课程来讲,我们所有的项目,全都是有用的,就看你能不能吸收下去,那如果你真的把所有的课程,所有的内容全部都吸收进来的话,那你出去要20k都没有问题,但是我没有说是任何一个同学都百分之百的能吸收掉,你能吸收个百分之60%就已经非常不错了,包括我们后边的项目,有些同学就说不重要了,它就不听了,直接去找工作了



当然可以,因为你现在掌握个60%,找工作拿10k没有什么问题,但是我们后边那个项目就没有用嘛?



当然是有用的,你了解到之后,你找工作工资肯定会更高一些,我们在选课程的时候,也都会选一些非常有用的课程给大家讲,没用的课程在设置课表的时候,就把它删掉了,没用写它干嘛



接下来就讲一个非常有用的,扫描二维码



那这个关于扫描二维码,这个我不用去给你们强调,它就很有用,你在哪里没有见过扫描二维码,到处都是扫描二维码,微信扫描二维码,支付宝扫描二维码,支付扫描二维码,干啥都扫描二维码,要扫描二维码,做起来非常简单,怎么做呢?



其实我们是有现成的项目ZXingCodeLibs.rar,解压之后直接运行就可以



我们在后边学习的时候,注意一个特点,就说每天怎么还是好多知识点,我都记不过来,后边在讲课的时候,不需要你记,包括我刚才讲友盟统计,你可以下去之后什么东西都不干,但是你必须记住4个字,友盟统计,你即使记不住,以后老板给你说友盟统计,你就要能反应上来,以前老师提到过,就够了



因为友盟统计这些第三方都有现成的文档,sdk,看下你就知道怎么用了嘛,还非得让老师每个都给你教一遍啊,人家已经把文档写的那么清楚了,你就照着文档一步一步往下走就行了吧



包括我之前讲的sharesdk分享,友盟统计,二维码,这些你都忘掉都行,先干什么后干什么,那些你全都不需要记,只需要记住sharesdk这个词就行了,那以后面试的时候,面试官不可能问你sharesdk第一步干嘛,第二步干嘛



包括这个扫描二维码,你不需要记任何东西,只需要知道在我的磁盘里边安安静静的躺着一个扫描二维码的demo就够了,等你真的用到的时候,你把它抄过去一运行就行了



我们现在把这个ZXingCodeLibs运行下,然后我们在网上随便找个二维码去扫下嘛,我们运行起来,看到它这个界面还有一个是否打开闪光灯的闪电按钮,然后中间一扫一扫的,那这时候我们就扫下鼠标垫的二维码嘛



这时候它就会在日志中将二维码扫描的信息打印出来,我们看到打印出来是个链接,即:

	http://mall.jd.com/index-68836.html



而我们二维码是不是有很多种啊,每个二维码代表的东西都不一样,那有时候他表示的是一个网络链接,那有时候它表示的可能是一段话,那有时候表示的可能是一个电话号码,它能表示任何东西,就任何的字符串,都是可以使用二维码来表示出来的





二维码是一个什么东西呢?

我们简单的去分析下这个二维码,二维码的特点,首先它是有这么三个角吧,这三个角是干什么用的呢?

这三个角是用来定位用的,怎么个定位法呢?

我们是要用摄像头去识别二维码,然后从这个信息里边去分析出它相关的一些内容吧,但是我们这个二维码的周边可能有一些乱七八糟的颜色图片啊的东西,可能影响我们对二维码区域的误判嘛,可能本来二维码在这,你误判到别的地方去了吧,这时候它这三个角是干嘛的,定位用的吧,三个点是不是就可以将这个二维码定位出来吧,而这三个角设计的这么大块,是不是很容易识别出来,那如果你把其中一个角挡住的话,就有可能导致二维码根本无法识别



然后我们看到二维码的其他地方,是不是点点点的黑点和白点吧,那黑点和白点表示什么意思呢?



那我们想象一下,任何的计算机字符串啦,乱七八糟的东西,我们追根究底最后是不是二进制啊,二进制是不是0和1啊,那二进制对应成图像的话,我们就分别用黑色和白色来表示是0还是1,所以说你会看到二维码中,一些地方是黑色的,一些地方是白色的,其实就是0,1这样的组合,那通过0,1的组合,是不是就能够把任何的字符串都拼出来啊,这就是二维码



那二维码的话,我们在线也能够生成一些二维码,比如说我们有一个在线二维码生成,就说你写一段话,它就能够把这段话生成二维码



我们搜索“草料二维码”,然后输入“itcast牛逼”,点击生成二维码



那这个二维码你可以扫一下,看看它是不是itcast牛逼,扫成功了之后,我们看到日志中打印的是:

result -->itcast牛逼



我们再增加一些文字,“itcast牛逼撒旦法斯蒂芬”,这时候文字变多了,我们看到生成的二维码是不是相对前一个变的密了一些,即文字越多,二维码越密



为什么是这样一个情况呢?



刚才说了,0,1,0,1嘛,比如说0代表白色,1代表黑色的话,那你的数据量越大,0,1,0,1的串是不是就越多啊,越多是不是就导致你的二维码就越密集,数据量越小,是不是就稀疏,所以说密集不密集这个还是有一定的关系的



而我们有些二维码,会发现中间有一个log,我们简单讲一下这个log的原理,比如说我们现在把这个二维码中间搞一个log, 然后再去扫,看能不能扫出来呢?能扫成功



为什么我用log把它中间挡住了,但是还能扫描出来呢?

有些同学说,那老师你刚才不是说了,它都是0,1,0,1的数据量嘛,你看我已经把中间那块数据给挡住了,它怎么还能出来



是因为我们在说这个0,1,0,1的时候,并不是纯粹的字符串的0,1是多少,我们就在这对应成黑框白框,不是这样的,而是要对这个0,1,0,1进行编码的运算,而这个运算就涉及到一个非常复杂的数学知识了,这个算法是由一些著名的数学家,他们搞出了这样一种算法,然后算完之后,把它编成另外一种0,1,0,1的串,而这个串有什么能力呢?



有一个自动容错能力,就说你只要不是挡太多,也就说你挡个20%的区域,它还是能够完整的把你这个原始数据给解析出来,这就是数学的魅力所在



感觉很神奇吧,明明挡住了,却能够把原始的串解析出来,当然不一定非得挡在正中间对吧,你挡在上边行不行啊,也行,挡在左边右边都行,只要不把人家这三个角挡住,其实它还有一个小角,只要不挡住这几个角就行,但一般放在中间好看一些,所以才放在了中间



这是因为它有容错处理,那关于扫描二维码的逻辑,是极其复杂的,我们肯定不需要去写,我们简单看下这个ZXingCodeLibs项目的代码,看他是怎么去写的



我们看到ZXingCodeLibs项目的libs文件夹下有一个zxing.jar,这是zxing的这样一个开源的第三方扫描二维码的工具,而我们看下这个包里的内容,是极其复杂的,它这个类,光类可能就上万行代码了,所以你要去自己写的话,根本就写不出来,所以我们不管是谁,即使是微信,它也有可能是集成了第3方的工具,只是可能对他进行了一点点的优化来去扫描二维码的,那我们作为一个开发人员,在使用扫描二维码的模块时,其实成本非常低,非常简单,那你只需要怎么去做呢?



 我们这个ZXingCodeLibs项目中,你会发现它只有一个activity,就是我们的CaptureActivity.java



因为我们是不是只有一个界面啊,一个界面是不是就一个activity啊,它从头到尾就只有一个activity,不用去管他怎么去写的,我们直接去看下它的布局文件capture.xml,它里边是有这么几个控件,一个是SurfaceView,那SurfaceView是用来捕获我们的视频图像,另外一个ViewfinderView是用来捕获二维码图像的,下边的imageView是闪光灯,还有一个textView,是我们的文字提示“将二维码图案放在取景框内,即可自动扫描”



那以后如果你想修改它的布局的话,你是不是直接在capture.xml中修改就可以了,只要不影响它大框架的前提下,比如说SurfaceView和ViewfinderView这两个控件你只要不去改它,别的控件你是不是可以随便改它的左右上下去调整



而在扫描二维码的代码CaptureActivity.java中,有一个方法叫做handleDecode,我们可以在这个方法里边拿到他扫描的结果



那这个handleDecode方法里边呢,是一个回调,那这个回调里边就可以把当前扫描二维码对象的rawResult值给我返回回来,而我在这个地方通过rawResult去get一下他的文字,然后再去打印了一下他的结果



同时其实我还做了一些处理,比如说我让他playBeepSoundAndVibrate,Vibrate是不是振动器的意思,就是在扫成功之后震动下,同时在播放一个声音吧



这是他锦上添花的一个效果,核心的就是这个地方拿到他的结果并打印日志



那讲到这里的话,你们以后如果要去使用它的话,怎么去做呢?



那你就把它的这个代码什么的,乱七八糟的,全都拷贝到你的项目里边来,包括权限,jar,然后你要跳到扫描二维码的页面的话,是不是跳到CaptureActivity.java中来啊,那在这个地方是不是就能拿到扫描二维码的一个结果啊,至于拿到结果之后,其实等你拿到这个结果,就证明你扫描二维码的功能已经完全的开发完了,后续的功能是属于你应用程序的业务逻辑了



因为扫描二维码这个扫了之后,是不是可以做任何事情,有时候你扫了之后是网跳浏览器加载网页,有时候你可能扫描二维码去加好友吧,有时候你扫描二维码是为了支付吧,那这些业务逻辑全都是谁做的,全都是你们公司的服务器端的开发人员去做的



我们比如说微信加好友,那微信加好友,这时候它会给你一个二维码,那这时候呢,别人拿着手机的微信去扫下二维码之后,它可能是一个特殊的网页链接,或者特殊的一段字符串,那我的微信一扫之后,因为我是微信,我一识别,二维码中就可以识别出对方好友的相关信息,比如说它用户的id之类的



然后我们在服务器端做一下加好友的操作就可以了,调一下那个链接就可以加好友了嘛



支付也一样,那支付的二维码可能代表的是加密之后的随机动态的密钥之类,比如支付宝要做安全,支付的金额之类的,全传给你,那我们需要拿支付宝去扫一下,扫完之后,获取到这个信息之后,通过这个信息去调一些接口啊之类的,然后服务器就直接把支付的逻辑就给我们处理完了



那我们客户端作为一个前端来讲的话,我们不需要考虑它后台到底怎么加好友,到底怎么支付,我们只负责扫出来,调接口,就ok,所以的话,我们搞android开发,80%的时间,全都是在调接口,调接口,所以今天还有同学再问,老师我这个评论模块怎么去做啊



我们是不是没有做过评论,评论不就是一个listview嘛,上边刷刷刷一堆的评论,下边是不是EditText去发送评论吧,listview界面是不是可以随便搞,而这个listview数据从哪里来呢?



肯定是调服务器的接口,服务器给你返回一个json数据,然后你解析之后是不是就出来了嘛



而我如何去写评论呢?



比如说我写个“我好伟大啊,阅兵好牛逼啊!”,你点发送之后,那这个数据发送给谁了,肯定是发送给服务器了嘛,服务器收到这条消息之后,给你返回发送成功的这样一个消息,我一接受到发送成功的消息之后,马上把listview去更新下,把我刚刚发的这些是不是也置0,把我刚刚发送的这些是不是也添加到listview里边来啊



全都是调服务器,本地我们其实很简单,所以说为什么搞android开发要比搞javaWeb要简单很多,android开发就是一个前端,它光调接口,光展现就行了,后边复杂逻辑,什么云计算,什么识别,什么乱七八糟的维护,什么支付宝安全之类的东西,全都由服务器搞定,服务器搞定之后,你告诉我调啥连接,我调就行了嘛,我一调就ok啦,全都是这样去搞的



所以说以后只要是遇到什么功能,只要这个数据可能是动态拿到的,不是本地写死的,那基本上都是从服务器拿到的,这就是我们讲的扫描二维码的这样一个逻辑,我们稍稍扩展了下
  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值