AndroidDay04-网络编程

一、网络编程的核心步骤

目前成都创新互联已为上1000+的企业提供了网站建设、域名、网站空间、网站托管、服务器租用、企业网站设计、新城网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。

    (1)在清单AndroidManifest.xml文件中添加INTERNET权限。

    (2)连接到网络地址的代码

      第1步:创建URL    

        String path = "http://192.168.17.98:8080/img/news.xml";
        URL url = new URL(path);

      第2步:通过URL获得连接HttpURLConnection 

        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

      第3步:通过HttpURLConnection连接设置一些请求的参数

        conn.setRequestMethod("GET"); //默认请求方式就是GET,要大写。
        conn.setConnectTimeout(5);   //链接网络超时时间,秒作单位。

      第4步:请求响应并获取服务器的响应码,判断响应码的状态,采用相应的动作。

        int code = conn.getResponseCode(); /*200  代表获取服务器资源全
                                                 部成功 206  请求部分资源*/ 
                if (code == 200) {
                   //解析连接的输入流,获取数据,进行其它操作。 
                  ...
                }

    通过查看API发现,HttpURLConnection及其父类,没有close这个关闭连接的方法。

二、Android中的消息机制

     在Android4.0之后,google工程师强制要求Android中的耗时操作(如上面的网络访问、拷贝

   大的数据)放在子线程中运行,否则程序在运行时就会报下面这个错误:

      android.os.NetworkOnMainThreadException 在主线程访问网络

     然而,在子线程中往往会有更新UI的操作(如改变activity中的组件的text值),但是更新UI

   的操作只能在主线程中完成,如果在子线程中有更新UI的动作,程序就会报以下错误:

      android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original

     thread that created a view hierarchy can touch its views. 只有主线程才可以更新ui

    这样就形成了矛盾,那么如何解决这一矛盾(耗时操作不能放在UI线程,子线程不能更新UI)

    呢?解决办法:有2种方式:

    第1种:消息机制Handler

      1)在类的成员位置上创建一个Handler对象,复写它的handleMessage(Message msg)方法。

       在这个方法中获取子线程传递过来的Message,然后更新UI。  

            //创建助手Handler   
            private Handler handler = new Handler(){
		public void handleMessage(aMessage msg) {
		   //获取消息的类型  
			switch (msg.what) {
			case LOADSUCESS:    //代表获取数据成功
			   //把数据取出来 
				String content =  (String) msg.obj;    //要强转
			   //显示源码的内容
				tv_content.setText(content);
				break;	
			case LOADERROR:     //代表获取数据失败 
				Toast.makeText(getApplicationContext(), "访问的资源不存在
				", 1).show();
				break;
			case LOADEXCEPTION:
				Toast.makeText(getApplicationContext(), "服务器忙!!!", 1)
				.show();
				break;
			}
			
			
		 };	
	     };

 

      2)在子线程中调用Handler对象的sendMessage(Message msg)方法,将获取的数据封装到

       Message中去。

            //创建message对象
		  Message msg = Message.obtain();    //这种方式得到Message对象,可以减少
		                                      创建Message的次数。还可以new个对象
		  msg.what = LOADSUCESS;    //标记Message的类型,int型。
					
		  msg.obj =content;    //这个属性可以携带任何数据类型
					
	    //发送一条消息  告诉系统我要更新ui handleMessage方法会立刻执行
		  handler.sendMessage(msg);

   在Handler类的内部,有一个死循环Looper,一直在监听Handler中的消息队列。在子线程中一发送消息,加入Handler的消息队列,死循环得知就取出消息发送给handMessage方法。原理图如下:

   Android Day04-网络编程

   第2种:runOnUiThread(Runnnable action)方法

       这是Activity类的一个方法,关于这个方法,Android官方文档说得很明白:

       如果当前线程是UI线程(主线程),那么action就会立即执行。如果当前线程不是UI线程,

   那么action就会放入UI线程的事件队列,也就是说它会被UI线程所执行。那么就可以把更新UI的

    方法放在这个action里面。

      核心代码:

        //更新ui
		runOnUiThread(new Runnable() {
			public void run() {				
				tv_content.setText(content);
		        }
		});

       但是在子线程一定不能更新UI吗?答案是可以。

      在程序运行之后,Android系统会自动开启一个审计系统,来监听子线程中是否有更新UI的

     动作。如果在子线程中有更新UI的动作,很简短很快的话,就不会被审计系统捕捉到,那么就

    不会报错。如果用SystemLock.sleep()模拟一个即使是毫秒级的耗时操作的话,也会报错。

      

    

