线程概览

线程是任何多任务系统的基石。可以被认为是一个主进程的多个子进程。这样做的目的就是了增加应用的性能。

应用主线程

当一个Android应用被打开的时候,系统会默认开辟一个线程。这个线程就被叫做是主线程。主线程的主要任务就是处理用户输入,即事件处理和view上的用户交互。任何应用里的其他组件,默认的,都是在主线程中运行的。

一个应用的任何组件,如果在主线程上执行一个耗时的任务的话,都会使整个应用等待这个任务的完成。如果耗时过长的话就会触发系统的“Application is unresponsive”警告。显然,这个是任何应用都不愿意出现的状况。在这种情况下,只能开辟一个单独的线程来执行这个耗时的任务,这样才不会干扰主线程上的其他任务。

线程Handler

所以,应用开发中最关键的一条就是永远不要在主线程上执行耗时过长的任务。另外一个同样重要的规则是另外开辟的单独的线程任何情况下、绝对不可以直接更新用户界面。任何对用户界面的更新都要在主线程中进行。之所以这样的原因是Android的UI不是线程安全的。在多线程环境下调用非线程安全的代码会导致断断续续的问题以及不可预料的应用行为。

要在子线程中更新用户界面就只能通过Handler来实现。

一个简单的Thread例子

这里会提供几个简单的例子来展示线程和Handler是如何使用的。第一步,展示一下耗时任务没有放在另外开辟的线程中,而放在主线程中出现的问题。首先创建一个Android项目叫做“ThreadExample”,包含一个单独的空白的activity:ThreadExampleActivity,layout叫做activity_thread_example。

具体的布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="fill_parent"android:layout_height="fill_parent"><TextViewandroid:id="@+id/text_view"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="Hello World, MyActivity"/><Button android:id="@+id/thread_button"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Press me"/></LinearLayout>

看起来是这样的:

保存。接下来,双击ThreadExampleActivity.java进入编辑模式。在这个activity文件中实现button的click方法。这个方法会在用户点击按钮之后被调用。这里主要是展示耗时任务的问题,所以会在主线程中发起一个20秒的延迟,之后更新TextView对象的文字。

代码如下:

package com.example.myapp;import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;public class ThreadExampleActivity extends Activity implements View.OnClickListener{/*** Called when the activity is first created.*/@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_thread_example);Button threadButton = (Button)findViewById(R.id.thread_button);threadButton.setOnClickListener(this);}@Overridepublic void onClick(View v) {long endTime = System.currentTimeMillis() + 20 * 1000;// waiting...while (System.currentTimeMillis() < endTime) {synchronized (this) {try{wait(endTime - System.currentTimeMillis());}catch (Exception e) {}}}// update `TextView`, after 20 secondsTextView textView = (TextView)findViewById(R.id.text_view);textView.setText("Button Pressed");}
}

以上代价在运行之后,点击一下按钮,这个时候整个应用就在20秒的等待中。再次或者多次点击这个按钮不会立刻有反应。这时候系统就会弹出一个提醒:应用正在忙:

因此,在按钮点击方法中,耗时的操作应该放置在另外一个单独的线程中。

创建一个新的线程

要创建一个新的线程,并让代码在这个线程中执行,需要把这些代码都放在Runnable接口的Run中。然后需要创建一个新的Thread对象。把Runnable接口的实例作为参数传给Thread的构造函数中。最后调用Thread实例的start方法来开辟线程并执行线程中的方法。

修改后的代码如下:

