AsyncTask异步任务

Android中的线程问题:

  • 主线程(Main Thread),UI线程。直接写在onCreate等方法中的代码都是写在主线程的,主线程用于负责UI页面的显示效果以及逻辑顺序的处理功能
  • 子线程(Worker Thread),工作线程, 凡是写在自己new Thread中的代码均是运行在子线程中的代码

通常情况下,会在子线程中进行耗时操作

为什么要在子线程中进行耗时操作??

  • 原因:Android系统为单线程模型。即同一时刻只能处理一件事,比如:有2个按钮,单击按钮1后,进行按钮1点击事件的处理,如果此时按钮1的点击事件需要很长事件的处理,那么,在按钮1的时间没处理完的过程中,无法处理其他的用户交互,如:按钮2的点击,返回键的点击等

  • 当主线程处理一件事儿超过5秒以上时,会显示一个ANR对象框ANR: Application Not Responseding 程序无响应.因此为了避免ANR的问题,会将耗时操作写在子线程中.

在Android应用中实现连网操作时,注意以下问题:

  1. Android4.0以上的设备中,不能够在主线程写连网操作的,如果倔强的在主线程中写了连网的代码,那么会爆出NetWorkOnMainThreadException。
  2. 要进行连网操作,必须添加连网权限:
 

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

3.只有主线程中才能进行更改UI的操作,即子线程不能更新UI

  • 为了解决在子线程中连网操作执行完成后,进行更新UI的操作,解决方式有以下3种:

    方式一: 使用封装好的AsyncTask(异步任务)完成此目标
    方式二 : 使用Thread+Hanlder完成此目标
    方式三: 使用封装好的Loader完成此目标

AsyncTask异步任务

  • 特点:此类中有2个特别主要的方法,其中一个方法(doInBackground)中的代码默认运行在子线程中,另一个方法(onPostExecute)会在doInBackground中的代码执行完成后自动调用此方法,并且这个onPostExecute方法中的代码时运行在主线程的

AsyncTask的使用:

  1. 创建一个AsyncTask的子类
  2. 设置AsyncTask的泛型
  3. 按报错要求添加doInBackground方法,并在方法中进行耗时操作(如连网操作)
  4. 手动添加onPostExecute方法,在方法中根据方法参数中的下载结果更新UI

样例如下:

    /*
     * 泛型1:用于限制当启动异步任务时,传递的数据的类型,如果不需要传递数据,在填写Void即可
     * 显示了doInBackground方法以及execute方法的参数类型
     * 泛型3:   用于限制本次连网操作的下载结果的类型
     *  限制了doInBackground方法返回值的类型以及onPostExecute方法的参数类型
     * */
    class MyTask extends AsyncTask<String, Void, Bitmap> {

        /**
         * 此方法中的代码默认运行在子线程中
         * 当此方法中的子线程执行完毕后,会自动调用onPostExecute方法
         * 并且会通过返回值将下载结果传递给onPostExecute方法
         * 
         *  参数:String...代表可变参数,即当调用此方法时(当启动异步任务时),可以传递任意个数的String对象
         */
        @Override
        protected Bitmap doInBackground(String... params) {
            // TODO Auto-generated method stub
            Log.i("oye", "doInBackground中: 当前线程名称为:  "+Thread.currentThread().getName());
            //下载图片并显示
            try {
                HttpURLConnection conn = (HttpURLConnection) new URL(params[0]).openConnection();
                conn.setRequestMethod("GET");
//              conn.setConnectTimeout(timeoutMillis)
                conn.connect();
                //连接成功
                if (conn.getResponseCode() == 200) {
                    // 获取连接的网址返回的数据
                    InputStream is = conn.getInputStream();

                    /**
                     * 当想要将is流中的数据显示到imageview上时
                     * 1. 要根据is流生成Bitmap对象 (Bitmap代表图片对象)
                     * 2. 让iv显示Bitmap对象即可
                     */
                    bitm  = BitmapFactory.decodeStream(is);
                    Log.i("oye", "获取到的应答结果的bitm为:    "+bitm);
                    return bitm;
                }

            } catch (MalformedURLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(Bitmap result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
            if (result != null) {
                iv.setImageBitmap(result);
            }

        }

    }

5 通过子类对象调用execute方法启动异步任务

 public void click (View v) {

        //启动异步任务
        new MyTask().execute(imgUrl);
    }

泛型2的作用:

 泛型2:
     *   显示publishProgress和onPublishProgress方法的参数类型
  • publishProgress和onProgressUpdate方法的作用:
//当子线程下载过程中需要更新UI显示时,调用publishProgress方法,
                        // 调用此方法后,系统会自动调用异步任务子类中onProgressUpdate 方法
                        publishProgress(total,current);
/*
         * 当在doInBackground中调用publishProgress方法时,会运行此方法
         * 此方法用于在下载的过程中更新UI页面
         * */
        //@Override
        protected void onProgressUpdate(Integer... values) {
            // TODO Auto-generated method stub
            super.onProgressUpdate(values);

            dialog.setMax(values[0]);
            dialog.setProgress(values[1]);

            dialog.setTitle(values[1] +"/"+values[0]);
        }

使用异步任务时的注意事项:

  1. 当需要多次启动异步任务时,必须每次新建对象然后通过execute方法启动
    即,以下写法为错误写法:
MyTask mt=new MyTask();
mt.execute(params);
mt.execute(params);

2 多个异步任务同时执行的问题:

public void click (View v) {
        /**
         * 当同时启动多个异步任务时,多个异步任务之间默认是依次排队执行
         * 即只有当第一个异步任务执行完毕之后,第二个异步任务才会开始执行
         */
//      new MyTask(pb1,iv1).execute(imgUrl);
//      new MyTask(pb2,iv2).execute(imgUrl2);

        /**
         * 如果需要让多个异步任务同时执行的话:
         * 创建Executor对象,线程池对象,用于控制当有多个异步任务需要启动下载时,同时可以执行几个异步任务
         * 使用executeOnExceuter方法替代execute方法启动
         */
        Executor exec = Executors.newFixedThreadPool(2);
        new MyTask(pb1,iv1).executeOnExecutor(exec, imgUrl);
        new MyTask(pb2,iv2).executeOnExecutor(exec, imgUrl2);

    }

你可能感兴趣的:(Android疑难点)