android 线程(AsyncTask+Handler)

android的线程是一个很重要的知识点!对用户的操作体验至关重要

一.什么是线程呢??

   给个通俗易懂的一个解释吧!进程好比是一个游泳池,在游泳池当中有很多的水管,每一个水管就相当于是一个线程。而其中有一根主要的水管,其他一些小的水管是给主水管提供辅助帮助,来完成对

泳池进水,出水的任务的。在android线程当中有一个UI线程(主线程),我们自己可以创建一些子线程来帮助主线程进行UI更新等操作。

二.为什么要创建线程呢?

  (1)是为了提高用户的操作体验避免产生ANR现象

    如果所有的操作都放在一个主线程当中,那只有执行完这一步才能执行下一步操作,如果当前操作很耗时(网络等原因),那应用就会一直"卡"在这一步操作当中,用户只有一直等待。等待时间过长

     给用户的体验月糟糕,应用的效益就会越低,以至于到最后没有人会用了。

 (2)对于网络耗时操作的异步处理

       主线程当中的事件处理不能太耗时,否则后续事件无法再5秒内得到响应,就会弹出ANR对话框。

   通常在主线程当中会执行如下一些方法:

    a.Activity的生命周期方法,  OnCreate(),OnStart(), OnResume()等

    b.事件处理方法,例如onClick()、onItemClick()等
        通常Android基类中以on开头的方法是在Main线程被回调的

  对于网络耗时操作就要开辟新的线程来进行处理,并与主线程进行交互。让用户不用等待结果的情况下,可以进行进行操作(如发送图片给服务器用户只管发送,什么时候服务器能接收到就是网络问题了)

三.子线程与主线程的通讯

  (1)android提供了一个辅助类AsyncTask。可以将完成一些耗时的事件,并将事件处理后的结果返回给主线程。但AsyncTask只适合创建单一的线程,创建多线程会比较复杂而且会产生很多重复代码。

下面就对AsyncTask做个简要的讲解:

  API是这样解释AsyncTask:This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers

一个异步任务的执行一般包括以下几个步骤:

  1.execute(Params... params),执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行。

  2.onPreExecute(),在execute(Params... params)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。

  3.doInBackground(Params... params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishProgress(Progress... values)来更新进度信息。

  4.onProgressUpdate(Progress... values),在调用publishProgress(Progress... values)时,此方法被执行,直接将进度信息更新到UI组件上。

  5.onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。

  在使用的时候,有几点需要格外注意:

  1.异步任务的实例必须在UI线程中创建。

  2.execute(Params... params)方法必须在UI线程中调用。

  3.不要手动调用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)这几个方法。

  4.不能在doInBackground(Params... params)中更改UI组件的信息。

  5.一个任务实例只能执行一次,如果执行第二次将会抛出异常

  意思是:这个类允许执行一些后台操作并将结果返回给UI线程而不用去创建线程和handler

  AsyncTsk下载网络图片:

public class MainActivity extends Activity {

    private ImageView image;
    private ProgressDialog dialog;
    private Button button;
    private String imageUrl = "http://img2.3lian.com/img2009/08/3lian14/173.jpg";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        image = (ImageView) findViewById(R.id.imageID);
        button = (Button) findViewById(R.id.button1);
        dialog = new ProgressDialog(MainActivity.this);
        dialog.setTitle("图片下载中");
        dialog.setMessage("请稍后");
        button.setOnClickListener(new Button.OnClickListener() {

            @Override
            public void onClick(View v) {
                new MyAsyncTask().execute(imageUrl);
            }
        });
    }

    /**
     * 这里的String参数对应AsyncTask中的第一个参数 这里的Bitmap返回值对应AsyncTask的第三个参数
     * 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改
     * 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作
     * 
     */
    class MyAsyncTask extends AsyncTask<String, Void, Bitmap> {

        /**
         * 完成耗时操作,注意这里不能直接操作UI
         */
        @Override
        protected Bitmap doInBackground(String... params) {
            HttpClient httpClient = new DefaultHttpClient();
            Bitmap bitmap = null;
            HttpGet httpGet = new HttpGet(params[0]);
            System.out.println(params[0]);
            try {
                HttpResponse response = httpClient.execute(httpGet);
                System.out.println(response.getStatusLine().getStatusCode());
                if (response.getStatusLine().getStatusCode() == 200) {
                    HttpEntity entity = response.getEntity();
                    // 将图片装换成字节流
                    byte[] data = EntityUtils.toByteArray(entity);
                    bitmap = BitmapFactory
                            .decodeByteArray(data, 0, data.length);
                }
            } catch (Exception e) {
                
            }
        
            return bitmap;
        }

        /**
         * 耗时操作执行前所执行的
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            dialog.show();
        }

        /**
         * 耗时操作执行后所执行的,主要执行更新UI的操作
         */
        @Override
        protected void onPostExecute(Bitmap result) {
            super.onPostExecute(result);
            dialog.dismiss();
            // 进行UI更新
            image.setImageBitmap(result);
        }

    }

}
View Code

 

对应的R.layout.activity_main.xml布局文件比较简单,这里就不给出来了

 

要注意是否打开了网络权限!!!

