在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还提供了cancelled(boolean)方法,它同样在主线程中执行,当异步任务取消时,onCancelled()会被调用,这个时候onPostExecute()则不会被调用,但是要注意的是,AsyncTask中的cancel(boolean)方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()判断终止任务。就好比想要终止一个线程,调用interrupt()方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。参数true表示立即取消任务(不一定成功),false则表示允许任务执行完成后再取消。

使用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);

Android 线程交互的更多相关文章

  1. Android的UI设计与后台线程交互

    本文将讨论Android应用程序的线程模型以及如何使用线程来处理耗时较长的操作,而不是在主线程中执行,保证用户界面(UI)的流畅运行.本文还将阐述一些用户界面(UI)中与线程交互的API.UI用户界面 ...

  2. Android中UI线程与后台线程交互设计的5种方法

    我想关于这个话题已经有很多前辈讨论过了.今天算是一次学习总结吧. 在android的设计思想中,为了确保用户顺滑的操作体验.一 些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必 ...

  3. Android中UI线程与后台线程交互设计的6种方法

    在android的设计思想中,为了确保用户顺滑的操作体验.一些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必须要重新开启 一个后台线程运行这些任务.然而,往往这些任务最终又会直 ...

  4. android线程 Handler Message Queue AsyncTask线程模型 线程交互 + 修改Button样式 示例 最终easy整合版

     首先原谅我把文章的标题写的这么长.其实我还嫌弃它短了因为 写不下去了所以我就不写了.因为我实在不知道该怎么定义这篇文章的标题或许应该叫 "乱谈"比较合适. 这样可能还体现了 ...

  5. Android中后台线程如何与UI线程交互

    我想关于这个话题已经有很多前辈讨论过了.今天算是一次学习总结吧. 在android的设计思想中,为了确保用户顺滑的操作体验.一些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必须 ...

  6. android 线程学习

    很多人觉得线程难理解,主要有两个问题: 线程休眠,既然线程已经休眠了,程序的运行速度还能提高吗? 线程体一般都进行死循环,既然线程死循环,程序就应该死掉了,就会没有反应. 1.关于线程休眠问题 对线程 ...

  7. android 线程

    android线程: 通用多个线程通信管理框架: 1.Handler监听者框架:子线程是事件源,主线程是监听者.        Handler作为子线程的监听器出现:主线程中生成Handler的子类, ...

  8. 优化 Android 线程和后台任务开发

    在 Android 开发中,你不应该做任何阻碍主线程的事情.但这究竟意味着什么呢?在这次海湾 Android 开发者大会讲座中,Ari Lacenski 认为对于长时间运行或潜在的复杂任务要特别小心. ...

  9. Android JS 交互出现 Uncaught Error: Error calling method on NPObject

    由于HTML5的功能越来越强大,native app的一些功能逐步被html页面代替,不可避免的JS交互也用到的也越来越多.在第一个版本向第二个版本迭代的过程中却发生了莫名其妙的问题,第一个版本JS调 ...

随机推荐

  1. spring-boot-starter大力出奇迹

    一.前言 ​ 上篇文章我们已经聊了SpringBoot的启动过程中的各类扩展点,那么从http://start.spring.io上我们生成的demo项目中,到目前就剩下了maven工程的pom.xm ...

  2. Android内存优化之磁盘缓存

    前言: 在上一篇文章中介绍了内存缓存,内存缓存的优点就是很快,但是它又有缺点: 空间小,内存缓存不可能很大: 内存紧张时可能被清除: 在应用退出时就会消失,做不到离线: 基于以上的缺点有时候又需要另外 ...

  3. 【原创】uC/OS 中LES BX,DWORD PTR DS:_OSTCBCur的作用及原理

    LES BX, DWORD PTR DS:_OSTCBCur ;OSTCBCur->OSTCBStkPtr = SS:SP!!! ], SS ;将当前SS(栈的基地址)寄存器值存放至当前任务控制 ...

  4. 从前端界面开发谈微信小程序体验

    本文由云+社区发表 作者介绍:练小习,2011年加入搜狐,负责搜狐相册的产品策划与前端开发.2015年后加入腾讯 ISUX (社交用户体验设计部),目前主要负责腾讯云的UI开发工作,专注于人机交互,有 ...

  5. ACM菜鸡退役帖——ACM究竟给了我什么?

    这个ACM退役帖,诸多原因(一言难尽...),终于决定在我大三下学期开始的时候写出来.下面说两个重要的原因. 其一是觉得菜鸡的ACM之旅没人会看的,但是新学期开始了,总结一下,只为了更好的出发吧. 其 ...

  6. 痞子衡嵌入式:ARM Cortex-M内核那些事(1)- 内核架构编年史

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是ARM内核架构历史. 众所周知,ARM公司是一家微处理器行业的知名企业,ARM公司本身并不靠自有的设计来制造或出售CPU,而是将处理器架 ...

  7. Apollo 10 — adminService 全量发布

    目录 UI 界面 Portal 服务 admin 服务 总结 1. UI 界面 2. Portal 服务 当我们点击上面的发布按钮的时候,调用的当然是 portal 的接口.具体代码如下: /** * ...

  8. 业务开发(八)—— Maven

    0x01.maven install成功后,项目旁边依然有个红叉 maven--update--选中下方Force Update of Snapsshots/Releases 0x02.An inte ...

  9. jquery发起get/post请求_或_获取html页面数据

    备注:我们经常会遇到使用jquery获取某个地址下的部分页面内容,然后替换当前页面对应内容,也就是:局部刷新功能. 当然也可以使用get/post请求获取数据,修改数据,可以参考以下JS代码: 走过的 ...

  10. 关于Newtonsoft.Json,LINQ to JSON的一个小demo

    nuget获取Newtonsoft.Json github地址:Newtonsoft.Json public static void Test1() { /* 文本格式如下 代码实现目的: 1.VR ...