Android--(15),networking

2013/2/22

lbs定位地图显示不了,只好先搞networking了


一,用http来获得网络服务(Consuming web service using http)

首先要往应用的AndroidManifest中添加网络许可:
<uses-permissionandroid:name=”android.permission.INTERNET”/>
然后用以下代码:

private InputStream OpenHttpConnection(String urlString) throws IOException
{
InputStream in = null;
intresponse = -1;
URL url = new URL(urlString); 
URLConnection conn = url.openConnection();
if(!(conn instanceof HttpURLConnection)) 
throw new IOException(“Not an HTTP connection”); 
try{
HttpURLConnection httpConn = (HttpURLConnection) conn;
httpConn.setAllowUserInteraction(false);
httpConn.setInstanceFollowRedirects(true);
httpConn.setRequestMethod(“GET”);
httpConn.connect();
response = httpConn.getResponseCode(); 
if(response == HttpURLConnection.HTTP_OK) {
in = httpConn.getInputStream(); 
} 
}
catch(Exception ex)
{
Log.d(“Networking”, ex.getLocalizedMessage());
throw new IOException(“Error connecting”); 
}
return in; 
}
我们实现了一个OpenHttpConnection方法,方法接收一个urlString来指定url地址,然后返回一个InputStream对象,可以让用户一个byte一个byte的读取网页数据。我们用HttpURLConnection对象来打开连接,然后设置HttpURLConnection对象的相关属性。最后,如果连接建立了(返回HTTP_ok),然后我们就可以从HttpURLConnection对象中获取InputStream。返回给用户。

1.1下载二进制数据。

我们可以从web中下载二进制数据,譬如显示一张图片(Bitmap格式):
private Bitmap DownloadImage(String URL)
	{
		Bitmap bitmap=null;
		InputStream in=null;
		try
		{
			in=OpenHttpConnection(URL);
			bitmap=BitmapFactory.decodeStream(in);
			in.close();
		}catch (IOException e1)
		{
			Log.d("NewWorking", e1.getLocalizedMessage());
		}
		return bitmap;
	}

上面的函数调用了上一个例子中的OpenHttpConnection函数,让InputStream对象in可以在指定的URl中读取二进制数据。然后用来之BitmapFactory类的decodeStream方法从web下载二进制数据到bitmap上面。
最后显示出来(可以用imageView控件)。
img= (ImageView) findViewById(R.id.img);
img.setImageBitmap(bitmap);
用ImageView的setImageBitmap方法就可以。但是,因为只用上面downloadImage的方法是同步的,意思是如果bitmap还没下载好的话,UI会卡住。在安卓3.0之后,所有的同步方法都要出现在一个异步类里面,用异步类就可以就可以用与Ui分开的线程来在后台执行任务,完成后直接返回结果。于是用了如下代码:
private class DownloadImageTask extends AsyncTask<String,Void,Bitmap>
	{
		protected Bitmap doInBackground(String... urls)
		{
			return DownloadImage(urls[0]);
		}
		protected void onPostExecute(Bitmap result)
		{
			ImageView img=(ImageView)findViewById(R.id.img);
			img.setImageBitmap(result);
		}
	}

DownloadImageTask类实现了异步处理下载数据,其中有两个方法:doInBackground和onPostExecute,我们把要执行的方法放在doInBackground,当任务执行完了之后,结果就会当成result返回给onPostExecute,然后我们再在里面处理返回的结果即可(这里为Bitmap)。最后我们只要执行它的Execute方法即可:

new DownloadImageTask().execute("http://www.mayoff.com/5-01cablecarDCP01934.jpg");

