App开发常用的一些网络通信技术,主要包括如何使用多线程完成异步操作、如何进行HTTP接口调用与图片获取、如何实现文件上传和下载操作、如何运用Socket通信技术。
一、 多 线 程
介绍多线程技术在App开发中的具体运用,首先说明如何利用Message配合Handler完成主线程与分线程之间的简单通信;然后阐述进度对话框的用法,以及如何自定义实现文本进度条与文本进度圈;接着讲述异步任务AsyncTask的具体用法和注意事项;最后分析异步服务IntentService的实现原理和开发步骤。
1.消息传递Message
Android系统存在限制,只有主线程才能直接操作界面,因此分线程想修改界面就得另想办法。
多线程技术并非单单用于拍照预览,还用于网络通信、后台服务等耗时场合,并且这些场合往往希望操纵现有的界 面。这要求有一种用于线程之间相互通信的机制。
为此,Android设计了一个Message消息工具,通过结合Handler与Message可简单有效地实现线程之间的通信。 分线程将消息传递给主线程,再由主线程来操作界面。
主线程与分线程之间传递消息的步骤
1). 在主线程中构造一个Handler对象,并启动分线程
启动分线程有两种方式,既可通过Handler对象的post方法启动Thread,也可直接调用Thread对象的start方 法。
2). 在分线程中构造一个Message对象的消息包
Message是多线程通信中的消息包裹,类似于Intent机制的Bundle工具。
3). 在分线程中通过Handler对象将Message消息发出去
处理器Handler的消息发送操作主要是各类send方法。
4). 主线程中的Handler对象处理接收到的消息
主线程根据收到的Message消息内容分别进行相应处理。
2.进度对话框ProgressDialog
分线程在处理事务期间不允许用户继续操作界面控件,这时可通过进度对话框ProgressDialog提示用户等待。 下面是进度对话框的常用方法。
setTitle:设置对话框的标题文本。
setMessage:设置对话框的消息内容。
setProgressStyle:设置进度条的样式。
setProgress:设置当前进度的数值。
show:显示对话框。
dismiss:关闭对话框。
进度对话框的展示效果
自定义进度条(上)
(1). 在长条样式中增加文字说明
1)通过自定义层次图形修改长条样式的展示效果。
2)重写ProgressBar的onDraw方法,往进度条上添加指定文本。
自定义进度条(下)
(2). 在圆圈进度中增加文字说明
仿照“ 圆弧进度动画”,先画个背景圆环,再根据进度比例画个前景圆弧,最后在圆心处添加进度文本。
3.异步任务AsyncTask
Android提供了AsyncTask这个轻量级的异步任务工具,内部已经封装好Thread+Handler的线程通信机制。
AsyncTask是一个模板类(AsyncTask
Params:任务启动时的输入参数。
Progress:任务执行过程中的进度。
Result:任务执行完的结果参数
如何使用AsyncTask(上)
开发者自定义的任务类需要实现以下方法。
onPreExecute:准备执行任务时触发。
doInBackground:在后台执行的业务处理。网络请求等异步处理操作都放在该方法中。注意,该方法运 行于分线程,不能直接操作界面。
onProgressUpdate:在doInBackground方法中调用publishProgress方法时触发。该方法通常用于在处理 过程中刷新进度条。
onPostExecute:任务执行完成时触发。
onCancelled :调用任务对象的cancel方法时触发。
如何使用AsyncTask(下)
AsyncTask有如下可直接调用的启停方法。
execute:开始执行异步处理任务。
executeOnExecutor:以指定的线程池模式执行任务。
publishProgress:更新进度。
get:获取处理结果。
cancel:取消任务。
isCancelled:判断该任务是否取消。
getStatus:获取任务状态。
使用AsyncTask的注意事项
AsyncTask在简单场合已经足够使用,如果要用于大量并发处理,就要注意以下两点:
(1)AsyncTask默认的线程池模式是SERIAL_EXECUTOR,即按照先后顺序依次调用。假设有两个网 络请求任务,第一个是文件下载,第二个是接口调用,那么接口调用任务会等待文件下载完毕后执行, 而不是在调用时立刻执行。
(2)由于顺序模式存在排队等待的情况,因此Android提供了executeOnExecutor方法,允许开发者指 定任务线程池。但该线程池的最大线程个数是CPU个数的两倍再加1,所以线程池的处理能力依赖于手 机的CPU数量。
4. 异步服务IntentService
后台服务经常要做一些耗时操作,比如批量处理、文件导入、网络访问等,此时不应该影响用户在界面上的操 作,而应该开启分线程执行耗时操作。可以通过Thread+Handler机制实现异步处理,也可以通过Android封装好的 异步服务IntentService处理。
使用IntentService有两个好处:
(1)免去复杂的消息通信流程;
(2)处理完成后无须手工停止服务,开发者可集中精力进行业务逻辑的编码。
IntentService的实现步骤
SDK源码里的IntentService实现步骤如下:
(1)创建异步服务时,初始化分线程的Handler对象。
(2)异步服务开始运行时,通过Handler对象将请求数据送给分线程。
(3)分线程在Handler对象的handleMessage方法中,先通过onHandleIntent方法执行具体的事务处理,再调 用stopSelf结束指定标识的服务。
使用IntentService的注意事项
(1)增加一个构造方法,并分配内部线程的唯一名称。
(2)onStartCommand方法要调用父类的onStartCommand,因为父类方法会向分线程传递消息。
(3)耗时处理的业务代码要写在onHandleIntent方法中,不可写在onStartCommand方法中。因为 onHandleIntent方法位于分线程,而onStartCommand方法位于主线程。
(4)IntentService实现了onStart方法,却未实现onBind方法,意味着异步服务只能用普通方式启停,不能用 绑定方式启停。
二、HTTP接口访问
介绍HTTP接口访问的相关技术与具体使用,首先说明如何利用连接管理器ConnectivityManager检测网络连接的状态;然后阐述App用于接口调用的移动数据格式JSON的构建与解析;接着举例说明通过HttpURLConnection实现基本的接口调用;最后讲述利用HttpURLConnection从网络获取小图片的方法。
1.网络连接检查
添加网络权限配置后,可利用连接管理器ConnectivityManager检测网络连接。
调用连接管理器对象的getActiveNetworkInfo方法,返回一个NetworkInfo实例,通过该实例可获取详细的网络连接 信息。
下面是NetworkInfo的常用方法。
getType:获取网络类型。
getState:获取网络状态。状态为CONNECTED表示已连接。
getSubtype:获取网络子类型。
网络连接的检查结果
2. 移动数据格式JSON
网络通信的交互数据格式有两大类,分别是JSON和XML 。
对于App来说,基本采用JSON格式与服务器通信。
JSON相比XML的优势主要有两个:
(1)手机流量很贵,表达同样的信息,JSON串比XML串短很多。
(2) JSON串解析得更快,也更省电,XML不但慢而且耗电。
如何解析json串(上)
Android自带JSON解析工具,提供对下列两个对象的解析处理:
1). JSONObject
JSONObject的常用方法如下:
构造函数:从指定字符串构造一个JSONObject对象。
getJSONObject:获取指定名称的JSONObject对象。
getString:获取指定名称的字符串。
getInt:获取指定名称的整型数。
getDouble:获取指定名称的双精度数。
getBoolean:获取指定名称的布尔数。
getJSONArray:获取指定名称的JSONArray数组对象。
put:添加一个JSONObject对象。
如何解析json串(下)
2). JSONArray
JSONArray的常用方法如下:
length:获取JSONArray数组的长度。
getJSONObject:获取JSONArray数组在指定位置的JSONObject对象。
put:往JSONArray数组中添加一个JSONObject对象
3.JSON串与实体类自动转换
json解析除了系统自带的org.json,谷歌公司也提供了一个增强库gson,专门用于json串的自动解析。
由于gson是第三方库,因此首先要修改模块的build.gradle文件,在里面的dependencies节点下添加下 面一行配置,表示导入指定版本的gson库:
implementation "com.google.code.gson:gson:2.8.2"
如何使用gson库
在java源码的文件头部添加如下一行导入语句,表示后面会用到Gson工具类:
import com.google.gson.Gson;
Gson常用的方法有两个:
(1)一个方法名叫toJson,可把数据对象转换为json字符串;
(2)另一个方法名叫fromJson,可将json字符串自动解析为数据对象。
4. HTTP接口调用
HTTP接口调用的代码标准有两个,分别是HttpURLConnection与HttpClient。
移动端的代码标准基本采用更轻量级的HttpURLConnection。
HTTP接口调用主要有GET和POST两种方式:
(1)GET方式只是简单的数据获取操作,类似于数据库的查询操作;
(2)POST方式有提交具体的表单信息,类似于数据库的增、删、改操作。
如何使用HttpURLConnection
HttpURLConnection实例从URL对象的openConnection方法获得。
下面是它的常用方法。
setRequestMethod:设置请求类型。
setConnectTimeout:设置连接的超时时间。
setRequestProperty:设置请求包头的属性信息。
getOutputStream:获取HTTP输出流。在此写入要发送的数据。
connect:建立HTTP连接。
getInputStream:获取HTTP输入流。在此读出接收的数据。
getResponseCode:获取HTTP返回码。
disconnect:断开HTTP连接。
HTTP调用的特殊情况处理
HTTP访问还要处理好几种特殊情况,否则就不会正常工作。常见的特殊情况有两种:
1. URL串中对汉字的转义处理
使用GET方式传递请求数据,参数放在URL中直接传送过去。如果参数值有汉字,就进行 UTF8编码转义处理,比如“你”要转为“%E4%BD%A0”。同理,对于服务器返回的UTF8编码也要 进行反转义。
2. 返回内容为压缩数据时的解压处理
HTTP请求的包头带有Accept-Encoding:gzip,deflate,表示客户端支持gzip压缩。服务器可 能返回gzip压缩的应答数据,此时应答包头中会有Content-Encoding:gzip。此时压缩数据必须先 解压才能正常读取。
HTTP接口调用的实现例子
利用Google Map的开放API,通过HTTP调用传入经纬度的数值,然后对方返回一个JSON格式的地址 信息字符串,通过解析JSON串就能得到具体的地址。
下面是根据经纬度成功获取地址信息的效果图。
5.HTTP图片获取
HttpURLConnection还可用于获取网络小图片。比如验证码图片、头像图标等,这些小图不大,一般也无 须缓存,可直接从网络上获取最新的图片。
通过HttpURLConnection获取图片的关键代码如下,主要是利用BitmapFactory的decodeStream方法得到位 图数据:
// 从HTTP连接获取输入流
InputStream is = conn.getInputStream();
// 对输入流中的数据进行解码,得到位图对象
resp_data.bitmap = BitmapFactory.decodeStream(is);
HTTP图片获取的实现效果
三、上传和下载
介绍App与服务器之间上传文件和下载文件的实现与管理,首先对下载管理器DownloadManager进行详细说明;然后 阐述基于Fragment技术的文件对话框实现;最后介绍通过HttpURLConnection的POST方式如何实现文件的上传操作。
1. 下载管理器DownloadManager
使用HttpURLConnection虽然可以获取小图片,但是这么做有诸多限制,比如:
(1)无法断点续传,一旦中途失败,只能从头开始获取。
(2)只能获取图片,不能获取其他文件。
(3)不是真正意义上的下载操作,没法设置下载参数。
因为下载功能比较常用且业务功能相对统一,所以Android提供了专门的下载工具—— DownloadManager统一管理下载 操作。
如何使用DownloadManager
下载管理器DownloadManager的对象从系统服务
Context.DOWNLOAD_SERVICE中获取,具体使用过程分为3步:
1). 构建下载请求
要想使用下载功能,首先得构建一个下载请求,说明从哪里下载、下载参数是什么、下载的文 件保存到哪里等。
2). 进行下载操作
构建完下载请求才能进行下载的相关操作。
3). 查询下载进度
如果App自身也想了解当前的下载进度,就要调用下载管理器的query方法。该方法返回结果集的Cursor 游标,通过游标即可获得下载信息。
下载服务涉及到的下载事件
系统的下载服务还提供3种下载事件,开发者可通过监听对应的广播消息进行相应的处理。
1). 下载完成事件 在下载完成时,系统会发出名为DownloadManager.ACTION_DOWNLOAD_COMPLETE的广 播。
2). 下载进行时的通知栏点击事件 在下载过程中,只要用户点击通知栏上的下载任务,系统就会发出行为名称是 DownloadManager.ACTION_NOTIFICATION_CLICKED的广播。
3). 下载完成后的通知栏点击事件 在不同时刻点击通知栏上的下载任务会触发不同的事件。
动态展示图片下载进度的效果
2. 文件对话框
下载和上传操作涉及文件的保存和打开,就像电脑上的文件对话框,既可选择文件又可保存文件。然而Android 没有提供现成的文件对话框控件,需要开发者自己实现文件对话框。
这里文件对话框的实现采用了DialogFragment。Fragment拥有如下子类,分别用于不同场合。
DialogFragment:用于对话框的碎片。
ListFragment:用于列表的碎片,目的是取代ListActivity。
PreferenceFragment:用于参数设置页面,目的是取代PreferenceActivity。
WebViewFragment:用于网页视图的碎片。
文件对话框的实现效果
3. 文件上传
文件上传的场合不是很多,通常用于上传用户头像、朋友圈发布图片和视频动态等,而且上传文件需要后端服 务器配合。
Android没有提供专门的文件上传工具,开发者得自己写代码实现上传功能。
简单实现文件上传的话,按照HTTP访问的POST流程,只是要采取multipart/form-data的方式分段传输,并加 入分段传输的边界字符串。
文件上传的实现效果
文件上传的效果如下图所示。倘若上传成功,还应给出服务器对应的文件下载地址,这样才好验证上传成功与否。
四、套接字Socket
介绍套接字Socket的技术手段与具体用途,首先说明如何使用网络地址工具InetAddress判断某个网络地址的连通性,然 后阐述Socket技术在计算机网络中所处的层次、应用方向以及基本用法。
1. 网络地址InetAddress
检查设备自身与某个网络地址的连通性用到了InetAddress工具,这是对网络地址的一个封装。
下面介绍该工具的主要方法说明。
getByName:根据主机IP或主机名称获取InetAddress对象。
getHostAddress:获取主机的IP地址。
getHostName:获取主机的名称。
isReachable:判断该地址是否可到达,即是否连通。
检查网络地址连通性的效果
2. Socket通信
计算机网络有一个大名鼎鼎的TCP/IP协议,TCP/IP是一个协议组,分为3个层次:
网络层:包括IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。
传输层:包括TCP协议和UDP协议。
应用层:包括HTTP、FTP、TELNET、SMTP、DNS等协议。
Socket属于传输层的技术, API实现TCP协议后即可用于HTTP通信,实现UDP协议后即可用于FTP通信,当然也 可以直接在底层进行点对点通信。
如何进行Socket编程(上)
Android的Socket编程主要使用Socket和ServerSocket两个类:
1). Socket
Socket是最常用的工具,客户端和服务端都要用到,描述了两边对套接字(Socket)处理的一般行为。主要方 法如下:
connect:连接指定IP和端口。该方法用于客户端连接服务端。
getInputStream:获取输入流,即自身收到对方发过来的数据。
getOutputStream:获取输入流,即自身向对方发送的数据。
getInetAddress:获取网络地址对象。
isConnected:判断socket是否连上。
isClosed:判断socket是否关闭。
close:关闭socket。
如何进行Socket编程(中)
2). ServerSocket ServerSocket仅用于服务端,在运行时不停地侦听指定端口。主要方法如下:
构造函数:指定侦听哪个端口。
accept:开始接收客户端的连接。有客户端连上时就返回一个Socket对象,若要持续侦听连接,则在循环语句 中调用该函数。
getInetAddress:获取网络地址对象。
isClosed:判断socket服务器是否关闭。
close:关闭socket服务器。
如何进行Socket编程(下)
举个Socket通信的案例,详细步骤说明如下:
(1)首先在客户端与服务端之间建立Socket连接。
(2)然后在Activity中启动Socket连接的线程,等待界面向Socket服务器发送消息,并准备接收消息。
(3)最后启动Socket服务器(其实一开始就要启动,这样App运行时才能马上连上后端服务器)。
Socket通信的实现效果