工大后院

 找回密码
 加入后院

扫一扫,访问微社区

QQ登录

只需一步,快速开始

搜索
查看: 4901|回复: 3

【翻译】The Busy Coder's Guider for Android development| Dealing with Thread

[复制链接]
发表于 2011-11-17 01:09 | 显示全部楼层 |阅读模式
本帖最后由 iptton 于 2011-11-17 01:10 编辑

未翻译完,原文:http://blog.csdn.net/iptton/article/details/6975134

英文pdf可到ppurl下载:http://www.ppurl.com/2011/04/the ... nt-version-3-6.html
上一次更新已经是很久以前的事了吧。从毕业到现在,心态似乎变了很多。无论如何,加油吧。
以下为有选择的翻译,你可以看到原文的所有技术细节,但不保证你能看到原文中作者代码实现之外的观点。
Chapter 20
Dealing with Threads


操作顺畅让人一气呵成的应用总能讨人喜欢。界面像卡了带的录像的软件没人会喜欢。【题外:ios应用与android应用相比,还真是应上这两种感觉】


应用主线程(The Main Application Thread)
你是不是觉得当你调用TextView的setText()方法时,界面马上就更新?
对,也不对。(You would be mistaken)
准确点说:所有对UI组合进行的修改都要通过一个消息队列(message queue)。调用setText()并不会刷新界面,它仅仅发送了一条“更新界面”的消息到消息队列中去。当消息队列开始出列时,执行到此消息时,界面才会真正更新。


维护这个消息队列的是“主程序线程”,或者叫“UI线程”(main application thread/UI thread)。只要此线程能一直执行,界面就能够刷新并响应用户的操作。【so long as:只要】


如果你把onCreate,onClick,onListItemClick等回调函数【或其它不需要更新UI的函数】也放在此线程中执行,那么,主线程将忙于处理你的回调函数而没时间去处理UI消息队列刷新页面,无法响应用户的操作了。如果这些挤占了UI消息队列的函数连续占用主线程数秒,Android将会提示你“界面无反应”(Application Not Responding),此时Activity也许会被关闭。


因此,要避免出现这些情况,你要把类似耗时的操作放在后台线程中去执行。【原文真好啰嗦...】如以下几种:
   1,网络访问,发送数据或下载数据
   2,文件操作(闪存读写经常是慢如蜗牛的)
   3,各种复杂耗时的计算


为此,Android提供了Java标准Thread,外加一些有用的封装,如java.util.concurrent包。


但是,要切记的一点是,你能且只能在主线程上修改UI!
所以,你需要把耗时的操作放在后台线程上跑,但这些操作可能又需要使用主线程更新UI。本章主要讨论后台线程如何和主线程通信以更新UI。


Making Progress with ProgressBars
在所有UI界面中,进度条是很常见的,balabala.(略过关于ProgressBar的介绍)..


Get Through the Handler
了解Handler
创建一个适当的后台线程的最好办法是实例化一个Handler子类。每个Activity中你只需要创建一个Handler,且不需要做任何“注册”工作,Anroid的线程子系统会自动帮你注册。
后台线程可以与Handler通信,而Handler的运行则是在UI线程中进行的。
后台线程与Handler通信有两种方法:message 和 Runnable。


一,Messages方式
发送Message之前,你可以先调用Message的obtainMessage方法获取一个Message对象【这种方法要比new Message要高效】。obtainMessage 有好几种调用方式,你可以指定消息的类型及所带的对象以区分消息。


获取消息后,你需要调用Handler的sendMessage系列方将message送到消息队列里:
  1,sendMessage 消息放到队列后面
  2,sendMessageAtFrontOfQueue 消息放到队列最前,优先处理
  3,sendMessaegAtTime 在指定的时间将消息放入队列,时间与SystemClock.uptimeMillis()同类格式
  4,sendMessageDelayed 延迟若干毫秒将消息放入队列  
  5,sendEmptyMessage 发送一个空消息。当你不需要指定消息类型时,可以用此方法而跳过obtainMessage这一步。
Handler实例需要实现handleMessage方法来处理消息队列里的消息。每次消息队列出列一条消息,handleMessage就会被调用一次。切记handleMessage方法是在UI线程里跑的,这个方法里的处理不能过于耗时,否则界面会“卡”。


