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实例来接受子线程发出的消息来更新界面元素。

 

欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 572064792 | Nodejs:329118122 做人要厚道,转载请注明出处!
















本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/p/4755152.html ,如需转载请自行联系原作者

相关文章
|
1月前
|
Java 调度 Android开发
构建高效Android应用:探究Kotlin多线程编程
【2月更文挑战第17天】 在现代移动开发领域,性能优化一直是开发者关注的焦点。特别是在Android平台上,合理利用多线程技术可以显著提升应用程序的响应性和用户体验。本文将深入探讨使用Kotlin进行Android多线程编程的策略与实践,旨在为开发者提供系统化的解决方案和性能提升技巧。我们将从基础概念入手,逐步介绍高级特性,并通过实际案例分析如何有效利用Kotlin协程、线程池以及异步任务处理机制来构建一个更加高效的Android应用。
41 4
|
1月前
|
API 数据库 Android开发
构建高效Android应用:探究Kotlin多线程优化策略
【2月更文挑战第14天】随着移动设备性能的日益强大,用户对应用程序的响应速度和流畅性要求越来越高。在Android开发中,合理利用多线程技术是提升应用性能的关键手段之一。Kotlin作为一种现代的编程语言,其协程特性为开发者提供了更为简洁高效的多线程处理方式。本文将深入探讨使用Kotlin进行Android多线程编程的最佳实践,包括协程的基本概念、优势以及在实际项目中的应用场景和性能优化技巧,旨在帮助开发者构建更加高效稳定的Android应用。
|
3月前
|
Java 调度 数据库
Android 性能优化: 如何进行多线程编程以提高应用性能?
Android 性能优化: 如何进行多线程编程以提高应用性能?
47 0
|
3月前
|
安全 Android开发 开发者
【Android开发小技巧】扔掉这坑人的 Handler
【Android开发小技巧】扔掉这坑人的 Handler
40 0
|
7月前
|
存储 SQL 安全
Android面试中问的线程相关问题
Android面试中问的线程相关问题
40 0
|
11天前
|
Java API 调度
安卓多线程和并发处理:提高应用效率
【4月更文挑战第13天】本文探讨了安卓应用中多线程和并发处理的优化方法,包括使用Thread、AsyncTask、Loader、IntentService、JobScheduler、WorkManager以及线程池。此外,还介绍了RxJava和Kotlin协程作为异步编程工具。理解并恰当运用这些技术能提升应用效率,避免UI卡顿,确保良好用户体验。随着安卓技术发展,更高级的异步处理工具将助力开发者构建高性能应用。
|
22天前
|
安全 Linux API
Android进程与线程
Android进程与线程
18 0
|
1月前
|
Java Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【2月更文挑战第17天】 随着移动设备性能的不断提升,用户对应用的响应速度和稳定性要求越来越高。在Android开发中,Kotlin语言以其简洁、安全的特点受到开发者青睐。然而,面对复杂的多线程任务,如何有效利用Kotlin进行优化,以提升应用性能,是本文探讨的重点。通过分析Kotlin并发工具的使用场景与限制,结合实例演示其在Android开发中的实践,旨在为开发者提供实用的多线程处理指南。
|
8月前
|
Android开发
Android 中ProgressDialog进度条对话框的使用(使用子线程模拟更新进度)
Android 中ProgressDialog进度条对话框的使用(使用子线程模拟更新进度)
101 0
|
8月前
|
安全 Java Android开发
Android 中AsyncTask后台线程,异步任务的理解
Android 中AsyncTask后台线程,异步任务的理解
101 0

热门文章

最新文章