Android多线程编程、异步机制(AsyncTask)

    • 线程间的通信模型
    • 为什么要使用线程呢
      • 下面有一个关于Thread的应用
        • 60秒倒计时
        • 布局
        • 第一种写法
        • 第二种写法
        • 布局
    • 线程的基本用法
    • 如何在子线程中更新UI
      • 处理耗时操作怎么办呢
    • 解析异步消息处理机制
    • AsyncTask的使用
      • 重写AsyncTask中的一些方法
        • MainActivity
        • 布局

线程间的通信模型

使用Handler来传递消息

Android多线程编程、异步机制(AsyncTask)_第1张图片

为什么要使用线程呢?

比如说发出一条网络请求时,考虑到网速等其他原因,服务器未必会立刻相应我们的请求,如果不将这类操作放在子线程里去运行,就会导致主线程被阻塞住,从而影响用户对软件的正常使用。

下面有一个关于Thread的应用

60秒倒计时

public class MainActivity extends Activity {
private Button mButton;
    private int count=60;
    private static final int TIME_DESC=0X23;
  // 下面是一个匿名内部类,作用和下面那个内部类是一样的
   private Handler handler=new Handler() {
        @Override
       public void handleMessage(Message msg) {
            switch (msg.what) {
               case TIME_DESC:
                    String time = (String) msg.obj;
                    mButton.setText(time);
                    break;
            }
       }
   };
// private MyHandler handler=new MyHandler();//初始化handler
//
// class MyHandler extends Handler {//这是一个内部类
// @Override
// public void handleMessage(Message msg) {
// switch (msg.what){
// case TIME_DESC:
// String time= (String) msg.obj;
// mButton.setText(time);
// break;
// }
// }
// }


        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mButton = (Button) findViewById(R.id.button);
            mButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    count = 60;
               new Thread(new Runnable() {
                   @Override
                   public void run() {
                       while (count>0){
                           count--;
                           try {
                               Thread.sleep(1000);
                           } catch (InterruptedException e) {
                               e.printStackTrace();
                           }                           
//Message msg=handler.obtainMessage();不用每次都重新生成一个Message对象,可以直接用这个方法代替下面这条语句
                           Message msg=new Message();
                           msg.obj=count+"秒";
                            msg.what=TIME_DESC;
                         handler.sendMessage(msg);
                        }
                    }
               }).start();
                    handler.sendEmptyMessage(TIME_DESC);
                }
            });
        }


}

布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="倒计时"/>
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="60秒"/>
</LinearLayout>

上面的程序是子线程给主线程发送消息
下面的程序是主线程给子线程发送消息

第一种写法

public class MainActivity extends Activity {
private Button mButton;
    private int count=60;
    private static final int TIME_DESC=0X23;
     private Handler handler=new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case TIME_DESC:
                    count--;
                    mButton.setText(count + "秒");
                    if (count > 0) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        handler.sendEmptyMessage(TIME_DESC);
                        break;
                    }
            }
        }
    };

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mButton = (Button) findViewById(R.id.button);
            mButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    count = 60;
                         handler.sendEmptyMessage(TIME_DESC);
                }
            });
        }


}

第二种写法

public class MainActivity extends Activity {
    private Button mButton1;
    private Button mButton2;
    private int count = 60;
    private static final int TIME_DESC = 0X23;
    private Handler handler;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton1 = (Button) findViewById(R.id.button1);
        mButton2 = (Button) findViewById(R.id.button2);
        mButton1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                count = 60;
                MyThread thread = new MyThread();
                thread.start();
                thread. handler.sendEmptyMessage(TIME_DESC);

            }
        });
        mButton2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                handler.sendEmptyMessage(TIME_DESC);
            }
        });


    }
    class MyThread extends Thread {
        private Handler handler;
        @Override
        public void run() {
            Looper.prepare();//创建一个Looper循环
            handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    Log.d("handler", "接收到主线程发过来的消息");

                }
            };

            Looper.loop();
        }
    }


}

布局

 <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发送消息给子线程"/>

线程的基本用法

1、定义一个线程只需要新建一个类继承自Thread,然后重写父类的run()方法,并在里面编写耗时逻辑即可。如下所示:

