精华内容
下载资源
问答
  • Android 网络框架 android-async-http

    千次阅读 2015-05-24 16:42:17
    这是Android网络框架系列的最后一篇了~~, 文章主要介绍第三方库 [android-async-http]((http://loopj.com/android-async-http/)的使用,之后作为Android网络框架系列的完结,对在开发中如何学习和使用第三方框架...

    转载请注明出处:明桑Android

    这是Android网络框架系列的最后一篇了~~,
    文章主要介绍第三方库 android-async-http的使用,之后作为Android网络框架系列的完结,对在开发中如何学习和使用第三方框架做个总结!

    1,Android Asynchronous Http Client简介

    android-async-http是封装了HttpClient的顶层实现,它所发起的Request全部运行于不同于UI线程的其它线程中,但它回调的处理逻辑和使用Handler发送message处理的回调逻辑一样,你也可以将它运行在Service线程或者background线程中。

    特点:

    • 异步HTTP请求,匿名回调响应函数
    • 在其他线程而非UI线程中发送Request
    • Requests使用线程池管理限制并发资源
    • 可以自定义GET/POST参数
    • 多种文件上传方式
    • JSON数据的上传
    • Request重试机制
    • 解析多种数据格式
    • 支持持久化存储Cookie
    • 支持SAX解析
    • 支持多种内容和语言编码格式,不仅仅是UTF-8

    更多特性android-async-http

    2,Android-async-http的使用

    1. 跟所有第三方库的使用一样,我们需要将其添加进我们的项目:可以直接下载最新.jar包( android-async-http.jar),也可以从github中将其clone到本地:android-async-http
      1. 如果是直接下载的.jar包,将其添加至项目libs文件夹下,并右键jar包,选择将其Add As Library:
      2. 如果是clone的项目,你还是需要将其编译成jar包,然后根据第1步操作添加进项目:以下是编译过程:
        首先进入你clone的android-async-http目录下,然后在命令行执行如下命令
    android update project -p
    ant jar

    2.android-async-http的基本用法
    我们首选需要创建一个AsyncHttpClient实例,然后用它发起Request

    AsyncHttpClietn client=new AsyncHttpClient();
        client.get("http://www.baidu.com",new AsyncHttpResponseHandler(){
            @Override
            public void onStart(){
                //在Request发起前调用
            }
            @Override
            public void onSuccess(int statusCode,Header[] headers,byte[] response){
                //正确响应时调用,statusCode=200,headers代表响应http头部,response:响应返回值
            }
            @Override
            public void onFailure(int statusCode,Header[] heards,byte[] errorResponse,Throwable e){
                //未正确响应时调用:statusCode=401\403\404\...
            }
            @Override
            public void onRetry(int retryNo){
                //重试请求时调用
    
            }
    
        });

    推荐用法:创建 static Http Client;

    跟Volley框架一样,我们应该单独创建一个类,将我能需要的代码初始化,以便于全局持有某些对象。在这里将我们所需要的封装在一个static Client类中,可以根据自己的需求具体编写自定义Client类,具体的使用参考API: AsyncHttpClient, RequestParams and AsyncHttpResponseHandler

    public class MyCLient{
            private static final String BASE_URL="http://www.baidu.com";
    
            //创建一个static client
            private static AsyncHttpClient client=new AsyncHttpClient();
    
            //重写我们所需要的get,post等方法
    
            public static get(String url,RequestParams params,AsyncHttpResonseHandler responseHand){
                client.get(url,params,respnseHand)
            }
            public static post(String url,RequestParams params,aAsyncHttpResponseHandler responseHand){
                client.post(url,params,responseHand);
            }
    
        }
    
        //然后在需要的地方调用即可,
        MyClient.get(url,params,new AsyncHttpResonseHandler(){...});

    3.Cookie的存储:通过使用PersistentCookieStore,它实现了HttpClient中CookieStore接口,自动将Cookie存储到本地SharedPerferences中,常用来存储用户登录信息等。

    AsyncHttpClient client=new AsyncHttpClient();
    
        PersistentCookieStore cookieStore=new PersistentCookieStore(context);
        client.setCookieStore(cookieStore);
    
        //当然你也可以自己将Cookie添加存储到本地,先创建Cookie对象,然后通过addCookie()添加
    
        BasicClientCookie myCookie=new BasicClientCOokie("cookieuser","hh");
        myCookie.setViersion(1);
        myCookie.setDomain("www.my.com");
        myCookie.setPath("/");
        client.addCookie(myCookie);

    4.为Request添加GET/POST参数:需要用到RequestParams类,
    创建方法有以下几种:

    RequestParams params=new RequestParams("username","coder");
        //或者
        RequestParams params=new RequestParams();
        params.put("username","coder");
        //也可以通过键值对Map对象添加
        HashMap<String,String> map=new HashMap<String,String>();
        map.put("username","coder");
        RequestParams params=new RequestParams(map);
    
        ....
        //还记得前面写过的get(),post()方法吗,RequestParams即是它的第二个可选参数
        client.get(url,params,new AsyncHttpResponseHandler(){..});

    5.通过RequestParams上传文件:上面我们知道了如何试着RequestParams,那么怎么才能上传一个文件呢,可以通过以下三种方式:

    通过输入流上传:

        InputStream myInputStream = blah;
        RequestParams params = new RequestParams();
        params.put("secret_passwords", myInputStream, 

    “passwords.txt”);

    通过文件对象上传:

        File myFile = new File("/path/to/file.png");
        RequestParams params = new RequestParams();
        try {
            params.put("profile_picture", myFile);
        } catch(FileNotFoundException e) {}

    通过字节数组上传:

        byte[] myByteArray = blah;
        RequestParams params = new RequestParams();
        params.put("soundtrack", new ByteArrayInputStream(myByteArray), "she-wolf.mp3");

    6.下载二进制文件:通过FileAsyncHttpResponseHandler类可以下载图片等二进制文件

        AsyncHttpClient client=new AsyncHttpClient();
        client.get("www.my.com/movies.jpg",new FileAsyncHttpResponseHandler(){
            @Override
            public void Success(int statusCode,Header[] headers,File response){
                //获取文件
            }
        })

    那我们如何发送其他类型的例如Json请求呢,其实观察可以发现只需要:new JsonHttpResponseHandler(){..}

    7.HTTP身份验证Request:webservice中常需要进行身份验证,可通过以下两种方式实现:

        AsyncHttpClient client=new AsyncHttpClient();
        //
        client.setBasicAuth("username","password/token");
        client.get("http://www.my.com");
    
        //也可以设置更详细的验证范围
        client.setBasicAuth("username","password",new AuthScope("www.my.com",80,AuthScope.ANY_REALM));
        client.get("http://www.my.com");

    以上即android-async-http 框架的基本用法,不写具体的Demo了,更多内容参见官网提供的API:android-sync-http API

    总结:


    Android网络框架的学习就告一段落了,主要介绍了VolleyPicassoandroid-sync-http三种框架的使用,希望能对正在学习Android网络编程部分的同学有所帮助。

    在使用中,我们需要根据自己的需求和各个框架的特点选择自己所需的框架,初期我们掌握基本的使用就可以了,很多时候我们应该去试着研究它们的源码,这样才能在以后的开发中运用自如。

    其实几篇文章下来,我们可以发现开发中使用第三方框架基本上无非以下几步

    1. 根据自己的需求去github等网站寻求目前比较流行的框架;
    2. 熟悉你所选定框架的特点和使用场景以及缺陷
    3. 将对应jar包引入项目。
    4. 根据官方示例掌握基本用法。
    5. 多做练习,熟悉使用场景。
    6. 研究源码,改写框架以适应自己的需求。

    接下来准备写篇实战的文章,灵活运用以上所学框架我们实现一个简单APP,有兴趣的关注微博或公众号,随时更新~~

    参考资料:

    Android Asynchronous Http Client


    展开全文
  • Android 网络请求及网络状态判断

    千次阅读 2016-08-17 17:53:38
    Android 网络请求及网络状态判断

    文章转载:

    博主anye_anye

    网址:http://blog.csdn.net/anye_anye/article/details/50569114

    Android 中判断网络连接是否可用


    一、判断网络连接是否可用

    public static boolean isNetworkAvailable(Context context) {   
        ConnectivityManager cm = (ConnectivityManager) context   
                .getSystemService(Context.CONNECTIVITY_SERVICE);   
        if (cm == null) {   
        } else {
           //如果仅仅是用来判断网络连接
            //则可以使用 cm.getActiveNetworkInfo().isAvailable();  
            NetworkInfo[] info = cm.getAllNetworkInfo();   
            if (info != null) {   
                for (int i = 0; i < info.length; i++) {   
                    if (info[i].getState() == NetworkInfo.State.CONNECTED) {   
                        return true;   
                    }   
                }   
            }   
        }   
        return false;   
       } 
    

    二、判断GPS是否打开

    public static boolean isGpsEnabled(Context context) {   
        LocationManager lm = ((LocationManager) context   
                .getSystemService(Context.LOCATION_SERVICE));   
        List<String> accessibleProviders = lm.getProviders(true);   
        return accessibleProviders != null && accessibleProviders.size() > 0;   
    }
    

    三、判断WIFI是否打开

    public static boolean isWifiEnabled(Context context) {   
        ConnectivityManager mgrConn = (ConnectivityManager) context   
                .getSystemService(Context.CONNECTIVITY_SERVICE);   
        TelephonyManager mgrTel = (TelephonyManager) context   
                .getSystemService(Context.TELEPHONY_SERVICE);   
        return ((mgrConn.getActiveNetworkInfo() != null && mgrConn   
                .getActiveNetworkInfo().getState() == NetworkInfo.State.CONNECTED) || mgrTel   
                .getNetworkType() == TelephonyManager.NETWORK_TYPE_UMTS);   
    }
    

    四、判断是否是3G网络

    public static boolean is3rd(Context context) {   
        ConnectivityManager cm = (ConnectivityManager) context   
                .getSystemService(Context.CONNECTIVITY_SERVICE);   
        NetworkInfo networkINfo = cm.getActiveNetworkInfo();   
        if (networkINfo != null   
                && networkINfo.getType() == ConnectivityManager.TYPE_MOBILE) {   
            return true;   
        }   
        return false;   
    }
    

    五、判断是wifi还是3g网络,用户的体现性在这里了,wifi就可以建议下载或者在线播放。

     public static boolean isWifi(Context context) {   
            ConnectivityManager cm = (ConnectivityManager) context   
                    .getSystemService(Context.CONNECTIVITY_SERVICE);   
            NetworkInfo networkINfo = cm.getActiveNetworkInfo();   
            if (networkINfo != null   
                    && networkINfo.getType() == ConnectivityManager.TYPE_WIFI) {   
                return true;   
            }   
            return false;   
        }
    

    Android开发请求网络方式详解

      现在就简单的介绍一下Android常用的联网方式,包括JDK支持的HttpUrlConnection,Apache支持的HttpClient,以及开源的一些联网框架(譬如AsyncHttpClient)的介绍。本篇博客只讲实现过程和方式,不讲解原理,否则原理用文字很难以讲清,其实我们知道怎么去用,就可以解决一些基本开发需要了。 
      绝大多数的Android应用联网都是基于Http协议的,也有很少是基于Socket的,我们这里主要讲解基于Http协议的联网方式。讲解实例是建立在一个模拟的登录小模块中进行,登录请求数据仅仅只有username和password两个简单字段。

    HttpUrlConnection

    HttpUrlConnection是JDK里提供的联网API,我们知道Android SDK是基于Java的,所以当然优先考虑HttpUrlConnection这种最原始最基本的API,其实大多数开源的联网框架基本上也是基于JDK的HttpUrlConnection进行的封装罢了,掌握HttpUrlConnection需要以下几个步骤:

    1、将访问的路径转换成URL。

    URL url = new URL(path);
    

    2、通过URL获取连接。

    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    

    3、设置请求方式。

    conn.setRequestMethod(GET);
    

    4、设置连接超时时间。

    conn.setConnectTimeout(5000);
    

    5、设置请求头的信息。

    conn.setRequestProperty(User-Agent, Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0));
    

    6、获取响应码

    int code = conn.getResponseCode();
    

    7、针对不同的响应码,做不同的操作

    7.1、请求码200,表明请求成功,获取返回内容的输入流

    InputStream is = conn.getInputStream();
    

    7.2、将输入流转换成字符串信息

    public class StreamTools {
    /**
     * 将输入流转换成字符串
     * 
     * @param is
     *            从网络获取的输入流
     * @return
     */
    public static String streamToString(InputStream is) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = is.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            baos.close();
            is.close();
            byte[] byteArray = baos.toByteArray();
            return new String(byteArray);
        } catch (Exception e) {
            Log.e(tag, e.toString());
            return null;
        }
    }
    }
    

    7.3、若返回值400,则是返回网络异常,做出响应的处理。

    HttpUrlConnection发送GET请求

    /**
     * 通过HttpUrlConnection发送GET请求
     * 
     * @param username
     * @param password
     * @return
     */
    public static String loginByGet(String username, String password) {
        String path = http://192.168.0.107:8080/WebTest/LoginServerlet?username= + username + &password= + password;
        try {
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5000);
            conn.setRequestMethod(GET);
            int code = conn.getResponseCode();
            if (code == 200) {
                InputStream is = conn.getInputStream(); // 字节流转换成字符串
                return StreamTools.streamToString(is);
            } else {
                return 网络访问失败;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return 网络访问失败;
        }
    }
    

    HttpUrlConnection发送POST请求

    /**
     * 通过HttpUrlConnection发送POST请求
     * 
     * @param username
     * @param password
     * @return
     */
    public static String loginByPost(String username, String password) {
        String path = http://192.168.0.107:8080/WebTest/LoginServerlet;
        try {
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5000);
            conn.setRequestMethod(POST);
            conn.setRequestProperty(Content-Type, application/x-www-form-urlencoded);
            String data = username= + username + &password= + password;
            conn.setRequestProperty(Content-Length, data.length() + );
            // POST方式,其实就是浏览器把数据写给服务器
            conn.setDoOutput(true); // 设置可输出流
            OutputStream os = conn.getOutputStream(); // 获取输出流
            os.write(data.getBytes()); // 将数据写给服务器
            int code = conn.getResponseCode();
            if (code == 200) {
                InputStream is = conn.getInputStream();
                return StreamTools.streamToString(is);
            } else {
                return 网络访问失败;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return 网络访问失败;
        }
    }
    

    HttpClient

      HttpClient是开源组织Apache提供的Java请求网络框架,其最早是为了方便Java服务器开发而诞生的,是对JDK中的HttpUrlConnection各API进行了封装和简化,提高了性能并且降低了调用API的繁琐。 
      Android因此也引进了这个联网框架,我们再不需要导入任何jar或者类库就可以直接使用,值得注意的是Android官方已经宣布不建议使用HttpClient了,我们再开发的时候尽量少用吧,但是用了也无妨!

    HttpClient发送GET请求

    1, 创建HttpClient对象

    2,创建HttpGet对象,指定请求地址(带参数)

    3,使用HttpClient的execute(),方法执行HttpGet请求,得到HttpResponse对象

    4,调用HttpResponse的getStatusLine().getStatusCode()方法得到响应码

    5,调用的HttpResponse的getEntity().getContent()得到输入流,获取服务端写回的数据

    /**
     * 通过HttpClient发送GET请求
     * 
     * @param username
     * @param password
     * @return
     */
    public static String loginByHttpClientGet(String username, String password) {
        String path = http://192.168.0.107:8080/WebTest/LoginServerlet?username=
                + username + &password= + password;
        HttpClient client = new DefaultHttpClient(); // 开启网络访问客户端
        HttpGet httpGet = new HttpGet(path); // 包装一个GET请求
        try {
            HttpResponse response = client.execute(httpGet); // 客户端执行请求
            int code = response.getStatusLine().getStatusCode(); // 获取响应码
            if (code == 200) {
                InputStream is = response.getEntity().getContent(); // 获取实体内容
                String result = StreamTools.streamToString(is); // 字节流转字符串
                return result;
            } else {
                return 网络访问失败;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return 网络访问失败;
        }
    }
    

    HttpClient发送POST请求

    1,创建HttpClient对象

    2,创建HttpPost对象,指定请求地址

    3,创建List,用来装载参数

    4,调用HttpPost对象的setEntity()方法,装入一个UrlEncodedFormEntity对象,携带之前封装好的参数

    5,使用HttpClient的execute()方法执行HttpPost请求,得到HttpResponse对象

    6, 调用HttpResponse的getStatusLine().getStatusCode()方法得到响应码

    7, 调用的HttpResponse的getEntity().getContent()得到输入流,获取服务端写回的数据

    /**
     * 通过HttpClient发送POST请求
     * 
     * @param username
     * @param password
     * @return
     */
    public static String loginByHttpClientPOST(String username, String password) {
        String path = http://192.168.0.107:8080/WebTest/LoginServerlet;
        try {
            HttpClient client = new DefaultHttpClient(); // 建立一个客户端
            HttpPost httpPost = new HttpPost(path); // 包装POST请求
            // 设置发送的实体参数
            List<namevaluepair> parameters = new ArrayList<namevaluepair>();
            parameters.add(new BasicNameValuePair(username, username));
            parameters.add(new BasicNameValuePair(password, password));
            httpPost.setEntity(new UrlEncodedFormEntity(parameters, UTF-8));
            HttpResponse response = client.execute(httpPost); // 执行POST请求
            int code = response.getStatusLine().getStatusCode();
            if (code == 200) {
                InputStream is = response.getEntity().getContent();
                String result = StreamTools.streamToString(is);
                return result;
            } else {
                return 网络访问失败;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return 访问网络失败;
        }
    }
    

    开源联网框架

    1、AsyncHttpClient

    除了上述Android官方推荐的联网框架以外,在开源世界里关于联网框架真是太多太多了,例如afinal,xutils等等,都是一些开源大牛自己封装的联网框架,并且在GitHub开源社区中可以下载到,其实类似的开源联网框架基本上也是基于HttpUrlConnection的进一步封装,大大提高了性能,同时更加简化了使用方法,这里先介绍AsyncHttpClient的一些使用方法。 AsyncHttpClient是一个非常优秀的联网框架,不仅支持所有Http请求的方式,而且还支持文件的上传和下载,要知道用HttpUrlConnection写一个文件上传和下载健全功能是很需要花费一定时间和精力的,因为请求头实在是太多了,稍有不慎就会写错。但是AsyncHttpClient已经封装好了这些“麻烦”,我们只需要下载到AsyncHttpClient的jar包或者源码导入项目中,Http,上传,下载等等,只需要几个简单的api即可搞定。 AsyncHttpClient的GitHub主页:https://github.com/AsyncHttpClient/async-http-client/

    AsyncHttpClient发送GET请求

    1,将下载好的源码拷贝到src目录下

    2,创建一个AsyncHttpClient的对象

    3,调用该类的get方法发送GET请求,传入请求资源地址URL,创建AsyncHttpResponseHandler对象

    4,重写AsyncHttpResponseHandler下的两个方法,onSuccess和onFailure方法

    /**
     * 通过AsyncHttpClient发送GET请求
     */
    public void loginByAsyncHttpGet() {
        String path = http://192.168.0.107:8080/WebTest/LoginServerlet?username=zhangsan&password=123;
        AsyncHttpClient client = new AsyncHttpClient();
        client.get(path, new AsyncHttpResponseHandler() {
    
            @Override
            public void onFailure(int arg0, Header[] arg1, byte[] arg2,
                    Throwable arg3) {
                // TODO Auto-generated method stub
                Log.i(TAG, 请求失败: + new String(arg2));
            }
    
            @Override
            public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
                // TODO Auto-generated method stub
                Log.i(TAG, 请求成功: + new String(arg2));
            }
        });
    }
    

    AsyncHttpClient发送POST请求

    1,将下载好的源码拷贝到src目录下

    2,创建一个AsyncHttpClient的对象

    3,创建请求参数,RequestParams对象

    4,调用该类的post方法发POST,传入请求资源地址URL,请求参数RequestParams,创建AsyncHttpResponseHandler对象

    5,重写AsyncHttpResponseHandler下的两个方法,onSuccess和onFailure方法

    /**
     * 通过AsyncHttpClient发送POST请求
     */
    public void loginByAsyncHttpPost() {
        String path = http://192.168.0.107:8080/WebTest/LoginServerlet;
        AsyncHttpClient client = new AsyncHttpClient();
        RequestParams params = new RequestParams();
        params.put(username, zhangsan);
        params.put(password, 123);
        client.post(path, params, new AsyncHttpResponseHandler() {
    
            @Override
            public void onFailure(int arg0, Header[] arg1, byte[] arg2,
                    Throwable arg3) {
                // TODO Auto-generated method stub
                Log.i(TAG, 请求失败: + new String(arg2));
            }
    
            @Override
            public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
                // TODO Auto-generated method stub
                Log.i(TAG, 请求成功: + new String(arg2));
            }
        });
    

    AsyncHttpClient上传文件

    1,将下载好的源码拷贝到src目录下

    2,创建一个AsyncHttpClient的对象

    3,创建请求参数,RequestParams对象,请求参数仅仅包含文件对象即可,例如:

    params.put(profile_picture, new File(/sdcard/pictures/pic.jpg));
    

    4,调用该类的post方法发POST,传入请求资源地址URL,请求参数RequestParams,创建AsyncHttpResponseHandler对象

    5,重写AsyncHttpResponseHandler下的两个方法,onSuccess和onFailure方法

    2、Android Volley框架使用详解

      Volley是一个由Google官方推出的网络通信库,它使得Android进行网络请求时更加方便、快速、健壮,同时对网络图片加载也提供了良好的支持。

    1、获取volley源码

      将编译得到的jar包引入到我们的项目中;可以通过网络搜索下载,也可以在此下载 
    http://download.csdn.net/detail/fenghai22/8472045

    2、使用实例

      使用Volley必须在AndroidManifest.xml中添加 android.permission.INTERNET权限,使用Volley时Google建议创建volley单例工具类

    public class VolleySingleton {
    private static VolleySingleton volleySingleton;
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;
    private Context mContext;
    public VolleySingleton(Context context) {
        this.mContext = context;
        mRequestQueue = getRequestQueue();
        mImageLoader = new ImageLoader(mRequestQueue,
        new ImageLoader.ImageCache(){
            private final LruCache<String,Bitmap> cache = new LruCache<String ,Bitmap>(20);
            @Override
            public Bitmap getBitmap(String url){
                return cache.get(url);
            }
            @Override
            public void putBitmap(String url,Bitmap bitmap){
                cache.put(url,bitmap);
            }
        });
    }
    public static synchronized VolleySingleton getVolleySingleton(Context context){
        if(volleySingleton == null){
            volleySingleton = new VolleySingleton(context);
        }
        return volleySingleton;
    }
    public RequestQueue getRequestQueue(){
        if(mRequestQueue == null){
            mRequestQueue = Volley.newRequestQueue(mContext.getApplicationContext());
        }
        return mRequestQueue;
    }
    public <T> void addToRequestQueue(Request<T> req){
        getRequestQueue().add(req);
    }
    public ImageLoader getImageLoader() {
        return mImageLoader;
    }
    }
    

      首先使用 Volley.newRequestQueue获取 RequestQueue对象

    RequestQueue mRequestQueue = VolleySingleton.getVolleySingleton(this.getApplicationContext()).getRequestQueue();
    

      RequestQueue是请求队列对象,它可以缓存所有的HTTP网络请求,然后按照其内部算法并发的发送这些网络请求,它能够很好的支撑高并发请求,不要每个请求都创建RequestQueue对象,创建多个RequestQueue会耗费资源

      发送StringRequest请求

    StringRequest stringRequest = new StringRequest(Request.Method.GET,"https://www.baidu.com",new Listener<String>(){
    
            @Override
            public void onResponse(String s) {
                //打印请求返回结果
                Log.e("volley",s);
            }
        },new ErrorListener(){
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                Log.e("volleyerror","erro2");
            }
        });
    //将StringRequest对象添加进RequestQueue请求队列中
       VolleySingleton.getVolleySingleton(this.getApplicationContext()).addToRequestQueue(stringRequest);
    

      到此已经完成了StringRequest请求。StringRequest提供了两个构造方法

    public StringRequest(java.lang.String url, com.android.volley.Response.Listener<java.lang.String> listener, com.android.volley.Response.ErrorListener errorListener);
    
    public StringRequest(int method, java.lang.String url, com.android.volley.Response.Listener<java.lang.String> listener, com.android.volley.Response.ErrorListener errorListener);
    

      参数method是HTTP的请求类型,通常有GET和POST两种;参数url是请求地址;参数listener是服务器响应成功的回调,参数errorListener是服务器响应失败的回调。如果想通过POST方式请求并携带参数,遗憾的是StringRequest并没有提供带参数请求,但是当发送POST请求时,Volley会调用StringRequest的父类Request的getParams()方法来获取POST参数,所以我们只要使用StringRequest匿名类重写getParams()方法将参数传递进去就可以实现带参数的StringRequest请求。

    StringRequest stringRequest = new StringRequest(Method.POST, url,  listener, errorListener) {  
    @Override  
    protected Map<String, String> getParams() throws AuthFailureError {  
        Map<String, String> map = new HashMap<String, String>();  
        map.put("params1", "value1");  
        map.put("params2", "value2");  
        return map;  
    }  
    }; 
    

    发送JsonObjectRequest请求

    JsonObjectRequest jr = new JsonObjectRequest(Request.Method.GET,url,null,new Response.Listener<JSONObject>(){
            @Override
            public void onResponse(JSONObject jsonObject) {
                Log.e("volley",jsonObject.toString());
            }
        },new ErrorListener(){
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                Log.e("volleyerror","erro");
            }
        });
     VolleySingleton.getVolleySingleton(this.getApplicationContext()).addToRequestQueue(jr);
    

    JsonObjectRequest的构造方法参数和StringRequest一致,不在此累赘。

    使用ImageRequest加载图片

    ImageView mImageView;
    String url = "http://i.imgur.com/7spzG.png";
    mImageView = (ImageView) findViewById(R.id.myImage);
    ImageRequest request = new ImageRequest(url,
    new Response.Listener<Bitmap>() {
        @Override
        public void onResponse(Bitmap bitmap) {
            mImageView.setImageBitmap(bitmap);
        }
    }, 0, 0, Config.RGB_565,
    new Response.ErrorListener() {
        public void onErrorResponse(VolleyError error) {
            mImageView.setImageResource(R.drawable.image_load_error);
        }
    });
    VolleySingleton.getVolleySingleton(this.getApplicationContext()).addToRequestQueue(request);
    

    ImageRequest的构造函数

    public ImageRequest(java.lang.String url, com.android.volley.Response.Listener<android.graphics.Bitmap> listener, int maxWidth, int maxHeight, android.graphics.Bitmap.Config decodeConfig, com.android.volley.Response.ErrorListener errorListener) { /* compiled code */ }
    

      参数url是图片地址,参数listener是请求响应成功回调,参数maxWidth是图片最大宽度,参数maxHeight是图片最大高度,如果指定的网络图片的宽度或高度大于这里的最大值,则会对图片进行压缩,指定成0的话就表示不管图片有多大,都不会进行压缩。参数decodeConfig是图片的颜色属性,其值是Bitmap.Config类的几个常量,参数errorListener是请求响应失败回调

    使用 ImageLoader 加载图片

    ImageLoader mImageLoader;
    ImageView mImageView;
    private static final String IMAGE_URL =
    "http://developer.android.com/images/training/system-ui.png";
    mImageView = (ImageView) findViewById(R.id.regularImageView);
    mImageLoader = VolleySingleton.getVolleySingleton(this.getApplicationContext()).getImageLoader();
    //IMAGE_URL是图片网络地址
    //mImageView是ImageView实例
    //R.drawable.def_image默认图片id
    //R.drawable.err_image加载图片错误时的图片
    mImageLoader.get(IMAGE_URL, ImageLoader.getImageListener(mImageView,
         R.drawable.def_image, R.drawable.err_image));
    

    使用NetworkImageView加载图片

    XML布局文件

    <com.android.volley.toolbox.NetworkImageView
        android:id="@+id/networkImageView"
        android:layout_width="150dp"
        android:layout_height="170dp"
        android:layout_centerHorizontal="true" />
    

    代码

    ImageLoader mImageLoader;
    NetworkImageView mNetworkImageView;
    private static final String IMAGE_URL =
    "http://developer.android.com/images/training/system-ui.png";
    mNetworkImageView = (NetworkImageView) findViewById(R.id.networkImageView);
    mImageLoader = VolleySingleton.getVolleySingleton(this.getApplicationContext()).getImageLoader();
    mNetworkImageView.setImageUrl(IMAGE_URL, mImageLoader);
    

      我们可以调用它的setDefaultImageResId()方法、setErrorImageResId()方法和setImageUrl()方法来分别设置加载中显示的图片,加载失败时显示的图片

    取消网络请求

      Volley还提供了取消网络请求的方法并且可以联动Activity的生命周期,比如在Activity的onStop()方法中调用cance()方法取消网络请求。

    public static final String TAG = "MyTag";
    StringRequest stringRequest; // Assume this exists.
    RequestQueue mRequestQueue;  // Assume this exists.
    
    // Set the tag on the request.
    stringRequest.setTag(TAG);
    
    // Add the request to the RequestQueue.
    mRequestQueue.add(stringRequest);
    

    Activity的onStop()方法

    @Override
    protected void onStop () {
    super.onStop();
    if (mRequestQueue != null) {
        mRequestQueue.cancelAll(TAG);
    }
    }
    展开全文
  • Android网络编程(六)OkHttp3用法全解析

    万次阅读 多人点赞 2016-04-21 12:26:19
    上一篇介绍了OkHttp2.x的用法,这一篇文章我们来对照OkHttp2.x版本来看看,OkHttp3使用起来有那些变化。当然,看这篇文章前建议看一下前一篇文章Android网络编程(五)OkHttp2.x用法全解析。

    相关文章
    Android网络编程(一)HTTP协议原理
    Android网络编程(二)HttpClient与HttpURLConnection
    Android网络编程(三)Volley用法全解析
    Android网络编程(四)从源码解析volley
    Android网络编程(五)OkHttp2.x用法全解析
    Android网络编程(六)OkHttp3用法全解析
    Android网络编程(七)源码解析OkHttp前篇[请求网络]
    Android网络编程(八)源码解析OkHttp后篇[复用连接池]
    Android网络编程(九)Retrofit2前篇[基本使用]
    Android网络编程(十)Retrofit2后篇[注解]
    Android网络编程(十一)源码解析Retrofit

    前言

    上一篇介绍了OkHttp2.x的用法,这一篇文章我们来对照OkHttp2.x版本来看看,OkHttp3使用起来有那些变化。当然,看这篇文章前建议看一下前一篇文章Android网络编程(五)OkHttp2.x用法全解析

    1.使用前准备

    Android Studio 配置gradle:

      compile 'com.squareup.okhttp3:okhttp:3.2.0'
      compile 'com.squareup.okio:okio:1.7.0'

    添加网络权限:

        <uses-permission android:name="android.permission.INTERNET"/>

    2.异步GET请求

    惯例,请求百度:

            private void getAsynHttp() {
            mOkHttpClient=new OkHttpClient();
            Request.Builder requestBuilder = new Request.Builder().url("http://www.baidu.com");
            //可以省略,默认是GET请求
            requestBuilder.method("GET",null);
            Request request = requestBuilder.build();
            Call mcall= mOkHttpClient.newCall(request);
            mcall.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
    
                }
    
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    if (null != response.cacheResponse()) {
                        String str = response.cacheResponse().toString();
                        Log.i("wangshu", "cache---" + str);
                    } else {
                        response.body().string();
                        String str = response.networkResponse().toString();
                        Log.i("wangshu", "network---" + str);
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(getApplicationContext(), "请求成功", Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            });
        }

    与2.x版本并没有什么不同,比较郁闷的是回调仍然不在UI线程。

    2.异步POST请求

    OkHttp3异步POST请求和OkHttp2.x有一些差别就是没有FormEncodingBuilder这个类,替代它的是功能更加强大的FormBody:

       private void postAsynHttp() {
            mOkHttpClient=new OkHttpClient();
            RequestBody formBody = new FormBody.Builder()
                    .add("size", "10")
                    .build();
            Request request = new Request.Builder()
                    .url("http://api.1-blog.com/biz/bizserver/article/list.do")
                    .post(formBody)
                    .build();
            Call call = mOkHttpClient.newCall(request);
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
    
                }
    
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    String str = response.body().string();
                    Log.i("wangshu", str);
    
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(getApplicationContext(), "请求成功", Toast.LENGTH_SHORT).show();
                        }
                    });
                }
    
            });
        }

    3.异步上传文件

    上传文件本身也是一个POST请求,上一篇没有讲,这里我们补上。首先定义上传文件类型:

        public static final MediaType MEDIA_TYPE_MARKDOWN
                = MediaType.parse("text/x-markdown; charset=utf-8");

    将sdcard根目录的wangshu.txt文件上传到服务器上:

        private void postAsynFile() {
            mOkHttpClient=new OkHttpClient();
            File file = new File("/sdcard/wangshu.txt");
            Request request = new Request.Builder()
                    .url("https://api.github.com/markdown/raw")
                    .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
                    .build();
    
                mOkHttpClient.newCall(request).enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
    
                    }
    
                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        Log.i("wangshu",response.body().string());
                    }
                });
            }
    

    当然如果想要改为同步的上传文件只要调用 mOkHttpClient.newCall(request).execute()就可以了。
    在wangshu.txt文件中有一行字“Android网络编程(六)OkHttp3用法全解析”我们运行程序点击发送文件按钮,最终请求网络返回的结果就是我们txt文件中的内容 :

    这里写图片描述

    当然不要忘了添加如下权限:

       <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
       <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    4.异步下载文件

    下载文件同样在上一篇没有讲到,实现起来比较简单,在这里下载一张图片,我们得到Response后将流写进我们指定的图片文件中就可以了。

        private void downAsynFile() {
            mOkHttpClient = new OkHttpClient();
            String url = "https://img-my.csdn.net/uploads/201603/26/1458988468_5804.jpg";
            Request request = new Request.Builder().url(url).build();
            mOkHttpClient.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
    
                }
    
                @Override
                public void onResponse(Call call, Response response) {
                    InputStream inputStream = response.body().byteStream();
                    FileOutputStream fileOutputStream = null;
                    try {
                        fileOutputStream = new FileOutputStream(new File("/sdcard/wangshu.jpg"));
                        byte[] buffer = new byte[2048];
                        int len = 0;
                        while ((len = inputStream.read(buffer)) != -1) {
                            fileOutputStream.write(buffer, 0, len);
                        }
                        fileOutputStream.flush();
                    } catch (IOException e) {
                        Log.i("wangshu", "IOException");
                        e.printStackTrace();
                   }
    
                   Log.d("wangshu", "文件下载成功");
               }
           });
       }

    5.异步上传Multipart文件

    这种场景很常用,我们有时会上传文件同时还需要传其他类型的字段,OkHttp3实现起来很简单,需要注意的是没有服务器接收我这个Multipart文件,所以这里只是举个例子,具体的应用还要结合实际工作中对应的服务器。
    首先定义上传文件类型:

    private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
    private void sendMultipart(){
        mOkHttpClient = new OkHttpClient();
        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("title", "wangshu")
                .addFormDataPart("image", "wangshu.jpg",
                        RequestBody.create(MEDIA_TYPE_PNG, new File("/sdcard/wangshu.jpg")))
                .build();
    
        Request request = new Request.Builder()
                .header("Authorization", "Client-ID " + "...")
                .url("https://api.imgur.com/3/image")
                .post(requestBody)
                .build();
    
       mOkHttpClient.newCall(request).enqueue(new Callback() {
           @Override
           public void onFailure(Call call, IOException e) {
    
           }
    
           @Override
           public void onResponse(Call call, Response response) throws IOException {
               Log.i("wangshu", response.body().string());
           }
       });
    }

    6.设置超时时间和缓存

    和OkHttp2.x有区别的是不能通过OkHttpClient直接设置超时时间和缓存了,而是通过OkHttpClient.Builder来设置,通过builder配置好OkHttpClient后用builder.build()来返回OkHttpClient,所以我们通常不会调用new OkHttpClient()来得到OkHttpClient,而是通过builder.build():

            File sdcache = getExternalCacheDir();
            int cacheSize = 10 * 1024 * 1024;
            OkHttpClient.Builder builder = new OkHttpClient.Builder()
                    .connectTimeout(15, TimeUnit.SECONDS)
                    .writeTimeout(20, TimeUnit.SECONDS)
                    .readTimeout(20, TimeUnit.SECONDS)
                    .cache(new Cache(sdcache.getAbsoluteFile(), cacheSize));
            OkHttpClient mOkHttpClient=builder.build();            

    7.关于取消请求和封装

    取消请求仍旧可以调用call.cancel(),这个没有变化,不明白的可以查看上一篇文章Android网络编程(五)OkHttp2.x用法全解析,这里就不赘述了,封装上一篇也讲过仍旧推荐OkHttpFinal,它目前是基于OkHttp3来进行封装的。

    8.关于源码Demo

    源码Demo很简单就四个按钮用来测试上面讲到的内容:

    这里写图片描述

    github源码下载


    公众号末尾1.1.jpg

    展开全文
  • Android网络请求三篇

    千次阅读 2016-11-02 23:18:39
    篇一:Android 网络操作和优化相关 篇二:Android网络请求心路历程 篇三:Android开源项目推荐之「网络请求哪家强」

    篇一:Android 网络操作和优化相关

    写在前面的话

    Android程序最重要的模块就是网络部分,如何从网络上下载数据,如何将处理过的数据上传至网络,往往是android程序的关键环节。前几天偶一朋友遇到这么一个问题:如何使用volley实现文件上传。最后问题解决了,小伙伴不禁有些飘飘然,大有一番天下之事皆逃不过我的魔掌的感觉。这时候coder君问了他几个问题,大家可以一起思考下:

    • TCP/IP协议、SOCKET、HTTP协议、HTTPS协议都是做什么的,他们之间有关系吗
    • 你是如何管理网络使用情况的
    • 常用的HTTP Client有哪些,该如何选择
    • 假如你需要修改的你网络请求框架,你要动多少代码
    • 你是怎么进行网络优化的

    小伙伴支支吾吾,其实他熟悉的只是Android中网络相关的那些点中的一小部分,不免有些盲人摸象的感觉,你呢?

    网络通信机制

    网络由下往上分为:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。

    IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对应于应用层,三者从本质上来说没有可比性,socket则是对TCP/IP协议的封装和应用。也可以说,TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。

    TCP/IP协议

    网络编程的目的就是直接或间接地通过网络协议与其他计算机进行通讯。

    网络编程中有两个主要的问题,一个是如何准确的定位网络上一台或多台主机;另一个就是找到主机后如何可靠高效的进行数据传输。目前使用最广泛的因特网协议是TCP/IP协议。

    在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以唯一地确定Internet上的一台主机。而TCP层则提供面向应用的可靠的或非可靠的数据传输机制,这是网络编程的主要对象,一般不需要关心IP层是如何处理数据的。

    Socket

    我们知道两个进程如果需要进行通讯最基本的一个前提能能够唯一的标示一个进程,在本地进程通讯中我们可以使用PID来唯一标示一个进程,但PID只在本地唯一,网络中的两个进程PID冲突几率很大,这时候我们需要另辟它径了,我们知道IP层的ip地址可以唯一标示主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。

    能够唯一标示网络中的进程后,它们就可以利用socket进行通信了,什么是socket呢?我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。

    Socket跟TCP/IP协议没有必然的联系。Socket编程接口在设计的时候,就希望也能适应其他的网络协议。所以说,Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,

    Http协议

    HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。

    HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。

    HTTP提供了封装或者显示数据的具体形式。Socket提供了网络通信的能力。

    Https协议

    HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。 它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。https:URL表明它使用了HTTP,但HTTPS存在不同于HTTP的默认端口及一个加密/身份验证层(在HTTP与TCP之间)。被广泛用于万维网上安全敏感的通讯,例如交易支付方面。

    Android网络权限

    <uses-permission android:name="android.permission.INTERNET" /><-- 允许应用程序打开网络套接字 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><-- 允许应用程序访问网络连接信息 -->

    选择一个HTTP Client

    大多数连接网络的 Android app 会使用 HTTP 来发送与接收数据。Android 提供了三种 HTTP client:HttpURLConnection、Apache HttpClient 和okhttp。二者均支持 HTTPS、流媒体上传和下载、可配置的超时、IPv6 与连接池(connection pooling)。【Apache HttpClient 已废,删除了】

    • Java.net 包中的 HttpURLConnection 类 HttpUrlConnection 是 JDK 里提供的联网 API,我们知道Android SDK 是基于 Java 的,所以当然优先考虑 HttpUrlConnection 这种最原始最基本的 API,其实大多数开源的联网框架基本上也是基于 JDK 的 HttpUrlConnection 进行的封装罢了

    • okhttp http 是现在主流应用使用的网络请求方式, 用来交换数据和内容, 有效的使用HTTP可以使你的APP变的更快和减少流量的使用。OkHttp是一个很棒HTTP客户端:

      • 支持 SPDY,可以合并多个到同一个主机的请求
      • 使用连接池技术减少请求的延迟(如果 SPDY 是可用的话)
      • 使用 GZIP 压缩减少传输的数据量
      • 缓存响应避免重复的网络请求

    当你的网络出现拥挤的时候,就是 OKHttp 大显身手的时候,它可以避免常见的网络问题,如果你的服务是部署在不同的 IP上面的,如果第一个连接失败, OkHTtp 会尝试其他的连接。这对现在 IPv4 + IPv6 中常见的把服务冗余部署在不同的数据中心上也是很有必要的。OkHttp 将使用现在 TLS 特性 (SNI ALPN) 来初始化新的连接,如果握手失败,将切换到 TLS 1.0。

    使用OkHttp很容易,同时支持异步阻塞请求和回调.

    如果你使用OkHttp ,你不用重写你的代码, okhttp-urlconnection模块实现了 java.net.HttpURLConnection 中的API, okhttp-apache模块实现了HttpClient中的API

    如何管理网络的使用情况

    如果我们的程序需要执行大量网络操作,那么应该提供用户设置选项,来允许用户控制程序的数据偏好。例如,同步数据的频率,是否只在连接到 WiFi 才进行下载与上传操作,是否在漫游时使用套餐数据流量等等。这样用户才不大可能在快到达流量上限时,禁止我们的程序获取后台数据,因为他们可以精确控制我们的 app 使用多少数据流量。

    检查网络连接

    在执行网络操作之前,检查设备当前连接的网络连接信息是个好习惯。这样可以防止我们的程序在无意间连接使用了非意向的网络频道。如果网络连接不可用,那么我们的应用应该优雅地做出响应。为了检测网络连接,我们需要使用到下面两个类:

    ConnectivityManager:它会回答关于网络连接的查询结果,并在网络连接改变时通知应用程序。
    NetworkInfo:描述一个给定类型(移动网络或 Wi-Fi等)的网络接口状态。

    下面这个方法可以找到的第一个已连接的网络接口,如果返回null,则表示没有已连接的网络接口(意味着网络连接不可用):

    public boolean isOnline() {
        ConnectivityManager connMgr = (ConnectivityManager)getSystemServic(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        return (networkInfo != null & networkInfo.isConnected());
    } 

    管理网络的使用情况

    我们可以实现一个偏好设置的选项,使用户能直接设置程序对网络资源的使用情况。例如:

    可以允许用户仅在连接到 Wi-Fi 时上传视频。
    可以根据诸如网络可用,时间间隔等条件来选择是否做同步的操作。

    检测网络连接变化

    要检测网络连接的变化需要用到BroadcastReceiver 的子类:NetworkReceiver。当设备网络连接改变时,NetworkReceiver 会监听到 CONNECTIVITY_ACTION,这时需要判断当前网络连接类型并相应的设置好 wifiConnected 与 mobileConnected。

    这里需要注意的是在使用BroadcastReceiver的时候,不必要的声明注册会浪费系统资源。最好在 onCreate()中注册 BroadcastReceiver NetworkReceiver,在 onDestroy()中销毁它。这样做会比在 manifest 里面声明 更轻巧。

    在一个单独的线程中执行网络操作

    网络操作会遇到不可预期的延迟。为了避免造成不好的用户体验,确保总是在 UI 线程之外单独的线程中执行网络操作。关于Android中的线程和进程可以参考这里。

    封装网络请求

    有了Http Client,我们需要考虑的是如何优雅的使用这些Http Client进行网络请求。这时候我们可以对Http Clent进行封装来满足我们的需求。封装的基本要点如下:

    -支持自定义请求的Header
    - 支持Http的基本请求方法:GET、POST、PUT、DELETE等
    - 支持文件上传和下载
    - 可以加载图片
    - 支持多任务网络请求操作
    - 支持缓存
    - 支持请求回调
    - 支持session的保持

    具体可以参考Volley源码分析、Retrofit分析等,看看这些成名已久的框架是如何封装的。

    网络请求框架

    以下是曾经很火或者现在很多的网络请求框架:

    Android Async HTTP
    AndroidAsync
    Volley
    Retrofit

    AsyncHttp是一个较高层的封装,底层使用的是HttpClient。

    Volley是Google推出的Android异步网络请求框架和图片加载框架。底层网络请求可以使用不同的网络库来处理,比如OkHttp,HttpClient。

    Retrofit是一个封装比较好的,相对更面向开发者的rest请求库,它的底层网络请求也可以使用不同的网络库来处理。

    网络请求框架再封装

    我在这些Tips让你的App更容易维护 简单提到过面向接口编程,这对于网络框架再封装很实用。

    我们可以定义一套我们自己的网络请求接口,接口规定我们需要有什么能力的框架。调用的话我们可以直接调用接口方法就好了,这样不管我们换什么网络框架,只要这个框架有网络请求的能力就行。举个例子,定义接口如下:

    public interface ISender {
    
        void send(METHOD method, String url, SenderCallback callback);
    
    }

    我们写一个类实现这个接口:

    public class BaseSender implements ISender {
        @Override
        public synchronized void sender(METHOD method, final String url, final SenderCallback callback) {
            //volley,retrofit,balabala,用任意网络请求框架实现。
    
        }
    }

    然后是我们的SendCallback

    public interface SendCallback {
    
        //请求成功回调
        void onSucceeed(String t);
    
        //请求失败回调
        void onError(String errorMsg);
    
    }

    最后是我们的调用:

    private ISender mHttp;
    
    public void getWeahter(String tag) {
    
        //调用接口里定义好的方法
        mHttp.sender();
    
    }

    这里传入我们需要的参数就好了。

    数据解析

    以下是比较流行的网络数据解析的库:

    • Gson
    • Jackson
    • FastJson
    • HtmlPaser
    • Jsoup

    网络数据解析比较基础,这里就不过多描述了。

    移动端网络优化

    对于手机程序,网络操作相对来说是比较耗电的行为。优化网络操作能够显著节约电量的消耗。一个网络请求可以简单分为连接服务器和获取数据两个部分。其中连接服务器前还包括 DNS 解析的过程;获取数据后可能会对数据进行缓存。那么我们如果要进行网络优化需要从这两个关键点入手。

    以下是具体可以优化的点:

    • 不用域名,用IP直连省去 DNS 解析过程,DNS 全名 Domain Name System,解析意指根据域名得到其对应的IP地址。
    • 服务器合理部署服务器多运营商多地部署,一般至少含三大运营商、南中北三地部署。
    • 根据当前的网络环境选择当下最佳的策略主要的实施步骤有两步:第1是检测收集当前的网络环境信息,第2是根据当前收集到的信息进行网络请求行为的调整。
    • 预取我们需要预先判断用户在此次操作之后,后续零散的请求是否很有可能会马上被触发,可以把后面几分钟有可能会使用到的零散请求都一次集中执行完毕。
    • 连接复用节省连接建立时间,如开启 keep-alive。
    • 用捆绑批量访问的方式来减少访问的频率预先判定那些可能马上就会使用到的网络资源,捆绑一起集中进行网络请求。这点我们可以考虑按照提前预期后续1-2分钟的数据作为基准。
    • 分优先级、延迟部分请求首先我们需要区分哪些网络请求是需要及时返回结果的,哪些是可以延迟执行的。我们可以有针对性的把请求行为捆绑起来,延迟到某个时刻统一发起请求。
    • 对传输的数据进行压缩网络传输数据量的大小主要由两部分组成:图片与序列化的数据,那么我们需要做的就是减少这两部分的数据传输大小。
    • 多连接对于较大文件,如大图片、文件下载可考虑多连接。 需要控制请求的最大并发量,毕竟移动端网络受限。
    • 避免不必要的同步操作应用程序的一个基础功能是能够保持确保界面上呈现的信息是即时最新的,例如呈现最新的新闻,天气,信息流等等信息。但是,过于频繁的促使手机客户端应用去同步最新的服务器数据会对性能产生很大的负面影响,不仅仅使得CPU不停的在工作,内存,网络流量,电量等等都会持续的被消耗,所以在进行网络请求操作的时候一定要避免多度同步操作。
    • 做好网络数据的缓存从手机的缓存中直接读取数据肯定比从网络上获取数据要更加的便捷高效,特别是对于那些会被频繁访问到的数据,需要把这些数据缓存到设备上,以便更加快速的进行访问。

    参考链接:

    http://www.trinea.cn/android/mobile-performance-optimization/
    http://hukai.me/android-performance-patterns-season-4/
    http://hukai.me/android-performance-patterns-season-2/


    来源:伯乐在线专栏作者 - PleaseCallMeCoder
    链接:http://android.jobbole.com/83394/


    篇二:Android网络请求心路历程

    网络请求是android客户端很重要的部分。下面从入门级开始介绍下自己Android网络请求的实践历程。希望能给刚接触Android网络部分的朋友一些帮助。
    本文包含:

    • HTTP请求&响应
    • Get&Post
    • HttpClient & HttpURLConnection
    • 同步&异步
    • HTTP缓存机制
    • Volley&OkHttp
    • Retrofit&RestAPI
    • 网络图片加载优化
    • Fresco&Glide
    • 图片管理方案

    HTTP请求&响应

    既然说从入门级开始就说说Http请求包的结构。
    一次请求就是向目标服务器发送一串文本。什么样的文本?有下面结构的文本。

    这里写图片描述
    HTTP请求包结构

    例子:

        POST /meme.php/home/user/login HTTP/1.1
        Host: 114.215.86.90
        Cache-Control: no-cache
        Postman-Token: bd243d6b-da03-902f-0a2c-8e9377f6f6ed
        Content-Type: application/x-www-form-urlencoded
    
        tel=13637829200&password=123456

    请求了就会收到响应包(如果对面存在HTTP服务器)
    HTTP响应包结构
    这里写图片描述
    例子:

        HTTP/1.1 200 OK
        Date: Sat, 02 Jan 2016 13:20:55 GMT
        Server: Apache/2.4.6 (CentOS) PHP/5.6.14
        X-Powered-By: PHP/5.6.14
        Content-Length: 78
        Keep-Alive: timeout=5, max=100
        Connection: Keep-Alive
        Content-Type: application/json; charset=utf-8
    
        {"status":202,"info":"\u6b64\u7528\u6237\u4e0d\u5b58\u5728\uff01","data":null}

    Http请求方式有如下几种,常用只有Post与Get。

    方法 描述
    GET 请求指定url的数据,请求体为空(例如打开网页)。
    POST 请求指定url的数据,同时传递参数(在请求体中)。
    HEAD 类似于get请求,只不过返回的响应体为空,用于获取响应头。
    PUT 从客户端向服务器传送的数据取代指定的文档的内容。
    DELETE 请求服务器删除指定的页面。
    CONNECT HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
    OPTIONS 允许客户端查看服务器的性能。
    TRACE 回显服务器收到的请求,主要用于测试或诊断。

    Get&Post

    网络请求中我们常用键值对来传输参数(少部分api用json来传递,毕竟不是主流)。
    通过上面的介绍,可以看出虽然Post与Get本意一个是表单提交一个是请求页面,但本质并没有什么区别。下面说说参数在这2者的位置。

    • Get方式
      在url中填写参数:
      http://xxxx.xx.com/xx.php?params1=value1&params2=value2

    甚至使用路由

      http://xxxx.xx.com/xxx/value1/value2/value3

    这些就是web服务器框架的事了。

    • Post方式
      参数是经过编码放在请求体中的。编码包括x-www-form-urlencodedform-data
      x-www-form-urlencoded的编码方式是这样:
      tel=13637829200&password=123456

    form-data的编码方式是这样:

     ----WebKitFormBoundary7MA4YWxkTrZu0gW
      Content-Disposition: form-data; name="tel"
    
      13637829200
      ----WebKitFormBoundary7MA4YWxkTrZu0gW
      Content-Disposition: form-data; name="password"
    
      123456
      ----WebKitFormBoundary7MA4YWxkTrZu0gW

    x-www-form-urlencoded的优越性就很明显了。不过x-www-form-urlencoded只能传键值对,但是form-data可以传二进制

    因为url是存在于请求行中的。
    所以Get与Post区别本质就是参数是放在请求行中还是放在请求体
    当然无论用哪种都能放在请求头中。一般在请求头中放一些发送端的常量。

    有人说:

    • Get是明文,Post隐藏
      移动端不是浏览器,不用https全都是明文。
    • Get传递数据上限XXX
      胡说。有限制的是浏览器中的url长度,不是Http协议,移动端请求无影响。Http服务器部分有限制的设置一下即可。
    • Get中文需要编码
      是真的…要注意。URLEncoder.encode(params, "gbk");

    还是建议用post规范参数传递方式。并没有什么更优秀,只是大家都这样社会更和谐。

    上面说的是请求。下面说响应。

    请求是键值对,但返回数据我们常用Json。
    对于内存中的结构数据,肯定要用数据描述语言将对象序列化成文本,再用Http传递,接收端并从文本还原成结构数据。
    对象(服务器)<–>文本(Http传输)<–>对象(移动端) 。

    服务器返回的数据大部分都是复杂的结构数据,所以Json最适合。
    Json解析库有很多Google的Gson,阿里的FastJson。
    Gson的用法看这里

    HttpClient & HttpURLConnection

    HttpClient早被废弃了,谁更好这种问题也只有经验落后的面试官才会问。具体原因可以看这里。

    下面说说HttpURLConnection的用法。
    最开始接触的就是这个。

      public class NetUtils {
            public static String post(String url, String content) {
                HttpURLConnection conn = null;
                try {
                    // 创建一个URL对象
                    URL mURL = new URL(url);
                    // 调用URL的openConnection()方法,获取HttpURLConnection对象
                    conn = (HttpURLConnection) mURL.openConnection();
    
                    conn.setRequestMethod("POST");// 设置请求方法为post
                    conn.setReadTimeout(5000);// 设置读取超时为5秒
                    conn.setConnectTimeout(10000);// 设置连接网络超时为10秒
                    conn.setDoOutput(true);// 设置此方法,允许向服务器输出内容
    
                    // post请求的参数
                    String data = content;
                    // 获得一个输出流,向服务器写数据,默认情况下,系统不允许向服务器输出内容
                    OutputStream out = conn.getOutputStream();// 获得一个输出流,向服务器写数据
                    out.write(data.getBytes());
                    out.flush();
                    out.close();
    
                    int responseCode = conn.getResponseCode();// 调用此方法就不必再使用conn.connect()方法
                    if (responseCode == 200) {
    
                        InputStream is = conn.getInputStream();
                        String response = getStringFromInputStream(is);
                        return response;
                    } else {
                        throw new NetworkErrorException("response status is "+responseCode);
                    }
    
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (conn != null) {
                        conn.disconnect();// 关闭连接
                    }
                }
    
                return null;
            }
    
            public static String get(String url) {
                HttpURLConnection conn = null;
                try {
                    // 利用string url构建URL对象
                    URL mURL = new URL(url);
                    conn = (HttpURLConnection) mURL.openConnection();
    
                    conn.setRequestMethod("GET");
                    conn.setReadTimeout(5000);
                    conn.setConnectTimeout(10000);
    
                    int responseCode = conn.getResponseCode();
                    if (responseCode == 200) {
    
                        InputStream is = conn.getInputStream();
                        String response = getStringFromInputStream(is);
                        return response;
                    } else {
                        throw new NetworkErrorException("response status is "+responseCode);
                    }
    
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
    
                    if (conn != null) {
                        conn.disconnect();
                    }
                }
    
                return null;
            }
    
            private static String getStringFromInputStream(InputStream is)
                    throws IOException {
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                // 模板代码 必须熟练
                byte[] buffer = new byte[1024];
                int len = -1;
                while ((len = is.read(buffer)) != -1) {
                    os.write(buffer, 0, len);
                }
                is.close();
                String state = os.toString();// 把流中的数据转换成字符串,采用的编码是utf-8(模拟器默认编码)
                os.close();
                return state;
            }
        }

    注意网络权限!被坑了多少次。

    <uses-permission android:name="android.permission.INTERNET"/>

    同步&异步

    这2个概念仅存在于多线程编程中。

    android中默认只有一个主线程,也叫UI线程。因为View绘制只能在这个线程内进行。
    所以如果你阻塞了(某些操作使这个线程在此处运行了N秒)这个线程,这期间View绘制将不能进行,UI就会卡。所以要极力避免在UI线程进行耗时操作。

    网络请求是一个典型耗时操作。

    通过上面的Utils类进行网络请求只有一行代码。

    NetUtils.get("http://www.baidu.com");//这行代码将执行几百毫秒。

    如果你这样写

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            String response = Utils.get("http://www.baidu.com");
        }

    就会死。。
    这就是同步方式。直接耗时操作阻塞线程直到数据接收完毕然后返回。Android不允许的。

    异步方式:

        //在主线程new的Handler,就会在主线程进行后续处理。
        private Handler handler = new Handler();
        private TextView textView;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            textView = (TextView) findViewById(R.id.text);
            new Thread(new Runnable() {
                @Override
                public void run() {
                        //从网络获取数据
                    final String response = NetUtils.get("http://www.baidu.com");
                        //向Handler发送处理操作
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                                //在UI线程更新UI
                            textView.setText(response);
                        }
                    });
                }
            }).start();
        }

    在子线程进行耗时操作,完成后通过Handler将更新UI的操作发送到主线程执行。这就叫异步。Handler是一个Android线程模型中重要的东西,与网络无关便不说了。关于Handler不了解就先去Google一下。
    关于Handler原理一篇不错的文章

    但这样写好难看。异步通常伴随者他的好基友回调。
    这是通过回调封装的Utils类。

        public class AsynNetUtils {
            public interface Callback{
                void onResponse(String response);
            }
    
            public static void get(final String url, final Callback callback){
                final Handler handler = new Handler();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        final String response = NetUtils.get(url);
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                callback.onResponse(response);
                            }
                        });
                    }
                }).start();
            }
    
            public static void post(final String url, final String content, final Callback callback){
                final Handler handler = new Handler();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        final String response = NetUtils.post(url,content);
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                callback.onResponse(response);
                            }
                        });
                    }
                }).start();
            }
        }

    然后使用方法。

        private TextView textView;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            textView = (TextView) findViewById(R.id.webview);
            AsynNetUtils.get("http://www.baidu.com", new AsynNetUtils.Callback() {
                @Override
                public void onResponse(String response) {
                    textView.setText(response);
                }
            });

    是不是优雅很多。
    嗯,一个蠢到哭的网络请求方案成型了。
    愚蠢的地方有很多:

    • 每次都new Thread,new Handler消耗过大
    • 没有异常处理机制
    • 没有缓存机制
    • 没有完善的API(请求头,参数,编码,拦截器等)与调试模式
    • 没有Https

    HTTP缓存机制

    缓存对于移动端是非常重要的存在。

    • 减少请求次数,减小服务器压力.
    • 本地数据读取速度更快,让页面不会空白几百毫秒。
    • 在无网络的情况下提供数据。
      缓存一般由服务器控制(通过某些方式可以本地控制缓存,比如向过滤器添加缓存控制信息)。通过在请求头添加下面几个字端:

    Request

    请求头字段 意义
    If-Modified-Since: Sun, 03 Jan 2016 03:47:16 GMT 缓存文件的最后修改时间。
    If-None-Match: “3415g77s19tc3:0” 缓存文件的Etag(Hash)值
    Cache-Control: no-cache 不使用缓存
    Pragma: no-cache 不使用缓存

    Response

    响应头字段 意义
    Cache-Control: public 响应被共有缓存,移动端无用
    Cache-Control: private 响应被私有缓存,移动端无用
    Cache-Control:no-cache 不缓存
    Cache-Control:no-store 不缓存
    Cache-Control: max-age=60 60秒之后缓存过期(相对时间)
    Date: Sun, 03 Jan 2016 04:07:01 GMT 当前response发送的时间
    Expires: Sun, 03 Jan 2016 07:07:01 GMT 缓存过期的时间(绝对时间)
    Last-Modified: Sun, 03 Jan 2016 04:07:01 GMT 服务器端文件的最后修改时间
    ETag: “3415g77s19tc3:0” 服务器端文件的Etag[Hash]值

    正式使用时按需求也许只包含其中部分字段。

    客户端要根据这些信息储存这次请求信息。

    然后在客户端发起请求的时候要检查缓存。遵循下面步骤:

    这里写图片描述

    注意服务器返回304意思是数据没有变动滚去读缓存信息。
    曾经年轻的我为自己写的网络请求框架添加完善了缓存机制,还沾沾自喜,直到有一天我看到了下面2个东西。(/TДT)/

    Volley&OkHttp

    Volley&OkHttp应该是现在最常用的网络请求库。用法也非常相似。都是用构造请求加入请求队列的方式管理网络请求。

    先说Volley:
    Volley可以通过这个库进行依赖.
    Volley在Android 2.3及以上版本,使用的是HttpURLConnection,而在Android 2.2及以下版本,使用的是HttpClient。
    Volley的基本用法,网上资料无数,这里推荐郭霖大神的博客
    Volley存在一个缓存线程,一个网络请求线程池(默认4个线程)。
    Volley这样直接用开发效率会比较低,我将我使用Volley时的各种技巧封装成了一个库RequestVolly.
    我在这个库中将构造请求的方式封装为了函数式调用。维持一个全局的请求队列,拓展一些方便的API。

    不过再怎么封装 Volley 在功能拓展性上始终无法与 OkHttp 相比。
    Volley 停止了更新,而OkHttp得到了官方的认可,并在不断优化。
    因此我最终替换为了 OkHttp

    OkHttp 用法见这里
    很友好的 API 与详尽的文档。
    这篇文章也写的很详细了。
    OkHttp 使用 Okio 进行数据传输。都是 Square 家的。
    但并不是直接用 OkHttp。Square 公司还出了一个 Retrofit 库配合 OkHttp 战斗力翻倍。

    Retrofit&RestAPI

    Retrofit 极大的简化了网络请求的操作,它应该说只是一个 Rest API管理库,它是直接使用 OKHttp 进行网络请求并不影响你对 OkHttp 进行配置。毕竟都是Square公司出品。
    RestAPI 是一种软件设计风格。
    服务器作为资源存放地。客户端去请求 GET,PUT, POST,DELETE 资源。并且是无状态的,没有 session 的参与。
    移动端与服务器交互最重要的就是 API 的设计。比如这是一个标准的登录接口。
    这里写图片描述

    你们应该看的出这个接口对应的请求包与响应包大概是什么样子吧。
    请求方式,请求参数,响应数据,都很清晰。
    使用Retrofit这些API可以直观的体现在代码中。

    这里写图片描述
    然后使用Retrofit提供给你的这个接口的实现类 就能直接进行网络请求获得结构数据。

    注意Retrofit2.0相较1.9进行了大量不兼容更新。google上大部分教程都是基于1.9的。这里有个2.0的教程。

    教程里进行异步请求是使用Call。Retrofit最强大的地方在于支持RxJava。就像我上图中返回的是一个Observable。RxJava上手难度比较高,但用过就再也离不开了。Retrofit+OkHttp+RxJava配合框架打出成吨的输出,这里不再多说。

    网络请求学习到这里我觉得已经到顶了。。

    网络图片加载优化

    对于图片的传输,就像上面的登录接口的avatar字段,并不会直接把图片写在返回内容里,而是给一个图片的地址。需要时再去加载。

    如果你直接用HttpURLConnection去取一张图片,你办得到,不过没优化就只是个BUG不断demo。绝对不能正式使用。
    注意网络图片有些特点:

    1. 它永远不会变
      一个链接对应的图片一般永远不会变,所以当第一次加载了图片时,就应该予以永久缓存,以后就不再网络请求。
    2. 它很占内存
      一张图片小的几十k多的几M高清无码。尺寸也是64*64到2k图。你不能就这样直接显示到UI,甚至不能直接放进内存。
    3. 它要加载很久
      加载一张图片需要几百ms到几m。这期间的UI占位图功能也是必须考虑的。
      说说我在上面提到的 RequestVolley 里做的图片请求处理(没错我做了,这部分的代码可以去github里看源码)。

    三级缓存

    网上常说三级缓存--服务器,文件,内存。不过我觉得服务器不算是一级缓存,那就是数据源嘛。

    内存缓存

    首先内存缓存使用LruCache。LRU是Least Recently Used 近期最少使用算法,这里确定一个大小,当Map里对象大小总和大于这个大小时将使用频率最低的对象释放。我将内存大小限制为进程可用内存的1/8.
    内存缓存里读得到的数据就直接返回,读不到的向硬盘缓存要数据。

    硬盘缓存

    硬盘缓存使用DiskLruCache。这个类不在API中。得复制使用。
    看见LRU就明白了吧。我将硬盘缓存大小设置为100M。

     @Override
      public void putBitmap(String url, Bitmap bitmap) {
          put(url, bitmap);
          //向内存Lru缓存存放数据时,主动放进硬盘缓存里
          try {
              Editor editor = mDiskLruCache.edit(hashKeyForDisk(url));
              bitmap.compress(Bitmap.CompressFormat.JPEG, 100, editor.newOutputStream(0));
              editor.commit();
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
    
      //当内存Lru缓存中没有所需数据时,调用创造。
      @Override
      protected Bitmap create(String url) {
          //获取key
          String key = hashKeyForDisk(url);
          //从硬盘读取数据
          Bitmap bitmap = null;
          try {
              DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
              if(snapShot!=null){
                  bitmap = BitmapFactory.decodeStream(snapShot.getInputStream(0));
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
          return bitmap;
      }

    DiskLruCache的原理不再解释了(我还解决了它存在的一个BUG,向Log中添加的数据增删记录时,最后一条没有输出,导致最后一条缓存一直失效。)

    硬盘缓存也没有数据就返回空,然后就向服务器请求数据。
    这就是整个流程。
    但我这样的处理方案还是有很多局限。

    • 图片未经压缩处理直接存储使用
    • 文件操作在主线程
    • 没有完善的图片处理API
    • 以前也觉得这样已经足够好直到我遇到下面俩。

    Fresco&Glide

    不用想也知道它们都做了非常完善的优化,重复造轮子的行为很蠢。
    Fresco 是 Facebook 公司的黑科技。光看功能介绍就看出非常强大。使用方法官方博客说的够详细了。
    真三级缓存,变换后的BItmap(内存),变换前的原始图片(内存),硬盘缓存。
    在内存管理上做到了极致。对于重度图片使用的APP应该是非常好的。
    它一般是直接使用SimpleDraweeView来替换ImageView,呃~侵入性较强,依赖上它 apk 包直接大1M。代码量惊人。

    所以我更喜欢 Glide,作者是 bumptech。这个库被广泛的运用在 google 的开源项目中,包括2014年google I/O 大会上发布的官方 app。
    这里有详细介绍。直接使用ImageView即可,无需初始化,极简的 API,丰富的拓展,链式调用都是我喜欢的。
    丰富的拓展指的就是这个。
    另外我也用过 Picasso。API 与 Glide 简直一模一样,功能略少,且有半年未修复的 BUG。

    图片管理方案

    再说说图片存储。不要存在自己服务器上面,徒增流量压力,还没有图片处理功能。
    推荐七牛与阿里云存储(没用过其它 π__π )。它们都有很重要的一项图片处理。在图片Url上加上参数来对图片进行一些处理再传输。
    于是(七牛的处理代码)

      public static String getSmallImage(String image){
            if (image==null)return null;
            if (isQiniuAddress(image)) image+="?imageView2/0/w/"+IMAGE_SIZE_SMALL;
            return image;
        }
    
        public static String getLargeImage(String image){
            if (image==null)return null;
            if (isQiniuAddress(image)) image+="?imageView2/0/w/"+IMAGE_SIZE_LARGE;
            return image;
        }
    
        public static String getSizeImage(String image,int width){
            if (image==null)return null;
            if (isQiniuAddress(image)) image+="?imageView2/0/w/"+width;
            return image;
        }

    既可以加快请求速度,又能减少流量。再配合Fresco或Glide。完美的图片加载方案。
    不过这就需要你把所有图片都存放在七牛或阿里云,这样也不错。

    图片/文件上传也都是使用它们第三方存储,它们都有SDK与官方文档教你。
    不过图片一定要压缩过后上传。上传1-2M大的高清照片没意义。


    文/Jude95(简书作者)
    原文:原文
    著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。


    篇三:Android开源项目推荐之「网络请求哪家强」

    原则

    本篇说的网络请求专指 http 请求,在选择一个框架之前,我个人有个习惯,就是我喜欢选择专注的库,其实在软件设计领域有一个原则叫做 「单一职责原则」,跟我所说的「专注」不谋而合,一个库能把一件事做好就很不错了。现如今有很多大而全的库,比如这个库可以网络请求,同时又可以图片加载,又可以数据存储,又可以 View 注解等等,我们使用这种库当然方便了,但是你有没有想过?这样会使得你整个项目对它依赖性太强,万一以后这个库不维护了,或者中间某个模块出问题了,这个影响非常大,而且我一直认为大而全的框架可能某一块都做的不够好,所以我在选择的时候更喜欢专注某一领域的框架。

    在上面原则的基础上,所以目前来说单纯的网络请求库就锁定在了 Volley、OkHttp、Retrofit 三个,android-async-http 的作者已经不维护,所以这里就不多说了,下面我们分别来说说这三个库的区别。

    一、 OkHttp

    我们知道在 Android 开发中是可以直接使用现成的 api 进行网络请求的,就是使用 HttpClient、HttpUrlConnection 进行操作,目前 HttpClient 已经被废弃,而 android-async-http 是基于 HttpClient 的,我想可能也是因为这个原因作者放弃维护。

    而 OkHttp 是 Square 公司开源的针对 Java 和 Android 程序,封装的一个高性能 http 请求库,所以它的职责跟 HttpUrlConnection 是一样的,支持 spdy、http 2.0、websocket ,支持同步、异步,而且 OkHttp 又封装了线程池,封装了数据转换,封装了参数使用、错误处理等,api 使用起来更加方便。可以把它理解成是一个封装之后的类似 HttpUrlConnection 的一个东西,但是你在使用的时候仍然需要自己再做一层封装,这样才能像使用一个框架一样更加顺手。

    OkHttp 的具体使用方法这里就不赘述,地址在这里:

    http://square.github.io/okhttp/

    二、 Volley

    Volley 是 Google 官方出的一套小而巧的异步请求库,该框架封装的扩展性很强,支持 HttpClient、HttpUrlConnection,甚至支持 OkHttp,具体方法可以看 Jake 大神的这个 Gist 文件:

    https://gist.github.com/JakeWharton/5616899

    而且 Volley 里面也封装了 ImageLoader ,所以如果你愿意你甚至不需要使用图片加载框架,不过这块功能没有一些专门的图片加载框架强大,对于简单的需求可以使用,对于稍复杂点的需求还是需要用到专门的图片加载框架。

    Volley 也有缺陷,比如不支持 post 大数据,所以不适合上传文件。不过 Volley 设计的初衷本身也就是为频繁的、数据量小的网络请求而生!
    关于 Volley 的具体用法可以见我很早在 GitHub 的一个 demo :

    https://github.com/stormzhang/AndroidVolley

    三、 Retrofit

    Retrofit 是 Square 公司出品的默认基于 OkHttp 封装的一套 RESTful 网络请求框架,不了解 RESTful 概念的不妨去搜索学习下,RESTful 可以说是目前流行的一套 api 设计的风格,并不是标准。Retrofit 的封装可以说是很强大,里面涉及到一堆的设计模式,你可以通过注解直接配置请求,你可以使用不同的 http 客户端,虽然默认是用 http ,可以使用不同 Json Converter 来序列化数据,同时提供对 RxJava 的支持,使用 Retrofit + OkHttp + RxJava + Dagger2 可以说是目前比较潮的一套框架,但是需要有比较高的门槛。

    Retrofit 的具体使用方法与地址在这里:

    http://square.github.io/retrofit/

    四 、Volley VS OkHttp

    毫无疑问 Volley 的优势在于封装的更好,而使用 OkHttp 你需要有足够的能力再进行一次封装。而 OkHttp 的优势在于性能更高,因为 OkHttp 基于 NIO 和 Okio ,所以性能上要比 Volley更快。

    估计有些读者不理解 IO 和 NIO 的概念,这里姑且简单提下,这两个都是 Java 中的概念,如果我从硬盘读取数据,第一种方式就是程序一直等,数据读完后才能继续操作,这种是最简单的也叫阻塞式 IO,还有一种就是你读你的,我程序接着往下执行,等数据处理完你再来通知我,然后再处理回调。而第二种就是 NIO 的方式,非阻塞式。

    所以 NIO 当然要比 IO 的性能要好了, 而 Okio 是 Square 公司基于 IO 和 NIO 基础上做的一个更简单、高效处理数据流的一个库。

    理论上如果 Volley 和 OkHttp 对比的话,我更倾向于使用 Volley,因为 Volley 内部同样支持使用 OkHttp ,这点 OkHttp 的性能优势就没了,而且 Volley 本身封装的也更易用,扩展性更好些。

    五、 OkHttp VS Retrofit

    毫无疑问,Retrofit 默认是基于 OkHttp 而做的封装,这点来说没有可比性,肯定首选 Retrofit。

    六、 Volley VS Retrofit

    这两个库都做了非常不错的封装,但是 Retrofit 解耦的更彻底,尤其 Retrofit 2.0 出来,Jake 对之前 1.0 设计不合理的地方做了大量重构,职责更细分,而且 Retrofit 默认使用 OkHttp ,性能上也要比 Volley 占优势,再有如果你的项目如果采用了 RxJava ,那更该使用 Retrofit 。

    所以说这两个库相比,Retrofit 毫无疑问更有优势,你在能掌握两个框架的前提下该优先使用 Retrofit。但是个人认为 Retrofit 门槛要比 Volley 稍高些,你要理解他的原理,各种用法,想彻底搞明白还是需要花些功夫的,如果你对它一知半解,那还是建议在商业项目使用 Volley 吧。

    七、 总结

    所以综上,如果以上三种网络库你都能熟练掌握,那么优先推荐使用 Retrofit ,前提是最好你们的后台 api 也能遵循 RESTful 的风格,其次如果你不想使用或者没能力掌握 Retrofit ,那么推荐使用 Volley ,毕竟 Volley 你不需要做过多的封装,当然如果你们需要上传大数据,那么不建议使用 Volley,否则你该采用 OkHttp 。

    最后,我知道可能有些人会纠结 Volley 与 OkHttp 的选择,那是因为我认为 OkHttp 还是需要一定的能力做一层封装的,如果你有能力封装的话那不如直接用 Retrofit 了,如果没能力封装还是乖乖的用 Volley 吧,如果你能有一些不错的基于 OkHttp 封装好的开源库,那么另说了,Volley 与 OkHttp 怎么选择随你便呗。

    最最后,以上只是我一家之言,如有误导,概不负责!欢迎讨论与交流。

    展开全文
  • android网络请求失败原因

    千次阅读 2017-04-21 10:35:46
    android网络请求失败原因
  • Android网络编程

    千次阅读 2012-05-10 19:17:27
    Android平台有三种网络接口可以使用,他们分别是:java.net.*(标准Java接口)、Org.apache接口和Android.net.*(Android网络接口)。下面分别介绍这些接口的功能和作用。 1.标准Java接口 java.net.*提供与联网有关的...
  • android网络编程及网络超时处理

    千次阅读 2016-08-19 14:21:06
    android网络编程及网络超时处理 本文是收录的两篇关于这方面的文章 Android超时机制的处理   由于手机端应用的响应,与当时的无线通信网络状况有很大的关联。而通信网络往往具有不稳定,延迟长的特点。所以...
  • Android网络编程之获取网络上的Json

    千次阅读 2014-09-27 00:21:19
    Android网络编程之获取网络上的Json 请尊重他人的劳动成果,转载请注明出处:Android网络编程之获取网络上的Json  为要获取网络上的Json所以需要服务器端提供的支持。 一、创建服务器端: 服务器端项目结构: ...
  • Android 网络编程 记录

    千次阅读 2014-05-08 21:21:57
    简介看了深入理解Android网络编程感觉不错,今天对Android网络编程进行了要点记录。内容Android基于网络技术和编程实践 要点 定义 描述 IP协议用于报文交换网络的一种面向数据的协议 TCP协议传输...
  • 本节展示网络数据请HttpURLConnection的post请求方式。 在Android应用程序中,...HttpURLConnection是最基础最简单的Android网络数据接口API。如果你的数据量不是很大的话,可以用HttpURLConnection来进行数据请求。
  • Android网络编程概述

    千次阅读 2015-03-09 21:48:33
    Android网络编程概述 首先,应该了解的几个问题: 1)Android平台网络相关API接口  a) java.net.*(标准Java接口)  java.net.*提供与联网有关的类,包括流、数据包套接字(socket)、Internet协议、常见Http...
  • 不知不觉终于来到Android网络编程这一章节,前面我们玩的都是单机,肯定是不过瘾是吧, 本节开始我们来学习Android网络编程相关的一些东西:Android端网络编程要干嘛?Http协议 的学习,使用自带扣脚Json解析类...
  • android网络相关权限

    千次阅读 2020-11-23 13:04:30
    uses-permission android:name="android.permission.INTERNET"> </uses-permission> <!-- 允许访问网络状态的权限 --> <uses-permission android:name="android.permission.ACCESS_NETWORK_...
  • Android网络开发 请求队列

    千次阅读 2015-11-02 17:14:53
    欢迎使用Markdown编辑器写博客本Markdown编辑器使用[StackEdit][6]修改而来,用它写博客,将会带来全新的体验哦:Android网络开发 请求队列文章出处:Android那些事儿的博客因为之前参与的网络开发项目都遇到一些...
  • Android网络编程(一)

    万次阅读 多人点赞 2012-11-22 17:19:24
    Android网络编程概述 首先,应该了解的几个问题: 1)Android平台网络相关API接口  a) java.net.*(标准Java接口)  java.net.*提供与联网有关的类,包括流、数据包套接字(socket)、Internet协议、常见...
  • Android 网络通信框架Volley的二次封装

    千次阅读 2016-04-02 18:31:09
    在android开发中不可避免地需要用到网络访问,多数情况下会...HttpURLConnection和HttpClient的用法还是稍微有些复杂,如果没有进行封装的话,很容易写出不少重复代码。这时就出现很多Android网络通信框架,比如AsyncHt
  • Android网络连接网络

    千次阅读 2013-03-10 17:58:35
    Android中,ConnectivityManager类代表网络连接服务,它被用来监控网络连接状态,配置失效重连,并控制网络天线等。获取Connectivity Manager实例的方法是使用getSystemService,并指定Context.CONNECTIVITY_...
  • Android网络连接判断

    千次阅读 2019-02-28 08:44:27
    要在android中判断网络是否连接,首先得在AndroidManifest.xml配置文件加入网络权限 &lt;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /&gt;&lt;uses-permission...
  • Android 网络权限配置

    千次阅读 2019-04-03 18:44:14
    Android开发应用程序时,如果应用程序需要访问网络权限,需要在 AndroidManifest.xml 中加入以下代码 如果不知道放在哪个位置,那就放在前边好了。 同样的如果用到其它的权限,也需要作出声明,部分权限列表如下: ...
  • ,Android 网络编程相关的包 : 9 包, 20 接口, 103 类, 6 枚举, 14异常; -- Java包 : java.net 包 (6接口, 34类, 2枚举, 12异常);-- Android包 : android.net 包 (1接口, 19类, 3枚举, 1异常), android.net.http 包...
  • 近期做简单的新闻客户端界面使用到了Jsoup获取,使用起来特别方便,这也是被我一个学长称为学android网络必学的一个东西,在此也是分享一下自己近期所学。   首先还是给出效果:     上面是通过textview...
  • Android网络判断

    千次阅读 2017-12-03 20:57:50
    关于网络判断,首先建立一个工具类,在需要进行网络判断的时候调用public class NetworkUtils { private NetworkUtils() { throw new UnsupportedOperationException("u can't instance me..."); } public static ...
  • Android 网络质量 + 下载测速 facebook/network-connection-class使用 facebook/network-connection-class源码地址 facebook/network-connection-class gitHub地址 使用方式 添加依赖 // 网络测速 compile '...
  • android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.scwang.smartrefresh.layout.SmartRefreshLayout android:id="@+id/smart_layout" ...
  • ym—— Android网络框架Volley(终极篇)

    万次阅读 2014-10-30 14:52:56
    没看使用过Volley的同学可以,先看看 Android网络框架Volley(体验篇)和Android网络框架Volley(实战篇)在进行本篇学习这样会更好的理解本篇内容. 本篇内容主要是讲Volley在某些细节方面的选择和实现.值得我们学习的...
  • Android 网络框架Volley JAR包生成

    千次阅读 2015-11-29 15:27:07
    Google I/O 2013上,谷歌发布了Android网络通信框架Volley,以前我都是直接使用源码编译的,那么问题来了,怎么直接打包成jar包使用呢? 不要着急,请看如下步骤: 1、下载源码 git clone ...
  • Android 网络权限问题

    千次阅读 2019-09-16 18:03:26
    Android 在调用socket创建套接字时出错,socket: Permission denied 对应errno为13,原因 https://developer.android.com/guide/topics/manifest/uses-permission-element.html 在AndroidManifest.xml中添加 如下图...
  • Android 网络管家ConnectivityManager

    万次阅读 2017-05-03 17:52:15
    Android提供了多种网络与数据传输通道,最常见的是WIFI和移动基站通信(Cellular),同时Android还可以将终端本身作为热点(Wifi Access Point),也可通过WIFI进行将两个终端直接进行连接,从而交互数据;同时还可利用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 85,113
精华内容 34,045
关键字:

android网络