Android 官方培训文档 笔记 (5)

5.1无线连接设备
除了能够在云端通信,安卓的无线API也允许同一局域网中的设备进行通信。甚至没有连接到网络上,而是物理上隔得很近,也可以相互通信。此外。网络服务发现(NSD)可以进一步通过应用程序运行能相互通信的服务去找寻附近运行相同服务的设备。把这个功能整合到我们的应用中,可以提供许多功能,如在同一个房间,用户玩游戏,可以利用NSD实现从一个网络摄像头获取图像,或远程登录到同一网络中的其他机器。

5.1.1 使用网络服务发现
添加网络服务发现到我们的app中,可以使我们的用户辨识在局域网中支持我们的app所请求的服务的设备。
在局域网内注册自己服务的第一步是创建  NsdServiceInfo  对象。此对象包含的信息能够帮助网络中的其他设备决定是否要连接到我们所提供的服务。
public void registerService(int port) {
    // Create the NsdServiceInfo object, and populate it.
    NsdServiceInfo serviceInfo  = new NsdServiceInfo();

    // The name is subject to change based on conflicts
    // with other services advertised on the same network.
    serviceInfo.setServiceName("NsdChat");
    serviceInfo.setServiceType("_http._tcp.");
    serviceInfo.setPort(port);
    ....
}
第二个参数设置了服务类型,即指定应用使用的协议和传输层。语法是“_< protocol >._< transportlayer >”。在上面的代码中,服务使用了TCP协议上的HTTP协议。想要提供打印服务(例如,一台网络打印机)的应用应该将服务的类型设置为“_ipp._tcp”。
发现网络中的服务
与注册网络服务类似,服务发现需要两步骤:用响应的回调函数设置发现监听器,以及调用discoverService()


连接到网络上的服务
当我们的应用发现了网上可接入的服务,首先需要调用resolveService()方法,以确定服务的连接信息。
实现NsdManager.resolveListener对象并将其传入resolveService()方法。并使用这个NsdManager.
ResolveListener对象获得包含连接信息的   NsdSerServiceInfo

当程序退出时注销服务
在应用的生命周期中开启和关闭NSD服务是十分关键的。在程序退出时注销服务可以防止其他程序因为不知道
服务退出而反复尝试连接的行为。另外,服务发现市一中开销很大的操作,应该随着父activity的暂停而停止。

选择一个HttpClient 
大多数连接网络的Android app 会使用http来发送与接受数据。android提供了两种http clients :httpUrlConnection
与Apache HttpClient 。二者均支持Https ,流媒体上传和下载,可配置的超时、IPV6与连接池。

5.3传输数据数据时避免消耗大量电量

5.3.1优化下载以高效访问网络
安卓上的app不仅仅可以在前台运行(重点关注延迟),也可以在后台运行(优先处理耗电量)。

app如何影响无线电泼状态机
每次创建一个新的网络连接,无线电就切换到full power状态。在上面典型的3G无线电波状态机情况下,
无线电会在传输数据时保持在full power的状态,加上一个附加的5秒拖尾时间,再之后会经过12秒进入到
low power能量状态,因此对于典型的3G设备,每一次数据传输的回话都会导致无线电消耗大概20秒时间
来提取电能。

实际上,这意味着一个每18秒传输1秒非捆绑数据的app,会一直保持激活状态(18=1秒的传输数据+5秒
过渡时间回到low power+12秒过渡事件回到standby)。因此每分钟会消耗18秒high power的电量,42
秒low power 的电量。
通过比较,同一个app,每分钟传输持续3秒的捆绑数据,会使得无线电波持续在high power状态仅仅8秒,
在low power状态仅仅12秒钟。

预取数据
预取数据是一种减少数据传输会话数量的有效方法,预取技术指的是在一定时间内,单次连接操作,以最大
的下载能力来下载所有用户可能需要的数据。

预取技术通过减少应用里由于在执行一个动作或者查看数据之前等待下载完成造成的延迟,来提高用户体验。
根据正在下载的数据大小与可能用到的数据量来决定预取的频率。做一个粗略的估计,根据上面介绍的状态
机,对于有50%的机会会被当前的用户会话用到的数据,我们可以预取大约6秒,这使得潜在可能要用到的
数据量与可能下载好的数据量相一致。