三、实现网络图片的缓存

     由于图片的加载是十分的消耗流量的,所以初次加载图片时可以将图片保存到缓存中,再从缓

   存中把图片显示到控件上,以后每次访问图片的时候先去找缓存中有没有这个图片,缓存中如果没

   有再去网络上加载图片至缓存。

      首先,先得学习两个类Base64和BitmapFactory。

     Base6是一个实用工具类,可以将byte[]编码成String,也可以将String解码成byte[]。

       decodeToString(byte[] input,int flags):即可将字节数组变成String。

     BitmapFactory顾名就是处理Bitmap的工具类,它可以从各种资源包括文件、流、字节数组来

     创建一个Bitmap对象。

       decodeStream(InputStream in):将流解析成Bitmap对象。

       decodeFile(String path):将文件的路径解析成Bitmap对象。

      图片缓存的实现流程:

     第1步:创建缓存图片文件的File对象  

     //通过Base64将图片的url地址解码成字节数组,再编码为String类型。
       File file =new File(getCacheDir(), Base64.encodeToString(path.getBytes(),                  Base64.DEFAULT));

     第2步:判断File对象是否存在且大小是否为0.

       if(file.exists() && file.length() > 0)
          {
    	//通过BitmapFactory的解析路径资源的方法获取Bitmap对象
    	    Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
    	//将Bitmap发送给消息助手,用于更新UI。
    	    Message msg = Message.obtain();
    		    msg.obj = bitmap;
    		    msg.what = LOADCACHE;
    		    handler.sendMessage(msg);
           }
           else
           {
           //连接网络,得到网络图片的流,然后对象写入到File图片缓存文件中去。
               ...
        	   InputStream in = conn.getInputStream();
               //将流写入缓存中
        	   OutputStream out = new FileOutputStream(file);
        	   int len = 0;
        	   byte[] buf = new byte[1024];
        	   while((len = in.read(buf))!= -1 )
        	   {
        		out.write(buf,0,len);
        	    }
        	   in.close();
                   out.close();
               //读取图片缓存文件,得到Bitmap对象。
               //将Bitmap发送给消息助手,用于更新UI。
                    Message msg = Message.obtain();
        	    msg.obj = BitmapFactory.decodeFile(file.getAbsolutePath());
        	    msg.what = LOADNET;
        	    handler.sendMessage(msg);
           }

     

四、延迟与定时操作   

     1) Handler类本身有个方法可以实现延迟操作

       public final boolean postDelayed (Runnable r, long delayMillis)

       官方文档是这么说明这个方法的:

       将Runnable加入消息队列,过了指定的时间delayMillis之后运行。Runnable会运行在

     Handler对象绑定的那个线程中。 

        handler.postDelayed(new Runnable() {
		    @Override
		    public void run() {
			tv.setText("123456");
		    }
	        }, 5000);	

    //上面这段代码的handler必需是成员位置已经创建的Handler对象,直接写new Handler会报下

      面这个异常。

    

    java.lang.RuntimeException: Can't create handler inside thread that has not called

                  Looper.prepare()

       原因是上面这段代码是写在一个子线程中的,如果直接new Handler,这个Handler对象属

    于子线程的内部成员,Runnable还是在子线程中执行的,仍然在子线程中更新UI。

        当然了,Handler类还有定时操作的API,用时再去查吧。   

    2) Timer实现延迟操作

      在JDK里,有个类Timer,可以实现延迟与定时操作,以及重复执行操作。

       schedule(TimerTask task, long delay)
          安排在指定延迟后执行指定的任务。

      //定义一个时钟
    	Timer timer = new Timer();
      //延迟执行时钟的run方法
		timer.schedule(new TimerTask() {
		    @Override
		    public void run() {
			runOnUiThread(new Runnable() {
				@Override
				public void run() {
					tv.setText("123456");
				}
			});
		    }
		},5000);

       

    

获取热点的IP地址

    https://blog.csdn.net/qq_19560943/article/details/54317932

 

  • 获取httpurlconnectionr的responseheader

    https://www.2cto.com/kf/201306/216213.html

          

OkHttp3在加速器领域的一个错误

     错误记录-CLEARTEXT communication to * not permitted by network

      https://www.jianshu.com/p/4c8dda36ab42



网站标题:AndroidDay04-网络编程
分享链接:http://pwwzsj.com/article/igdsij.html