Android.os.NetworkOnMainThreadException异常的解决方式

笔者原以为,从后端的角度来讲,android和java客户端程序是大同小异的。

然而笔者有一次写一个爬取网页链接的小demo时,发现还是有不少区别的。

Android.os.NetworkOnMainThreadException异常产生的原因

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

贴上代码

public class NetworkConnect {
	public static Gson gson = new Gson();
	public static String token = "5de492fe8fff9510a2c0e84f250c669557502862";

	public static String getJson(String httpUrl, String httpArg) {
		MyThread t = new MyThread(httpUrl,httpArg);
		t.start();
		try {
			t.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("main thread stop");

		String result =t.getString();
		return result;// 通过out.Stream.toByteArray获取到写的数据;
	}

	private static class MyThread extends Thread{
		ByteArrayOutputStream outStream;
		String httpUrl;
		String httpArg;
		public MyThread(String httpUrl, String httpArg) {
			outStream = new ByteArrayOutputStream();
			this.httpUrl=httpUrl;
			this.httpArg=httpArg;
		}

		@Override
		public void run() {
			byte[] data = new byte[1024];
			int len = 0;
			URL url;
			try {
				if (httpUrl.contains("github")) {
					if (httpArg.contains("?"))
						url = new URL(httpUrl + httpArg + "&access_token=" + token);
					else
						url = new URL(httpUrl + httpArg + "?access_token=" + token);
				} else {
					url = new URL(httpUrl + httpArg);
				}

				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				InputStream inStream = conn.getInputStream();
				while ((len = inStream.read(data)) != -1) {
					outStream.write(data, 0, len);
				}
				inStream.close();
				System.out.println("subthread stop");
			} catch (MalformedURLException e) {
				e.printStackTrace();
				// TODO Auto-generated catch block
			} catch (IOException e) {
				e.printStackTrace();
				// TODO Auto-generated catch block
			}
		}

		public String getString(){
			return new String(outStream.toByteArray());
		}
	}

如图所示,将主要的获取json的Http请求放到子线程MThread中,将数据请求异步进行,可以解决上面问题。

PS:这是很久前的代码了,因为数据量很少,基本能够保证子线程在主线程之前返回完整数据。这样其实相当于仅仅解决异常。如果更合理一些的话,应当在子线程中以Message方式通知主线程数据获取完毕。

           //采用传送消息的模式 把view操作消息发给主线程  
                Message msg = new Message();  
                msg.what=DATA_FIN;  
                msg.obj=new String(outStream.toByteArray());  
                handler.sendMessage(msg);  
            }  
        } catch (Exception e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
            Toast.makeText(MainActivity.this, "访问网络失败",0).show();  
        }  
    }  
}.start();  

然后在主线程中定义handler

private Handler handler =new Handler(){  
        public void handleMessage(android.os.Message msg)  
        {  
            if(msg.what==DATA_FIN)  
            {  
                //Do Something
                .......
            }  
        };  
    };  

如此保证多线程中通信完毕。

附上顺序图(暂时上传不上去)

其他方法

1、把android:targetSdkVersion=”Version Num”这句话从AndroidManifest.xml去掉,问题就解决了,但不是治本的解决方案

Android.os.NetworkOnMainThreadException异常的解决方式_第1张图片

2、在进行网络通信的部分,单独执行异步任务

创建一个异步的任务类,异步任务类里包含网络消息队列(Queue)。当要发送消息的时候(也就是原来进行网络操作的地方,我们假设是发送消息),我们将网络消息加入Queue,在异步任务里面用一个while循环查询Queue,当Queue中有数据的时候就进行发送,没有的话,sleep一段时间。

附上代码

public class SendThread extends AsyncTask{
    private static Queue queue = new LinkedList();  
    //发送请求消息
    public static Boolean SendOrder(String Order){
        queue.offer(Order);
        return true;
    }
    
    //后台循环处理
    @Override
    protected Object doInBackground(Object... params) {
        String string;
        while (true) {
            if ((string = queue.poll()) != null) {
                //若有队列中有消息,就异步发送
                UdpHelper.sendReally(string);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    
                    e.printStackTrace();
                    
                }
            }
            

        }
    }

}

3、可以再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());
        }

可以忽略如NetworkOnMainThreadException这样的限制策略

转载于:https://my.oschina.net/u/3508669/blog/910411

你可能感兴趣的:(Android.os.NetworkOnMainThreadException异常的解决方式)