通用技术 Android 的单线程模型以及 UI 线程问题记录

kiven · 2014年03月19日 · 最后由 Archer 回复于 2014年03月20日 · 3446 次阅读

Andoid UI toolkit 并不是线程安全的,所以你不能从非 UI 线程来操纵 UI 组件。你必须把所有的 UI 操作放在 UI 线程里,所以 Android 的单线程模型有两条原则:

1.不要阻塞 UI 线程。
2.不要在 UI 线程之外访问 Android UI toolkit(主要是这两个包中的组件:android.widget and android.view)。

那我之前写的一个 demo 中,在 UI 线程中我需要发起一个网络请求并解析响应的结果,调试几次发现,每次到达发送 http 请求时就会抛出异常。。

谷歌了一大把,终于发现本文最前面讲的东西。在安卓 4.0 以后采用了严苛模式,造成这样的错误原因是代码不符合 Android 规范,如果把上面访问方式改为异步操作就不会出现在 4.0 上访问出现 android.os.NetworkOnMainThreadException 异常

方法一:
那么为了保留这个项目并可运行,我们可以在 oncreate 方法的一开始就添加如下代码:

    //严苛模式(StrictMode)
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() //构造StrictMode          
    .detectDiskReads() //当发生磁盘读操作时输出  
    .detectDiskWrites()//当发生磁盘写操作时输出  
    .detectNetwork()  //访问网络时输出,这里可以替换为detectAll() 就包括了磁盘读写和网络I/O  
    .penaltyLog()  //以日志的方式输出  
    .build());  
    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()  
    .detectLeakedSqlLiteObjects() //探测SQLite数据库操作  
    .penaltyLog() //以日志的方式输出  
    .penaltyDeath()  
    .build());

方法二:
或者
“只有在 UI 线程中的对象才能操作 UI 线程中的对象,为了将非 UI 线程中的数据传送到 UI 线程,可以使用一个 Handler 运行在 UI 线程中。Handler 是 Android framework 中管理线程的部分,一个 Handler 对象负责接收消息然后处理消息。”

我可以为一个新的线程创建一个 Handler,也可以创建一个 Handler 然后将它和已有线程连接。

代码如下:

//获取网络资源..采用handler+Thread模式实现多线程异步加载
    private void loadContent(){
        Thread thread = new Thread(){
            @Override
            public void run(){
                //载入xml文件
                try {
                    //先得到文章信息
                    title = analyzeXML(i);//analyzeXML()为我一个自定义方法,返回一个String
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                // 模拟网络延时
                SystemClock.sleep(2000);
                Message msg = new Message();
                //将title通过sendMessage方法发送到handler中
                msg.obj = title;
                myHandler.sendMessage(msg);
            }
        };
        thread.start();
        }
    //更新UI
private Handler myHandler = new Handler(){
    @SuppressWarnings("unused")
    public void handlerMessage(Message msg){
        //super.handleMessage(msg);
        //取得其他线程发送过来的Message对象
        //设置textview控件的值
        textview.setText("第"+(i+1)+"篇文章标题是:"+msg.obj);
    }
};

搞定!
另外注明,本人乃刚入手学习安卓,不对之处请各位多多指教~~~

共收到 8 条回复 时间 点赞

你可以考虑使用 intent service 来实现。 把逻辑都放到后台去。
我也不太喜欢在 ui 的线程内再启动新线程, 这样感觉代码太乱。

9楼 已删除

#3 楼 @lihuazhang 恒温,我们的 kiven 正在调试我们的 DiggerPlus 官方应用......哈哈

#4 楼 @qddegtya 你们也有自己的官方应用了啊, 赞

#6 楼 @seveniruby web 端也是各种优化,各种新功能,近期忙啊

#6 楼 @seveniruby 没,正开始搞呢,估计还得一段时间

#8 楼 @kiven 今天电脑带来了么....

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册