Android线程和线程Handler基础一览
线程概览
线程是任何多任务系统的基石。可以被认为是一个主进程的多个子进程。这样做的目的就是了增加应用的性能。
应用主线程
当一个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"
>
<TextView
android: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.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread_example); Button threadButton = (Button)findViewById(R.id.thread_button);
threadButton.setOnClickListener(this);
} @Override
public 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 seconds
TextView textView = (TextView)findViewById(R.id.text_view);
textView.setText("Button Pressed");
}
}
以上代价在运行之后,点击一下按钮,这个时候整个应用就在20秒的等待中。再次或者多次点击这个按钮不会立刻有反应。这时候系统就会弹出一个提醒:应用正在忙:
因此,在按钮点击方法中,耗时的操作应该放置在另外一个单独的线程中。
创建一个新的线程
要创建一个新的线程,并让代码在这个线程中执行,需要把这些代码都放在Runnable接口的Run中。然后需要创建一个新的Thread对象。把Runnable接口的实例作为参数传给Thread的构造函数中。最后调用Thread实例的start方法来开辟线程并执行线程中的方法。
修改后的代码如下:
@Override
public void onClick(View v) {
Runnable runnable = new Runnable() {
@Override
public 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() {
@Override
public void handleMessage(Message message) {
TextView textView = (TextView)findViewById(R.id.text_view);
textView.setText("Button Pressed!");
}
}; //... }
上面的代码中声明了一个handler并实现了handleMessage回调方法。当子线程发出message的时候可以被这个方法处理。在这个实例中,只是简单地在代码中设置了TextView实例的文字。
现在就剩下修改button点击事件中创建的线程了。我们需要在这个线程里发出一个消息告诉handler20秒的延时任务已经执行完成。
修改后的代码如下:
@Override
public void onClick(View v) {
Runnable runnable = new Runnable() {
@Override
public 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 over
handler.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实例。
@Override
public void onClick(View v) {
Runnable runnable = new Runnable() {
@Override
public 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() {
@Override
public 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实例来接受子线程发出的消息来更新界面元素。
Android线程和线程Handler基础一览的更多相关文章
- Android 线程通讯类Handler
handler是线程通讯工具类.用于传递消息.它有两个队列: 1.消息队列 2.线程队列 消息队列使用sendMessage和HandleMessage的组合来发送和处理消息. 线程队列类似一段代码, ...
- (原)Android在子线程用handler发送的消息,主线程是怎么loop到的?
来自知乎:https://www.zhihu.com/question/48130951?sort=created 大家都知道Android的Looper是ThreadLocal方式实现,每个线程 ...
- Android——线程通讯类Handler(转)
原文地址:http://uule.iteye.com/blog/1705951 handler是线程通讯工具类.用于传递消息.它有两个队列:1.消息队列2.线程队列 消息队列使用sendMessage ...
- android线程 Handler Message Queue AsyncTask线程模型 线程交互 + 修改Button样式 示例 最终easy整合版
首先原谅我把文章的标题写的这么长.其实我还嫌弃它短了因为 写不下去了所以我就不写了.因为我实在不知道该怎么定义这篇文章的标题或许应该叫 "乱谈"比较合适. 这样可能还体现了 ...
- Android中的线程池概述
线程池 Android里面,耗时的网络操作,都会开子线程,在程序里面直接开过多的线程会消耗过多的资源,在众多的开源框架中也总能看到线程池的踪影,所以线程池是必须要会把握的一个知识点; 线程运行机制 开 ...
- android中的线程池学习笔记
阅读书籍: Android开发艺术探索 Android开发进阶从小工到专家 对线程池原理的简单理解: 创建多个线程并且进行管理,提交的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度和管理 ...
- android开发之线程
线程(android) 在java中我们学习了线程,线程,是进程的一个单位,在程序要运行时,会开启线程,运行程序,我们要创建线程就需要我们去继承接口Thread或者实现Runabl ...
- android中工作线程安全
当应用程序启动,创建了一个叫“main”的线程,用于管理UI相关,又叫UI线程.其他线程叫工作线程(Work Thread). Single Thread Model 一个组件的创建并不会新建一个线程 ...
- Android ActivityThread(主线程或UI线程)简介
1. ActivityThread功能 它管理应用进程的主线程的执行(相当于普通Java程序的main入口函数),并根据AMS的要求(通过IApplicationThread接口,AMS为Client ...
随机推荐
- 淘宝开源系统监控工具Tsar
Tsar是淘宝开发的一个非常好用的系统监控工具,在淘宝内部大量使用 它不仅可以监控CPU.IO.内存.TCP等系统状态,也可以监控Apache,Nginx/Tengine,Squid等服务器状态 ...
- Life is in the little things --- Spreading wildly on Facebook
这是在FaceBook上疯传的一组图 简笔画的图画的不算精细 但却狠狠地击中许多人的心灵 有时候生活中简单的一件小事, 恰恰是使得你的人生变得更有意义的一件大事! 别人总告诉你 人生是这样的 ▼ ...
- L2tp协议简单解析
1.L2TP简介 L2TP(Layer 2 Tunneling Protocol,二层隧道协议)是VPDN(Virtual PrivateDial-up Network,虚拟私有拨号网)隧道协议的一种 ...
- sql一个题的解法分析讲解
本篇讲述的是对一个sql面试题的细致语法讲解.关于执行流程(on where),内连接,外连接(左右)上实用.关于这些基本的语法知识请参考我前面的sql基本语法. S(SNO,SNAME)学生学号,姓 ...
- while
public class TestWhile { /** * 测试循环 */ public static void main(String[] args) { int a=1; while(a< ...
- redmine邮件配置
网上找了半天,有很多答案,最后自己测试找出一个解决办法. 1.找到安装位置 D:\Bitnami\redmine-2.5.2-2\apps\redmine\htdocs\config下的文件confi ...
- 疯狂JAVA——第二章 理解面向对象
面向对象的三大特征:继承.封装和多态 面向对象的方式实际上由OOA(面向对象分析).OOD(面向对象设计)和OOP(面相对象编程)三个部分组成,其中OOA和OOD的结构需要用一个描述方式来描述并记录, ...
- c# 结构体 集合 复习
添加5个学生的信息到集合中,每个学生都有:学号,姓名,成绩,3个内容,添加完毕后将学生的分数从高到低排列并打印出来,使用结构体 using System; using System.Collectio ...
- 第八章 高级搜索树 (a2)伸展树:双层伸展
- Oracle数据库迁移的几种方式
面试: 一.exp/imp逻辑备份与恢复: 二.Storage存储迁移: 将数据文件.控制文件.日志文件.spfile挂到新机器上,然后在新机器上启动数据库. 三.利用data guard迁移: 四. ...