• 安卓权威编程指南-笔记(第23章 HTTP与后台任务)


    1. 网络连接基本

     1 //通过指定URL获取原始数据,并返回一个字节流数组。
     2     public byte[] getUrlBytes(String urlSpec)throws IOException{
     3 
     4         //根据传入的字符串参数,创建一个URL对象
     5         URL url = new URL(urlSpec);
     6         //通过url.openConnection()方法得到HttpUrlConnection对象。
     7         HttpURLConnection connection = (HttpURLConnection)url.openConnection();
     8 
     9         try{
    10             ByteArrayOutputStream out = new ByteArrayOutputStream();
    11            /*
    12            *  虽然 HttpURLConnection 对象提供了一个连接,但只有在调用 getInputStream() 方法时
    13            *   (如果是POST请求,则调用 getOutputStream() 方法),它才会真正连接到指定的URL地址。
    14             */
    15             InputStream in = connection.getInputStream();
    16 
    17             //如果连接失败就抛出错误
    18             if(connection.getResponseCode() != HttpURLConnection.HTTP_OK){
    19                 throw new IOException(connection.getResponseMessage() + ": with" + urlSpec);
    20             }
    21 
    22             //写入
    23             int bytesRead = 0;
    24             byte[] buffer = new byte[1024];
    25             while((bytesRead = in.read(buffer)) > 0){
    26                 out.write(buffer,0,bytesRead);
    27             }
    28             out.close();
    29             return out.toByteArray();
    30         }finally {
    31             connection.disconnect();
    32         }
    33     }
    34 
    35     //将getUrlBytes(String)方法返回的结果转换成String
    36     public String getUrlString(String urlSpec)throws IOException{
    37         return new String(getUrlBytes(urlSpec));
    38     }

    最后记得在AndroidManifest.xml文件中添加联网权限。

    2. 线程与主线程

    线程是个单一执行序列。单个线程中的代码会逐步执行。所有Android应用的运行都是从主线程开始的。然而,主线程不是线程那样的预定执行序列。相反,它处于一个无限循环的运行状态,等待着用户或系统触发事件的发生。事件触发后,主线程便负责执行代码,以响应这些事件。

    事件处理循环让UI代码得以按顺序执行。这可以保证任何事件处理都不会发生冲突,同时代码也能够快速响应执行。

    网络连接需要时间,Web 服务器可能需要1~2秒的时间来响应访问请求,文件下载则耗时更久。考虑到这个因素,android 禁止任何主线程网络连接行为。即使强行在主线程中进行网络连接,Android 也会抛出 NetworkOnMainThreadException 异常。

    而网络连接相比其他任务更耗时。等待响应期间,用户界面毫无反应,这可能会导致应用无响应(Application Not Responding,ANR)现象发生,也就是一个弹框,要求你关闭应用。 
    怎样使用后台线程最容易呢?答案就是使用 AsyncTask 类。

    3. AsyncTask

    AsyncTask是一个抽象类 
    在继承AsyncTask类的时候指定3个泛型参数
    [1] Params 在执行AsyncTask时需要传入的参数,可用于在后台任务中使用
    [2] Progress 后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位
    [3] Result 当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型
    
    public class DownloadTask extends AsyncTask<Void,Integer,Boolean> {
    
        //这个方法在后台任务开始执行之前调用,用于进行一些界面上的初始化操作
        @Override
        protected void onPreExecute() {
            progressDialog.show(); //显示进度对话框
            super.onPreExecute();
        }
    
        //当后台任务执行完毕并通过return语句进行返回时,这个方法很快会被调用
        @Override
        protected void onPostExecute(Boolean aBoolean) {
            progressDialog.dismiss();//关闭进度对话框
            if(result){
                Toast.makeText(context,"Download successedd",Toast.LENGTH_SHORT).show();
            }else {
                Toast.makeText(context,"Download failed",Toast.LENGTH_SHORT).show();
            }
            super.onPostExecute(aBoolean);
        }
    
        //当在后台调用了publishProgress(progress...)方法后 此方法很快会被调用 可以对UI进行操作
        @Override
        protected void onProgressUpdate(Integer... values) {
            //在这里更新下载进度
            progressDialog.setMessage("Download"+values[0]+"%");
            super.onProgressUpdate(values);
        }
    
    
        //此方法的代码会在子线程中运行,处理耗时任务用,此方法不可以进行UI操作,但可以调用publishProgress(progress...)来完成
        @Override
        protected Boolean doInBackground(Void... params) {
            try{
                while(true){
                    int downloadPercent = doDownload(); //这是一个虚构的方法
                    publishProgress(downloadPercent);
                    if(downloadPercent >= 100)
                        break;
                }
            }catch (Exception e){
                return false;
            }
            return null;
        }
    }

    AsyncTask的取消

    AsyncTask.cancel(boolean) 方法有两种工作模式:粗暴的和温和的。如果调用 cancel(false) 方法,它只是温和地设置 isCancelled() 的状态为 true 。随后, AsyncTask 会检查doInBackground(...) 方法中的 isCancelled() 状态,然后选择提前结束运行。

    如果调用 cancel(true) 方法,它会粗暴地终止 doInBackground(...) 方法当前所在的线程。

    应该在什么时候、什么地方撤销 AsyncTask 呢?

    要看情况了。先问问自己,如果fragment或activity已销毁了或是看不到了, AsyncTask 当前的工作可以停止吗?如果可以,就在onStop(...) 方法里(看不到视图),或者在 onDestroy(...) 方法里(fragment/activity实例已销毁)撤销 AsyncTask 实例。

    即使fragment/activity已销毁了(或者视图已看不到了),也可以不撤销 AsyncTask ,让它运
    行至结束。不过,这可能会引发潜在的内存泄漏,也可能会出现UI更新问题(因为UI已失效)。如果不管用户怎么操作,一定要保证重要工作的完成,可以考虑其他解决方案,比如使用 Service。

    AsyncTask 的替代方案

    AsyncTaskLoader:是个抽象Loader,可以使用 AsyncTask 把数据加载工作转移到其他线程上。我们创建的loader类几乎都是 AsyncTaskLoader 的子类。 AsyncTaskLoader 能在不阻塞主线程的前提下获取到数据,并把结果发送给目标对象。

    LoaderManager 会帮我们妥善管理loader及其加载的数据。而且, LoaderManager 还负责启动和停止loader,以及管理loader的生命周期。

    4. JSON解析

    JSON对象是一系列包含在 { } 中的名值对。JSON数组是包含在 [ ] 中用逗号隔开的JSON对象列表。对象彼此嵌套形成层级关系。

    son.org API提供有对应JSON数据的Java对象,如 JSONObject 和 JSONArray 。使用JSONObject(String) 构造函数,可以很方便地把JSON数据解析进相应的Java对象。更新fetchItems() 方法执行解析任务

                          左边为要解析的JSON数据。

    JSONObejct解析代码如下:

    private void parseItems(List<GalleryItem> items, JSONObject jsonBody)throws IOException,JSONException{
    
            //得到叫做photos的嵌套JSONObject
            JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
            //得到photos里面的叫photo的JSONArray
            JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
    
            //JsonArray中包含若干个JSONObject, 将这些数据一一取出。
            for(int i=0; i<photoJsonArray.length(); i++){
                JSONObject photoJsonObject = photoJsonArray.getJSONObject(i);
    
                GalleryItem item = new GalleryItem();
    
                item.setId(photoJsonObject.getString("id"));
                item.setCaption(photoJsonObject.getString("title"));
    
                //并不是每个图片都有对应的url_s连接,所以需要添加一个检查。
                if(!photoJsonObject.has("url_s")){
                    continue;
                }
    
                item.setUrl(photoJsonObject.getString("url_s"));
                items.add(item);
            }
        }

     Gson解析方式在23章的挑战练习中。

  • 相关阅读:
    BZOJ 3110 【ZJOI2013】 K大数查询
    【mysql的编程专题⑥】视图
    【mysql的编程专题⑤】自定义函数
    MySQL错误代码大全【转载】
    【mysql的编程专题④】存储过程
    【mysql的编程专题③】内置函数
    【mysql的编程专题】触发器
    【mysql的编程专题①】流程控制与其他语法
    多线程监听串口
    IMX6输出可控PWM
  • 原文地址:https://www.cnblogs.com/chase1/p/7209372.html
Copyright © 2020-2023  润新知