欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 线程交互

程序员文章站 2022-06-21 10:37:48
在Android开发过程中,耗时操作是不允许写在主线程(UI线程)中的,以免由于等待时间过长而发生ANR。所以耗时操作需要创建子线程来完成,然而往往这些操作都需要与主线程进行通讯交互(例如更新主线程的UI),但android规定除了UI线程外,其他线程都不可以对UI控件进行访问或操控,所以我们需要通 ......

 在Android开发过程中,耗时操作是不允许写在主线程(UI线程)中的,以免由于等待时间过长而发生ANR。所以耗时操作需要创建子线程来完成,然而往往这些操作都需要与主线程进行通讯交互(例如更新主线程的UI),但android规定除了UI线程外,其他线程都不可以对UI控件进行访问或操控,所以我们需要通过一些方法来实现这些功能。

 

1. Handler:

 

handler是android中专门用来在线程之间传递信息类的工具。API参考:https://developer.android.google.cn/reference/android/os/Handler

1、在B线程中调用Looper.prepare和Looper.loop。(主线程不需要)
2、编写Handler类,重写其中的handleMessage方法。
3、创建Handler类的实例,并绑定looper
4、调用handler的sentMessage方法发送消息。

 

  • 子线程更新主线程(UI)

因为主线程自带Looper机制,所有我们不用创建Looper:

Handler mHandler = new Handler(){  
 
@Override  
public void handleMessage(Message msg) {  
    super.handleMessage(msg);  
    switch (msg.what) {  
        case 1:  
            //do something,refresh UI;  
            break;  
        default:  
            break;  
        }  
    }    
   
};  

 

然后开启一个子线程,在子线程里直接使用Handler发送消息:

new Thread() {
    public void run() {
    Message message = new Message();
    message.what = 1;     message.obj = "子线程发送的消息Hi~Hi";     mHandler .sendMessage(message);
  }; }.start();

 

  • 子线程之间交互

public class ThreadActivity extends AppCompatActivity {

  private final String TAG = "ThreadActivity";

  // 子线程Handler
  private Handler mSubHandler = null;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_thread1);
    new MyThread().start();
    createThread();
  }


  /**
   * 创建子线程,用于发送消息
   */
  private void createThread() {
    new Thread() {
      @Override
      public void run() {
        int count = 0;
        while (count < 10) {
          Message msg = new Message();
          msg.obj = "子线程计时器:" + count;
          msg.what = 1;
          // 使用子线程Handler发送消息
          mSubHandler.sendMessage(msg);
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          count++;
        }
      }
    }.start();
  }

  /**
   * 用于接收子线程发送过来的消息
   */
  class MyThread extends Thread {

    @Override
    public void run() {
      Looper.prepare();
      mSubHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
          switch (msg.what) {
            case 1:
            Log.i(TAG, (String) msg.obj);
            break;
          }
        }
      };
      Looper.loop();
    }
  }
}

 

2. HandlerThread:

HandlerThread是一个包含Looper的Thread,我们可以直接使用这个 Looper 创建 Handler。API参考:https://developer.android.google.cn/reference/android/os/HandlerThread

 

HandlerThread适用于单线程+异步队列模型场景,相对Handler + Thread简洁。

// 也可实现run方法
HandlerThread mHandlerThread = new HandlerThread("HandlerThread_Test"); mHandlerThread.start(); Handler mThreadHandler = new Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: Log.i(TAG, "threadName--" + Thread.currentThread().getName() + (String) msg.obj); break; } } }; // 发送消息至HanderThread mThreadHandler.sendMessage(msg);

 

3. runOnUiThread

Activity 里面的runOnUiThread( Runnable )方法便子线程更新UI更为简洁。另外还有View.Post(Runnable)和View.PostDelayed(Runnabe,long)方法,用法与runOnUiThread基本相同。

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep( 1000 );
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // 执行UI操作 
                Toast.makeText(MainActivity.this, "Test", Toast.LENGTH_SHORT).show();
            }
        });
    }
}).start();

 

4. AsyncTask

API参考:https://developer.android.google.cn/reference/android/os/AsyncTask

