【边做项目边学Android】异常处理:android.os.NetworkOnMainThreadException--多线程问题

一切搞定,以为高枕无忧了,结果还是有问题!

log开始报错了,获取更新信息异常!!!debug一下,发现Exception:android.os.NetworkOnMainThreadException

这个异常大概意思是在主线程访问网络时出的异常。 Android在4.0之前的版本 支持在主线程中访问网络,但是在4.0以后对这部分程序进行了优化,也就是说访问网络的代码不能写在主线程中了。

查看网上的解决方法,在Android中实现异步任务机制有两种方式,Handler和AsyncTask。

Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制。

为了简化操作,Android1.5提供了工具类android.os.AsyncTask,它使创建异步任务变得更加简单,不再需要编写任务线程和Handler实例即可完成相同的任务。

这里我们采用AsyncTask的处理方式。

先来看看AsyncTask的定义:

public abstract class AsyncTask<Params, Progress, Result> {

三种泛型类型分别代表“启动任务执行的输入参数”、“后台任务执行的进度”、“后台计算结果的类型”。在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用java.lang.Void类型代替。

AsyncTask的执行分为四个步骤

每一步都对应一个回调方法,这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。
1) 子类化AsyncTask
2) 实现AsyncTask中定义的下面一个或几个方法
         onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。
        doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
        onProgressUpdate(Progress...),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
        onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.

 

为了正确的使用AsyncTask类,以下是几条必须遵守的准则:

1) Task的实例必须在UI thread中创建
2) execute方法必须在UI thread中调用
3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法
4) 该task只能被执行一次,否则多次调用时将会出现异常
      doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为 doInBackground接受的参数,第二个为显示进度的参数,第第三个为doInBackground返回和onPostExecute传入的参数。

下面是项目中具体的处理方法:

package com.liuhao.mobilesafe.engine;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.content.Context;
import android.os.AsyncTask;

import com.liuhao.mobilesafe.domain.UpdateInfo;

public class UpdateInfoService {

	private Context context; //应用程序环境的上下文信息

	public UpdateInfoService(Context context) {
		this.context = context;
	}

	// 将与网络通信的过程封装在ServiceInBackGround的doInBackground方法中
	private class ServiceInBackGround extends AsyncTask<Integer, Void, UpdateInfo>{

		@Override
		protected UpdateInfo doInBackground(Integer... params) {
			String path = context.getResources().getString(params[0]); //根据urlId获取资源文件中对应的内容
			UpdateInfo info = new UpdateInfo();
			URL url;
			try {
				url = new URL(path);
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				conn.setConnectTimeout(20000);
				conn.setRequestMethod("GET");
				
				InputStream is = conn.getInputStream(); //得到url对应的文件流,应该是xml文件流,需要对其进行解析
				
				info = UpdateInfoParser.getUpdateInfo(is); //对xml文件流进行解析,获取到更新信息实体
			} catch (Exception e) {
				e.printStackTrace();
			}
			return info;
		}
		
		@Override
		protected void onPostExecute(UpdateInfo result) {
			super.onPostExecute(result);
		}
		
	}
	
	/**
	 * @param urlId
	 *            服务器资源路径对应的id
	 * @return 更新信息
	 * @throws Exception
	 */
	public UpdateInfo getUpdateInfo(int urlId) throws Exception {
//		new serviceInBackGround().execute(urlId).get();
		return new ServiceInBackGround().execute(urlId).get();
	}

}


最后需要说明AsyncTask不能完全取代线程,在一些逻辑较为复杂或者需要在后台反复执行的逻辑就可能需要线程来实现了。

异常都解决后,再次运行程序:

点击进入程序,会弹出升级对话框:

Screenshot_2014-09-28-15-24-56

查看日志:

image

用户点击确定:

image

点击取消:

image


您的关注是我坚持写作的动力,如果觉得有用,欢迎关注我的微信,海量学习资源免费送!

你的关注是对我最大的鼓励!



参考:

http://www.cnblogs.com/dawei/archive/2011/04/18/2019903.html

http://daoshud1.iteye.com/blog/1843655

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
android.os.NetworkOnMainThreadExceptionAndroid平台中的一个异常,通常是由于在主线程中执行了网络操作而引发的。 Android应用程序的UI线程(主线程)主要用于处理用户界面的更新,如响应用户的操作、刷新UI元素等。然而,在Android平台上,从Android 4.0(即API Level 11)开始,禁止在主线程中执行耗时的网络操作,以避免阻塞用户界面的响应性能。 如果在主线程中尝试进行网络操作,就会抛出NetworkOnMainThreadException异常。这是为了提醒开发者在执行网络操作时应使用其他线程,例如后台线程AsyncTask线程池。 为了解决这个问题,可以采取以下方式之一: 1. 使用AsyncTask:将网络操作放在AsyncTask的doInBackground()方法中执行,该方法在后台线程中运行,并在完成后通过onPostExecute()方法将结果返回给主线程。 ```java class MyTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... params) { // 在后台线程中执行网络操作 return null; } @Override protected void onPostExecute(Void result) { // 在主线程中更新UI或处理结果 } } // 启动任务 new MyTask().execute(); ``` 2. 使用Handler:在主线程中创建一个Handler,在其中使用post()方法来将网络操作放在Runnable中执行。从而使网络操作在后台线程中运行。 ```java Handler handler = new Handler(); handler.post(new Runnable() { @Override public void run() { // 在后台线程中执行网络操作 } }); ``` 无论选择哪种方式,都可以避免在主线程中执行网络操作而导致NetworkOnMainThreadException异常。这样可以确保应用程序的响应性能,并提供更好的用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值