当然也可以一次下载多个图片:
import android.widget.Toast;
…
private class DownloadImageTask extends AsyncTask 
<String, Bitmap, Long> {
//---takes in a list of image URLs in String type---
protected Long doInBackground(String... urls) {
long imagesCount = 0;
for(inti = 0; i < urls.length; i++) { 
//---download the image---
Bitmap imageDownloaded = DownloadImage(urls[i]);
if(imageDownloaded != null) {
//---increment the image count---
imagesCount++;
try{
//---insert a delay of 3 seconds---
Thread.sleep(3000);
} catch(InterruptedException e) { 
e.printStackTrace();
}
//---return the image downloaded---
publishProgress(imageDownloaded); 
}
}
//---return the total images downloaded count---
return imagesCount;
}
//---display the image downloaded---
protected void onProgressUpdate(Bitmap... bitmap) { 
img.setImageBitmap(bitmap[0]); 
}
//---when all the images have been downloaded---
protected void onPostExecute(Long imagesDownloaded) {
Toast.makeText(getBaseContext(), 
“Total “+ imagesDownloaded + “ images downloaded”,
Toast.LENGTH_LONG).show();
}
}
注意这里的DownloadImageTask接受了多个url,并且顺序下载图片:这里更实现了多一个方法--onProgressUpdate,原因是当下载多个图片的时候,我们打算每次下载好一幅图片都更新一次ImageView对象,在doInBackground中可以用publishProgress来将阶段性的结果(这里是一副bitmap)交给onProgressUpdate,然后进行阶段性的更新。

1.2下载文本信息


--当然也可以下载文本信息啦,为了实现任务,我们定义了一个DownloadText的方法:
private String DownloadText(String URL)
	{
		int BUFFER_SIZE=200;
		InputStream in=null;
		try
		{
			in=OpenHttpConnection(URL);
			
		}catch(IOException e)
		{
			Log.d("Networking", e.getLocalizedMessage());
			
		}
		InputStreamReader isr=new InputStreamReader(in);
		int charRead;
		String str="";
		char [] inputBuffer=new char[BUFFER_SIZE];
		try
		{
			while((charRead=isr.read(inputBuffer))>0)
			{
				String readString=String.copyValueOf(inputBuffer, 0, charRead);
				str+=readString;
				inputBuffer=new char[BUFFER_SIZE];
			}
		in.close();
		}catch(IOException e)
		{
			Log.d("Networking", e.getLocalizedMessage());
			return "";
		}
		return str;
	}

同样,这个方法也是使用OpenHttpConnection方法打开一个web连接,返回一个InputStream对象。

二,用GET方法来获得网络服务。


2.1以Xml格式获取网络服务。

接下来我们看下如何使用网络服务--用户提供信息,然后从网络获得结果的xml文件,最后从中提取有用的信息。
例如:
如果输入:
http://services.aonaware.com/DictService/DictService.asmx/Define?word=”+ 你想要查的单词
那这个网站就会返回:
<?xml version=”1.0” encoding=”utf-8”?>
<WordDefinition xmlns=”http://services.aonaware.com/webservices/”>
<Word>string</Word>
<Definitions>
<Definition>
<Word>string</Word>
<Dictionary>
<Id>string</Id>
<Name>string</Name>
</Dictionary>
<WordDefinition>string</WordDefinition>
</Definition>
<Definition>
<Word>string</Word>
<Dictionary>
<Id>string</Id>
<Name>string</Name>
</Dictionary>
<WordDefinition>string</WordDefinition>
</Definition>
</Definitions>
</WordDefinition>

其中<Definition>标签里面的内容就是查询单词的解释,我们编程的把它弄出来。观察如下代码:
private String WordDefinition(String word)
	{
		InputStream in=null;
		String strDefinition="";
		try
		{
			in=OpenHttpConnection("http://services.aonaware.com/DictService/DictService.asmx/Define?word="+word);
			Document doc=null;
			DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
			DocumentBuilder db;
			try
			{
				db=dbf.newDocumentBuilder();
				doc=db.parse(in);
				
			}catch(ParserConfigurationException e)
			{
				e.printStackTrace();
			} catch(Exception e) {
				// TODOAuto-generated catch block
				e.printStackTrace();
			}
			doc.getDocumentElement().normalize();
			NodeList definitionElements=doc.getElementsByTagName("Definitions");
			for(int i=0;i<definitionElements.getLength();i++)
			{
				Node itemNode=definitionElements.item(i);
				if(itemNode.getNodeType()==Node.ELEMENT_NODE)
				{
					Element definitionElement=(Element)itemNode;
					NodeList wordDefinitionElements=(definitionElement).getElementsByTagName("WordDefinition");
					strDefinition="";
					for(int j=0;j<wordDefinitionElements.getLength();j++)
					{
						Element wordDefinitionElement=
								(Element)wordDefinitionElements.item(j);
						NodeList textNodes=
								((Node)wordDefinitionElement).getChildNodes();
						strDefinition+=((Node)textNodes.item(0)).getNodeValue()+".\n";
						
					}
				}
			}
		}catch(IOException e1)
		{
			Log.d("networking", e1.getLocalizedMessage());
			
		}
		return strDefinition;
	}

