一、网络编程的核心步骤
(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方法。原理图如下:
第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
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。