Android中的网络技术总结
来源:互联网 发布:wap淘宝流量是什么意思 编辑:程序博客网 时间:2024/05/20 15:39
Android中的网络技术总结(大部分内容借用《第一行代码》第二版):
1.使用Http协议访问网络
如果真的说要去深入分析 HTTP 协议,可能需要花费整整一本书的篇幅。这里我当然不会这干,因为毕竟你是跟着我学习 Android 开发的,而不是网站开发。对于 HTTP 协议,你只需要稍微了解一些就足够了,它的工作原理特别的简单,就是客户端向服务器发出一条HTTP请求,服务器收到请求之后会返回一些数据给客户端,然后客户端再对这些数据进行解析和处理就可以了。是不是非常简单?一个浏览器的基本工作原理也就是如此了。比如说上一节中使用到的 WebView 控件,其实也就是我们向百度的服务器发起了一条 HTTP 请求,接着服务器分析出我们想要访问的是百度的首页,于是会把该网页的 HTML 代码进行返回,然后 WebView再调用手机浏览器的内核对返回的 HTML代码进行解析,最终将页面展示出来。
简单来说,WebView已经在后台帮我们处理好了发送HTTP请求、接收服务响应、解析返回数据,以及最终的页面展示这几步工作,不过由于它封装得实在是太好了,反而使得我们不能那么直观地看出 HTTP协议到底是如何工作的。因此,接下来就让我们通过手动发送HTTP请求的方式,来更加深入地理解一下这个过程。
1.1 使用HTTPURLConnection
在过去,Android发送Http请求一般有两种方式:HTTPURLConnection和HttpClient。不过由于HttpClient存在API数量较多,扩展困哪等缺点,Android团队越来越不建议我们使用这种方式。终于在Android6.0系统中,HttpClient的功能被完全移除了,标志着此功能被正式弃用,因此本小节我们就学习一下现在官方建议使用的HTTPURLConnection用法。
首先需要获取到的实例,一般只需new出一个URL对象,并传入目标的网络地址,然后调用一下openConnection()方法即可,如下所示:
URL url = new URL("http://www.baidu.com");HttpURLConnection connection = (HttpURLConnection)url.openConnection();
在得到HttpURLConnection的实例对象之后,我们可以设置一下HTTP请求所使用的方法。常用的方法主要有两个:GET和POST。GET表示希望从服务器那里获取数据。而POST则表示希望提交数据给服务器。写法如下:
connection.setRequestMethod("GET");
接下来就是进行一系列的自由的定制了。比如设置连接超时,读取超时的毫秒数,以及服务器希望得到的一些消息头等。这部分内容根据自己的实际情况进行编写,示例写法如下:
connection.setConnectTimeout(8000);connection.setReadTimeout(8000);
之后再调用getInputStream()方法就可以获取到服务器返回的数据流了,剩下的任务就是对输入流进行读取,如下所示:
InputStream in = connection.getInputStream();
最后可以调用disconnect()方法将这个HTTP请求关闭掉,如下所示:
connection.disconnect();
下面进行一个详细的例子进行学习,如何获取百度首页返回的HTML信息呢?代码如下所示:
class MyThread extends Thread{ @Override public void run() { String html = ""; BufferedReader br = null; HttpURLConnection connection = null; try { URL url = new URL(str);///str为网址URI字符串 connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(8000); connection.setReadTimeout(8000); InputStream in = connection.getInputStream(); br = new BufferedReader(new InputStreamReader(in)); String data = null; while((data = br.readLine())!=null){ html = html + data; } }catch (IOException e){ }finally { if(br != null){ try { br.close(); } catch (IOException e) { e.printStackTrace(); } } if(connection!=null){ connection.disconnect(); } //之后是执行得到服务器发送的数据html的处理逻辑 } } }
由于网络请求是耗时的操作,所以网络请求逻辑应该放在线程当中去执行。
1.2 使用OkHttp
当然我们并不是只能使用HTTPURLConnection,完全没有任何其他选择,事实上开源盛行的今天,有许多出色的网络通信库都可以替代原生的HTTPURLConnection,而其中OkHttp无疑是做得最出色的一个。
OkHttp是由鼎鼎大名的Square()公司开发的,这个公司在开源事业上面贡献良多,除了OkHttp之外,还开发了像Picasso,Retrofit等著名的开源项目。OkHttp不仅在接口封装上做得简单易用,就连在底层实现上也是自成一派,比起原生的HTTPURLConnection,可以说有过之而无不及,现在已经成了广大Android开发者首选的网络通信库。那么本节我们就来学习一下OkHttp的用法,OkHttp的项目主页地址是:https://github.com/square/okhttp。
在使用OkHttp之前,我们需要现在项目中添加OkHttp库的依赖。编辑app/build.gradle文件,在dependencies闭包中添加一下内容:
dependencies { ... compile 'com.squareup.okhttp3:okhttp:3.7.0' ...}
添加以上依赖会自动下载两个库,一个是OkHttp库,一个是Okio库,后者是前者的通信基础,下面我们来看看OkHttp的具体用法,首先需要创建一个OkHttpClient实例对象,如下所示:
OkHttpClient client = new OkHttpClient();
接下来,我们如果想要发起一条HTTP请求,就需要创建一个Request对象:
Request request = new Request.Builder.builder();
当然,上述的代码只是创建一个空的Request对象,并没有什么实际作用,我们可以在最终的build()方法之前连缀很多其他的方法来丰富这个Request对象。比如可以通过url()方法来设置目标的网络地址,如下所示:
Request request = new Request.Builder() .url("https://www.baidu.com") .build();
之后再调用OkHttpClient的newCall()方法创建一个Call对象,并调用它的execute()方法来发送请求并获取服务器返回的数据,如下所示:
Response response = client.newCall(request).execute();
其中Response对象就是服务器返回的数据了,我们可以使用如下写法来得到返回的具体内容:
String responseData = response.body().string();
如果是发起一条POST,请求会比GET请求稍微复杂一点,我们需要先构建出一个RequestBody对象来存放待提交的参数,如下所示:
RequestBody requestBody = new FormBody().Builder() .add("username","admin") .add("password",123456) .build();
然后在Request.Builder中调用一下post()方法,并将ResquestBody对象传入:
Request request = new Request.Builder() .url("https://www.baidu.com") .post(requestBody) .build();
接下来的操作就跟跟一样,调用execute()方法来发送请求并获取服务器返回的数据即可。示例代码如下所示:
class MyThread extends Thread{ @Override public void run() { String html = ""; OkHttpClient okHttpClient = new OkHttpClient(); Request request = new Request.Builder() .url(str)//str为网址URI字符串 .build(); try { Response response = okHttpClient.newCall(request).execute(); html = response.body().string(); } catch (IOException e) { e.printStackTrace(); } Intent intent = new Intent(MainActivity.this,SecondActivity.class); intent.putExtra("html",html); startActivity(intent); }}
2.解析网络数据
2.1 解析XML格式数据
解析XML数据其实有很多种方式,本节我们学习比较常用的两种,Pull解析和SAX解析。Pull解析的步骤如下所示:
Pull方式解析XML数据:
首先,要获取一个XmlPullParseFactory实例对象,并借助这个实例对象得到XmlPullParser对象,然后调用XmlPullParser的setInput()方法将服务器返回的XML数据设置进去就可以开始解析了。解析的过程也非常简单,通过getEventType()方法可以得到当前的解析事件,然后在一个while循环中不断地进行解析,如果当前的解析事件不等于XmlPullParser.END_DOCUMENT,说明解析工作还没完成,调用next()方法得到下一个解析事件。
在while循环中,我们通过getName()得到当前节点的名字,如果发现节点名等于id,name,或version,就调用nextText()方法来获取节点内具体的内容,每当解析完一个app节点后就把所有的数据记录下来。
String XML_DATA = "<apps>\n" + "<app>\n" + "<id>1</id>\n" + "<name>Google Maps</name>\n" + "<version>1.0</version>\n" + "</app>\n" + "<app>\n" + "<id>2</id>\n" + "<name>Chrome</name>\n" + "<version>2.1</version>\n" + "</app>\n" + "<app>\n" + "<id>3</id>\n" + "<name>Google Play</name>\n" + "<version>2.3</version>\n" + "</app>\n" + "</apps>"; String data = ""; try { XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory.newInstance(); XmlPullParser xmlPullParser = xmlPullParserFactory.newPullParser(); xmlPullParser.setInput(new StringReader(XML_DATA)); int eventType = xmlPullParser.getEventType(); String name= ""; String id = ""; String version = ""; while(eventType != XmlPullParser.END_DOCUMENT ){ String nodeName = xmlPullParser.getName(); switch (eventType){ //开始解析某个节点 case XmlPullParser.START_TAG: if("id".equals(nodeName)){ id = xmlPullParser.nextText(); }else if("name".equals(nodeName)){ name = xmlPullParser.nextText(); }else if("version".equals(nodeName)){ version = xmlPullParser.nextText(); } break; case XmlPullParser.END_TAG: if("app".equals(nodeName)) { data = data + "节点id:" + id + ",节点name:" + name + ",节点version" + version + "\n"; } break; default: break; } eventType = xmlPullParser.next(); } } catch (XmlPullParserException|IOException e) { e.printStackTrace(); } //解析成功的数据全部存放于data中了
使用SAX方式解析XML数据
Pull解析方式虽然好用,但是它不是唯一的选择,SAX解析也是一种非常好用的XML格式数据解析
方式,虽然它比Pull解析方式要复杂一些,但在语义方面更加清楚。
通常情况下,我们都会去创建一个类去继承DefaultHandler,并重写父类的5个方法,如下所示:
public class MyHandler extends DefaultHandler { @Override public void startDocument() throws SAXException { super.startDocument(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { super.startElement(uri, localName, qName, attributes); } @Override public void characters(char[] ch, int start, int length) throws SAXException { super.characters(ch, start, length); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { } @Override public void endDocument() throws SAXException { super.endDocument(); }}
这五个方法一看就很清楚吧?startDocument()方法会在开始 XML 解析的时候调用,startElement()方法会在开始解析某个结点的时候调用,characters()方法会在获取结点中内容的时候调用,endElement()方法会在完成解析某个结点的时候调用,endDocument()方法会在完成整个 XML 解析的时候调用。其中,startElement()、characters()和 endElement()这三个方法是有参数的,从 XML 中解析出的数据就会以参数的形式传入到这些方法中。需要注意的是,在获取结点中的内容时,characters()方法可能会被调用多次,一些换行符也被当作内容解析出来,我们需要针对这种情况在代码中做好控制。
SAX解析需要的DefaultHandler子类:
public class MyHandler extends DefaultHandler { private Context mContext; private String nodeName = "";//记录当前节点名 private StringBuilder id ; private StringBuilder name ; private StringBuilder version; private String data = ""; MyHandler(Context context){ mContext = context; id = new StringBuilder(); name = new StringBuilder(); version = new StringBuilder(); } @Override public void startDocument() throws SAXException { //刚开始解析的时候调用 super.startDocument(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { super.startElement(uri, localName, qName, attributes); //开始解析某个节点的时候调用 nodeName = localName;//记录当前的节点 } @Override public void characters(char[] ch, int start, int length) throws SAXException { super.characters(ch, start, length); if("id".equals(nodeName)){ id .append(ch, start, length); }else if("name".equals(nodeName)){ name.append(ch, start, length); }else if("version".equals(nodeName)){ version.append(ch, start, length); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { //结束当前节点的时候调用 if(localName.equals("app")){ data = data + "节点id:" + id.toString().trim() + ",节点name:" + name.toString().trim() + ",节点version" + version.toString().trim() + "\n"; //清空id,name,version id.setLength(0); name.setLength(0); version.setLength(0); } } @Override public void endDocument() throws SAXException { super.endDocument(); Intent intent = new Intent(mContext,SecondActivity.class); intent.putExtra("html",data); mContext.startActivity(intent); }}
解析时的代码:
String XML_DATA = "<apps>\n" + "<app>\n" + "<id>1</id>\n" + "<name>Google Maps</name>\n" + "<version>1.0</version>\n" + "</app>\n" + "<app>\n" + "<id>2</id>\n" + "<name>Chrome</name>\n" + "<version>2.1</version>\n" + "</app>\n" + "<app>\n" + "<id>3</id>\n" + "<name>Google Play</name>\n" + "<version>2.3</version>\n" + "</app>\n" + "</apps>"; try { SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); XMLReader xmlReader = saxParserFactory.newSAXParser().getXMLReader(); MyHandler myHandler = new MyHandler(MainActivity.this); xmlReader.setContentHandler(myHandler); xmlReader.parse(new InputSource(new StringReader(XML_DATA))); }catch (SAXException|ParserConfigurationException e){ } catch (IOException e) { e.printStackTrace(); }
2.2 解析JSON格式数据
现在你已经掌握了XML格式数据的解析,那么接下来,我们再来学习一下JSON数据格式的解析,与XML格式数据相比,JSON格式数据的主要优势在于它的体积更小,在网络传输的时候可以更省流量,但缺点在于,它的语义性比较差,看起来不如XML格式数据直观。JSON格式数据解析的常用的解析方式由JSONObject(官方提供)和GSON(谷歌开源库)两种,当然还有些其他诸如:Jackson,FastJSON也不错,下面我们来学习一下JSONObject(官方提供)和GSON(谷歌开源库)这两种解析方式吧!
JSONObject方式解析JSON格式数据:
String JSON_DATA = "[{\"id\":\"5\",\"version\":\"5.5\",\"name\":\"Angry Birds\"},\n" + "{\"id\":\"6\",\"version\":\"7.0\",\"name\":\"Clash of Clans\"},\n" + "{\"id\":\"7\",\"version\":\"3.5\",\"name\":\"Hey Day\"}]"; String data = ""; try { JSONArray jsonArray = new JSONArray(JSON_DATA);把JSON格式数据交给JSONArray进行解析 for(int i=0;i<jsonArray.length();i++){ //通过JSONArray的getJSONObject()方法获取具体的JSONObject,可以说一个JSONObject就是一条数据 JSONObject jsonObject = jsonArray.getJSONObject(i); //解析每一条数据的具体属性 String id = jsonObject.getString("id"); String name = jsonObject.getString("name"); String version = jsonObject.getString("version"); data = data + "节点id:" + id + ",节点name:" + name + ",节点version" + version + "\n"; } }catch (Exception e){ } Intent intent = new Intent(MainActivity.this,SecondActivity.class); intent.putExtra("html",data); startActivity(intent);
使用GSON方式解析JSON格式数据:
如果你认为JSONObject来解析JSON数据已经非常简单了,那你就太容易满足了。谷歌提供的GSON开源库可以让你对JSON格式数据的解析简单到你不敢想象的地步,那我们是肯定不能错过这次学习的机会。
首先第一步就是在项目的依赖库中添加GSON的依赖库:
dependencies { ... compile 'com.google.code.gson:gson:2.8.0' ...}
那么GSON库究竟神奇在哪里?其实它主要就是可以把一段JSON格式数据的字符串自动映射成一个对象,从而不需要我们再手动去编写代码进行解析了。
比如说一段 JSON 格式的数据如下所示:
{"name":"Tom","age":20}
那我们就可以定义一个 Person类,并加入 name 和 age 这两个字段,然后只需简单地调
用如下代码就可以将 JSON 数据自动解析成一个 Person 对象了:
Gson gson = new Gson();Person person = gson.fromJson(jsonData, Person.class);
如果需要解析的是一段 JSON数组会稍微麻烦一点,我们需要借助 TypeToken 将期望解
析成的数据类型传入到 fromJson()方法中,如下所示:
List<Person> people = gson.fromJson(jsonData, new TypeToken<List<Person>>(){}.getType());
好了,基本的用法就是这样,下面就让我们来真正地尝试一下吧。首先新增一个App类,并加入 id、name 和 version 这三个字段,有些复杂的JSON数据格式手动封装一个类出来是相当耗时又耗力的,这里使用一个Android Studio中带的一个工具就可以把JSON数据格式自动解析成一个封装类,如何安装和使用这个工具,看以下链接:
http://www.cnblogs.com/tianmanyi/p/6028624.html
通过此工具,我们成功得到一个与我们要解析的JSON格式数据对应的App类,其代码如下所示:
public class App { /** * id : 5 * version : 5.5 * name : Angry Birds */ private String id; private String version; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getName() { return name; } public void setName(String name) { this.name = name; }}
接下来就是进行GSON解析的代码的编写了:
String JSON_DATA = "[{\"id\":\"5\",\"version\":\"5.5\",\"name\":\"Angry Birds\"},\n" + "{\"id\":\"6\",\"version\":\"7.0\",\"name\":\"Clash of Clans\"},\n" + "{\"id\":\"7\",\"version\":\"3.5\",\"name\":\"Hey Day\"}]"; String data = ""; Gson gson = new Gson(); List<App> appList = gson.fromJson(JSON_DATA,new TypeToken<List<App>>(){}.getType()); for(App app:appList){ data = data + "节点id:" + app.getId() + ",节点name:" + app.getName() + ",节点version" + app.getVersion() + "\n"; } Intent intent = new Intent(MainActivity.this,SecondActivity.class); intent.putExtra("html",data); startActivity(intent);
3.WebView的用法
有时候我们可能碰到一些比较特殊的需求,比如说要求在应用程序中显示一些网页。相信每个人都知道,加载和显示网页通常都是浏览器的任务,但是需求里又明确指出,不允许打开系统浏览器,而我们当然也不可能自己去编写一个浏览器出来,这时应该怎么办呢?
不用担心,Android早就已经考虑了这种需求,并提供了一个WebView控件,借助它我们就可以在自己的应用程序里嵌入一个浏览器,从而非常轻松的展示各种各样的网页。
WebView的用法也是非常的简单,下面我们通过一个例子来学习一下吧!比如:让我们的应用显示百度首页搜索引擎的例子:
布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <WebView android:id="@+id/web_view" android:layout_width="match_parent" android:layout_height="match_parent" /></LinearLayout>
Java代码部分:
public class MainActivity extends AppCompatActivity { private WebView webView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); webView = (WebView) findViewById(R.id.web_view); webView.getSettings().setJavaScriptEnabled(true);//让WebView支持JavaScript脚本 webView.setWebViewClient(new WebViewClient());//这段代码的作用是,当需要从一个网页跳转到另一个网页时,我们希望目标网页仍然在当前WebView中显示,而不是打开系统浏览器。 webView.loadUrl("https://www.baidu.com/");//展示百度首页 } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if(event.getKeyCode() == KeyEvent.KEYCODE_BACK ){ if(webView.getUrl().equals("https://www.baidu.com/")){ finish(); }else { webView.loadUrl("https://www.baidu.com/"); } } return true; }}
最后别忘记了添加网络访问的权限:< uses-permission android:name=”android.permission.INTERNET” />
- Android中的网络技术总结
- Android中的网络技术基础用法
- 网络技术总结
- 网络技术总结
- Android网络技术
- Android网络技术
- Android网络技术
- android网络技术
- Android网络技术
- android网络技术
- Android网络技术
- 计算机三级--网络技术总结
- 下一代网络技术思考题总结
- 三级网络技术总结
- 网络技术知识总结
- 三级网络技术学习总结
- Android笔记三(网络技术)
- Android网络技术之HttpURLConnection
- CommonJS与AMD的规范和兼容
- VTK学习-第一个多柱体生成
- Java基础-方法区以及static的内存分配图
- 数据库基本类型知识
- Hbase学习
- Android中的网络技术总结
- Notification 的 TransactionTooLargeException的问题
- (思考)知识整理方法
- CIO和CTO
- Android Studio导入Eclipse项目的两种方法
- js正则表达式
- LRU算法
- Java程序设计学习知识点总结
- 382. 三角形计数 [LintCode]