AsyncTask是一个抽象类,它是由Android封装的一个轻量级异步类。它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。AsyncTasks应该用于短操作(最多几秒)。如果您需要保持线程长时间运行,强烈建议您使用java.util.concurrent包提供的各种API,例如Executor,ThreadPoolExecutor和FutureTask。 异步任务由3个泛型类型定义:Params,Progress和Result,以及4个步骤:onPreExecute,doInBackground,onProgressUpdate和onPostExecute。

使用AsyncTask时需要继承AsyncTask类并必须实现doInBackground(params...)方法,大多数情况下学需要实现onPostExecute(Result)方法。

 

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }

    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }

    protected void onPostExecute(Long result) {
        showDialog("Downloaded " + result + " bytes");
    }
}

 

创建成功后执行任务非常简单:

new DownloadFilesTask().execute(url1, url2, url3); // Params

 

AsyncTask的泛型类型:

  • Params:在执行时传递给任务的参数类型;
  • Progress:异步任务执行过程中,返回执行进度值的类型;
  • Result:后台任务执行结果类型。

并非所有类型都必须在异步任务中使用,如果不需要使用,则用Void来代替。

rivate class MyTask extends AsyncTask<Void, Void, Void> { ... }

 

当执行一个异步任务时,需要经历4个步骤:

  • onPreExecute():在异步任务执行前,在UI主线和中调用。此步骤通常用于设置任务,例如在用记界面中显示进度条。
  • doInBackground(Params...):onPreExecute()执行完后,立即在后台线程中调用此方法。此步骤用于执行运行时间可能较长的后台任务,参数由execute(params...)传入。通过此步骤得到结果并返回到最后一步。在计算过程中,可通过调用publishProgress(Progress...)方法,并通过onProgressUpdate(Progress...)更新UI显示任务执行进度。
  • onProgressUpdate(Progress...):当publishProgress(Progress...)方法执行后,此步骤在UI主线程中被调用,并更新UI当前任务进度。
  • onPostExecute(Result):在后台线程计算完成后在UI线程上调用。 后台线程计算的结果作为参数传递给此步骤。

AsyncTask还提供了onCancelled()方法,它同样在主线程中执行,当异步任务取消时,onCancelled()会被调用,这个时候onPostExecute()则不会被调用,但是要注意的是,AsyncTask中的cancel()方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()判断终止任务。就好比想要终止一个线程,调用interrupt()方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。

使用AsyncTask还需要注意以下问题:

  • 异步任务的实例必须在UI线程中创建,即AsyncTask对象必须在UI线程中创建。
  • execute(Params...) 必须在UI线程上执行。
  • 不要手动调用onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...)方法。
  • 该任务只能执行一次(如果尝试执行第二次执行,则会引发异常)。
  • 不能在doInBackground(Params... params)中更改UI组件的信息。
  • AsyncTask不与任何组件绑定生命周期,所以在Activity/或者Fragment中创建执行AsyncTask时,最好在Activity/Fragment的onDestory()调用 cancel(boolean);
  • 如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。
  • 屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask(非静态的内部类)会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。

AsyncTask里面有两种线程池供我们调用,默认使用SERIAL_EXECUTOR。AsyncTask的核心线程是5,队列容量是128,最大线程数是9。

  • THREAD_POOL_EXECUTOR, 异步线程池。
  • SERIAL_EXECUTOR,同步线程池。

 

 一个简单的AsyncTask例子:

自定义AsyncTask:

class TestAsyncTask extends AsyncTask<Integer, Integer, Integer> {

    @Override
    protected Integer doInBackground(Integer... integers) {
        int count = integers[0];
        int len = integers[1];
        while (count < len) {
            int pro = 100 * count / len;
            Log.i(TAG, "----------" + pro + ":" + count + ":" + len);
            publishProgress(pro);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count++;
        }
        return count;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        mText.setText("Progress:" + values[0] + "%");
    }

    @Override
    protected void onPostExecute(Integer integer) {
        mText.setText("Finished:" + integer + "%");
    }
}

 

创建TestAsyncTask实例:

mAsyncTask.execute(0, 100);