通常来说,预取1-5MB会比较好,这种情况下,我们仅仅只需要每隔2-5分钟开始另一段下载。
根据这个原理,大数据的下载,比如视频文件,应该每隔2-5分钟开始另一端下载,这样能有效的预取到
下面几分钟内的数据进行预览。

值得注意的是,更进一步的下载应该是捆绑的。

一个音乐播放器
一个比较好的方法是维护正在播放的那首歌曲的缓冲区。对于流媒体音乐,不应该维护一段连续的数据流,
这样会使得无线电波一直保持激活状态,而应该用http流直播来集中传输音频流。(下载好2MB,开始一次
取出,再去下载下面的2MB)。

一个新闻阅读器
一个比较好的方法是在启动的时候预取一个合理数量的数据,比如在启动的时候预取第一条新闻的标题与缩略图
信息,确保较短的启动时间。之后继续获取剩余新闻的标题和缩略图信息。同时获取至少在主要标题列表可用的
每篇文章的文本。

批量处理传送和连接
每次发起一个连接——不论相关数据的大小——当使用典型的3G无线网络时,可能会导致无线电消耗大约20秒
的电量。
一个app每20秒ping一次服务器,仅仅是为了确认app正在运行和对用户课件,那么无线电会无限期地处于开启
状态,导致即使再没有实际数据传输的情况下,仍会消耗大量电量。

因此,对传送的数据进行捆绑操作和创建一个等待传输队列就显得非常重要。操作正确的话,可以使得大量的数据
集中进行发送,这样使得无线电波的激活时间尽可能的少,同时减少大部分电量的花费。

意味着我们应该通过队列延迟容忍传送来批量处理我们的传输数据,和抢占调度更新和预取,使得当要求时间敏感
传输时,数据会被全部执行。同样地,我们的计划更新和定期的预取应该开启等待传输队列的执行工作。

减少连接
通常来说,重用已经存在的网络连接比起重新建立一个新的连接更有效率。重用网络连接同样可以使得在拥挤不堪
的网络环境中进行更加智能地作出反应。
一个有用的妥协不是立即关闭连接,而是在固定期间的timeout之前关闭(即稍微晚点却又不至于到timeout)

最小定期更新造成的影响
每次app去向server询问检查是否有更新操作的时候,都会激活无线电,这样造成了不惜要的能量消耗。
GCM是一个用来从server到特定app传输数据的轻量级机制。使用GCM,server会在某个app需要获取新数据的时候
比起轮询方式(app为了即时拿到最新的数据需要定时去pIngserver),GCM这种由事件驱动的模式会在仅仅有数据
更新的时候通知app去创建网络连接来获取数据。

使用不严格的重复通知和指数避退算法来优化轮询
CSMA/CD协议中,一旦检测到冲突,为降低再冲突的概率,需要等待一个随机时间,然后再使用 CSMA方法试图传输。为了保证这种退避维持稳定,采用了二进制指数退避算法的技术,其算法过程如下:
1. 将冲突发生后的时间划分为长度为2t的 时隙
2. 发生第一次冲突后,各个站点等待0或1个时隙再开始重传
3. 发生第二次冲突后,各个站点随机地选择等待0,1,2或3个时隙再开始重传
4. 第i次冲突后,在0至2的i次方减一间随机地选择一个等待的时隙数,再开始重传
5. 10次冲突后,选择等待的时隙数固定在0至1023(2的10次方减一)间
6. 16次冲突后,发送失败,报告上层。
另一个方法是在 app 在上一次更新操作之后还未被使用的情况下,使用指数退避算法  exponential back-off algorithm  来减少更新频率。断言一个最小的更新频率和任何时候使用 app 都去重置频率通常都是有用的方法。例如

SharedPreferences sp =
  context.getSharedPreferences(PREFS, Context.MODE_WORLD_READABLE);

boolean appUsed = sp.getBoolean(PREFS_APPUSED, false);
long updateInterval = sp.getLong(PREFS_INTERVAL, DEFAULT_REFRESH_INTERVAL);

if (!appUsed)
  if ((updateInterval *= 2) > MAX_REFRESH_INTERVAL)
    updateInterval = MAX_REFRESH_INTERVAL;

Editor spEdit = sp.edit();
spEdit.putBoolean(PREFS_APPUSED, false);
spEdit.putLong(PREFS_INTERVAL, updateInterval);
spEdit.apply();

