连接Internet资源
首先需要在应用程序清单中添加一个INTERNET uses-permission节点。
<uses-permission android:name="android.permission.INTERNET"/>
打开Internet数据流
String myFeed = getString(R.String.myFeed); URL url = new URL(quakeFeed); URLConnection connection = url.openConnection(); HttpURLConnection httpConnection = (HttpURLConnection) connection; int responsed = httpConnection.getResponseCode(); if (responsed == HttpURLConnection.HTTP_OK) { //DoSomthing... }
尝试在主UI线程上执行网路操作会引发NetWorkOnMainThreadException异常。所以,一定要在后台线程中执行。
分析XML
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); //装载内容流 Document dom = db.parse(in); //获取根节点 Element docEle = dom.getDocumentElement(); //遍历子节点 NodeList nl = docEle.getElementsByTagName("entry"); if (nl != null && nl.getLength() > 0) { for (int i = 0; i < nl.getLength(); i++) { Element entry = (Element) nl.item(i); Element title = (Element) entry.getElementsByTagName("title").item(0); Element g = (Element) entry.getElementsByTagName("georess:point").item(0); Element when = (Element) entry.getElementsByTagName("updated").item(0); Element link = (Element) entry.getElementsByTagName("link").item(0); String point = g.getFirstChild().getNodeValue(); String dt = when.getFirstChild().getNodeValue(); } }
使用DocumentBuilder来装载内容流至Document对象。
使用getDocumentElement方法获取Document对象上的文档根节点。
通过getElementsByTagName获取指定名称的节点集合。
遍历节点时通过.item(index)、getElementsByTagName.item(index)、getFirstChild()等方法获取子节点。
Example:从http://earthquake.usgs.gov/读取当天全球地震数据
创建一个地震数据对象,用于封装地震数据
package com.example.guqiang.earthquake.Models; import android.location.Location; import java.text.SimpleDateFormat; import java.util.Date; /** * Created by GuQiang on 2016/9/29. * 地震数据对象 */ public class Quake { private Date date; public String getLink() { return link; } public Quake(String link, Date date, String details, Location location, double magnitude) { this.link = link; this.date = date; this.details = details; this.location = location; this.magnitude = magnitude; } @Override public String toString() { SimpleDateFormat sdf=new SimpleDateFormat("HH:MM"); String dateString=sdf.format(this.date); return dateString+": "+magnitude+" "+details; } public Date getDate() { return date; } public String getDetails() { return details; } public Location getLocation() { return location; } public double getMagnitude() { return magnitude; } private String details; private Location location; private double magnitude; private String link; }
创建一个派生自ListFragment的Fragment,用来在View显示时开启后台线程读取数据、解析XML、填充数据源、显示数据。
package com.example.guqiang.earthquake.Layouts; import android.app.ListFragment; import android.location.Location; import android.os.Bundle; import android.util.Log; import android.widget.ArrayAdapter; import com.example.guqiang.earthquake.Models.Quake; import com.example.guqiang.earthquake.R; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.GregorianCalendar; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; /** * Created by GuQiang on 2016/9/29. * 用于显示地震列表 */ public class EarthquakeListFragment extends ListFragment { ArrayAdapter<Quake> quakesAdapter; ArrayList<Quake> earthquakes = new ArrayList<>(); @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); int layoutId = android.R.layout.simple_list_item_1; //数据源 quakesAdapter = new ArrayAdapter<Quake>(getActivity(), layoutId, earthquakes); //设定列表于数据源的绑定 this.setListAdapter(quakesAdapter); //在后台线程中执行网络操作,如果在主UI线程中执行会引发NetworkOnMainThreadException Thread t = new Thread(new Runnable() { @Override public void run() { refreshEarthquakes(); } }); t.start(); } private static final String TAG = "EARTHQUAKE"; private android.os.Handler handler = new android.os.Handler(); public void refreshEarthquakes() { URL url; try { //创建获取当天数据的URI String quakeFeed = getString(R.string.quake_feed); SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd"); String today = sDateFormat.format(new Date()); quakeFeed = quakeFeed + "&starttime=" + today; url = new URL(quakeFeed); //获取数据 URLConnection connection = url.openConnection(); HttpURLConnection httpConnection = (HttpURLConnection) connection; int responsed = httpConnection.getResponseCode(); if (responsed == HttpURLConnection.HTTP_OK) { //获取返回数据流 InputStream in = httpConnection.getInputStream(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); //分析地震源 Document dom = db.parse(in); Element docEle = dom.getDocumentElement(); //清除旧的地震数据 earthquakes.clear(); //获得每个地震项的列表 NodeList nl = docEle.getElementsByTagName("event"); if (nl != null && nl.getLength() > 0) { for (int i = 0; i < nl.getLength(); i++) { //只解析了发生时间和名称 Element entry = (Element) nl.item(i); Element title = (Element) entry.getElementsByTagName("description").item(0); Element when = (Element) entry.getElementsByTagName("origin").item(0); String details = title.getLastChild().getFirstChild().getNodeValue(); String dt = when.getFirstChild().getFirstChild().getFirstChild().getNodeValue(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd‘T‘hh:mm:ss.SSS‘Z‘"); Date qdate = new GregorianCalendar(0, 0, 0).getTime(); try { qdate = sdf.parse(dt); } catch (ParseException e) { Log.d(TAG, "Date parsing exception." + e); } Location l = new Location("dumpGPS"); final Quake quake = new Quake("", qdate, details, l, 0d); //处理一个新发现的地震 handler.post(new Runnable() { @Override public void run() { addNewQuake(quake); } }); } } } } catch (MalformedURLException e) { Log.d(TAG, "MalformedURLException"); } catch (IOException e) { Log.d(TAG, "IOException"); } catch (ParserConfigurationException e) { Log.d(TAG, "Parser Configuration Exception"); } catch (SAXException e) { Log.d(TAG, "SAXException"); } finally { } } private void addNewQuake(Quake quake) { //将新地震源添加到地震列表中 earthquakes.add(quake); //向Array Adapter通知改变 quakesAdapter.notifyDataSetChanged(); } }
主界面中包含刚刚创建的 EarthquakeListFragment
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <fragment android:layout_width="match_parent" android:layout_height="match_parent" android:name="com.example.guqiang.earthquake.Layouts.EarthquakeListFragment" android:id="@+id/EarthquakeListFragment" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" /> </LinearLayout>
Manifest文件中设定程序需要网络权限。
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.guqiang.earthquake"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-permission android:name="android.permission.INTERNET"/> </manifest>
运行效果如下:
下载文件
使用Download Manager服务。Download Manager通过处理HTTP连接、监控连接的变化和系统重新启动来确保每一次下载都能成功完成。
使用getSystemService方法请求DOWNLOAD_SERVICE。
String serviceString= Context.DOWNLOAD_SERVICE; DownloadManager dm=(DownloadManager)getSystemService(serviceString);
getSystemService方法基于context
使用Download Manager下载文件
Uri uri= Uri.parse("XXXX.zip"); DownloadManager.Request request=new DownloadManager.Request(uri); long reference=dm.enqueue(request);
在Request对象中,可以通过调用方法addRequestHeader()和setMimeType()给请求添加HTTP报头或者重写服务器返回的MIME类型。
也可以给某个下载指定连接条件。setAllowedNetworkTypes方法限制下载类型为Wi-Fi或者移动网络。在手机漫游时,setAllowOverRoaming方法可以有预见性的阻止下载。
下面的代码片段展示了如何确保只有连接到Wi-Fi时才进行大文件的下载。
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
在Android API Level 11中引入了一个便利的方法getRecommendedMaxBytesOverMobile,它会通过返回一个在移动数据连接上传输时推荐的最大字节数来确认是否应该限制下载为Wi-Fi。
想要在下载完成后接收通知,就需要注册一个Receiver来接收ACTION_DOWNLOAD_COMPLETE广播。
监控下载的完成
BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { long reference = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); if(myDownRef==reference){ //对指定的下载文件进行操作 } } };
通过调用Download Manager的openDownloadedFile方法,可以获得文件的Parcel File Descriptor,查询Download Manager来获得文件的位置。
指定下载位置
默认情况下,Download Manager会把下载的文件保存到共享下载缓存中,而且使用系统生成的文件名。
可以在Request对象中调用setDestinationUri方法,指定存储路径。
DownloadManager.Request request = new DownloadManager.Request(uri); request.setDestinationUri(uri);
也可以使用 setDestinationInExternalFilesDir 指定使用外部存储文件夹中存储
request.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS,"AAA");
取消和删除下载
downloadManager.remove(REFERENCE_ID_1,REFERENCE_ID_2);
如果下载被取消,它所关联的文件-未完全下载的和完全下载的-都会被删除。