1.首先我们打开了一个连接,表示要查询的单词是apple。
in=OpenHttpConnection("http://services.aonaware.com/DictService/DictService.asmx/Define?word="+word);
2.因为查询的结果返回的是一个xml文件,我们创建了一个Document对象(用DocumentBuilderFactory和 DocumentBuilder类),来提取里面的信息:
Document doc = null;
DocumentBuilderFactory dbf = 
DocumentBuilderFactory.newInstance();
DocumentBuilder db; 
try{
db = dbf.new DocumentBuilder();
doc = db.parse(in);
} catch(ParserConfigurationException e) {
// TODOAuto-generated catch block
e.printStackTrace();
} catch(Exception e) {
// TODOAuto-generated catch block
e.printStackTrace();
} 
doc.getDocumentElement().normalize(); 

返回的doc对象包含的就是通过服务返回的xml文件了。
3.最后我们用getElementByTagName(),通过提供标签的名字来获取其里面的内容:
NodeList definitionElements=doc.getElementsByTagName("Definitions");
4.最后用二重循环把所有的wordDefinition标签的内容提取出来:
for(int i=0;i<definitionElements.getLength();i++)
			{
				Node itemNode=definitionElements.item(i);
				if(itemNode.getNodeType()==Node.ELEMENT_NODE)
				{
					Element definitionElement=(Element)itemNode;
					NodeList wordDefinitionElements=(definitionElement).getElementsByTagName("WordDefinition");
					strDefinition="";
					for(int j=0;j<wordDefinitionElements.getLength();j++)
					{
						Element wordDefinitionElement=
								(Element)wordDefinitionElements.item(j);
						NodeList textNodes=
								((Node)wordDefinitionElement).getChildNodes();
						strDefinition+=((Node)textNodes.item(0)).getNodeValue()+".\n";
						
					}
				}
			}
由于不止一个defintion标签,而每个definition标签里面又不止一个wordDefinition对象。外层循环先遍历所有的definition标签,然后在里面寻找wordDefintion标签,最后用getNodeValue方法来获得内容。拼接成一个完成的wordDefinition。

2.2以json(javascript object notation)形式获取服务。

--xml文件有时候会变的很大,让移动设备的cpu和内存都难以处理,故可以采用json格式来获取网络服务返回的信息。
json的形式如下:

