Android4.0网络操作必须放在子线程中

好久没写技术博客了。关于为什么这么久没写,是因为写伤了。写过几篇博客后,深刻感觉到要把博客写好是一件很累的事情,花的时间也肯定不会少。记得前段时间写Android中Activity四种启动模式和taskAffinity属性详解这篇博客,前前后后写了一个星期,从那之后真有点怕写这么长的博客了。所以, 以后尽可能不写这种总结性的,篇幅较长的博客,因为自己水平真的有限,写这种长博客太累, 写了之后别人还不见得有耐心读完。另一方面, 写这种长博客花的时间和精力太多, 很累人,并且只能下了班之后抽晚上的时间写。由于不满足自己现在的水平,我业余时间最想做的事是多看书,多补充知识。少写博客,要写的话也是那种短的博客, 可能是工作中解决了一个问题,并且认为这个问题很有代表性,就会整理一下写成一小篇;也可能平时写了一个小demo,能说明一些问题,也会写成短篇博客。

最近一直在做一个移动办公的项目,今天写登陆验证这块的时候被一个问题给缠住了。下面会通过这个简单的小问题来引出这篇文章的主题


Android4.0中网络操作必须放在子线程中


这个登陆验证是一个简单的不能再简单的登陆验证了, 就是把用户名和密码还有其他一些信息以参数的形式发送到后台, 然后读出后台返回的验证结果。在进行http请求时,使用的是标准的UrlConnection类,下面是这个功能的代码:

private static boolean userLoginCheck1( Context context){
		

		//设置登录验证的各项参数
        List<BasicNameValuePair> params = new LinkedList<BasicNameValuePair>();
        params.add(new BasicNameValuePair("yhid", userName));
        params.add(new BasicNameValuePair("yhkl", passwd));
        params.add(new BasicNameValuePair("sbid", DeviceTool.getDeviceId(context)));
        params.add(new BasicNameValuePair("clientIp", IPTool.getPsdnIp()));
        params.add(new BasicNameValuePair("ywxtbm", "BGPTNEW"));
        params.add(new BasicNameValuePair("ywxtmc", "办公平台升级"));


        URL url = null;
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = null;

        try{
            
        	//设置url地址    
            url = new URL(URLConstant.USER_LOGIN_CHEAK_ADDRESS);
            
            String paramString = URLEncodedUtils.format(params, "GBK");	//请求参数编码为GBK
            
            byte[] dataToSend = paramString.getBytes();					//post请求中的实体数据

            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setConnectTimeout(5000);
            connection.setRequestProperty("Content-Length", String.valueOf(dataToSend.length));
            
            connection.getOutputStream().write(dataToSend);				//发送数据
            
            if(connection.getResponseCode() != 200){
            	Log.i("eSDK", "验证登陆请求响应码不是200");
            	return false;
            }

            InputStream stream = connection.getInputStream();
            reader = new BufferedReader(new InputStreamReader(stream, "GBK"));

            String lines = "";
            while ((lines = reader.readLine()) != null){
                sb.append(lines);
            }
            
            Log.i("eSDK", "login cheak response: " + sb.toString());
            
            //对返回的数据进行判断, 以验证是否可以登陆
			return "验证成功".equals(sb.toString());
            
        }catch (Exception e){
        	Log.i("eSDK", "logincheck fail" );
        	return false;
        }finally{
            if (reader != null){
                try{
                    reader.close();
                }
                catch (IOException e){}
            }
        }
		
	}

做过网络编程的, 都会对这段代码非常非常熟悉,就是一个标准的http请求。但是我却在这里卡住了,程序逻辑没错啊, 却怎么也通不过,每次单步跟踪到
 connection.getOutputStream().write(dataToSend);

这句代码就会抛出异常,真是被搞的很郁闷。最后将这段代码拿到java工程中运行, 没有任何问题。这时,我意识到自己犯了一个愚蠢的错误。由于是边写代码边测试,所以没有把这个方法放到子线程中执行。android2.3中只是建议把网络操作放到子线程中,而android4.0要求必须把和网络相关的操作放到子线程中。其实这个知识点我是知道的,因为我不是第一次犯这种错误,但是出现问题时却没往这个方面思考。所以:

当你在Android4.0中调用和网络相关的api时总是不能成功,那么检查一下是否在子线程中调用的;
当你在Android4.0中写以上类似的方法时,如果总是在connection.getOutputStream()处抛异常,那么检查一下是否在子线程中调用;

Android4.0中的这一个特性很坑爹,因为如果你在主线程中进行网络操作,它只是让你失败, 并不会给出任何通知,所以如果不知道这个特性,或者是没考虑到这个方面,那么解决问题是很费时间的!个人认为android有必要在检测到主线程中有网络操作时,抛出运行时异常,比如可以定义一个RunInWrongThreadException。


你可能感兴趣的:(android,线程,网络,网络编程,Android4.0)