class MyThread extends Thread{
  @Override
  public void run(){
  //处理具体的逻辑
}

2、如何启动这个线程呢?

只需要new出一个MyThread的实例,然后调用它的start()方法,这样run()方法中的代码就会在子线程当中运行了。如下所示:

new MyThread().start或者是:MyThread myThread=new MyThread();
   myThread.start();

3、使用继承的方式耦合性有点高,更多的时候我们都会选择使用实现Runnable 接口的方式来定义一个线程。如下所示:

class MyThread implements Runnable{
@Override
public void run(){
//处理具体的逻辑
}

启动该线程的方法:

MyThread myThread=new MyThread();
new Thread(myThread).start();

4、当然,如果不想专门在定义一个类去实现Runnable接口,也可以使用匿名内部类的方式,我个人更喜欢这种方式。

new Thread (new Runnable(){
@Override
public void run(){
//处理具体的逻辑
}
}).start();

如何在子线程中更新UI

和许多其他的GUI库一样,Android的UI也是线程不安全的,也就是说,如果想要更新应用程序里的UI元素,则必须在主线程中运行,否则就会出现异常。

处理耗时操作怎么办呢?

如果要执行一些耗时操作的时候,我们就必须会用到子线程——在子线程里去执行这些耗时操作,然后根据任务的执行结果来更新相应的UI控件。

对于这种情况,Android提供了一套异步消息处理机制,完美的解决了在子线程中进行进行UI操作的问题。

案例可参考前面的60秒倒计时代码
总结:在案例中先定义一个整形常量TIME_DESC=0X23;用于表示更新mButton这个动作,然后新增一个Handler对象,并重写父类的handlerMessage()方法,在这里对具体的Message进行处理,如果发现Message的what字段的值等于TIME_DESC,就将TextView显示的内容进行处理,即减去1秒。

mButton按钮的点击事件中的代码,可以看到这次我们并没有在子线程里直接进行UI操作,而是创建一个Message对象,并将它的what字段的值指定为TIME_DESC;然后调用Handler的sendMessage()方法将这条Message发送出去,很快,Handler就会收到这条Message,并在handlerMessage()方法中对它进行处理。此时handlerMessage()方法中代码就是在主线程当中运行的,接下来对Message携带的what字段的值进行判断,如果等于TIME_DESC,就会改变时间数。

public class MainActivity extends Activity {
private Button mButton;
    private int count=60;
    private static final int TIME_DESC=0X23;
  // 下面是一个匿名内部类,
   private Handler handler=new Handler() {
        @Override
       public void handleMessage(Message msg) {
            switch (msg.what) {
               case TIME_DESC:
                    String time = (String) msg.obj;
                    mButton.setText(time);
                    break;
            }
       }
   };

       @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mButton = (Button) findViewById(R.id.button);
            mButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    count = 60;
               new Thread(new Runnable() {
                   @Override
                   public void run() {
                       while (count>0){
                           count--;
                           try {
                               Thread.sleep(1000);
                           } catch (InterruptedException e) {
                               e.printStackTrace();
                           }                           
//Message msg=handler.obtainMessage();不用每次都重新生成一个Message对象,可以直接用这个方法代替下面这条语句
                           Message msg=new Message();
                           msg.obj=count+"秒";
                            msg.what=TIME_DESC;
                         handler.sendMessage(msg);
                        }
                    }
               }).start();
                    handler.sendEmptyMessage(TIME_DESC);
                }
            });
        }


}

解析异步消息处理机制

Android中的异步消息处理主要由四个部分组成,Message、Handler、MessageQueue和Looper。

1、Message

Message是在线程之间传递的消息,它可以在内部携带少量信息,用于在不同的线程之间交换数据。比如Message的what字段、arg1和arg2字段(携带一些整形数据)、使用obj字段携带一个Object对象。

2、Handler

主要用于发送和处理消息,发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列的处理后,最终会传到Handler的handlerMessage()方法中。

3、MessageQueue

MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理,每个线程中只会有一个MessageQueue对象。

4、Looper

Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无线循环当中,然后每当发现MessageQueue中存在一条消息,就会将a它提出来,并传递到Handler的handlerMessage()方法中。每个线程中也只会有一个Looper对象。

AsyncTask的使用

AsyncTask是一个抽象类,所以我们在使用它的时候,必须要创建一个子类去继承它,在继承时我们可以为AsyncTask类指定三个泛型参数。三个参数的用途如下:

1、Params

在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。

2、Progress

后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。

3、Result

当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。

class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
.........
}

Android多线程编程、异步机制(AsyncTask)_第2张图片

重写AsyncTask中的一些方法

MainActivity

package com.example.administrator.asynctaskdemo;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;

public class MainActivity extends AppCompatActivity {
    private Button mButton;
    private ProgressBar mprogressBar;
    private int count = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.button);
        mprogressBar = (ProgressBar) findViewById(R.id.progressbar);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MyTask task = new MyTask();
                task.execute("去执行吧!");//execute只能在主线程中调用
            }
        });
    }

    class MyTask extends AsyncTask<String, String, String> {
        //AsyncTask(异步任务)是一个抽象类,实现异步任务的基本步骤:继承抽象类AsyncTask实现抽象类中的几个基本方法。
        @Override
        protected void onProgressUpdate(String... values) {//更新进度条
            super.onProgressUpdate(values);
            int count = Integer.parseInt(values[0]);
            mprogressBar.setProgress(count);
        }

        @Override
        protected void onPostExecute(String s) {//执行完成
            super.onPostExecute(s);
            mButton.setText(s);
        }

        @Override
        protected String doInBackground(String... params) {//后台执行中
            while (count < 101) {
                count++;
                publishProgress("" + count);//publishProgress用来把数据传入到onProgressUpdate中
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }

            return "已经加载完成";
        }
    }

}

布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<Button
    android:id="@+id/button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="开始"/>
<ProgressBar
    android:id="@+id/progressbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    style="@style/Widget.AppCompat.ProgressBar.Horizontal"/>

</LinearLayout>

Android多线程编程、异步机制(AsyncTask)_第3张图片

你可能感兴趣的:(多线程,UI,异步,AsyncTask)