@Overridepublic void onClick(View v) { Runnable runnable = new Runnable() {@Overridepublic void run() {long endTime = System.currentTimeMillis() + 20 * 1000;// waiting...while (System.currentTimeMillis() < endTime) {synchronized (this) {try{wait(endTime - System.currentTimeMillis());}catch (Exception e) {}}}}};Thread thread = new Thread(runnable);thread.start();}

当应用再次运行起来之后。点击按钮之后把造成延时的任务都放在了新的线程中运行,主线程可以及时响应用户的任何操作,包括无休止的按钮点击。事实上,每次的点击都会创建一个新的线程,这样任务就可以在多个线程中并发执行。

两外一个需要注意的地方是,点击按钮之后更新TextView的文字的代码被去掉了。就像之前提到的,要更新界面上的内容只能在主线程中进行。要实现这个功能就需要给单独开辟的线程引入Handler实例。

实现一个Thread Handler

线程的Handler的实现是放在主线程中的,主要就是用来响应子线程的message并根据这个message来更新主线程的。

Handler继承自Android的Handler类。用来表明线程的Runnable实例即将执行,或overridehandleMessage方法,这个方法接受和处理子线程发送的message。本例会用Handler来更新用户界面。

修改后的代码如下:

package com.example.myapp;import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;public class ThreadExampleActivity extends Activity implements View.OnClickListener {Handler handler = new Handler() {@Overridepublic void handleMessage(Message message) {TextView textView = (TextView)findViewById(R.id.text_view);textView.setText("Button Pressed!");}};//...

}

上面的代码中声明了一个handler并实现了handleMessage回调方法。当子线程发出message的时候可以被这个方法处理。在这个实例中,只是简单地在代码中设置了TextView实例的文字。

现在就剩下修改button点击事件中创建的线程了。我们需要在这个线程里发出一个消息告诉handler20秒的延时任务已经执行完成。

修改后的代码如下:

 @Overridepublic void onClick(View v) {Runnable runnable = new Runnable() {@Overridepublic void run() {long endTime = System.currentTimeMillis() + 20 * 1000;// waiting...while (System.currentTimeMillis() < endTime) {synchronized (this) {try{wait(endTime - System.currentTimeMillis());}catch (Exception e) {}}}// waiting is overhandler.sendEmptyMessage(0);}};Thread thread = new Thread(runnable);thread.start();}

这段修改中唯一的修改就是增加了的就是handler调用sendEmptyMessage方法。由于handler实例不需要特别发送什么message所以这里只发送空消息。执行代码之后,点击按钮,等待20秒。TextView就会显示新的文本。

给Handler传递消息

之前的代码调用了handleMessage方法。但是这个方法并没有发挥出message可以发送数据给handler的优点。下面就会对现有的代码做出更多的修改来在子线程和handler实例之间传递数据。首先,在创建的子线程中会从系统获取到date和time,并转换成字符串。这些内容会保存在一个bundle实例中。然后调用handler的obtainMessage方法从message池中获取一个message实例。最后,这个保存了系统信息的bundle会被添加到message实例中并被sendMessage方法发送给handle实例。

@Overridepublic void onClick(View v) {Runnable runnable = new Runnable() {@Overridepublic void run() {Message message = handler.obtainMessage();Bundle bundle = new Bundle();SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss MM/dd/yyy", Locale.US);String dateString = dateFormat.format(new Date());bundle.putString("thread_date", dateString);    // key is `thread_date`
message.setData(bundle);handler.sendMessage(message);}};Thread thread = new Thread(runnable);thread.start();}

接下来更新handleMessage方法。用这个方法把接收到的时间显示在TextView实例中。

Handler handler = new Handler() {@Overridepublic void handleMessage(Message message) {Bundle bundle = message.getData();String dateString = bundle.getString("thread_date");TextView textView = (TextView)findViewById(R.id.text_view);textView.setText(dateString);}};

最后编译运行代码,点击按钮测试一下我们的修改是否成功。

总结

本教程就是提供一个对于Android应用实现多线程的概览。当一个app运行在一个进程中的时候,系统会给这个app穿件一个主线程。主线程的主要功能就是处理用户输入,所以任何执行时间过长的任务都会导致主线程无法及时响应用户后续的输入。所以,耗时的任务都应该放在另外开辟的子线程中执行。这些都是很基础的。因为Android用户界面的各种元素都是非线程安全的,所以对于界面的修改智能在主线程中进行。在主线程中可以使用Handler实例来接受子线程发出的消息来更新界面元素。

欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 572064792 | Nodejs:329118122 做人要厚道,转载请注明出处!
本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/p/4755152.html,如需转载请自行联系原作者

Android线程和线程Handler基础一览相关推荐

  1. 【Android开发】线程与消息处理-Handler消息传递机制之Looper

    在前面已经介绍了在Android中如何创建.开启.休眠和中断线程.不过,此时并没有在新创建的子线程中对UI界面上的内容进行操作,如果应用前面介绍的方法对UI界面进行操作,将抛出异常. 为此,Andro ...

  2. android Handler UI线程后台线程通信

    本文转自http://blog.csdn.net/qianquanyiyan/article/details/8215887,所有权力归原作者所有. 一.Handler的定义: 主要接受子线程发送的数 ...

  3. Android攻城狮 Handler与子线程

    Handler和线程是相关联的.再次强调:不可以在主线程中做耗时操作,就比如示例里面主线程中的 handlerMessage(),不能执行耗时操作.创建一个与自定义线程相关的Handler的代码: c ...

  4. Android入门第37天-在子线程中调用Handler

    简介 前一章我们以一个简单的小动画来解释了Handler. 这章我们会介绍在子线程里写Handler.如果是Handler写在了子线程中的话,我们就需要自己创建一个Looper对象了:创建的流程如下: ...

  5. 【Android】线程间通信——Handler消息机制

    文章目录 引言 Java层 永动机跑起来 示例 Looper Handler MessageQueue 永动机停下 Native层 nativeInit() nativePollOnce() nati ...

  6. Android 面试必备 - 线程

    前言 最近准备更新 Android 面试必备基础知识系列,有兴趣的可以关注我的微信公众号 stormjun94,有更新时,第一时间会在微信公众号上面发布,同时,也会同步在 GitHub 上面更新,如果 ...

  7. android线程及线程池

    众所周知,在UI系统中进行一些耗时操作,都会导致卡顿现象,因为一次刷新在16ms,如果当次操作过了这个时间,那么用户就能感觉到明显的卡顿,甚至引起ANR . 对于这种情况,一般都是再起一个线程,进行一 ...

  8. Android 多线程及线程通信

    2019独角兽企业重金招聘Python工程师标准>>> AsyncTask AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单.相对来说AsyncTask更轻 ...

  9. Android中的线程处理

    注:本文来自CSDN博主的一篇文章,个人觉得写得很好,值得借鉴,故收录在此 Android进程模型 在安装Android应用程序的时候,Android会为每个程序分配一个Linux用户ID,并设置相应 ...

最新文章

  1. .net 开源组件推荐 之 StackExchange
  2. 20行python代码的入门级小游戏-python实现石头剪刀布小游戏
  3. mysql tags_mysql tags table解决方法
  4. 松下NPM服务器怎么备份系统,松下NPM基本操作手册与教程
  5. linux驱动位置_Linux驱动程序学习一 (续)
  6. Vue的v-if与v-show的区别
  7. Java mongo入门
  8. Acrobat Pro DC 教程,如何编辑 PDF 文件中的文本和图片?
  9. python在windows 比linux 慢,为什么python在Windows上这么慢?
  10. 深度学习计算机视觉的简介_商业用途计算机视觉简介
  11. 小众绿软|游戏:Lode Runner 2(3D挖金子,英文版)(LodeRunner2)
  12. PowerBI制作报表背景图
  13. 思科实验4.网络层:路由器IP地址配置
  14. busybox tools
  15. 细数那些最令人难忘的电视剧
  16. 66.android 导入项目报错Error:Execution failed for task ':app:validateDebugSigning'. Keystore file F:\myA
  17. Python获取用电情况数据-AHPU校园网
  18. 解决小程序Failed to load localimage resource/pages/index/index/in.jpgtheserverrespondedwithastatusof500问题
  19. VM16虚拟机去虚拟化心得1
  20. 最难找工作的10种大学生

热门文章

  1. WPF 用Main函数方式启动程序
  2. 升级win10遇到的一些问题
  3. maven插件开发(二)
  4. SharePoint 2007有性能问题? 先试试这篇.
  5. 微软谷歌出资500万美元推出 Alpha-Omega 项目,提升软件供应链安全
  6. 黑客游戏未发先被黑:游戏开发商Ubisoft 和 Crytek遭勒索攻击
  7. 惠普:某些 SSD 驱动的寿命只有32,768 小时(3年多),立即更新!
  8. 成都睿铂 | 落差区域无人机倾斜摄影航线的规划要点
  9. BZOJ3233:[AHOI2013]找硬币(DP)
  10. 如何使用以下命令 ls cat mv touch 以及如何使用 explainshell.com 这个网站