rescheduleUpdates(updateInterval);
executeUpdateOrPrefetch();
初始化一个网络连接的花费不会因为是否成功下载了数据而改变。对于那些成功完成是很重要的时间敏感的传输,我们可以使用指数退避算法来减少重复尝试的次数,这样能够避免浪费电量。例如:
private void retryIn(long interval) {
  boolean success = attemptTransfer();

  if (!success) {
    retryIn(interval*2 < MAX_RETRY_INTERVAL ?
            interval*2 : MAX_RETRY_INTERVAL);
  }
}
5.3.4
例如:如果LTE无线电的带宽与电量消耗都是3G无线电的2倍,我们应该在每次会话的时候都下载4倍于3G的数据量,或者差不多10MB .当然,下载这么多数据的时候,我们需要好好考虑预取本地存储的效率并且需要经常刷新预取的缓存。

我们可以使用connnectivity manager来判断当前激活的无限电波,并且根据不同结果来修改预取操作。

5.4 云同步 

5.4.1使用备份 API 
对于一些数据量相对较少的情况下(通常少于1MB),例如用户偏好设置、笔记、游戏分数或者是其他的一些状态数据,可以使用backup API来提供一个轻量级的解决方案。

注册Android backup Service 
这节课中所使用的Android backup Service 需要进行注册。

编写备份代理
创建辈分代理最简单的方法就是继承BackupAgentHelper。重写onCreate()方法。
onCreate()中创建一个 BackupHelper,目前 Android Framework 包含了两个帮助类: FileBackupHelper  与 SharedPreferencesBackupHelper
在我们创建一个帮助类并且指向需要备份的数据的时候,仅仅需要使用addhelper()方法将它们添加到backAgentHelper当众,之后再增加一个key
用来恢复数据。
为了使程序更加灵活,FileBackupHelper的构造函数可以带有任意数量的文件名,我们只需要简单地通过额外增加一个参数,就能实现同时对最高分
文件与游戏进度文件进行备份。

备份用户同样比较简单。和创建FilebackupHelper一样创建一个sharedPreferenceBackupHelper。在这种情况下,不是添加文件名到构造函数中。而是
添加被应用所使用的shared Preference Groups的名称。

请求备份
为了请求一个备份,仅仅需要创建一个BackupManager实例,然后调用它的dataChanged()方法即可。

恢复备份数据
如果确实有必须手动去触发恢复,只需要调用requestRestor()方法就可以了。

5.6使用Sync Adapter传输数据

我们可以考虑一下使用Android的同步适配器框架。该框架可以用来帮主管理数据,自动传输数据,以及协调不同应用间的同步问题。
插件架构
自动执行
自动网络检测
提升电池使用效率
账户管理和授权

5.6.1 创建Stub授权器

Sync Adapter 框架假定我们的 Sync Adapter 在同步数据时,设备存储端关联了一个账户,且服务器端需要进行登录验证。因此,我们需要提供一个叫做授权器(Authenticator)的组件作为 Sync Adapter 的一部分。该组件会集成在 Android 账户及认证框架中,并提供一个标准的接口来处理用户凭据,比如登录信息。

即使我们的应用不使用账户,我们仍然需要提供一个授权器组件。在这种情况下,授权器所处理的信息将被忽略,所以我们可以提供一个包含了方法存根(Stub Method)的授权器组件。同时我们需要提供一个绑定  Service,来允许 Sync Adapter 框架调用授权器的方法。


添加一个Stub 授权器组件
要在应用中添加一个 Stub 授权器,首先我们需要创建一个继承 AbstractAccountAuthenticator  的类,在所有需要重写的方法中,我们不进行任何处理,仅返回 null 或者抛出异常。


将授权器绑定到框架
为了让Sync Adapter框架可以访问我们的授权器,我们必须为它创建一个绑定服务。这一服务提供一个Android Binder对象,允许框架调用我们的授权器,并且在授权器和框架间
传递数据。
因为框架会在它第一次需要访问授权器时启动该service,所以我们也可以使用该服务来实例化授权器。具体而言,我们需要在服务的Service.onCreate()方法中调用授权器的
构造函数。

添加授权器的元数据文件
在<Mainfest>文件中声明授权器

5.6.2创建stub Content provider 
Sync Adapter  框架是设计成用来和设备数据一起工作,而这些设备数据应该被灵活且安全的Contentprovider
框架管理。因此,Sync Adapter框架会期望应用已经成为它的本地数据定义了Content provider。

