第四篇:获取网络数据(下)

上次我们成功获得了来自网络的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




Android 如何获取WiFi网关的IP地址_gson



然后点击右边的Install安装,完成后重启AS,这样这个插件就可以用了。



实际使用时我们需要先新建一个类BingPic(名称可以自行改动),然后在菜单里找到Code->Generate..->GsonFormat,然后把json字符串粘贴进弹出的窗口就可以生成相应的Bean类了,实际效果就像下面这样:



Android 如何获取WiFi网关的IP地址_android_02




Android 如何获取WiFi网关的IP地址_gson_03




最后就是在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();
    }


最后调试一下来看看效果:

Android 如何获取WiFi网关的IP地址_android_04





可以说是符合预期了,不过瑕疵多了点。


下一期的话先暂停一点进度,做一点面子工程来让用户用着舒服一点。


(说明:这次做的很多已经跟最初贴上去的那个git项目有不少出入了,基本都是改良和规范,新的代码我也贴上github了大家自取)