[
{
“appeId”:”1”,
“survId”:”1”,
“location”:””,
“surveyDate”:”2008-03 14”,
“surveyTime”:”12:19:47”,
“inputUserId”:”1”,
“inputTime”:”2008-03-14 12:21:51”,
“modifyTime”:”0000-00-00 00:00:00”
},
{
“appeId”:”2”,
“survId”:”32”,
“location”:””,
“surveyDate”:”2008-03-14”,
“surveyTime”:”22:43:09”,
“inputUserId”:”32”,
“inputTime”:”2008-03-14 22:43:37”,
“modifyTime”:”0000-00-00 00:00:00”
},
{
“appeId”:”3”,
“survId”:”32”,
“location”:””,
“surveyDate”:”2008-03-15”,
“surveyTime”:”07:59:33”,
“inputUserId”:”32”,
“inputTime”:”2008-03-15 08:00:44”,
“modifyTime”:”0000-00-00 00:00:00”
},
{
“appeId”:”4”,
“survId”:”1”,
“location”:””,
“surveyDate”:”2008-03-15”,
“surveyTime”:”10:45:42”,
“inputUserId”:”1”,
“inputTime”:”2008-03-15 10:46:04”,
“modifyTime”:”0000-00-00 00:00:00”
},
{
“appeId”:”5”,
“survId”:”32”,
“location”:””,
“surveyDate”:”2008-03-16”,
“surveyTime”:”08:04:49”,
“inputUserId”:”32”,
“inputTime”:”2008-03-16 08:05:26”,
“modifyTime”:”0000-00-00 00:00:00”
},
{
“appeId”:”6”,
“survId”:”32”,
“location”:””,
“surveyDate”:”2008-03-20”,
“surveyTime”:”20:19:01”,
“inputUserId”:”32”,
“inputTime”:”2008-03-20 20:19:32”,
“modifyTime”:”0000-00-00 00:00:00”
}
]
注意每一对的数据都是由key/value的形式组成,而且每个数据都被分在不同的组中。和xml不同的是,这里没有标签名。我们就可以直接从中提取需要的数据。
首先要定义一个从指定url中获取json对象的方法:
	public String readJSONFeed(String URL)
	{
		StringBuilder stringBuilder=new StringBuilder();
		HttpClient client=new DefaultHttpClient();
		HttpGet httpGet=new HttpGet(URL);
		try
		{
			HttpResponse response=client.execute(httpGet);
			StatusLine statusLine=response.getStatusLine();
			int statusCode=statusLine.getStatusCode();
			if(statusCode==200)
			{
				HttpEntity entity=response.getEntity();
				InputStream content=entity.getContent();
				BufferedReader reader=new BufferedReader
						(new InputStreamReader(content));
				String line;
				while((line=reader.readLine())!=null)
				{
					stringBuilder.append(line);
				}
			}else{Log.e("JSON", "Failed");}
		}catch(ClientProtocolException e)
		{
			e.printStackTrace();
		}catch(IOException e)
		{
			e.printStackTrace();
		}
		
		return stringBuilder.toString();
	}

最后返回的就是上面的json对象。之后就可以对其进行提取。
跟上面的例子一样,新建一个异步的方法来进行信息的获取:

private class ReadJSONFeedTask extends AsyncTask<String,Void,String>
	{
		protected String doInBackground(String... urls)
		{
			return readJSONFeed(urls[0]);
			
		}
		protected void onPostExecute(String result)
		{
			Toast.makeText(getBaseContext(), result, Toast.LENGTH_SHORT).show();
			try{
				JSONArray jsonArray=new JSONArray(result);
				Log.i("JSON", "Number of surveys in feed :"+jsonArray.length());
				for(int i=0; i<jsonArray.length();i++)
				{
					JSONObject jsonObject=jsonArray.getJSONObject(i);
					Toast.makeText(getBaseContext(), jsonObject.getString("appeId")+" - "+jsonObject.getString("inputTime"), Toast.LENGTH_SHORT).show();
					
				}
			}catch(Exception e)
			{
				e.printStackTrace();
			}
		}
	}
readJsonFeed方法首先被执行,完成后结果返回给onPostExecute中的result处。再由onPostExecute提取信息。
由于result里面的是包含信息的字符串,可以用
JSONArray jsonArray=new JSONArray(result);
来将其转换成JSONArray对象。注意这里获取到时每一个block里面的所有信息(也就是上面由大括号分组的每组信息),由于我们只需要提取appeId和inputTime信息,于是再返回的每组信息的JSONObject对象里面,再可以使用getString方法提供对应的key来获取特定的value,最后显示出来。
for(int i=0; i<jsonArray.length();i++)
				{
					JSONObject jsonObject=jsonArray.getJSONObject(i);
					Toast.makeText(getBaseContext(), jsonObject.getString("appeId")+" - "+jsonObject.getString("inputTime"), Toast.LENGTH_SHORT).show();
					
				}

三,sockets编程


