其他线程handler和ui组件与toast深入探讨

看《疯狂安卓讲义》第三版212页。3.5handler消息传递机制中3.5.2节,使用新线程计算质数
有一个下面的代码

package org.crazyit.handler;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity
{
    static final String UPPER_NUM = "upper";
    EditText etNum;
    CalThread calThread;
    // 定义一个线程类
    class CalThread extends Thread
    {
        public Handler mHandler;
        public void run()
        {
            Looper.prepare();
            mHandler = new Handler()
            {
                // 定义处理消息的方法
                @Override
                public void handleMessage(Message msg)
                {
                    if(msg.what == 0x123)
                    {
                        int upper = msg.getData().getInt(UPPER_NUM);
                        List<Integer> nums = new ArrayList<Integer>();
                        // 计算从2开始、到upper的所有质数
                        outer:
                        for (int i = 2 ; i <= upper ; i++)
                        {
                            // 用i除以从2开始、到i的平方根的所有数
                            for (int j = 2 ; j <= Math.sqrt(i) ; j++)
                            {
                                // 如果可以整除,则表明这个数不是质数
                                if(i != 2 && i % j == 0)
                                {
                                    continue outer;
                                }
                            }
                            nums.add(i);
                        }
                        // 使用Toast显示统计出来的所有质数
                        Toast.makeText(MainActivity.this, nums.toString()
                            , Toast.LENGTH_LONG).show();
                    }
                }
            };
            Looper.loop();
        }
    }
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        etNum = (EditText)findViewById(R.id.etNum);
        calThread = new CalThread();
        // 启动新线程
        calThread.start();
    }
    // 为按钮的点击事件提供事件处理方法
    public void cal(View source)
    {
        // 创建消息
        Message msg = new Message();
        msg.what = 0x123;
        Bundle bundle = new Bundle();
        bundle.putInt(UPPER_NUM ,
                Integer.parseInt(etNum.getText().toString()));
        msg.setData(bundle);
        // 向新线程中的Handler发送消息
        calThread.mHandler.sendMessage(msg);
    }
}

本段代码就是把一个计算质数的东西放到一个新线程里面计算。避免ui线程出现ANR(application not responding)异常。
这个代码可以进行一下简化,剔除质数计算步骤。简化后代码如下

public class MainActivity extends Activity
{

    EditText etNum;
    CalThread calThread;
    // 定义一个线程类
    class CalThread extends Thread
    {
        public Handler mHandler;
        public void run()
        {
            Looper.prepare();
            mHandler = new Handler()
            {
                // 定义处理消息的方法
                @Override
                public void handleMessage(Message msg)
                {
                    if(msg.what == 0x123)
                    {

                       Toast.makeText(MainActivity.this,"显示信息",Toast.LENGTH_LONG).show();
                    }
                }
            };
            Looper.loop();
        }
    }
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        etNum = (EditText)findViewById(R.id.etNum);
        calThread = new CalThread();
        // 启动新线程
        calThread.start();
    }
    // 为按钮的点击事件提供事件处理方法
    public void cal(View source)
    {

        calThread.mHandler.sendEmptyMessage(0x123);
    }
}

执行结果如下
这里写图片描述

可以看到。当点击按钮之后。可以显示一个Toast信息。。但是问题来了Toast信息不是ui的一部分吗?我们是在一个非ui线程里面操作了UI。这个是安卓不允许的。为了验证,我们可以把toast改成设置EditText的文字。代码如下(只给出了Thread的代码)

class CalThread extends Thread
    {
        public Handler mHandler;
        public void run()
        {
            Looper.prepare();
            mHandler = new Handler()
            {
                // 定义处理消息的方法
                @Override
                public void handleMessage(Message msg)
                {
                    if(msg.what == 0x123)
                    {
                     //Toast.makeText(MainActivity.this,"显示",Toast.LENGTH_LONG).show();
                        etNum.setText("显示信息");
                    }
                }
            };
            Looper.loop();
        }
    }

运行结果如下
当点击按钮发送消息程序退出。信息如下