运行效果:

  

  (2)利用Handler来创建线程下载图片

  Handler当中涉及到的一些主要类:

  Handler:就是在android当中发送message和接收message,通过接收到的message来完成与主线程的交互

  Looper:负责管理MessageQueue和消息循环的

  Message:就相当于是货车,装载各种信息

  MessageQueue:消息队列,Message就是在MessageQueue里面进行先进先出的循环,保存有待线程处理的消息。

他们之间的关系是:在其他线程当中调用Handler.sendMessage()方法,将Message添加到主线程当中的MessageQueue当中去,

主线程的Looper从消息队列当中提取Handler发送过来的Message,回调Handler的handlerMessage()方法进行消息处理,来更新UI

下面用几个例子来说明Hnadler多线程的利用:

  在利用Handler创建多线程之前,来说明一下Runnable:Handler利用Runnable不一定就开辟了新线程。

  Runnable是一个接口,不是一个线程,但开辟新线程的时候要继承这个接口。但当我们用的匿名内部内类是在主线程当中的,那new Runnable就没有开辟新线程

  下面这个例子就没有开新线程:

  1)Handler+Runnable

这个例子是没有开新的线程而是在主线程当中下载的图片,运行程序后会发现,三张图片要同时被加载完成后才一起显示,这就造成了主界面的线程堵塞。所以这种模式不建议使用

主要代码:

 

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final String url1 = "http://img2.3lian.com/img2009/08/3lian14/173.jpg";
        final String url2 = "http://down.tutu001.com/d/file/20100528/5cbd29cade6126e6c35112d18e_560.jpg";
        final String url3 = "http://down.tutu001.com/d/file/20100528/93ed3168a11ad23a6ee676ecc9_560.jpg";
        (MainActivity.this.findViewById(R.id.button1))
                .setOnClickListener(new Button.OnClickListener() {

                    @Override
                    public void onClick(View v) {
                        getImage(url1, R.id.imageView1);
                        getImage(url2, R.id.imageView2);
                        getImage(url3, R.id.imageView3);
                    }
                });
    }

    Handler handler = new Handler();

    public void getImage(final String url, final int ImageID) {
        handler.post(new Runnable() {

            @Override
            public void run() {
                HttpClient httpClient = new DefaultHttpClient();
                HttpGet httpGet = new HttpGet(url);
                try {
                    HttpResponse response = httpClient.execute(httpGet);
                    if (response.getStatusLine().getStatusCode() == 200) {
                        HttpEntity entity = response.getEntity();
                        byte[] data = EntityUtils.toByteArray(entity);
                        Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,
                                data.length);

                        ImageView image11 = (ImageView) MainActivity.this
                                .findViewById(ImageID);
                        image11.setImageBitmap(bitmap);

                    }

                } catch (Exception e) {
                    Toast.makeText(MainActivity.this, "网络问题", Toast.LENGTH_LONG)
                            .show();
                    System.out.println("网络问题");
                }

            }
        });

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}
View Code

 

  **运行了这段代码后,会发现三张图片是一起出现的

 

2)Handler+Thread+Message  实现多线程的异步加载图片

  

public class MainActivity extends Activity {
    
    
    final String url1 = "http://img2.3lian.com/img2009/08/3lian14/173.jpg";
    final String url2 = "http://down.tutu001.com/d/file/20100528/5cbd29cade6126e6c35112d18e_560.jpg";
    final String url3 = "http://down.tutu001.com/d/file/20100528/93ed3168a11ad23a6ee676ecc9_560.jpg";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((Button)MainActivity.this.findViewById(R.id.button1)).setOnClickListener(new Button.OnClickListener() {
            
            @Override
            public void onClick(View v) {
                getImage(url1, R.id.imageView1);
                getImage(url2, R.id.imageView2);
                getImage(url3, R.id.imageView3);
            }
        });
    }

    // 进入了主线程的Looper进行UI更改
    Handler myHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg != null) {
                ImageView image = (ImageView) findViewById(msg.what);
                image.setImageBitmap((Bitmap) msg.obj);

            }
        };

    };

    // 异步加载图片方法
    public void getImage(final String url, final int imageID) {
        // 开线程获取网络图片
        new Thread() {
            @Override
            public void run() {
                HttpClient httpClient = new DefaultHttpClient();
                HttpGet httpget = new HttpGet(url);
                try {
                    HttpResponse response = httpClient.execute(httpget);
                    if (response.getStatusLine().getStatusCode() == 200) {
                        HttpEntity entity = response.getEntity();
                        byte[] data = EntityUtils.toByteArray(entity);
                        Bitmap bm = BitmapFactory.decodeByteArray(data, 0,
                                data.length);
                        Message message = myHandler.obtainMessage();
                        message.obj = bm;
                        message.what = imageID;
                        myHandler.sendMessage(message);
                        // ImageView imageview = (ImageView)
                        // findViewById(imageID);
                        // imageview.setImageBitmap(bm);
                    }

                } catch (Exception e) {
                }

            }

        }.start();

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}
View Code

  调用getImage()方法,开辟属于自己的线程,将得到的图片信息保存在Message当中,发送给Handler进行UI 的更新。之间发送给Handler进行处理时不分先后的。

 

这段代码的效果就是三个ImageView加载自己的图片,相互之间没有联系的。这就形成了异步。

 

 

 

 

posted @ 2014-04-09 00:17  perfect亮  阅读(454)  评论(0编辑  收藏  举报