--用http服务虽然比较方便,但是每个http连接都会被当成新的连接,有时候会造成不必要的带宽浪费。我们可以用sockets技术,避免这种资源浪费。


1.首先创建一个CommsThread的类,继承Thread类,于是这个类就可以在另外的线程里面工作,而不至于使UI线程卡住。
public class CommsThread extendsThread {
}
2.然后在这个类里面定义三个对象:
private final Socket socket;
private final InputStream inputStream;
private final OutputStream outputStream;
这三个对象分别是:1.socket对象提供用户方的tcp socket。2.InputStream 从socket处读取消息 。 3.outputStream 对象把消息通过socket发送出去。
3.CommsThread的构造方法,输入一个socket对象,然后将它和InputStream和outputStream绑定起来。
public CommsThread(Socket sock) {
socket= sock;
InputStream tmpIn = null;
OutputStream tmpOut = null; 
try{
//---creates the inputstream and outputstream objects
// for reading and writing through the sockets---                                                                                                   tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch(IOException e) {
Log.d(“SocketChat”, e.getLocalizedMessage());
} 
inputStream= tmpIn;
outputStream= tmpOut;
}
4.在这个类里面的run方法(在调用thread类的start方法后执行)一直检查InputStream是否收到消息,当收到了新的消息,就把最新消息更新到ui界面上。
public void run()
	{
		byte[] buffer=new byte[1024];
		int bytes;
		while(true)
		{
			try{
				bytes=inputStream.read(buffer);
				MainActivity.UIupdater.obtainMessage
				(0,bytes,-1,buffer).sendToTarget();
			}catch(IOException e)
			{
				break;
			}
		}
	}

5.还定义了一个write方法。一个cancel方法,分别用于向socket Connection写入连接,和取消socket Connection。
public void write(byte[] bytes)
	{
		try
		{
			outputStream.write(bytes);
		}catch(IOException e){}
	}
public void cancel()
	{
		try{
			socket.close();
		}catch(IOException e){}
	}
6.在主活动的java文件里面,定义了三个继承于AsyncTask的类:
(1)CreateCommThreadTask类
private class CreateCommThreadTask extends AsyncTask<Void,Integer,Void>
	{
		protected Void doInBackground(Void... params)
		{
			try{
			serverAddress=InetAddress.getByName("192.168.1.110");
			socket=new Socket(serverAddress,500);
			commsThread=new CommsThread(socket);
			commsThread.start();
			sendToServer(NICKNAME);
			}catch(UnknownHostException e)
			{
				Log.d("Sockets", e.getLocalizedMessage());
			}catch(IOException e)
			{
				Log.d("Sockets", e.getLocalizedMessage());
				
			}
			return null;
		}
	}
这类异步的创建了一个指向指定ip(192.168.1.110)和端口(500)的socket对象。然后把这个对象传给上面建立的CommsThread类。让它可以使用这个socket对象。由于第一个传送的信息将会被看作是昵称,于是我们先发送了一个nickName
(2)WriteToServerTask类
private class WriteToServerTask extends AsyncTask
	<byte[],Void,Void>
	{
		protected Void doInBackground(byte[]... data)
		{
			commsThread.write(data[0]);
			return null;
		}
		
	}
这个方法允许你异步的通过CommsThread对象发送信息。
(3)CloseSocketTask类
private class CloseSocketTask extends AsyncTask
	<Void,Void,Void>
	{
		protected Void doInBackground(Void... params)
		{
			try
			{
				socket.close();
			}catch(IOException e)
			{
				Log.d("Sockets", e.getLocalizedMessage());
			}
			return null;
		}
	}
顾名思义,这个是取消连接的类。
5.最后,我们创建了一个sendToServer方法。用于发送信息:
	private void sendToServer(String message)
	{
		byte[] theByteArray=message.getBytes();
		new WriteToServerTask().execute(theByteArray);
	}
这个方法使用了之前我们创建的异步类来发送信息。
在onResume和onPause方法中,我们可以加入创建和关闭socket的方法,让应用只有在前台的时候才能接受到信息。

2013/02/27



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值