android访问网络异常的解决方法和在子线程中操作UI的方法

先下结论:android访问网络不能在主线程中进行网络连接,因此,必须另起一个线程进行网络连接方面的操作。而操作UI只能在主线程中进行,如果要在子线程中操作UI的界面组件,就需要借助Handler的消息传递机制的方法。

Android4.0 以后不允许在主线程进行网络连接

Android4.0 以后不允许在主线程进行网络连接,否则会出现 android.os.NetworkOnMainThreadException因此,必须另起一个线程进行网络连接方面的操作。

旧写法:

package com.lujinhong.irmcdc.dao;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class ResourceDao {

	String returnLine = "hi";

	public String getAllContentByName() {
		Runnable r = new NetWorkHandler();
		Thread thread = new Thread(r);
		thread.start();

		try {
			Thread.sleep(15000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return returnLine;
	}

	private class NetWorkHandler implements Runnable {

		String line = "line";
		Socket socket = null;
		Scanner scanner = null;
		@Override
		public void run() {
			try {
				socket = new Socket("time-A.timefreq.bldrdoc.gov", 13);
				scanner = new Scanner(socket.getInputStream());
				while (scanner.hasNextLine()) {
					line = scanner.nextLine();
					returnLine += line;
				}

			} catch (UnknownHostException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				scanner.close();
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

1、由于网络连接需要一定时间,为了在主界面上进行网络信息的展现,暂时用sleep()方法简单实现,使主线程等待网络信息读取完成。

Thread.sleep(5000);

刚刚上手android,在写一个利用Socket与服务器端进行通信的Demo时候报了一个android.os.NetworkOnMainThreadException的异常:

服务器端:

public class SimpleServer { 
public static void main(String[] args){ 
try { 
ServerSocket ss=new ServerSocket(40000); 
System.out.println("等待连接......"); 
while(true){ 
Socket s=ss.accept(); 
OutputStream os=s.getOutputStream(); 
os.write("hello".getBytes("utf-8")); 
os.close(); 
s.close(); 
} 
} catch (IOException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 
} 
} 
} 

求助度娘后发现是SDK的版本问题,android4.0中访问网络不能在主程序中进行,行吧,那就老老实实的重新开启一个线程:

new Thread(){ 
public void run(){ 
try { 
Socket s=new Socket("172.22.16.26", 40000); 
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream())); 
String line=br.readLine(); 
show.setText("服务器说:"+line); 
br.close(); 
s.close(); 
} catch (UnknownHostException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 
} catch (IOException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 
} 
} 
}.start(); 

结果又报了异常:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

相信各位已经看出了在下犯了一个很低级的错误:在新启的线程中操作UI,UI线程是非线程安全的,Android平台不允许Activity新启动的线程访问该Activity里的界面组件,这时就要借助Handler的消息传递机制了:

new Thread(){ 
public void run(){ 
try { 
Socket s=new Socket("172.18.6.26",40000); 
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream())); 
String line=br.readLine(); 
//System.out.println("服务器说:"+line); 
Message msg=new Message(); 
msg.what=0x123; 
msg.obj=line; 
myhandler.sendMessage(msg); 
br.close(); 
s.close(); 
} catch (UnknownHostException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 
} catch (IOException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 
} 
} 
}.start(); 

然后重写handlerMessage方法获取消息:

final Handler myhandler=new Handler(){ 
public void handleMessage(Message msg){ 
if(msg.what==0x123){ 
System.out.println("--------------->"+msg.obj); 
show2.setText((String)msg.obj); 
} 
} 
}; 

当新线程发送消息时,该方法自动被调用,handlerMessage(Message msg)方法依旧位于主线程中,所以可以动态的修改UI组件

网络连接不能放在主线程android 4.0 以上   
网络连接不能放在主线程4.0 以下 好像可以

在Android4.0以后,会发现,只要是写在主线程(就是Activity)中的HTTP请求,运行时都会报错,这是因为Android在4.0以后为了防止应用的ANR(aplication Not Response)异常,即使这里表达不是很清晰,大家应该都明白吧,哈哈

就针对此问题有两种解决的方法:

1.可以再Activity的onCreate()方法中加入这样一段代码,如下:

if (Build.VERSION.SDK_INT >= 11) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads  ().detectDiskWrites().detectNetwork().penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects().detectLeakedClosableObjects().penaltyLog().penaltyDeath().build());
}

后就可以在主线程中进行网络操作了

2.一般情况我们应该这样做

启动一条子线程进行你的网络请求。

当然,如果你的应用程序执行的网络请求数据量很小的话,可以使用第一种方案。