Android线程

        当应用程序启动时,系统为应用程序启动一个称为"main"的执行线程。该线程非常重要,因为它负责分派包括绘制事件在内的事件给相应的用户界面组件。同时也是在该线程中你的应用程序和安卓UI工具包内组件进行交互。正因为如此主线程有时也称为UI线程。

        系统不会为每个组件创建独立的线程。同一进程内的所有组件都在主线程内实例化,对各个组件的系统调用都在该线程内分派。因此相应的系统回调方法总是在进程的主线程内运行。例如,用户触摸屏幕上按钮时,应用程序的UI线程将触摸事件分派给相应的组件,从而导致了按钮的按下状态和将重绘请求发送到事件队列。UI线程从事件队列取出事件并通知组件重绘。

       当应用程序为了与用户交互而进行密集操作,单线程模式可能导致不良的应用表现除非恰当的实现你的应用程序。特别是在UI线程内执行所有的操作,执行像网络访问、数据库查询这样耗时的操作可能阻塞整个UI。当主线程阻塞时,包括绘制事件在内的所有事件将不会被分派,在用户看来程序好像挂起来了。更为糟糕的是如果UI线程被挂起的时间超过若干秒(大约5秒钟),用户将看到“application not  responding”对话框。用户将决定退出你的程序并将你的程序卸载掉。

       此外安卓的UI工具包(toolkit)不是线程安全,所以你不应该在工作线程内操作你的UI,你应该在UI线程内执行所有的UI操作。因此对于安卓的单线程模型有两规则:1.不要阻塞UI线程。 2.不要在UI线程以外的地方访问安卓的UI。

工作线程:

       由以上对单线程模型的描述可知在响应UI时不要阻塞UI线程是至关重要的。如果你所要执行的操作不是轻量级,你应该在单独的线程内进行(后台或者工作线程)。例如以下是单击监听器的代码,在独立的线程内下载图片并且在在ImageView上显示。

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
            mImageView.setImageBitmap(b);
        }
    }).start();
}

起初这好像工作的很正常,因为它创建了一个新线程执行网络操作。然而它违反了单线程模型的第二个规则:不要在UI线程之外访问安卓的UI toolkit --- 这个例子在工作线程而不是UI线程修改了ImageView。这可能导致未预料和未定义的程序行为,追踪该问题可能很难很耗时。

为了解决这个问题,安卓提供了若干个在其他线程中访问UI线程的办法。以下是可能会给你提供帮助的方法列表: Activity.runOnUiThread(Runnable)

 View.post(Runnable)

                                                                                                                                                                                                         View.postDelayed(Runnable, long)

例如,你可以使用View.post(Runnable)方法修正以上的代码:

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
            mImageView.post(new Runnable() {
                public void run() {
                    mImageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}

现在这个实现是线程安全的:网络操作在独立的线程内执行,对ImageView的处理在UI线程内进行。然而随着操作复杂度的提高,这样的代码将变得难以理解和难以维护。为了处理与工作线程的更复杂的交互,你应该考虑在工作线程内使用Handler来处理从UI线程内传递过来的信息。也许最简单的解决方案是继承AsyncTask类,该类简化了需要与UI交互的工作线程的执行。

使用AsyncTask:

AsyncTask允许你对用户界面执行异步操作。它将在工作线程中执行能引起阻塞的操作,然后将结果发布给UI线程而不用你自己处理线程或者handler。使用它时你应该继承AsyncTask并且实现doInBackground()方法,该方法在后台线程线程池内执行。为了更新你的UI,你应该实现onPostExecute()方法。该方法接受doInBackground()方法并在UI线程内执行,所以你可以安全的更新UI。接着你可以在UI线程内调用execute()方法启动任务。

       例如你可以按这种方式使用AsyncTask实现前面的例子:

public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    /** The system calls this to perform work in a worker thread and
      * delivers it the parameters given to AsyncTask.execute() */
    protected Bitmap doInBackground(String... urls) {
        return loadImageFromNetwork(urls[0]);
    }
    
    /** The system calls this to perform work in the UI thread and delivers
      * the result from doInBackground() */
    protected void onPostExecute(Bitmap result) {
        mImageView.setImageBitmap(result);
    }
}

       现在UI是安全的并且代码很简洁,因为它将任务分成需要在工作线程中执行的部分和需要在UI线程中执行的部分。你应该阅读AsyncTask reference来完全理解怎样使用这个类,这有一个它怎样工作的概述: 1. 你可以使用泛型来指定参数、中间值和任务最终值的类型。

                                                                 2. doInBackground()方法自动在工作线程内执行。

                                                                 3. onPreExecute()、onPostExecute()和onProgressUpdate()方法都是在UI线程内调用。

                                                                 4. doInBackground()方法的返回值被送到onPostExecute()方法。

                                                                 5. 你可以在doInBackground()调用publishProgress()方法来在UI线程内执行onProgressUpdate()。

                                                                 6. 你可以在任何时候在任何线程内取消任务。

在Activity内使用工作线程时你可能遇到的另一个问题是未预料到的重启,由于运行时配置改变(例如当用户切换屏幕)可能销毁你的线程 。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值