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”
}
]
{
“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