第四篇:获取网络数据(下)
上次我们成功获得了来自网络的Json字符串,但是显然还需要对json字符串进行处理,使其变成我们需要的url和文字说明。
而GSON恰好就是我们需要的工具。GSON是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库,它可以将一个Json字符转成一个Java对象,或者将一个Java对象转化为Json字符串。 它具有以下几个特点:快速、高效 ;代码量少、简洁 ;面向对象;数据传递和解析方便。
正好借着这个机会我们也可以学习外部库的使用。
第一部分:GSON
为了使用GSON库,首先在app/build.gradle中添加依赖,这也是使用其他外部库的第一步:
dependencies {
...
compile 'com.google.code.gson:gson:2.6.2'
}
如果你使用的是Eclipse,那么请去Google官网下载相应的jar包,效果完全一样。
然后GSON是基于事件驱动的,根据所需要取的数据通过建立一个对应于JSON数据的JavaBean类才能解析出所需JSON数据,也就是为GSON提供处理后的格式标准。关于JavaBean类我推荐想了解的看一下 这一篇博文,说得很简单易懂。不过使用AS的话并不需要人工编写Bean类,因为已经有非常懒人的插件GsonFormat了,下面讲一讲怎么用:
首先在菜单里找到File->Settings..->Plugins–>Browse repositores..,然后搜索GsonFormat
然后点击右边的Install安装,完成后重启AS,这样这个插件就可以用了。
实际使用时我们需要先新建一个类BingPic(名称可以自行改动),然后在菜单里找到Code->Generate..->GsonFormat,然后把json字符串粘贴进弹出的窗口就可以生成相应的Bean类了,实际效果就像下面这样:
最后就是在DataAsyncTask里重新实现doInBackground()方法了,直接上代码:
@Override
protected Void doInBackground(Integer... param) {
try{
String path = "http://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=5&mkt=zh-CN";
URL url = new URL(path);
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
urlConn.setConnectTimeout(5000);
urlConn.connect();
if (urlConn.getResponseCode() == 200) {
String data = readStream(urlConn.getInputStream());
Type type= new TypeToken<BingPic>(){}.getType();
Gson gson = new Gson();
BingPic bingPic = gson.fromJson(data,type);
Log.i(TAG, "请求成功");
} else {
Log.i(TAG, "请求失败");
}
urlConn.disconnect();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
可以看到实际过程分为三步:获取Bean类格式->声明Gson变量->调用fromJson()方法生成BingPic类型的变量。这样我们需要的数据就存储在这个bingPic变量里了。
第二部分:信息的处理
转换是完成了,可是我们还需要把这些转换后的数据展现出来。
首先回到getData()方法,上次我们把DataAsyncTask放在了这里,但是我们知道这里应该返回一个List,那么DataAsyncTask就应该返回一个List。而为了让它能够返回数据,这里就要用到AsyncTask的get()方法了,使用方法如下:
public List<Map<String,Object>> getData()
{
try{
myTask = new DataAsyncTask();
list.addAll(myTask.execute(0).get());
}catch (Exception e){
e.printStackTrace();
}
return list;
}
addAll()我们上次已经讲过了,不过添加上这行代码编译器也会立刻提醒你类型不对,仔细一看就能发现get()实际返回的是onPostExecute(Result)里的Result参数,也就是doInBackground()的返回值。这样的话我们就在doInBackground()里完成List的构建就可以了,需要注意的是DataAsyncTask本身的继承和构造方法都要相应修改:
package com.xugino.bingpicanother;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static android.content.ContentValues.TAG;
class DataAsyncTask extends AsyncTask<Integer,Void,List<Map<String,Object>>> {
private List<Map<String,Object>> list;
DataAsyncTask(){
super();
list = new ArrayList<>();
}
@Override
protected List<Map<String,Object>> doInBackground(Integer... param) {
try{
String path = "http://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=5&mkt=zh-CN";
URL url = new URL(path);
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
urlConn.setConnectTimeout(5000);
urlConn.connect();
if (urlConn.getResponseCode() == 200) {
int i;
Map<String,Object> map;
String data = readStream(urlConn.getInputStream());
Type type= new TypeToken<BingPic>(){}.getType();
Gson gson = new Gson();
BingPic bingPic = gson.fromJson(data,type);
for(i=0;i<5;i++){
map=new HashMap<>();
map.put("pic",getBitmap("http://cn.bing.com"+bingPic.getImages().get(i).getUrl()));
map.put("text",bingPic.getImages().get(i).getCopyright());
map.put("time",bingPic.getImages().get(i).getEnddate());
list.add(map);
}
Log.i(TAG, "请求成功");
} else {
Log.i(TAG, "请求失败");
}
urlConn.disconnect();
}catch (Exception e){
e.printStackTrace();
}
return list;
}
private static Bitmap getBitmap(String path) throws IOException {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if (conn.getResponseCode() == 200){
InputStream inputStream = conn.getInputStream();
Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeStream(inputStream),150,150,true);
conn.disconnect();
return bitmap;
}
conn.disconnect();
return null;
}
private String readStream(InputStream inputStream) throws IOException{
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while((len=inputStream.read(buffer))!=-1){
outputStream.write(buffer,0,len);
outputStream.flush();
}
outputStream.close();
inputStream.close();
return outputStream.toString();
}
}
新增添的getBitmap()方法用于从图片的url得到可以显示的bitmap,需要注意createScaledBitmap()方法是带有缩放变形的生成,这也是为了能把整张图都放进来。
当然Adapter也要相应的修改一下:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if(convertView==null){
viewHolder = new ViewHolder();
convertView=mInflater.inflate(R.layout.list_item,null);
viewHolder.pic=convertView.findViewById(R.id.item_pic);
viewHolder.text=convertView.findViewById(R.id.item_text);
viewHolder.time=convertView.findViewById(R.id.item_time);
viewHolder.btn=convertView.findViewById(R.id.download_btn);
convertView.setTag(viewHolder);
}else{
viewHolder=(ViewHolder)convertView.getTag();
}
viewHolder.pic.setImageBitmap((Bitmap) data.getList().get(position).get("pic"));
viewHolder.text.setText((CharSequence) data.getList().get(position).get("text"));
viewHolder.time.setText((CharSequence) data.getList().get(position).get("time"));
viewHolder.btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
return convertView;
}
为了方便访问减少冗余操作,重新实现DataResource()和initData():
public class DataResource {
private List<Map<String,Object>> list;
private DataAsyncTask myTask;
public DataResource(){
list=new ArrayList<>();
}
public List<Map<String,Object>> getList()
{
return list;
}
public void getData()
{
try{
myTask = new DataAsyncTask();
list.addAll(myTask.execute(0).get());
}catch (Exception e){
e.printStackTrace();
}
}
}
private void initData(){
data.getData();
mList.addAll(data.getList());
mAdapter.notifyDataSetChanged();
}
最后调试一下来看看效果:
可以说是符合预期了,不过瑕疵多了点。
下一期的话先暂停一点进度,做一点面子工程来让用户用着舒服一点。
(说明:这次做的很多已经跟最初贴上去的那个git项目有不少出入了,基本都是改良和规范,新的代码我也贴上github了大家自取)