Android面试知识点解析
程序员文章站
2022-04-11 22:02:38
Android面试知识点解析。
自我介绍:
您好,我是xxx,从事android开发也有好几年了,总共呆过两家公司,第一家是xx,干了1年,在这家公司做过3个项目,刚开始的...
Android面试知识点解析。
自我介绍: 您好,我是xxx,从事android开发也有好几年了,总共呆过两家公司,第一家是xx,干了1年,在这家公司做过3个项目,刚开始的时候主要写一些需求文档,用xmind整理项目功能,做一些简单的界面,大家都知道搞it的技术很重要,所以我在完成自己的工作之余就经常看其他模块的实现代码,尽快提升自己的技术,也积极参与同事的讨论,提出一些自己的想法,很快业务也熟悉了;我记得在第二个项目的时候就能独立完成一些模块的功能,(做完第三个项目的时候/xxxx年xx月的时候/一年后)后来为了有更好的发展,就来到了第二家公司;这家公司对我的提升很多,项目很多,做了5、6个吧,包括电商类的,金融类的,教育类的,医疗类的都有,其中大部分是团队开发,也有独立开发的,比如哪个哪个,在团队开发中,我主要担任的也是核心开发人员,负责框架的搭建,需求的整理和分析,项目任务的安排和进度的把控,及项目规范性和性能的把控;这家公司成长很多,挺感谢公司的,不过现在做的项目和之前的业务都差不多,公司组织架构也比较稳定了,觉得发展到了一个瓶颈,加上现在移动互联发展这么快,新技术天天在更新,智能家居,虚拟现实,vr,直播都是比较火的方向,我觉得也是以后的发展方向,it行业不跟着时代跟着技术进步,迟早会被淘汰,最近考虑了一下,还是决定离职,换个环境,周围的同事朋友也有劝我不要这么做的,我是一个不喜欢安逸的人,老这样也没意思,也为了以后的发展还是要大胆的尝试探索,投了一下简历,也算是和咱公司有缘分,就到这来面试了,说了这么多,口也渴了,停顿一秒。。。。面试官该说话了(当然前面随时会打断),应对问题,面试官一般问一些公司情况,个人情况,就会让介绍项目,那就开始项目介绍节奏。。。,如果没有让介绍项目,谈的不多,有空档就主动介绍项目(不要冷场,挺尴尬的), 项目介绍:这个项目是6月份开始做的,9月份第一版上线, 1.准备好录音笔,模拟面试的时候就使用(真正去面试的时候,第一次都会忘了录,或录音失败),面试录音诊断是找到面试问题的唯一利器 2.学历证(本科)和公司章提前买好,不要到了入职还没有,影响得之不易的offer,加急制作,费用高 3.改简历,投简历,开始每天150份,智联,51job,拉钩,boss,猎聘,100offer等,开始面试电话可能不多,耐心等待,如果投完900家,还是没有什么电话,可以重新注册一个账号再投 4.招聘信息共享,待会面试题,一个没成,后续的补上,有备而去,很重要 5.到了面试地点,去早了直接进去不要等,不能按时到,提前和人事说一下(如果能联系上) 6.见到了人事,寒暄几句(天冷天热,过来快慢,是否堵车,公司情况(位置好坏,好找不好找,办公区环境(大,宽敞,干净))),恰当的夸夸她(根据情况,不要生硬),询问技术面试官的职位和姓,见到时好打招呼,一定要起身,男的主动握手(女的就不要了,除非她先伸手) 7.如果两个或以上,确定哪个是头,主要和他交流,其他适当照顾到 8.自我介绍,介绍项目时,一定要自然,在回顾过去,眼睛可以平时或向上一点,不能左右顾盼 技术升级:算法,底层,项目技术要精(项目也许不在多),框架洗讲,直播,sdk开发,自定义view,RxJava,reactnative,插件化 1.下拉刷新时头部导航条的透明度会随着scrollview的滑动距离改变而改变,滑动到一定距离时,界面右下方会出现一个小火箭,点击它,界面会有一个置顶功能。首先我会自定义一个组件,继承LinearLayout,添加一个header view,保证是第一个添加到linearlayout的最上端,设置topMargin的值为负的header View高度,即将其隐藏在最上方,之后需要添加footer view,由于是线性布局可以直接添加,只要AdapterView的高度是MATCH_PARENT,那么footer view就会被添加到最后,并隐藏。下来刷新和上拉加载都要进行手指滑动距离的判断,当header view的topMargin>=0时,说明已经完全显示出来了,修改header view 的提示状态,footer 准备刷新,手指移动过程,还没有释放移动footer view高度同样和移动header view高度是一样,都是通过修改header view的topmargin的值来达到。为了实现偏移控制,一般自定义View/ViewGroup都需要重载computeScroll()方法,我会判断scrollview的滑动距离动态去给导航条的背景设置透明度,滑动渐变的效果,当滑到一定距离时,我会把小火箭显示出来,点击是会调用scrollview的scrollTo()方法,实现置顶功能。 2.图片的缓存处理 在加载图片的时候,如果图片比较大时而界面上只需要显示一小块,那么会造成内存浪费,继续加载更多也可能会出现内存溢出,这时使用缓存技术是必备的,这里我用的是IamgeLoader的LruCache类,首先将ImageLoader类设成单例,并在构造函数中初始化了LruCache类,把它的最大缓存容量设为最大可用内存的1/8。然后又提供了其它几个方法可以操作LruCache,以及对图片进行压缩和读取。首先自定义一个继承自ScrollView的类,这样就允许用户可以通过滚动的方式来浏览更多的图片。这里提供了一个loadMoreImages()方法,是专门用于加载下一页的图片的,因此在onLayout()方法中我们要先调用一次这个方法,以初始化第一页的图片。然后在onTouch方法中每当监听到手指离开屏幕的事件,就会通过一个handler来对当前ScrollView的滚动状态进行判断,如果发现已经滚动到了最底部,就会再次调用loadMoreImages()方法去加载图片。在这个方法中,使用了一个循环来加载这一页中的每一张图片,每次都会开启一个LoadImageTask,用于对图片进行异步加载。然后在LoadImageTask中,首先会先检查一下这张图片是不是已经存在于SD卡中了,如果还没存在,就从网络上下载,然后把这张图片存放在LruCache中。接着将这张图按照一定的比例进行压缩,并找出当前高度最小的一列,把压缩后的图片添加进去就可以了。另外,为了保证图片都能够合适地被回收,这里还加入了一个可见性检查的方法,即checkVisibility()方法。这个方法的核心思想就是检查目前照片墙上的所有图片,判断出哪些是可见的,哪些是不可见。然后将那些不可见的图片都替换成一张空图,这样就可以保证程序始终不会占用过高的内存。当这些图片又重新变为可见的时候,只需要再从LruCache中将这些图片重新取出即可。如果某张图片已经从LruCache中被移除了,就会开启一个LoadImageTask,将这张图片重新加载到内存中。 断点续传: 1:当用户点击下载按钮是就要把文件路径的信息传给后台由service的onstartcom的方法接受穿过来的参数,由于service和activity都是主线程不能做耗时操作所以要开启一个子线程去做耗时操作网络下载和把数据写到本地文件并且把进度信息本地文件保存到数据库并把进度传给activity通过广播发送网络文件 网络下载的关键点: 1:获取网络文件的长度 2:在本地创建一个文件,设置其长度 3:从数据库中获取上次下载的进度 4:从上次下载的位置下载数据,同时保存进度到数据库 5将下载进度回传activity 6:下载完成后删除下载信息 3.流式布局的实现 自定义ViewGroup,重点重写下面两个方法 1)onMeasure:测量子view的宽高,设置自己的宽和高 2)onLayout:设置子view的位置,onMeasure:根据子view的布局文件中属性,来为子view设置测量模式和测量值 当这个流式布局在被加载如内存并显示在屏幕上这一过程中,首先会调用view.measure(w,h)这个方法,表示测量view的宽度与高度,其中参数w与h分别表示这个控件的父控件的宽高。在view.measure()方法的调用过程中又会调用view本身的一个回调方法,onMeasure(),这个是view自身的一个回调方法,用于让开发者在自定义View的时候重新计算自身的大小。一般会在这个方法中循环遍历,计算出这个控件的全部子孙控件的宽高。在View的宽高计算完成以后,考虑将这个控件显示到屏幕的指定位置上,此时view的onLayout()方法会被调用。 一般同时会在这个方法中计算出全部子孙控件在这个控件中的位置。在onMeasure()中首先通过循环,遍历这个控件的所有子控件,同时调用子控件的measure()方法,这时measure方法的两个参数是控件能给这个子控件的最大宽高这里getChildMeasureSpec()方法的作用是用来计算一个合适子视图的尺寸大小(宽度或者高度),结合我们从子视图的LayoutParams所给出的MeasureSpec信息来获取最合适的结果。比如,如果这个View知道自己的大小尺寸,并且子视图的大小恰好跟父窗口一样大,父窗口必须用给定的大小去layout子视图,通过循环遍历,控制每个item子控件的显示位置,如果当前行还能放得下一个item,就放到当前行,如果放不下就放到下一行的最左边。 4.支付功能: 微信支付: 1)从微信开放平台上下载微信支付的SDK,使用其libammsdk.jar包。 2)在微信开放平台注册我们的应用(应用需要填写包名,应用的签名等)可以获取到APPID和APPSecret,然后申请开通支付功能,提交企业以及银行账户资料等待审核,审核通过了在线签订合同,之后就可以进行微信支付了,同时商户会收到邮件获得财付通商户账户,支付秘钥key,财付通权限秘钥,财付通商户标示id等开发关键数据。 3)预付订单:当用户确定购买商品之后,首先请求微信服务器获取access_tocken,提交预付订单package,添加签名,提交预付订单后会获取微信服务器返回的prepayid,接着把请求参数返回给手机端。 4)发起支付:调用api.sendReq(req);发起支付,发起支付的时候需要传入支付相关信息,相关信息以KEY-VALUE键值对方式用&拼接组成。例如:订单信息(订单号,交易付款时间,购买数量等),商品信息(商品名称,商品单价,商品描述等),支付信息(支付金额等)和notify_url(这个是通知地址,也就是微信的服务器把支付后成功与否等结果信息同步给我们公司服务器时候使用的地址)等。还需要拼接一个重要的签名参数sign,sign的值是上述这些交易信息MD5加密签名后的结果。 5)支付信息提交后会提交到微信的服务器,微信服务器完成整个支付功能后会给用户(客户端和服务器分别提示)成功或者失败的提示。 支付宝支付: 1)首先从支付宝平台下载SDK,使用其中的alipay.jar包。 2)当用户确定购买商品之后,首先生成订单,我们订单生成的规则就是用两个2-9的随机数+当前时间的毫秒值作为订单号。 3)调用支付宝的接口:调用支付宝的支付方法(方法Alipay.pay(String info)),支付方法需要传入支付相关信息,相关信息以KEY-VALUE键值对方式用&拼接组成。订单信息(订单号,交易付款时间,购买数量等),商品信息(商品名称,商品单价,商品描述等),支付信息(买方,卖方支付宝账号信息,支付金额等)和notify_url(这个是通知地址,也就是支付宝的服务器把支付后成功与否等结果信息同步给我们公司服务器时候使用的地址)等。还需要拼接一个重要的签名参数sign,sign的值是上述这些交易信息RSA签名后的结果。 4)支付信息提交后会提交到支付宝的服务器,支付宝的服务器完成整个支付功能后会给用户成功或者失败的提示界面。这个过程都是支付宝的功能。支付宝支付完成后,也会把信息同步给我们的服务器(也就是通过前面的notify_url这个地址)。 5)我们的服务器处理完毕之后,调用pay方法的时候其实也会接到返回值。返回值是json格式里面有很多信息,我们主要从中取结果状态码(resultStatus),根据状态码的数值判断支付是否成功,除了支付宝本身的提示界面以外,我们也会给用户一个我们自己的提示。 5.xmpp实现聊天功能: 我项目中即时通讯主要使用XMPP实现的,它有一个开源项目androidpn,使用的时候客户端要用到asmack.jar包,这个jar包提供了通讯的功能,主要使用jar包中的XMPPConnection类。主要实现如下: 1)使用XMPPConnection类与服务器openfire建立持久连接 2)在持久连接基础上进行用户注册(用户注册之后服务器就会记录该用户的信息,下次就能够识别) 3)在连接基础上进行用户登录认证(登录之后服务端会知道当前用户的状态)服务端只要记录了该用户的信息,就可以往用户手机上发送推送的信息了。 实现通讯功能主要是使用jar包中的 Chat 类完成信息的发送,发送信息的时候要注意的是用户的格式是用户名称@域/命名空间(例如:要给loupengfei聊天的话地址为:loupengfei@127.0.0.1/AndroidpnClient),这样服务端就能够把信息转给响应名称的人。 接收信息: 1)在用户建立连接的时候就定义了一个指定命名空间的解析器provider(负责解析服务端发过来的xml格式的消息) 2)同时也注册一个包监听器PacketListener,当有信息发送过来的时候我们定义的解析器会收到信息,在解析中使用PULL解析方式把xml中信息解析出来然后封装成我们自定义的一个IQ的子类(就是消息Bean),然后带着消息进入到包监听类中,在包监听类中结合handler把聊天内容等更新到UI上。 6.自定义圆形头像: 1)首先通过setImageBitmap()方法设置图片Bitmap 2)进入构造函数CircleImageView()获取自定义参数,以及调用setup()函数 3)进入setup()函数(非常关键),进行图片画笔边界画笔(Paint)一些重绘参数初始化:构建渲染器BitmapShader用Bitmap来填充绘制区域,设置样式和内外圆半径计算等,以及调用updateShaderMatrix()函数和 invalidate()函数 4)进入updateShaderMatrix()函数,计算缩放比例和平移,设置BitmapShader的Matrix参数等 5)触发ondraw()函数完成最终的绘制,使用配置好的Paint先画出绘制内圆形来以后再画边界圆形。 7.瀑布流照片墙的实现 瀑布流的布局方式虽然看起来好像排列的很随意,其实它是有很科学的排列规则的。整个界面会根据屏幕的宽度划分成等宽的若干列,由于手机的屏幕不是很大,这里我就分成三列。每当需要添加一张图片时,会将这张图片的宽度压缩成和列一样宽,再按照同样的压缩比例对图片的高度进行压缩,然后在这三列中找出当前高度最小的一列,将图片添加到这一列中。之后每当需要添加一张新图片时,都去重复上面的操作,就会形成瀑布流格局的照片墙。 8.屏幕适配 1)比重适配,控件在其父布局中的显示权重,一般用于线性布局中,如果控件过多,且显示比例也不相同的时候,控制起来就比较麻烦了,毕竟反比不是那么好确定的。 2)多个布局文件适配,就是对不同大小的屏幕提供不同的layout。比如横竖屏的切换,可以在res目录下建立layout-port和layout-land两个目录,里面分别放置竖屏和横屏两种布局文件,以适应对横屏竖屏自动切换。 3)多个drawable适配(图片的适配),就是对不同密度的屏幕提供不同的图片。在进行开发的时候,我们需要把合适大小的图片放在合适的文件夹里面。应尽量使用.9格式的图片,如需对密度为low的屏幕提供合适的图片,需新建文件夹drawable-ldpi/,并放入合适大小的图片。相应的,medium对应drawable-mdpi/,high对应drawable-hdpi/,extra high对应drawable-xhdpi/。 4)多个values适配,为了优质的用户体验,需要去针对不同的dpi设置,编写多套数值文件,要把生成的所有values文件夹放到res目录下,当设计师把UI高清设计图给你之后,你就可以根据设计图上的尺寸,以某一个分辨率的机型为基础,找到对应像素数的单位,然后设置给控件。 9.内存优化 1)静态变量引起内存泄露 静态变量是类相关的变量,它的生命周期是从这个类被声明,到这个类彻底被垃圾回收器回收才会被销毁。所以,一般情况下,静态变量从所在的类被使用开始就要一直占用着内存空间,直到程序退出。如果不注意,静态变量引用了占用大量内存的资源,造成垃圾回收器无法对内存进行回收,就可能造成内存的浪费。 2)使用Application的Context 在Android中,Application Context的生命周期和应用的生命周期一样长,而不是取决于某个Activity的生命周期。如果想保持一个长期生命的对象,并且这个对象需要一个 Context,就可以使用Application对象。可以通过调用Context.getApplicationContext()方法或者 Activity.getApplication()方法来获得Application对象。 3)及时关闭资源 Cursor是Android查询数据后得到的一个管理数据集合的类。正常情况下,如 果我们没有关闭它,系统会在回收它时进行关闭,但是这样的效率特别低。如果查询得到的数据量较小时还好,如果Cursor的数据量非常大,特别是如果里面 有Blob信息时,就可能出现内存问题。所以一定要及时关闭Cursor。 4)避免使用浮点数 通常的经验是,在Android设备中,浮点数会比整型慢两倍。 5)使用实体类比接口好 按照传统的观点Map会更好些,因为这样你可以改变他的具体实现类,只要这个类继承自Map接口。传统的观点对于传统的程序是正确的,但是它并不适合嵌入式系统。调用一个接口的引用会比调用实体类的引用多花费一倍的时间。如果HashMap完全适合你的程序,那么使用Map就没有什么价值。如果有些地方你不能确定,先避免使用Map,剩下的交给IDE提供的重构功能好了。(当然公共API是一个例外:一个好的API常常会牺牲一些性能) 6)避免使用枚举 枚举变量非常方便,但不幸的是它会牺牲执行的速度和并大幅增加文件体积。 使用枚举变量可以让你的API更出色,并能提供编译时的检查。所以在通常的时候你毫无疑问应该为公共API选择枚举变量。但是当性能方面有所限制的时候,你就应该避免这种做法了。 7)使用Bitmap及时调用recycle() Bitmap是内存消耗大户,绝大多数的OOM崩溃都是在操作Bitmap时产生的,我们需要根据需求去加载图片的大小,例如在列表中仅用于预览时加载缩略图,只有当用户点击具体条目想看详细信息的时候,这时另启动一个fragment/activity/对话框等等,去显示整个图片。直接使用ImageView显示bitmap会占用较多资源,特别是图片较大的时候,可能导致崩溃,使用BitmapFactory.Options设置inSampleSize, 这样做可以减少对系统资源的要求。使用Bitmap过后,就需要及时的调用Bitmap.recycle()方法来释放Bitmap占用的内存空间,而不要等Android系统来进行释放。 8)对Adapter进行优化 如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它;如果内存 空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。软引用可以和一个引用队 列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。 如果一个对象只具有弱引用,那么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引 用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。弱 引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联 的引用队列中。 弱引用与软引用的根本区别在于:只具有弱引用的对象拥有更短暂的生命周期,可能随时被回收。而只具有软引用的对象只有当内存不够的时候才被回收,在内存足够的时候,通常不被回收。 listview如何优化? 1)convertView复用,对convetView进行判空,当convertView不为空时重复使用,为空则初始化,从而减少了很多不必要的View的创建 2)定义一个ViewHolder,封装Listview Item条目中所有的组件,将convetView的tag设置为ViewHolder,不为空时通过ViewHolder的属性获取对应组件即可 3)当ListView加载数据量较大时可以采用分页加载和图片异步加载 handler的原理? Handler主要用于线程间的通信。 一个Handler允许发送和处理Message和Runable对象,UI主线程会自动分配一个Looper(消息轮询器),每个Looper中封装着MessageQueue(消息队列),遵循先进先出原则。Looper负责不断的从自己的消息队列里取出队头的任务或消息执行。一般是在子线程执行完耗时操作之后,通过Handler的sendMessage或post方法将Message和Runable对象传递给MessageQueue,而且在这些对象离开MessageQueue时,Handler负责执行他们(用到handleMessage方法,主要执行刷新UI的代码)。 其中Message类就是定义了一个信息,这个信息中包含一个描述符和任意的数据对象,这个信息被用来传递给Handler.Message对象提供额外的两个int域和一个Object域。 Tcp和Udp的区别和联系? TCP---传输控制协议,提供的是面向连接、可靠的字节流服务,传输数据前经过“三次握手”建立连接,保证数据传输的可靠性,但效率比较低。一般用于对于数据传输安全性较高的场合。 UDP---用户数据报协议,是一个简单的面向数据报的运输层协议,面向无连接。UDP不提供可靠性,数据传输可能发生错序,丢包,但效率较高。一般用于对于实时性要求较高的场合。 方法的重写和重载? 重写是子类的方法覆盖父类的方法,要求方法名和参数都相同 。 重载是在同一个类中的两个或两个以上的方法,拥有相同的方法名,但是参数却不相同,方法体也不相同,最常见的重载的例子就是类的构造函数。 ArrayList和LinkedList区别? 存数据,ArrayList数组存储数据,索引值以下标来搜索,查询比较方,删除增加比较麻烦,但是linkedList以链表式存储数据,对于增删比较方便。 Final ,finally,finalized,区别? final用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。 finally是异常处理语句结构的一部分,表示总是执行。 finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。 加载大图、多图如何防止内存溢出? 大图:解析每张图片的时候都先检查一下图片的大小,确保图片不会超出内存大小,如果只需要加载一张比较小的图片展示出来,显然加载大图的原图是不值得的,可以通过设置BitmapFactory.Options中inSampleSize的值实现图片压缩。 多图:屏幕上显示的图片可以通过滑动屏幕等事件不断地增加,最终导致OOM,可以使用LruCache类,主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。 如何人解决anr?forceclose(强制关闭) (1) AsyncTask,其中doInBackground()和onPostExecute(Result)两个方法非常重要 doInBackground() 这个方法运行在后台线程中,主要负责执行那些很耗时的操作,如访问网络。该方法必须重写。 onPostExecute(Result) 这个方法运行于UI主线程,在doInBackground(Params…)方法执行后调用,该方法用于接收后台任务执行后返回的结果,刷新UI显示。 (2.)子线程 + handler 在子线程中执行完耗时操作需要刷新UI时,通过handler.sendMessage()发消息给主线程, 然后在主线程Handler中的handleMessage()方法中执行刷新UI操作 android中内存如何优化? 1)谨慎使用静态变量 2)使用Application的Context 3)及时关闭资源(io流、cursor对象) 4)使用Bitmap及时调用recycle() 5)对Adapter进行优化 冒泡排序: for (int i = 0; i < score.length-1; i++){ //最多做n-1趟排序 for(int j = 0 ;j < score.length - i - 1; j++){ if(score[j] < score[j + 1]){ //把小的值交换到后面 int temp = score[j]; score[j] = score[j + 1]; score[j + 1] = temp; } } } 微信支付: 1)从微信开放平台上下载微信支付的SDK,使用其libammsdk.jar包。 2)在微信开放平台注册我们的应用(应用需要填写包名,应用的签名等)可以获取到APPID和APPSecret,然后申请开通支付功能,提交企业以及银行账户资料等待审核,审核通过了在线签订合同,之后就可以进行微信支付了,同时商户会收到邮件获得财付通商户账户,支付秘钥key,财付通权限秘钥,财付通商户标示id等开发关键数据。 3)预付订单:当用户确定购买商品之后,首先请求微信服务器获取access_tocken,提交预付订单package,添加签名,提交预付订单后会获取微信服务器返回的prepayid,接着把请求参数返回给手机端。 4)发起支付:调用api.sendReq(req);发起支付,发起支付的时候需要传入支付相关信息,相关信息以KEY-VALUE键值对方式用&拼接组成。例如:订单信息(订单号,交易付款时间,购买数量等),商品信息(商品名称,商品单价,商品描述等),支付信息(支付金额等)和notify_url(这个是通知地址,也就是微信的服务器把支付后成功与否等结果信息同步给我们公司服务器时候使用的地址)等。还需要拼接一个重要的签名参数sign,sign的值是上述这些交易信息MD5加密签名后的结果。 5)支付信息提交后会提交到微信的服务器,微信服务器完成整个支付功能后会给用户(客户端和服务器分别提示)成功或者失败的提示。 支付宝支付: 1)首先从支付宝平台下载SDK,使用其中的alipay.jar包。 2)当用户确定购买商品之后,首先生成订单,我们订单生成的规则就是用两个2-9的随机数+当前时间的毫秒值作为订单号。 3)调用支付宝的接口:调用支付宝的支付方法(方法Alipay.pay(String info)),支付方法需要传入支付相关信息,相关信息以KEY-VALUE键值对方式用&拼接组成。订单信息(订单号,交易付款时间,购买数量等),商品信息(商品名称,商品单价,商品描述等),支付信息(买方,卖方支付宝账号信息,支付金额等)和notify_url(这个是通知地址,也就是支付宝的服务器把支付后成功与否等结果信息同步给我们公司服务器时候使用的地址)等。还需要拼接一个重要的签名参数sign,sign的值是上述这些交易信息RSA签名后的结果。(加密就是数字签名) 4)支付信息提交后会提交到支付宝的服务器,支付宝的服务器完成整个支付功能后会给用户成功或者失败的提示界面。这个过程都是支付宝的功能。支付宝支付完成后,也会把信息同步给我们的服务器(也就是通过前面的notify_url这个地址)。 5)我们的服务器处理完毕之后,调用pay方法的时候其实也会接到返回值。返回值是json格式里面有很多信息,我们主要从中取结果状态码(resultStatus),根据状态码的数值判断支付是否成功,除了支付宝本身的提示界面以外,我们也会给用户一个我们自己的提示。 xmpp实现聊天功能: 我项目中即时通讯主要使用XMPP实现的,它有一个开源项目androidpn,使用的时候客户端要用到asmack.jar包,这个jar包提供了通讯的功能,主要使用jar包中的XMPPConnection类。主要实现如下: 1)使用XMPPConnection类与服务器建立持久连接 2)在持久连接基础上进行用户注册(用户注册之后服务器就会记录该用户的信息,下次就能够识别) 3)在连接基础上进行用户登录认证(登录之后服务端会知道当前用户的状态)服务端只要记录了该用户的信息,就可以往用户手机上发送推送的信息了。 实现通讯功能主要是使用jar包中的Chat类完成信息的发送,发送信息的时候要注意的是用户的格式是用户名称@域/命名空间(例如:要给loupengfei聊天的话地址为:loupengfei@127.0.0.1/AndroidpnClient),这样服务端就能够把信息转给响应名称的人。 接收信息: 1)在用户建立连接的时候就定义了一个指定命名空间的解析器provider(负责解析服务端发过来的xml格式的消息) 2)同时也注册一个包监听器PacketListener,当有信息发送过来的时候我们定义的解析器会收到信息,在解析中使用PULL解析方式把xml中信息解析出来然后封装成我们自定义的一个IQ的子类(就是消息Bean),然后带着消息进入到包监听类中,在包监听类中结合handler把聊天内容等更新到UI上。 四大组件详细介绍: Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。 BroadcastReceive广播接收器: Service 是一段长生命周期的,没有用户界面的程序,可以用来开发如监控类程序。 android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。这些数据可以存储在文件系统中、在一个SQLite数据库、或以任何其他合理的方式, 其他应用可以通过ContentResolver类(见ContentProviderAccessApp例子)从该内容提供者中获取或存入数据.(相当于在应用外包了一层壳), 只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中 它的好处:统一数据访问方式。 java中异常处理: Activity的启动模式: standard:默认模式,可以不用写配置。在这个模式下,都会默认创建一个新的实例。因此,在这种模式下,可以有多个相同的实例,也允许多个相同Activity叠加。 singleTop:可以有多个实例,但是不允许多个相同Activity叠加。即,如果Activity在栈顶的时候,启动相同的Activity,不会创建新的实例,而会调用其onNewIntent方法。 singleTask:只有一个实例。在同一个应用程序中启动他的时候,若Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。 如果是在别的应用程序中启动它,则会新建一个task,并在该task中启动这个Activity,singleTask允许别的Activity与其在一个task*存,也就是说,如果我在这个singleTask的实例中再打开新的Activity,这个新的Activity还是会在singleTask的实例的task中。 singleInstance:只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。 线程:被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元 webview的辅助类: 作用: 通过url地址加载互联网或本地网页 WebSettings 设置WebView的一些属性、状态等,例如允许使用javascript,允许使用缓存,允许使用内置的缩放组件 WebViewClient 主要帮助WebView处理各种通知、请求事件(例如,点击链接时候如何显示界面,页面开始加载,加载完毕之后有何动作等) WebChromeClient 辅助WebView处理Javascript的对话框、网站图标、网站Title、加载进度等 java的反射机制原理以及应用场合: JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有 属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态 获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 OSI七层模型:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层 常用的存储方式有哪些? SQLite: SQLite是一个轻量级的数据库,支持基本SQL语法,是常被采用的一种数据存储方式。Android为此数据库提供了一个名为SQLiteDatabase的类,封装了一些操作数据库的API。 SharedPreference: 除SQLite数据库外,另一种常用的数据存储方式,其本质就是一个xml文件,常用于存储较简单的参数设置。 File: 即常说的文件(I/O)存储方法,常用语存储大数量的数据,但是缺点是更新数据将是一件困难的事情。 ContentProvider: Android 系统中能实现所有应用程序共享的一种数据存储方式,由于数据通常在各应用间的是互相私密的,所以此存储方式较少使用,但是其又是必不可少的一种存储方式。 例如音频,视频,图片和通讯录,一般都可以采用此种方式进行存储。每个ContentProvider都会对外提供一个公共的URI(包装成Uri对 象),如果应用程序有数据需要共享时,就需要使用ContentProvider为这些数据定义一个URI,然后其他的应用程序就通过 Content Provider传入这个URI来对数据进行操作。 网络存储: 从网络读取数据和写入数据。 Android提供了通过网络来实现数据的存储和获取的方法。 我们可以调用WebService返回的数据或是解析HTTP协议实现网络数据交互。 package org.rsa.util; import javax.crypto.Cipher; import java.security.*; import java.security.spec.RSAPublicKeySpec; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.InvalidKeySpecException; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.io.*; import java.math.BigInteger; /** * RSA 工具类。提供加密,解密,生成密钥对等方法。 * 需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar。 * RSA加密原理概述 * RSA的安全性依赖于大数的分解,公钥和私钥都是两个大素数(大于100的十进制位)的函数。 * 据猜测,从一个密钥和密文推断出明文的难度等同于分解两个大素数的积 * =================================================================== * (该算法的安全性未得到理论的证明) * =================================================================== * 密钥的产生: * 1.选择两个大素数 p,q ,计算 n=p*q; * 2.随机选择加密密钥 e ,要求 e 和 (p-1)*(q-1)互质 (最大公因数为1的两个数叫互质数) * 3.利用 Euclid 算法计算解密密钥 d , 使其满足 e*d = 1(mod(p-1)*(q-1)) (其中 n,d 也要互质) * 4:至此得出公钥为 (n,e) 私钥为 (n,d) * =================================================================== * 加解密方法: * 1.首先将要加密的信息 m(二进制表示) 分成等长的数据块 m1,m2,...,mi 块长 s(尽可能大) ,其中 2^s *@author 董利伟 *版本: *描述: *创建时间:2008-9-23 下午09:58:16 *文件描述: *修改者: *修改日期: *修改描述: */ public class RSAUtil { //密钥对 private KeyPair keyPair = null; /** * 初始化密钥对 */ public RSAUtil(){ try { this.keyPair = this.generateKeyPair(); } catch (Exception e) { e.printStackTrace(); } } /** * 生成密钥对 * @return KeyPair * @throws Exception */ private KeyPair generateKeyPair() throws Exception { try { //KeyPairGenerator 类用于生成公钥和私钥对。密钥对生成器是使用 getInstance 工厂方法 KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",new org.bouncycastle.jce.provider.BouncyCastleProvider()); //这个值关系到块加密的大小,可以更改,但是不要太大,否则效率会低 final int KEY_SIZE = 1024; //每个提供者都必须提供(并记录)默认的初始化,以防客户端没有显式初始化 keyPairGen.initialize(KEY_SIZE, new SecureRandom());//随机源 KeyPair keyPair = keyPairGen.genKeyPair(); return keyPair; } catch (Exception e) { throw new Exception(e.getMessage()); } } /** * 生成公钥 * @param modulus * @param publicExponent * @return RSAPublicKey * @throws Exception */ private RSAPublicKey generateRSAPublicKey(byte[] modulus, byte[] publicExponent) throws Exception { KeyFactory keyFac = null; try { keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider()); } catch (NoSuchAlgorithmException ex) { throw new Exception(ex.getMessage()); } RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(publicExponent)); try { return (RSAPublicKey) keyFac.generatePublic(pubKeySpec); } catch (InvalidKeySpecException ex) { throw new Exception(ex.getMessage()); } } /** * 生成私钥 * @param modulus * @param privateExponent * @return RSAPrivateKey * @throws Exception */ private RSAPrivateKey generateRSAPrivateKey(byte[] modulus, byte[] privateExponent) throws Exception { KeyFactory keyFac = null; try { keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider()); } catch (NoSuchAlgorithmException ex) { throw new Exception(ex.getMessage()); } RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger(privateExponent)); try { return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec); } catch (InvalidKeySpecException ex) { throw new Exception(ex.getMessage()); } } /** * 加密 * @param key 加密的密钥 * @param data 待加密的明文数据 * @return 加密后的数据 * @throws Exception * * 用于将Cipher初始化为解密模式的常量 public final static int DECRYPT_MODE 用于将Cipher初始化为加密模式的常量 public final static int ENCRYPT_MODE */ public byte[] encrypt(Key key, byte[] data) throws Exception { try { Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider()); //完成用于加密或是解密操作的初始化 cipher.init(Cipher.ENCRYPT_MODE, key); //获得加密块大小,如:加密前数据为128个byte,而key_size=1024 加密块大小为127 byte,加密后为128个byte; //因此共有2个加密块,第一个127 byte第二个为1个byte int blockSize = cipher.getBlockSize();//获得加密块加密后块大小 int outputSize = cipher.getOutputSize(data.length);//获得输出缓冲区字节长度: int leavedSize = data.length % blockSize; int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize; byte[] raw = new byte[outputSize * blocksSize]; int i = 0; while (data.length - i * blockSize > 0) { if (data.length - i * blockSize > blockSize) // 按单部分操作加密或解密数据,或者结束一个多部分操作。 // 调用doFinal方法将会重置Cipher对象到使用init进行初始化时的状态,就是说,Cipher对象被重置,使得可以进行更多数据的加密或解密, cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize); else cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize); //这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到ByteArrayOutputStream中 //,而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了OutputSize所以只好用dofinal方法。 i++; } return raw; } catch (Exception e) { throw new Exception(e.getMessage()); } } /** * 解密 * @param key 解密的密钥 * @param raw 已经加密的数据 * @return 解密后的明文 * @throws Exception */ public byte[] decrypt(Key key, byte[] raw) throws Exception { try { Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider()); cipher.init(cipher.DECRYPT_MODE, key); int blockSize = cipher.getBlockSize(); //字节数组流: ByteArrayOutputStream bout = new ByteArrayOutputStream(64); int j = 0; while (raw.length - j * blockSize > 0) { bout.write(cipher.doFinal(raw, j * blockSize, blockSize)); j++; } return bout.toByteArray(); } catch (Exception e) { throw new Exception(e.getMessage()); } } /** * 返回公钥 * @return * @throws Exception */ public RSAPublicKey getRSAPublicKey() throws Exception{ //获取公钥 RSAPublicKey pubKey = (RSAPublicKey) keyPair.getPublic(); //获取公钥系数(字节数组形式) byte[] pubModBytes = pubKey.getModulus().toByteArray(); //返回公钥公用指数(字节数组形式) byte[] pubPubExpBytes = pubKey.getPublicExponent().toByteArray(); //生成公钥 RSAPublicKey recoveryPubKey = this.generateRSAPublicKey(pubModBytes,pubPubExpBytes); return recoveryPubKey; } /** * 获取私钥 * @return * @throws Exception */ public RSAPrivateKey getRSAPrivateKey() throws Exception{ //获取私钥 RSAPrivateKey priKey = (RSAPrivateKey) keyPair.getPrivate(); //返回私钥系数(字节数组形式) byte[] priModBytes = priKey.getModulus().toByteArray(); //返回私钥专用指数(字节数组形式) byte[] priPriExpBytes = priKey.getPrivateExponent().toByteArray(); //生成私钥 RSAPrivateKey recoveryPriKey = this.generateRSAPrivateKey(priModBytes,priPriExpBytes); return recoveryPriKey; } } 测试 package org.yanzi.websafe; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import org.md5.util.MD5Util; import org.rsa.util.RSAUtil; public class TestCode { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub /****************************RSA加密解密测试********************************/ try { RSAUtil rsa = new RSAUtil(); String str = "yanzi1225627"; RSAPublicKey pubKey = rsa.getRSAPublicKey();//返回公钥 RSAPrivateKey priKey = rsa.getRSAPrivateKey();//获取私钥 //加密的时候使用公钥,和需要加密文字的byte[] byte[] enRsaBytes = rsa.encrypt(pubKey,str.getBytes()); String enRsaStr = new String(enRsaBytes, "UTF-8"); System.out.println("加密后==" + enRsaStr); System.out.println("解密后==" + new String(rsa.decrypt(priKey, rsa.encrypt(pubKey,str.getBytes())))); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } /************************************MD5加密测试*****************************/ // String srcString = "yanzi1225627"; // System.out.println("MD5加密后 = " + MD5Util.getMD5String(srcString)); } } package org.md5.util; import java.security.MessageDigest; public class MD5Util { public final static String getMD5String(String s) { char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; try { byte[] btInput = s.getBytes(); //获得MD5摘要算法的 MessageDigest 对象 MessageDigest mdInst = MessageDigest.getInstance("MD5"); //使用指定的字节更新摘要 mdInst.update(btInput); //获得密文 byte[] md = mdInst.digest(); // 这个方法是加密后返回的byte数组 //把密文转换成十六进制的字符串形式 int j = md.length;// 记录md的长度 char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // hexDigits 数组中对应的十六进制数放入str中 str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { e.printStackTrace(); return null; } } } 所谓流媒体是指采用流式传输的方式在Internet播放的媒体格式。 流媒体又叫流式媒体,它是指商家用一个视频传送服务器把节目当成数据包发出,传送到网络上。用户通过解压设备对这些数据进行解压后,节目就会像发送前那样显示出来。 流媒体(Streaming Media)的出现极大地方便了人们的工作和生活。在地球的另一端,某大学的课堂上,某个教授正在兴致盎然地传授一门你喜欢的课程,想听?太远!放弃?可惜!没关系,网络时代能满足你的愿望。在网络上找到该在线课程,课程很长,但没关系,只管点击播放,教授的身影很快出现在屏幕上,课程一边播放一边下载,虽然远在天涯,却如亲临现场!除了远程教育,流媒体在视频点播、网络电台、网络视频等方面也有着广泛的应用。 流媒体,又叫流式媒体,是边传边播的媒体,是多媒体的一种。边传边播是指媒体提供商在网络上传输媒体的"同时",用户一边不断地接收并观看或收听被传输的媒体。"流"媒体的"流"指的是这种媒体的传输方式(流的方式),而并不是指媒体本身。 基本信息 中文名称 流媒体 外文名称 Streaming media 目录 1媒体技术 2基本介绍 3技术应用 4技术问题 5常用格式 6传输协议 7播放方式 折叠编辑本段媒体技术 折叠流式传输的基础 在网络上传输音/视频等多媒体信息,目前主要有下载和流式传输两种方案。A/V文件一般都较大,所以需要的存储容量也较大;同时由于网络带宽的限制,下载常常要花数分钟甚至数小时,所以这种处理方法延迟也很大。流式传输时,声音、影像或动画等时基媒体由音视频服务器向用户计算机的连续、实时传送,用户不必等到整个文件全部下载完毕,而只需经过几秒或十数秒的启动延时即可进行观看。当声音等时基媒体在客户机上播放时,文件的剩余部分将在后台从服务器内继续下载。流式不仅使启动延时成十倍、百倍地缩短,而且不需要太大的缓存容量。流式传输避免了用户必须等待整个文件全部从Internet上下载才能观看的缺点。 流媒体指在Internet/Intranet中使用流式传输技术的连续时基媒体,如:音频、视频或多媒体文件。流式媒体在播放前并不下载整个文件,只将开始部分内容存入内存,流式媒体的数据流随时传送随时播放,只是在开始时有一些延迟。流媒体实现的关键技术就是流式传输。 流式传输定义很广泛,现在主要指通过网络传送媒体(如视频、音频)的技术总称。其特定含义为通过Internet 将影视节目传送到PC机。实现流式传输有两种方法:实时流式传输(Realtime streaming)和顺序流式传输(progressive streaming)。一般说来,如视频为实时广播,或使用流式传输媒体服务器,或应用如RTSP的实时协议,即为实时流式传输。如使用HTTP服务器,文件即通过顺序流发送。采用哪种传输方法依赖你的需求。当然,流式文件也支持在播放前完全下载到硬盘。 折叠顺序流式传输 顺序流式传输是顺序下载,在下载文件的同时用户可观看在线媒体,在给定时刻,用户只能观看已下载的那部分,而不能跳到还未下载的前头部分,顺序流式传输不象实时流式传输在传输期间根据用户连接的速度做调整。由于标准的HTTP服务器可发送这种形式的文件,也不需要其他特殊协议,它经常被称作HTTP流式传输。顺序流式传输比较适合高质量的短片段,如片头、片尾和广告,由于该文件在播放前观看的部分是无损下载的,这种方法保证电影播放的最终质量。这意味着用户在观看前,必须经历延迟,对较慢的连接尤其如此。对通过调制解调器发布短片段,顺序流式传输显得很实用,它允许用比调制解调器更高的数据速率创建视频片段。尽管有延迟,毕竟可让你发布较高质量的视频片段。顺序流式文件是放在标准HTTP或 FTP服务器上,易于管理,基本上与防火墙无关。顺序流式传输不适合长片段和有随机访问要求的视频,如:讲座、演说与演示。它也不支持现场广播,严格说来,它是一种点播技术。 折叠实时流式传输 实时流式传输指保证媒体信号带宽与网络连接配匹,使媒体可被实时观看到。实时流与HTTP流式传输不同,他需要专用的流媒体服务器与传输协议。实时流式传输总是实时传送,特别适合现场事件,也支持随机访问,用户可快进或后退以观看前面或后面的内容。理论上,实时流一经播放就可不停止,但实际上,可能发生周期暂停。实时流式传输必须配匹连接带宽,这意味着在以调制解调器速度连接时图象质量较差。而且,由于出错丢失的信息被忽略掉,网络拥挤或出现问题时,视频质量很差。如欲保证视频质量,顺序流式传输也许更好。实时流式传输需要特定服务器,如:QuickTime Streaming Server、RealServer与Windows Media Server。这些服务器允许你对媒体发送进行更多级别的控制,因而系统设置、管理比标准HTTP服务器更复杂。实时流式传输还需要特殊网络协议,如:RTSP (Realtime Streaming Protocol)或MMS (Microsoft Media Server)。这些协议在有防火墙时有时会出现问题,导致用户不能看到一些地点的实时内容。 折叠流媒体技术原理 流式传输的实现需要缓存。因为Internet以包传输为基础进行断续的异步传输,对一个实时A/V源或存储的A/V文件,在传输中它们要被分解为许多包,由于网络是动态变化的,各个包选择的路由可能不尽相同,故到达客户端的时间延迟也就不等,甚至先发的数据包还有可能后到。为此,使用缓存系统来弥补延迟和抖动的影响,并保证数据包的顺序正确,从而使媒体数据能连续输出,而不会因为网络暂时拥塞使播放出现停顿。通常高速缓存所需容量并不大,因为高速缓存使用环形链表结构来存储数据:通过丢弃已经播放的内容,流可以重新利用空出的高速缓存空间来缓存后续尚未播放的内容。——流式传输的实现需要合适的传输协议。由于TCP需要较多的开销,故不太适合传输实时数据。在流式传输的实现方案中,一般采用HTTP/TCP来传输控制信息,而用RTP/UDP来传输实时声音数据。流式传输的过程一般是这样的:用户选择某一流媒体服务后,Web浏览器与Web服务器之间使用HTTP/TCP交换控制信息,以便把需要传输的实时数据从原始信息中检索出来;然后客户机上的Web浏览器启动A/VHelper程序,使用HTTP从Web服务器检索相关参数对Helper程序初始化。这些参数可能包括目录信息、A/V数据的编码类型或与A/V检索相关的服务器地址。 A/VHelper程序及A/V服务器运行实时流控制协议(RTSP),以交换A/V传输所需的控制信息。与CD播放机或VCRs所提供的功能相似,RTSP提供了操纵播放、快进、快倒、暂停及录制等命令的方法。A/V服务器使用RTP/UDP协议将A/V数据传输给A/V客户程序(一般可认为客户程序等同于Helper程序),一旦A/V数据抵达客户端,A/V客户程序即可播放输出。 需要说明的是,在流式传输中,使用RTP/UDP和RTSP/TCP两种不同的通信协议与A/V服务器建立联系,是为了能够把服务器的输出重定向到一个不同于运行A/VHelper程序所在客户机的目的地址。实现流式传输一般都需要专用服务器和播放器,其基本原理如图所示。 折叠智能流技术(SureStream) 今天,28.8Kbps调制解调器是Internet连接的基本速率,cable modem、 ADSL、DSS、ISDN等发展快,内容提供商不得不要么限制发布媒体质量,要么限制连接人数。根据RealNetwork站点统计,对28.8Kbps调制解调器,实际流量为10bps到26Kbps,呈钟形分布,高峰在20Kbps。这意味着若内容提供商选择20Kbps固定速率,将有大量用户得不到好质量信号,并可能停止媒体流而引起客户端再次缓冲,直到接收足够数据。一种解决方法是服务器减少发送给客户端的数据而阻止再缓冲,在RealSystem 5.0中,这种方法称为“视频流瘦化”。这种方法的限制是RealVideo文件为一种数据速率设计,结果可通过抽取内部帧扩展到更低速率,导致质量较低。离原始数据速率越远,质量越差。另一种解决方法是根据不同连接速率创建多个文件,根据用户连接,服务器发送相应文件,这种方法带来制作和管理上的困难,而且,用户连接是动态变化的,服务器也无法实时协调。 智能流技术通过两种途径克服带宽协调和流瘦化。首先,确立一个编码框架,允许不同速率的多个流同时编码,合并到同一个文件中;第二,采用一种复杂客户/服务器机制探测带宽变化。 针对软件、设备和数据传输速度上的差别,用户以不同带宽浏览音视频内容。为满足客户要求,Progressive networks公司编码、记录不同速率下媒体数据,并保存在单一文件中,此文件称为智能流文件,即创建可扩展流式文件。当客户端发出请求,它将其带宽容量传给服务器,媒体服务器根据客户带宽将智能流文件相应部分传送给用户。以此方式,用户可看到最可能的优质传输,制作人员只需要压缩一次,管理员也只需要维护单一文件,而媒体服务器根据所得带宽自动切换。智能流通过描述I现实世界Internet上变化的带宽特点来发送高质量媒体并保证可靠性,并对混合连接环境的内容授权提供了解决方法。流媒体实现方式如下: * 对所有连接速率环境创建一个文件 * 在混合环境下以不同速率传送媒体 * 根据网络变化,无缝切换到其它速率 * 关键帧优先,音频比部分帧数据重要 * 向后兼容老版本RealPlayer 折叠智能流 在RealSystem G2中是对所谓自适应流管理(ASM)API的实现,ASM描述流式数据的类型,辅助智能决策,确定发送那种类型数据包。文件格式和广播插件定义了ASM规则。用最简单的形式分配预定义属性和平均带宽给数据包组。对高级形式,ASM规则允许插件根据网络条件变化改变数据包发送。每个ASM规则可有一定义条件的演示式,如演示式定义客户带宽是5,000到15,000Kbps,包损失小于2.5%。如此条件描述了客户当前网络连接,客户就订阅此规则。定义在规则中的属性有助于RealServer有效传送数据包,如网络条件变化,客户就订阅一个不同规则。 折叠编辑本段基本介绍 流媒体是指以流的方式在网络中传输音频、视频和多媒体文件的形式。 流媒体文件格式是支持采用流式传输及播放的媒体格式。流式传输方式是将视频和音频等多媒体文件经过特殊的压缩方式分成一个个压缩包,由服务器向用户计算机连续、实时传送。在采用流式传输方式的系统中,用户不必像非流式播放那样等到整个文件全部下载完毕后才能看到当中的内容,而是只需要经过几秒钟或几十秒的启动延时即可在用户计算机上利用相应的播放器对压缩的视频或音频等流式媒体文件进行播放,剩余的部分将继续进行下载,直至播放完毕。 这个过程的一系列相关的包称为“流”。流媒体实际指的是一种新的媒体传送方式,而非一种新的媒体。流媒体技术全面应用后,人们在网上聊天可直接语音输入;如果想彼此看见对方的容貌、表情,只要双方各有一个摄像头就可以了;在网上看到感兴趣的商品,点击以后,讲解员和商品的影像就会跳出来;更有真实感的影像新闻也会出现。 流媒体技术发端于美国。在美国目前流媒体的应用已很普遍,比如惠普公司的产品发布和销售人员培训都用网络视频进行。 流式传输方式则是将整个A/V及3D等多媒体文件经过特殊的压缩方式分成一个个压缩包,由视频服务器向用户计算机连续、实时传送。在采用流式传输方式的系统中,用户不必像采用下载方式那样等到整个文件全部下载完毕,而是只需经过几秒或几十秒的启动延时即可在用户的计算机上利用解压设备(硬件或软件)对压缩的A/V、3D等多媒体文件解压后进行播放和观看。此时多媒体文件的剩余部分将在后台的服务器内继续下载。 折叠编辑本段技术应用 互联网的迅猛发展和普及为流媒体业务发展提供了强大市场动力,流媒体业务正变得日益流行。 流媒体技术广泛用于多媒体新闻发布、在线直播、网络广告、电子商务、视频点播、远程教育、远程医疗、网络电台、 实时视频会议等互联网信息服务的方方面面。流媒体技术的应用将为网络信息交流带来革命性的变化,对人们的工作和生活将产生深远的影响。一个完整的流媒体解决方案应是相关软硬件的完美集成,它大致包括下面几个方面的内容: 内容采集、 视音频捕获和压缩编码、内容编辑、内容存储和播放、应用服务器内容管理发布及用户管理等。 流媒体技术和声音信息经过压缩处理后放上网站服务器,让用户一边下载一边观看、收听,而不要等整个压缩文件下载到自己的计算机上才可以观看的网络传输技术。该技术先在使用者端的计算机上创建一个缓冲区,在播放前预先下一段数据作为缓冲,在网路实际连线速度小于播放所耗的速度时,播放程序就会取用一小段缓冲区内的数据,这样可以避免播放的中断,也使得播放品质得以保证。 传输流程 在流式传输的实现方案中,一般采用HTTP/TCP来传输控制信息,而用RTP/UDP来传输实时声音数据。具体的传输流程如下: (1)Web浏览器与Web服务器之间使用HTTP/TCP交换控制信息,以便把需要传输的实时数据从原始信息中检索出来。 (2)用HTTP从Web服务器检索相关数据,由A/V播放器进行初始化。 (3)从Web服务器检索出来的相关服务器的地址定位A/V服务器。 (4)A/V播放器与A/V服务器之间交换A/V传输所需要的实时控制协议。 (5)一旦A/V数据抵达客户端,A/V播放器就可播放。 技术方式 目前主流的流媒体技术有三种,分别是RealNetworks公司的RealMedia、Microsoft公司的WindowsMediaTechnology和Apple公司的QuickTime。这三家的技术都有自己的专利算法、专利文件格式甚至专利传输控制协议。 1.Apple公司的QuickTime QuickTime是一个非常老牌的媒体技术集成,是数字媒体领域事实上的工业标准。之所以说集成这个词是因为QuickTime实际上是一个开放式的架构,包含了各种各样的流式或者非流式的媒体技术。QuickTime是最早的视频工业标准,1999年发布的QuickTime4.0版本开始支持真正的流式播放。由于QuickTime本身也存在着平台的便利(MacOS),因此也拥有不少的用户。QuickTime在视频压缩上采用的是SorensonVideo技术,音频部分则采用QDesignMusic技术。QuickTime最大的特点是其本身所具有的包容性,使得它是一个完整的多媒体平台,因此基于QuickTime可以使用多种媒体技术来共同制作媒体内容。同时,它在交互性方面是三者之中最好的。例如,在一个QuickTime文件中可同时包含midi、动画gif、flash和smil等格式的文件,配合QuickTime的WiredSprites互动格式,可设计出各种互动界面和动画。QuickTime流媒体技术实现基础是需要3个软件的支持,QuickTime播放器、QuickTime编辑制作、QuickTimeStreaming服务器。 2.RealNetworks公司的RealMedia RealMedia发展的时间比较长,因此具有很多先进的设计,例如,ScalableVideoTechnology可伸缩视频技术可以根据用户电脑速度和连接质量而自动调整媒体的播放质素。Two—passEncoding两次编码技术可通过对媒体内容进行预扫描,再根据扫描的结果来编码从而提高编码质