zoukankan      html  css  js  c++  java
  • Android多线程编程之AsyncTask

    进程?线程?

      进程是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态的概念。每个进程都有自己的地址空间(进程空间)。进程空间的大小与处理机位数有关。进程至少有5种基本状态:初始态,执行态,等待状态,就绪状态,终止状态。

      在多用户环境下,一个服务器通常需要接受大量的不学定数量用户的并发请求,而为每一个请求都创建一个进程非常不明智,无论从系统资源开销方面还是响应用户请求的效率上来看。这也是多线程诞生的一个原因。线程是进程的一部分,一个没有线程的进程可以看做是单线程。线程也是CPU调度的一个基本单位

    多线程的实现 

    1)Activity.runOnUiThread(Runnable)  

    2)View.post(Runnable) ;View.postDelay(Runnable , long)  

    3)Handler  

    4)AsyncTask  

    为何引入AsnyncTask?

    在Android程序开始运行的时候会单独启动一个进程,默认情况下所有这个程序操作都在这个进程中进行。一个Android程序默认情况下只有一个进程,但是一个进程却是可以有许多线程的。

      在这些线程中,有一个线程叫做UI线程,也叫做Main Thread,除了Main Thread之外的线程都可称为Worker Thread。Main Thread主要负责控制UI页面的显示、更新、交互等。因此所有在UI线程中的操作要求越短越好,只有这样用户才会觉得操作比较流畅。一个比较好的做法 是把一些比较耗时的操作,例如网络请求、数据库操作、复杂计算等逻辑都封装到单独的线程,这样就可以避免阻塞主线程。Android的程序运行时是多线程的,为了更方便的处理子线程和UI线程的交互,引入了AsyncTask。

      Android是单线程模型,这意味着Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行,所以单纯的new一个Thread并且start()是不行的,因为这违背了Android的单线程模型。那么如何用好多线程呢?总结一下:  
    事件处理的原则:所有可能耗时的操作都放到其他线程去处理

    AsyncTask介绍
     
      Android的AsyncTask比Handler更轻量级一些,适用于简单的异步处理。
      首先明确Android之所以有Handler和AsyncTask,都是为了不阻塞主线程(UI线程),且UI的更新只能在主线程中完成,因此异步处理是不可避免的。

      Android为了降低这个开发难度,提供了AsyncTask。AsyncTask就是一个封装过的后台任务类,顾名思义就是异步任务。

      AsyncTask直接继承于Object类,位置为android.os.AsyncTask。要使用AsyncTask工作我们要提供三个泛型参数,并重载几个方法(至少重载一个)。

    AsyncTask定义了三种泛型类型 Params,Progress和Result。

    • Params 启动任务执行的输入参数,比如HTTP请求的URL。
    • Progress 后台任务执行的百分比。
    • Result 后台执行任务最终返回的结果,比如String。

    使用AsyncTask 一个异步加载数据最少要重写以下这两个方法:

    • doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。
    • onPostExecute(Result) 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回

    有必要的话你还得重写以下这三个方法,但不是必须的:

    • onProgressUpdate(Progress…) 可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
    • onPreExecute() 这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
    • onCancelled() 用户调用取消时,要做的操作

     

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

      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.一个任务实例只能执行一次,如果执行第二次将会抛出异常。

    下面是一个模拟加载视频、数据、连接网络的进度条案例

     activity_main.xml

     1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:paddingBottom="@dimen/activity_vertical_margin"
     6     android:paddingLeft="@dimen/activity_horizontal_margin"
     7     android:paddingRight="@dimen/activity_horizontal_margin"
     8     android:paddingTop="@dimen/activity_vertical_margin"
     9     tools:context="com.example.threadasynctask.MainActivity" >
    10 
    11     <TextView
    12         android:id="@+id/tv_start"
    13         android:layout_width="wrap_content"
    14         android:layout_height="wrap_content"
    15         android:text="@string/hello_world" />
    16 
    17     <Button
    18         android:id="@+id/btn_start"
    19         android:layout_width="wrap_content"
    20         android:layout_height="wrap_content"
    21         android:layout_alignParentRight="true"
    22         android:text="@string/strat" />
    23 
    24     <ProgressBar
    25         android:id="@+id/progressBar1"
    26         style="?android:attr/progressBarStyleHorizontal"
    27         android:layout_width="match_parent"
    28         android:layout_height="wrap_content"
    29         android:layout_alignRight="@+id/btn_start"
    30         android:layout_below="@+id/btn_start"
    31         android:layout_marginTop="19dp" />
    32 
    33 </RelativeLayout>

    MyTask.java
     1 package com.example.threadasynctask;
     2 
     3 import android.os.AsyncTask;
     4 import android.widget.ProgressBar;
     5 import android.widget.TextView;
     6 
     7 /**
     8  * 生成该类的对象,并调用execute方法之后  
     9  * 首先执行的是onProExecute方法  
    10  * 其次执行doInBackgroup方法 
    11  * @author Administrator
    12  *
    13  */
    14 public class MyTask extends AsyncTask<Integer, Integer, String>{
    15 
    16     private TextView textView;  
    17     private ProgressBar progressBar; 
    18     private String str;
    19     
    20     
    21     public MyTask(TextView textView, ProgressBar progressBar) {
    22          super();  
    23          this.textView = textView;  
    24         this.progressBar = progressBar;
    25 
    26     }
    27     
    28     /**
    29      * 这里的Integer参数对应AsyncTask中的第一个参数  
    30      * 这里的String返回值对应AsyncTask的第三个参数
    31      * 在执行过程中可以调用publicProgress(Progress…)来更新任务的进度
    32      * 1.所有耗时的代码,写到这里来(数据库、蓝牙、网络服务)
    33      * 2.绝对不能碰UI
    34      */
    35     @Override
    36     protected String doInBackground(Integer... params) {
    37         int i = 0 ;
    38         for(i = 0 ; i <= 100 ; i++){
    39             str = i+"%"; //模拟加载数据
    40             try {
    41                 Thread.sleep(100);
    42             } catch (InterruptedException e) {
    43                 e.printStackTrace();
    44             }
    45             publishProgress(i);
    46         }
    47         //返回前端
    48         return i + params[0].intValue() + "";
    49     }
    50     
    51     //准备,该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI空间进行设置  
    52      protected void onPreExecute() {  
    53           textView.setText("开始执行异步线程");  
    54          } 
    55 
    56     
    57     //做完后执行
    58      /**
    59       *  这里的String参数对应AsyncTask中的第三个参数
    60       *  (也就是接收doInBackground的返回值)  
    61       * 在doInBackground方法执行结束之后在运行,
    62       * 并且运行在UI线程当中 可以对UI空间进行设置
    63       */
    64     @Override
    65     protected void onPostExecute(String result) {
    66         textView.setText("异步操作执行结束" + result);  
    67     }
    68     
    69     
    70     //分步完成
    71     /**
    72      * 这里的Intege参数对应AsyncTask中的第二个参数  
    73      * 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行  
    74      * onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操
    75      */
    76         @Override
    77         protected void onProgressUpdate(Integer... values) {
    78             //0,1,2,3,4
    79             int vlaue = values[0];  
    80              progressBar.setProgress(vlaue);  
    81              textView.setText("已完成"+str); 
    82         }
    83     
    84     
    85 }

     MainActivity.java

    package com.example.threadasynctask;
    
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.ProgressBar;
    import android.widget.TextView;
    
    
    public class MainActivity extends Activity {
    
        
        private Button btn;
        private TextView tv;
        private ProgressBar progressBar;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
    
            tv =(TextView)findViewById(R.id.tv_start);
            btn =(Button)findViewById(R.id.btn_start);
            progressBar = (ProgressBar)findViewById(R.id.progressBar1);
            
            btn.setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    MyTask mt = new MyTask(tv,progressBar);
                    mt.execute(1000);//里面的参数是传给doInBackground
                }
                
            });
        }
    }

    效果如下: 

      

  • 相关阅读:
    lamp配置多个虚拟站点
    linux下lamp环境修改网站根目录
    CentOS 6.7快速搭建lamp环境
    GridView 使用方法总结 (一)
    asp.net学习之Repeater控件
    通过js获取前台数据向一般处理程序传递Json数据,并解析Json数据,将前台传来的Json数据写入数据库表中
    ExtJs特点、优缺点及注意事项
    Js获取当前日期时间及其它操作
    C#实现MD5加密
    SqlDataReader、SqlDataAdapter與SqlCommand的 区别
  • 原文地址:https://www.cnblogs.com/lgk1002/p/6134061.html
Copyright © 2011-2022 走看看