Android UI组件的动态更新

本文探讨了Android中UI动态更新的三种方法:Handler+Thread、post+Thread以及AsyncTask。强调了UI线程不应执行耗时操作以避免ANR异常,并分别通过示例代码介绍了三种方式如何在新线程中处理耗时任务并更新UI。最后,建议使用AsyncTask,因为它更简洁且易于实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        Android的程序启动时,会同时启动一条主线程,又称为UI线程,主要负责UI相关的操作,包括:按键事件、触屏事件以及屏幕绘图事件等等。UI线程应该尽量避免耗时操作,否则将出现著名的ANR异常(Application Not Reponse)。

        但是,有时候需要实现UI的动态更新,而更新操作可能又是耗时的。此时,耗时操作则必须放在新线程里进行。下面以一个简单例子,讨论一下UI动态更新的方法。

一、Handler + Thread

直接上代码

package com.dale.uiupdate;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ImageView;


public class MainActivity extends Activity {
	
	ImageView mIv;
	
	
	Handler mHandler1 = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            mIv.getDrawable().setLevel(msg.what);
        }        
    };
    
    class Thread1 extends Thread {

        @Override
        public void run() {
            try {
                for(int i=0; i!=10; i++) {
                    mHandler1.sendEmptyMessage(i%3);
                    sleep(1000);
                } 
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mIv = (ImageView) findViewById(R.id.iv);
        //通过新开线程发送消息给Handler,由Handler来更新当前UI状态
        new Thread1().start();
    }

}

界面布局时分简单,仅一个ImageView

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.dale.uiupdate.MainActivity" >

    <ImageView
        android:id="@+id/iv"
        android:layout_width="80dp"
        android:layout_height="200dp"
        android:src="@drawable/img_view" />

</RelativeLayout>


这个ImageView加载的是一个LevelListDrawable,img_view.xml

<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:drawable="@drawable/red"
        android:maxLevel="0"/>
    <item
        android:drawable="@drawable/green"
        android:maxLevel="1"/>
    <item
        android:drawable="@drawable/blue"
        android:maxLevel="2"/>

</level-list>

用到的资源为ColorDrawable,drawables.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    
    <drawable name="red">#f00</drawable>
    <drawable name="green">#0f0</drawable>
    <drawable name="blue">#00f</drawable>
    
</resources>


 

        程序启动时,通过新建独立线程(理解为处理耗时操作的线程),并不断发送UI更新消息,由Handler来处理,根据参数不断改变LevelListDrawable的层级,从而实现UI动态更新。

二、post + Thread

package com.dale.uiupdate;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ImageView;


public class MainActivity extends Activity {
	
	ImageView mIv;
	
	
	int mnCur;
    Runnable run = new Runnable() {

        @Override
        public void run() {
            mIv.getDrawable().setLevel(mnCur%3);
        }
    };
    
    class Thread2 extends Thread {

        @Override
        public void run() {
        	for(int i=0; i!=10; i++) {
                mnCur = i;
                mIv.post(run);
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mIv = (ImageView) findViewById(R.id.iv);
        //通过新开线程来更改当前level值mnCur,经由mIv调用post来更改UI状态
        new Thread2().start();
    }

}

同样,此方法创建了一个新线程来处理耗时操作,但是更新UI,则利用了View的post方法。官的说明如下:

public boolean post(Runnable action)

/**
     * Causes the Runnable to be added to the message queue.
     * The runnable will be run on the user interface thread.
     *
     * @param action The Runnable that will be executed.
     *
     * @return Returns true if the Runnable was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     *
     */

        即,将一个Runnable对象添加到消息队列,此Runnable将会运行在UI线程。

        耗时操作由新建线程实现,处理结果则由post来通知到UI线程,成功实现UI动态更新。 

三、AsyncTask

通过异步任务类AsyncTask实现UI更新,代码如下

package com.dale.uiupdate;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ImageView;


public class MainActivity extends Activity {
	
	ImageView mIv;
	
	class AsyncTaskHandler extends AsyncTask<ImageView, Integer, Boolean> {
        
        ImageView param;

        @Override
        protected void onProgressUpdate(Integer... values) {
            param.getDrawable().setLevel(values[0]);
        }

        @Override
        protected void onPostExecute(Boolean result) {
        }

        @Override
        protected Boolean doInBackground(ImageView... params) {
            param = params[0];
            int i=0;
            while(i <= 10) {
                try {
                    Thread.sleep(1000);
                    int cur = param.getDrawable().getLevel();
                    publishProgress((cur+1) % 3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                i++;
            }
            return true;
        }        
    }
    
	AsyncTaskHandler mAsyncTaskHandler = new AsyncTaskHandler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mIv = (ImageView) findViewById(R.id.iv);
        //通过实现AsyncTask来处理UI更新
        mAsyncTaskHandler.execute(mIv);
    }

}


AsyncTask<Params, Progress, Result>是一个抽象类,其中的三个泛型类型:

  • Params: 启动任务执行的输入参数类型
  • Progress: 后台任务完成的进度值类型
  • Result: 后台执行任务完成后的返回结果类型

代码实现了的三个方法:

  • doInBackground(Params... params):后台线程将要完成的任务,调用publishProgress来更新任务运行进度
  • onProgressUpdate(Progress... values):在doInBackground中调用了publishProgress将会触发此方法,更新处理进度
  • onPostExecute(Result result):doInBackground执行完后,系统自动调用此方法,并将doInBackground的返回值传入此方法

 实现了AsyncTask抽象类并重写需要的方法后,即可在UI线程中调用execute方法,开启后台耗时任务。通常AsyncTask适用于“秒级耗时任务”,即在几十秒以内为宜,过长的耗时任务,官方建议采用其他方案。 

四、结语

        第一种和第二种方案,归根结底,都是利用新建线程处理耗时任务,然后将耗时任务的处理结果通知到UI线程(一个通过UI线程的Handler,一个通过UI组件)。而AsyncTask具体实现原理,我未作深入研究,但从原理来讲,应该也是类似的。总之一句话:UI线程不能执行耗时操作,否则出现响应异常问题。三种方案对比来看,AsyncTask不需要借助线程和Handler,实现起来较简单,推荐使用。

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值