安卓学习笔记(一)——线程的用法及怎样在子线程中更新UI

创建线程方法:

1、新建一个继承自Thread的类MyThread,然后重写父类的run()方法,在里面写要执行的内容;

class MyThread extends Thread {

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

}

启动线程时,需要new出MyThread的实例,然后调用它的start()方法。

new MyThread().start();

2、定义一个雷MyThread,并实现Runnable接口,然后重写run()方法;

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

}

启动线程时,先要new出MyThread的实例,然后将对象传给new出的Thread实例,再调用Thread的start方法。

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

3、使用匿名类的方式。

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

 

在子线程中更新UI:由于安卓更新UI元素必须在主线程中,否则就会出现异常。有时候我们必须在子线程里去执行一些耗时任务,然后根据任务的执行结果来更新相应的UI控件,比如:执行网络请求,需要将传回的数据更新到UI上。

操作方法:

一、在主线程中new出Handler的实例handler,在Handler的handleMessage()方法中传回的Message对象属性进行判断,当满足要求时,执行UI更新的操作。

 

在子线程的run()方法内,1、实例化Message对象message;2、对象调用Message的what变量,设置message的属性;3、对象调用sendMessage()方法,将Message对象发送出去。

private Handler handler = new Handler() {
  public void handleMessage(Message msg) {

    switch (msg.what) {
    case UPDATE_TEXT:
      // 在这里可以进行UI操作
      text.setText("Nice to meet you");
      break;
    default:
      break;
    }
  }

new Thread(new Runnable() {
  @Override
  public void run() {
    Message message = new Message();
    message.what = UPDATE_TEXT;
    handler.sendMessage(message); // 将Message对象发送出去
  }
}).start();

 

安卓中的异步消息处理主要有四个部分组成:Message、Handler、MessageQueue、Looper

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

2、Handler:主要用于发送和处理消息。发送消息使用Handler的sendMessage()方法;发出的消息经过处理后,最终会传递到Handler的handleMessage()方法中。

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

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

 

二、使用AsyncTask:由于AsyncTask是一个抽象类,所以如果我们想使用它,就必须创建一个子类去继承它。在继承时我们可以为AsyncTask类指定三个泛型参数,如下:

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

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

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

我们还需要去重写AsyncTask中的几个方法才能完成对任务的定制。经常需要去重写的方法有以下四个。

1.  onPreExecute()
这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比
如显示一个进度条对话框等。
2.  doInBackground(Params...)
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时务。任务一旦完成就可以通过 return语句来将任务的执行结果返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。
3.  onProgressUpdate(Progress...)
当在后台任务中调用了 publishProgress(Progress...)方法后,这个方法就会很快被调
用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对 UI进行操作,利用参数中的数值就可以对界面元素进行相应地更新。
4.  onPostExecute(Result)
当后台任务执行完毕并通过 return语句进行返回时,这个方法就很快会被调用。返
回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些 UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。

class DownloadTask extends AsyncTask {
 
  @Override 

 protected void onPreExecute() {
    progressDialog.show(); // 显示进度对话框
  }
 
  @Override
  protected Boolean doInBackground(Void... params) {
    try {
      while (true) {
        int downloadPercent = doDownload(); // 这是一个虚构的方法
        publishProgress(downloadPercent);
        if (downloadPercent >= 100) {
          break;
        }
      }

    } catch (Exception e) {
      return false;
    }
    return true;
  }
 
  @Override
  protected void onProgressUpdate(Integer... values) {
    // 在这里更新下载进度
    progressDialog.setMessage("Downloaded " + values[0] + "%");
  }
 
  @Override
  protected void onPostExecute(Boolean result) {
    progressDialog.dismiss(); // 关闭进度对话框
    // 在这里提示下载结果
    if (result) {
      Toast.makeText(context, "Download succeeded",Toast.LENGTH_SHORT).show();
    } else {
      Toast.makeText(context, " Download failed",
Toast.LENGTH_SHORT).show();
    }
  }
}

设置完后就可以启动这个任务:new DownloadTask().execute();

你可能感兴趣的:(安卓学习笔记)