以下为本例代码:

<?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"
>
<ProgressBar android:id="@+id/progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>




Java代码:

package com.commonsware.android.threads;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ProgressBar;
import java.util.concurrent.atomic.AtomicBoolean;
  
public class HandlerDemo extends Activity {
        ProgressBar bar;
        Handler handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                        bar.incrementProgressBy(5);
                }
        };
        AtomicBoolean isRunning = new AtomicBoolean(false);


        @Override
        public void onCreate(Bundle icicle) {
                super.onCreate(icicle);
                setContentView(R.layout.main);
                bar = (ProgressBar) findViewById(R.id.progress);
        }


        public void onStart() {
                super.onStart();
                bar.setProgress(0);
                Thread background = new Thread(new Runnable() {
                        public void run() {
                                try {
                                        for (int i = 0; i < 20 && isRunning.get(); i++) {
                                                Thread.sleep(1000);
                                                handler.sendMessage(handler.obtainMessage());
                                        }
                                } catch (Throwable t) {
                                        // just end the background thread
                                }
                        }
                });
                isRunning.set(true);
                background.start();
        }


        public void onStop() {
                super.onStop();
                isRunning.set(false);
        }
}
做为Activity构造函数的一部分,我们先创建了一个Handler实例【关于Java中成员初始化的问题,可以看这里http://blog.csdn.net/macheng365/article/details/6403050】。本例中handleMessage只是简单地每次给progressBar添加5个进度点。
再看看onStart和onStop两个方法。onStart方法中,我们创建了个后台线程,在真正的应用里,这个线程会做些有意义的事,本例仅让它休眠1秒然后发消息更新progressBar,如此反复20次。创建完后台线程后,onStart方法就结束了,因为onStart方法是在UI线程里执行的,所以这至关重要。

运行后效果如下图:




尽管此例代码中我们UI线程来更新progressBar,但实际上是不需要的,因为ProgressBar是"UI Thread safe"的,也就是说,你可以在任意线程中对它进行更新操作,它内部会将相关操作放到UI线程里执行。


二,Runnable方式
如果不想劳烦message【需要事先设计各种message类型及所传参数】,你可以传一个Runnable对象给Hanldler,该对象也一样可以在UI线程中跑。类似地,Handler提供了post系列的方法来传Runnable对象。


Where, Oh Where Has My UI Thread Gone?
有时候,你的代码可能需要复用,或者以打包jar的方式,或者直接源代码的方式。这时你不能确定你的代码是否在UI线程中跑。你可以调用Activity的runOnUiThread方法来解决这个问题。




Asyncing Feeling
Android1.5引入一个新的后台操作方法:AsynTask(异步任务)。
营销界有一句话:“"When a man buys a 1/4" drill bit at a hardware store, he does not want a 1/4" drill bit – he wants 1/4" holes”(一个买1/4英寸钻孔机的人所需要的不是一个1/4英寸的钻孔机而是需要1/4英寸的孔)。同样道理,开发都需要的不是后台线程,而是一个可以避免应用出现“应用无反应”状态的方法。AsynTask就提供了这样的便利。
使用AsynTask,你需要做以下事:
   1,创建AsynTask子类,通过为所需要使用它的类的内部私有类。
   2,重写一个或多个AsynTask方法以满足你的需求。
   3,创建此类的实例,并执行execute方法。
使用AsynTask方法,你将不需要做以下事情:
    1,创建后台线程
2,结束后台线程
3,调用各中方法以使UI相关的操作能在UI线程上进行


AsyncTask, Generics, and Varargs
AsynTask,泛型及可变参数
AsynTask子类的创建要比Runnable稍为复杂,因为它使用了泛型及可变参数。你需要明确以下三种数据类型:

--to be continue
   
头像被屏蔽
发表于 2012-5-4 08:31 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

头像被屏蔽
发表于 2012-7-15 14:45 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

头像被屏蔽
发表于 2012-7-24 12:27 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 加入后院

本版积分规则

QQ|Archiver|手机版|小黑屋|广告业务Q|工大后院 ( 粤ICP备10013660号 )

GMT+8, 2024-5-2 22:58

Powered by Discuz! X3.5

Copyright © 2001-2024 Tencent Cloud.

快速回复 返回顶部 返回列表