5.6.3创建Sync Adapter 
5.6.4执行 Sync Adapter 

5.7使用Volley传输网络数据
Volley有如下优点:
1、自动调度网络请求。
2、高并发网络连接
3、通过标准http cache coherence (高速缓存一致性)缓存磁盘和内存透明的响应。
4、支持指定请求的优先级
5、支持指定请求的优先级
6、撤销请求API
7、框架容易被定制
8、强大的指令可以使得异步加载网络数据并正确地显示到UI的操作更简单。
9、包含了调试与追踪工具

Volley擅长执行用来显示UI的RPC类型操作,例如获取搜索结果的数据。它轻松的整合了任何协议,并
输出操作结果的数据,可以是原始的字符串,也可以是图片,或者是JSON,通过提供内置的我们可能
使用到的功能,Volley可以使得我们免去重复编写样板代码,使我们可以把关注点放在app的功能逻辑

Volley不适合用来下载打的数据文件,因为Volley会保持在解析的过程中所有的响应。

5.7.1发送简单的网络请求
1)Add the Internet Permission 
2)User new RequestQueue
3)Send a Request 
为了发送一个请求,你只需要构造一个请求并通过add()方法添加到RequestQueue中,一旦你添加了这个请求
它会通过队列,得到处理,然后得到原始的响应数据并返回。

当你执行add()方法时,Volley触发执行一个缓存处理线程以及一系列网络处理线程。当你添加一个请求到
队列中,它将被缓存线程所捕获并触发:如果这个请求可以被缓存处理,那么会在缓存线程中执行响应数据
的解析并返回到主线程。如果请求不能被缓存所处理,它会被放到网络队列中。网络线程池中的第一个可用
的网络线程会从队列中获取到这个请求并执行HTTP操作,解析工作线程的响应数据,把数据写道缓存中并把
接卸之后的数据返回到主线程。

4)Cancel a Request 
对请求Request对象调用Cancel()方法取消一个请求。一旦取消,Volley会确保你的响应hanlder不会被执行。
这意味着在实际操作中你可以在activity的onstop方法中取消所有pending在队列中的请求。你不需要通过检测
getactivity()==null来丢弃你的响应handler。
你可以为每个请求对象都绑定一个tag对象,然后你可以使用这个tag提供取消的范围。你可以为你的所有请求
都绑定到执行的activity上,然后你可以在onstop方法执行requestQueue.cancleall(this)。

5.7.2建立请求队列
这节课会介绍一种推荐的实现方式:创建一个单例的RequestQueue,这使得 RequestQueue能够保持在我们
app的生命周期中。

建立网络和缓存
一个RequestQueue需要两部分来支持它的工作:一部分是网络操作,用来传输请求,另一个是用来处理缓存
操作的Cache。在Volley的工具箱中包含了标准的实现方式:DiskBaseCache提供了每个文件与之对应响应
数据一一映射的缓存实现。BasicNetwork提供了一个基于AndroidHttpClient 或者HttpURLConnection的网络
传输。

使用单例模式
如果我们的应用持续地使用网络,更加高效的方式应该是建立一个RequestQueue的单例,这样它能够持续
保持在整个app的生命周期中。我们可以通过多种方式来实现这个单例。
推荐的方式是实现一个单例类,里面封装了RequestQueue()对象与其他Volley功能。
另一种方法是继承Application类,并在Application.OnCreate()方法里面建立RequestQueue。但是我们
不推荐这个方法,因为一个static的单例能够以一种更加模块化的方式提供同样的功能。

一个关键的概念是RequestQueue必须使用Application context来实例化,而不是Activity context。这确保了
RequestQueue在我们的app的生命周期中一直存货,而不会因为activity的重新创建而被重新创建

5.7.3 创建标准的网络请求 
请求一张图片
Volley为请求图片提供如下的类。用来支持不同的层级进行图片处理
ImageRequest ——一个封装好的,用来处理URL请求图片并且返回一张解完码的位图(Bitmap)。它同样提供了
一些简便的接口方法,例如指定一个大小进行重新裁剪。它的主要好处是Volley会确保类似decode,resize等
耗时操作在工作线程中执行。

