NetworkOnMainThreadException

别人的坑都要跳一跳。一直听网上说android只有一个UI主线程,不能再UI主线程做耗时操作。。。我也知道,但是。。。言归正传

NetworkOnMainThreadException:网络请求在MainThread中产生的异常

Class Overview
The exception that is thrown when an application attempts to perform a networking operation on its main thread.
This is only thrown for applications targeting the Honeycomb SDK or higher. Applications targeting earlier SDK versions are allowed to do networking on their main event loop threads, but it’s heavily discouraged. See the document Designing for Responsiveness.Also see StrictMode.

不再允许网络请求等相关耗时操作直接在Main Thread类中,其实本来就不应该这样做,直接在UI线程进行网络操作,会阻塞UI、用户体验相当不好,而且多个线程并发,就不好控制变量的传递了。

解决的方法很简单,在你那个操作前面new一个Thread,在子线程中做这个耗时操作。

new Thread(new Runnable() {
            @Override
            public void run() {
            //这里写你的耗时操作
            }
        }).start();

这样,就不会报错了。那么问题又来了,如果你有多个地方做耗时操作或者你想更新UI怎么破。那么你就要用到Andorid中独有的Handler消息传递机制:

由于Android要求在UI线程中更新界面,在Android的应用启动时,会创建一个主线程(UI主线程),主线程会创建一个
消息队列来处理各种消息。当你创建子线程时,你可以在你的子线程中拿到父线程中
创建的Handler 对象,就可以通过该对象向父线程的消息队列发送消息通知UI主线程更新了。
还有一种情况,就是你new了两个Thread,有可能你以为程序运行的顺序是从上到下的,你第二个Thread里面要用到第一个Thread运行完后的结果变量,往往在某些情况下会报那个变量是空指针,为什么,因为线程使并发操作的,有可能你第一个Thread还没执行完,第二个Thread就已经开始了。这也是我初学时候经常做的蠢事了。可以利用handler消息传递机制,在第一个Thread完成后发送一个message给Handler,在接收到这个message后再去执行下一个Thread操作。
角色描述:
1) Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的Message Queue(消息队列)
2) Handler: 你可以构造Handler对象来与Looper沟通,以便push 新消息到 Message Queue里,或者接收Looper从Message Queue 里所送来的消息。
3)Message Queue(消息队列):是用来存放线程放入的消息。
4)线程:UI thread 通常就是 main thread,而Android 启动程序时会替它建立一个Message Queue(消息队列)。每一个线程里可含有一个 Looper 对象以及一个 Message Queue 数据结构。在你的应用程序里,
可以定义 Handler 的子类别来接收 Looper 所送出的消息。

这里写图片描述

for example:

package com.example.test;

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

public class TestAct extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 开启一个子线程,进行网络操作,等待有返回结果,使用handler通知UI
        new Thread(networkTask).start();
        // 也可以直接new Runnable对象,做耗时操作
        // new Thread(new Runnable() {
        //
        // @Override
        // public void run() {
        //
        // }
        // }).start();
    }

    Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            int a = msg.arg1;
            // 得到包裹
            Bundle data = msg.getData();
            // 通过键找到值
            String value = data.getString("key");
            // 这里得到线程最终的结果后可以做更新界面
        }
    };

    Runnable networkTask = new Runnable() {
        @Override
        public void run() {
            // 耗时操作
            // 在这里进行 http request.网络请求相关操作
            Message msg = Message.obtain();
            msg.arg1 = 1;
            msg.arg2 = 2;
            msg.obj = "已完成";
            // 可以传递数据包Bundle,打包带走
            Bundle data = new Bundle();
            data.putString("key", "value");
            msg.setData(data);
            handler.sendMessage(msg);
        }
    };

}

然后我就开始百度,发现了异步加载类asynctask,最后总结出来,asynctask除了比较简化代码外(在单独线程),数据量多且复杂使用handler+thread :相比较AsyncTask来说能更好的利用系统资源且高效。asynctask更多的是在主界面点击后,在后台运行的异步任务。。。。粗浅的理解,同时觉得AsyncTask在多处需要更新UI主线程简直是个灾难。。。。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值