11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test W/dalvikvm: threadid=11: thread exiting with uncaught exception (group=0xa4cb9b20)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime: FATAL EXCEPTION: Thread-123
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime: Process: com.example.bleuesprit.test, PID: 7638
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6024)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:853)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.view.ViewGroup.invalidateChild(ViewGroup.java:4320)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.view.View.invalidate(View.java:10878)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.widget.TextView.invalidateRegion(TextView.java:4651)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.widget.TextView.invalidateCursor(TextView.java:4594)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.widget.TextView.spanChange(TextView.java:7502)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.widget.TextView$ChangeWatcher.onSpanAdded(TextView.java:9214)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.text.SpannableStringBuilder.sendSpanAdded(SpannableStringBuilder.java:979)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:688)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:588)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.text.Selection.setSelection(Selection.java:76)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.text.Selection.setSelection(Selection.java:87)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.text.method.ArrowKeyMovementMethod.initialize(ArrowKeyMovementMethod.java:302)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.widget.TextView.setText(TextView.java:3801)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.widget.TextView.setText(TextView.java:3671)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.widget.EditText.setText(EditText.java:80)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.widget.TextView.setText(TextView.java:3646)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at com.example.bleuesprit.test.MainActivity$CalThread$1.handleMessage(MainActivity.java:36)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:102)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at android.os.Looper.loop(Looper.java:136)
11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime:     at com.example.bleuesprit.test.MainActivity$CalThread.run(MainActivity.java:40)

原因在这里

11-09 09:20:04.483 7638-7665/com.example.bleuesprit.test E/AndroidRuntime: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views

说明只有ui线程能更新ui组件。但是问题来了Toast不是ui的一部分吗?
为了验证我们继续.如果toast不是ui的一部分。那么我们可以在任意线程里面构造一个toast然后显示出来。代码如下


public class MainActivity extends Activity {

    EditText etNum;
    CalThread calThread;

    // 定义一个线程类
    class CalThread extends Thread {

        public void run() {


            Toast.makeText(MainActivity.this, "显示信息", Toast.LENGTH_LONG).show();

        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        etNum = (EditText) findViewById(R.id.etNum);
        calThread = new CalThread();
        // 启动新线程
        calThread.start();
    }

    // 为按钮的点击事件提供事件处理方法
    public void cal(View source) {

       //
    }
}

这个就是完全在一个线程启动之后显示一个toast

11-09 20:10:19.765 1578-1597/com.example.bleuesprit.test E/AndroidRuntime: FATAL EXCEPTION: Thread-99
11-09 20:10:19.765 1578-1597/com.example.bleuesprit.test E/AndroidRuntime: Process: com.example.bleuesprit.test, PID: 1578
11-09 20:10:19.765 1578-1597/com.example.bleuesprit.test E/AndroidRuntime: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
11-09 20:10:19.765 1578-1597/com.example.bleuesprit.test E/AndroidRuntime:     at android.os.Handler.<init>(Handler.java:200)
11-09 20:10:19.765 1578-1597/com.example.bleuesprit.test E/AndroidRuntime:     at android.os.Handler.<init>(Handler.java:114)
11-09 20:10:19.765 1578-1597/com.example.bleuesprit.test E/AndroidRuntime:     at android.widget.Toast$TN.<init>(Toast.java:327)
11-09 20:10:19.765 1578-1597/com.example.bleuesprit.test E/AndroidRuntime:     at android.widget.Toast.<init>(Toast.java:92)
11-09 20:10:19.765 1578-1597/com.example.bleuesprit.test E/AndroidRuntime:     at android.widget.Toast.makeText(Toast.java:241)
11-09 20:10:19.765 1578-1597/com.example.bleuesprit.test E/AndroidRuntime:     at com.example.bleuesprit.test.MainActivity$CalThread.run(MainActivity.java:26)

其实意思就是

Can't create handler inside thread that has not called Looper.prepare()

这说明好像toast是ui的一部分必须要在handler里面才能更改。但是又问题来了其他线程的handler真的能改任意ui吗?不能已经试过了。那么为什么会出现这样的问题。我需要仔细研究一下代码再说。回来补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值