Imageloader——一个用来处理加载与缓存从网络上获取到的图片的帮助类
ImageLoader是大量ImageRequest的协调器。例如,在listview中需要显示大量缩略图的时候。ImageLoader为
通常的Volley cache提供了更加前瞻的内存缓存,这个缓存对于防止图片抖动非常有用。这还使得在不阻塞或者
延迟主线程的前提下实现缓存命中。Imageloader还能够实现响应联合,避免几乎每一个响应回调里面都设置
Bitmap到view上面。响应联合使得能够同时提交多个响应。

NetworkImageLoader——在Imageloader的基础上建立,并且通过网络URL取回的图片的情况下,有效地替换
ImageView。如果view从层次结构中分离,NetworkImageView也可以管理取消挂起请求。

上面的代码是通过前一节的单例类来访问RequestQueue与ImageLoader。这种方法保证了我们的app创建这些
类的实例会持续存在于app的生命周期。这对于ImageLoader(一个用来处理加载与缓存图片的帮助类)很重要
的原因是:内存缓存的主要功能是允许非抖动旋转。使用单例模式可以使得Bitmap的缓存比activity存在的时间长
。如果我们在activity中创建ImageLoader。这个ImageLoader有可能会在每次旋转设备都被重新创建,可能导致抖动

举一个LRU cache的例子
Volley 工具箱中提供了一种通过  DiskBasedCache  类实现的标准缓存。这个类能够缓存文件到磁盘的指定目录。但是为了使用  ImageLoader ,我们应该提供一个自定义的内存 LRC bitmap 缓存,这个缓存实现了  ImageLoader.ImageCache  接口。我们可能想把缓存设置成一个单例。
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import android.util.DisplayMetrics;
import com.android.volley.toolbox.ImageLoader.ImageCache;

public class LruBitmapCache extends LruCache<StringBitmap>
        implements ImageCache 
{

    public LruBitmapCache(int maxSize) {
        super(maxSize);
    }
     //构造器嵌套构造器
    public LruBitmapCache(Context ctx) {
        this(getCacheSize(ctx));
    }

    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }

    @Override
    public Bitmap getBitmap(String url) {
        return get(url);
    }

    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        put(url, bitmap);
    }

    // Returns a cache size equal to approximately three screens worth of images.
    public static int getCacheSize(Context ctx) {
        final DisplayMetrics displayMetrics = ctx.getResources().
                getDisplayMetrics();
        final int screenWidth = displayMetrics.widthPixels;
        final int screenHeight = displayMetrics.heightPixels;
        // 4 bytes per pixel
        final int screenBytes = screenWidth * screenHeight * 4;

        return screenBytes * 3;
    }
}
请求JSON
Volley提供了以下的类用来执行JSON请求
jsonArrayRequest——一个为了获取给定URL的JSONArray响应正文的请求。
jsonObjectRequest——一个为了获取给定URL的JSONOBJECT响应正文的请求。

这两个类都是基于一个公共基类JsonRequest。

5.7.4 实现自定义网络请求

编写一个自定义请求
大多数的请求类型都已经包含在Volley的工具箱里面。如果我们的请求返回数值是一个String,image或者json。
那么是不需要自己去实现请求的。
那些需要自定义的请求类型,我们需要执行以下操作:
1)继承Request<T>类,<T>表示解析过的响应请求预期的数据类型。因此如果我们需要解析的响应类型是
一个String ,可以通过继承Request<String>来创建自定义的请求。
2)实现抽象方法 parseNetworkResponse 与deliverResponse()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
电子图书资源服务系统是一款基于 Java Swing 的 C-S 应用,旨在提供电子图书资源一站式服务,可从系统提供的图书资源中直接检索资源并进行下载。.zip优质项目,资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松copy复刻,拿到资料包后可轻松复现出一样的项目。 本人系统开发经验充足,有任何使用问题欢迎随时与我联系,我会及时为你解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(若有),项目具体内容可查看下方的资源详情。 【附带帮助】: 若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步。 【本人专注计算机领域】: 有任何使用问题欢迎随时与我联系,我会及时解答,第一时间为你提供帮助,CSDN博客端可私信,为你解惑,欢迎交流。 【适合场景】: 相关项目设计中,皆可应用在项目开发、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面中 可借鉴此优质项目实现复刻,也可以基于此项目进行扩展来开发出更多功能 【无积分此资源可联系获取】 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。积分/付费仅作为资源整理辛苦费用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值