我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发送和接收网络数据。Android系统中主要提供了两种方式来进行HTTP通信,HttpURLConnection和HttpClient,几乎在任何项目的代码中我们都能看到这两个类的身影,使用率非常高。
不过HttpURLConnection和HttpClient的用法还是稍微有些复杂的,如果不进行适当封装的话,很容易就会写出不少重复代码。于是乎,一些Android网络通信框架也就应运而生,比如说AsyncHttpClient,它把HTTP所有的通信细节全部封装在了内部,我们只需要简单调用几行代码就可以完成通信操作了。再比如Universal-Image-Loader,它使得在界面上显示网络图片的操作变得极度简单,开发者不用关心如何从网络上获取图片,也不用关心开启线程、回收图片资源等细节,Universal-Image-Loader已经把一切都做好了。
Android开发团队也是意识到了有必要将HTTP的通信操作再进行简单化,于是在2013年Google I/O大会上推出了一个新的网络通信框架——Volley。Volley可是说是把AsyncHttpClient和Universal-Image-Loader的优点集于了一身,既可以像AsyncHttpClient一样非常简单地进行HTTP通信,也可以像Universal-Image-Loader一样轻松加载网络上的图片。除了简单易用之外,Volley在性能方面也进行了大幅度的调整,它的设计目标就是非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。
Volley的原理:在连接网络时,Volley会先在缓存中找,如果缓存中有这样的响应,就直接返回给UI,如果没有就去线程缓存池中找线程,在线程缓存池中的线程个数是有限的,如果没有线程,就新建立一个线程。如果有就等待这个线程完成任务,然后获得返回值信息。而不是新建一个线程。如下图所示:
android Studio在项目中右键——>open Modul Setting——>Depending——>‘+’——>files library 搜索Volley下载就可以了。
1)建立一个相应队列
2)建立一个响应,第一个参数为;响应方法:POST,GET。第二个参数为Url,第三个为:响应成功时执行,第四个为失败时执行
3)将响应加入响应队列
注意:不要忘记导包:在项目中右键——>open Modul Setting——>Depending——>+——files library 搜索Volley下载就可以了。
4)如果是POST方法则建立一个StringRequest的匿名内部类,复写getParams()方法,在这个方法中建立一个map容器,将数据加载到map中,最后返回map就可以了。
代码如下:
GET方法:
RequestQueue queue = Volley.newRequestQueue(this);//建立一个相应队列
//建立一个响应,第一个参数为;响应方法:POST,GET。第二个参数为Url,第三个为:响应成功时执行,第四个为失败时执行
StringRequest request = new StringRequest(Request.Method.GET, "http://360.com", new Response.Listener<String>() {
@Override
public void onResponse(String response) {
textView_Volley.setText(response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
textView_Volley.setText("错误");
}
});
queue.add(request);//将响应加入响应队列
POST方法:
RequestQueue queue = Volley.newRequestQueue(this);//建立一个相应队列
//建立一个响应,第一个参数为;响应方法:POST,GET。第二个参数为Url,第三个为:响应成功时执行,第四个为失败时执行
StringRequest request = new StringRequest(Request.Method.POST, "http://192.168.0.43:8080/www/MyserverTest", new Response.Listener<String>() {
@Override
public void onResponse(String response) {
textView_Volley.setText(response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
textView_Volley.setText("错误");
}
}){
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> map = new HashMap<>();
map.put("name","xiaoming");
map.put("password","123");
return map;
}
};
queue.add(request);//将响应加入响应队列
Volley可以避免建立许多的线程去请求网络,但它不能避免建立多个RequestQueue ,所以我们一般是用单例模式去创建RequestQueue 的实例。代码如下:
public class Mysinger {
private static Mysinger mysinger;
private static RequestQueue requestQueue;
public static RequestQueue getRequestQueue() {
return requestQueue;
}
private Mysinger (Context context){
requestQueue = Volley.newRequestQueue(context);
}
public static synchronized Mysinger getInstance( Context context){
if(mysinger==null){
mysinger= new Mysinger(context);
}
return mysinger;
}
}
前面我们已经学习过了StringRequest和JsonRequest的用法,并且总结出了它们的用法都是非常类似的,基本就是进行以下三步操作即可:
创建一个RequestQueue对象。
创建一个Request对象。
将Request对象添加到RequestQueue里面。
其中,StringRequest和JsonRequest都是继承自Request的,所以它们的用法才会如此类似。那么不用多说,今天我们要学习的ImageRequest,相信你从名字上就已经猜出来了,它也是继承自Request的,因此它的用法也是基本相同的,首先需要获取到一个RequestQueue对象,可以调用如下方法获取到:
RequestQueue mQueue = Volley.newRequestQueue(context);
接下来自然要去new出一个ImageRequest对象了,代码如下所示:
ImageRequest imageRequest = new ImageRequest(
"http://developer.android.com/images/home/aw_dac.png",
new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
imageView.setImageBitmap(response);
}
}, 0, 0, Config.RGB_565, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
imageView.setImageResource(R.drawable.default_image);
}
});
可以看到,ImageRequest的构造函数接收六个参数,第一个参数就是图片的URL地址,这个没什么需要解释的。第二个参数是图片请求成功的回调,这里我们把返回的Bitmap参数设置到ImageView中。第三第四个参数分别用于指定允许图片最大的宽度和高度,如果指定的网络图片的宽度或高度大于这里的最大值,则会对图片进行压缩,指定成0的话就表示不管图片有多大,都不会进行压缩。第五个参数用于指定图片的颜色属性,Bitmap.Config下的几个常量都可以在这里使用,其中ARGB_8888可以展示最好的颜色属性,每个图片像素占据4个字节的大小,而RGB_565则表示每个图片像素占据2个字节大小。第六个参数是图片请求失败的回调,这里我们当请求失败时在ImageView中显示一张默认图片。
最后将这个ImageRequest对象添加到RequestQueue里就可以了,如下所示:
mQueue.add(imageRequest);
从架构上我们可以看到,volley有设置缓存机制,当找不到数据缓存或数据缓存过期时,才会联网获取新的数据。Volley 本身有缓存机制,不仅仅默认缓存图片,也有缓存Json数据。通过手机文件管理软件,我们发现Volley缓存地址:/data/data/软件包/cache/volley 目录下。
那么,在联网获取了数据缓存后,如何获取到Volley缓存中的数据呢?在百度上找了一整天的资料都没有说明如何获取到最新的数据。最后还是再stack overflow中找到了相关的资料。
RequestQueue类中有一个子函数getCache()可以返回Cache实例,通过调用改实例中的get(url)函数可以查看手机磁盘中是否保存有缓存数据,其成员变量data保存着缓存的数据内容。即:queue.getCache().get(url).data
所以,我们可以通过以下语句,来选择获取缓存数据或者向服务器获取最新数据。
if(queue.getCache().get(url)!=null){
//response exists
String cachedResponse = new String(queue.getCache().get(url).data);
}else{
//no response
queue.add(stringRequest);
}
其实这样做还是有缺陷的,那就是如果服务器更新了数据的话,则我们客户端没办法获取最新数据,而是从缓存中调取缓存数据。
为此,我一个比较笨的方法是:判断网络是否可用,如果可用则更新数据,当网络不可用时,采用缓存数据。
Context context = getActivity().getApplicationContext();
if(!isNetworkAvailable(context)){
getFromDiskCache(url); //如果没网,则调取缓存数据
}else{
//有网则从网上更新数据
//……(省略)
}
其中isNetworkAvailable()函数用于判断网络是否可用:
public static boolean isNetworkAvailable(Context context) {
try {
ConnectivityManager manger = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = manger.getActiveNetworkInfo();
//return (info!=null && info.isConnected());
if(info != null){
return info.isConnected();
}else{
return false;
}
} catch (Exception e) {
return false;
}
}
getFromDiskCache()函数用于获取缓存数据(以JSONArray为例):
private void getFromDiskCache(String url) {
if(mQueue.getCache().get(url)!=null){
try {
String str = new String((mQueue.getCache().get(url).data);
JSONArray response = new JSONArray(str);
//……(省略操作)
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
Log.d(TAG, 没有缓存数据);
}
}
如果你觉得ImageRequest已经非常好用了,那我只能说你太容易满足了 ^_^。实际上,Volley在请求网络图片方面可以做到的还远远不止这些,而ImageLoader就是一个很好的例子。ImageLoader也可以用于加载网络上的图片,并且它的内部也是使用ImageRequest来实现的,不过ImageLoader明显要比ImageRequest更加高效,因为它不仅可以帮我们对图片进行缓存,还可以过滤掉重复的链接,避免重复发送请求。
由于ImageLoader已经不是继承自Request的了,所以它的用法也和我们之前学到的内容有所不同,总结起来大致可以分为以下四步:
创建一个RequestQueue对象。
创建一个ImageLoader对象。
获取一个ImageListener对象。
调用ImageLoader的get()方法加载网络上的图片。
下面我们就来按照这个步骤,学习一下ImageLoader的用法吧。首先第一步的创建RequestQueue对象我们已经写过很多遍了,相信已经不用再重复介绍了,那么就从第二步开始学习吧,新建一个ImageLoader对象,代码如下所示:
ImageLoader imageLoader = new ImageLoader(mQueue, new ImageCache() {
@Override
public void putBitmap(String url, Bitmap bitmap) {
}
@Override
public Bitmap getBitmap(String url) {
return null;
}
});
可以看到,ImageLoader的构造函数接收两个参数,第一个参数就是RequestQueue对象,第二个参数是一个ImageCache对象,这里我们先new出一个空的ImageCache的实现即可。
接下来需要获取一个ImageListener对象,代码如下所示:
ImageListener listener = ImageLoader.getImageListener(imageView,
R.drawable.default_image, R.drawable.failed_image);
我们通过调用ImageLoader的getImageListener()方法能够获取到一个ImageListener对象,getImageListener()方法接收三个参数,第一个参数指定用于显示图片的ImageView控件,第二个参数指定加载图片的过程中显示的图片,第三个参数指定加载图片失败的情况下显示的图片。
最后,调用ImageLoader的get()方法来加载图片,代码如下所示:
imageLoader.get("http://img.my.csdn.net/uploads/201404/13/1397393290_5765.jpeg", listener);
get()方法接收两个参数,第一个参数就是图片的URL地址,第二个参数则是刚刚获取到的ImageListener对象。当然,如果你想对图片的大小进行限制,也可以使用get()方法的重载,指定图片允许的最大宽度和高度,如下所示:
imageLoader.get("http://img.my.csdn.net/uploads/201404/13/1397393290_5765.jpeg",
listener, 200, 200);
现在运行一下程序并开始加载图片,你将看到ImageView中会先显示一张默认的图片,等到网络上的图片加载完成后,ImageView则会自动显示该图,效果如下图所示。
HttpUtils也是连接网络的一种框架,也是分为POST方法和GET方法。
注意要导包,在android Studio在项目中右键——>open Modul Setting——>Depending——>‘+’——>files library 搜索xutils下载就可以了。有时还不起作用,运行一下就可以了。
代码如下:
HttpUtils client = new HttpUtils();
client.send(HttpRequest.HttpMethod.GET, "http://www.baidu.com", new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
utils_textView.setText(responseInfo.result);
}
@Override
public void onFailure(HttpException e, String s) {
utils_textView.setText("错误");
}
});
POST方法:
HttpUtils client = new HttpUtils();
RequestParams params = new RequestParams();
params.addBodyParameter("name", "liujiaorui");
params.addBodyParameter("password", "12qa");
client.send(HttpRequest.HttpMethod.POST, "http://192.168.0.198:8080/MyAndroidServlet/MyServlet", params, new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
mTextViewcontent.setText(responseInfo.result);
}
@Override
public void onFailure(HttpException e, String s) {
mTextViewcontent.setText("网络连接错误");
}
});
当我们导入xutils包后,可以利用注解机制省略很多代码,例如每个控件的findViewById()和点击事件的.setOnClickListener(this);
1)在Activity中给声明的控件加注解,例如:
@ViewInject(R.id.util_button)
private Button utils_button;
@ViewInject(R.id.textView_utils)
private TextView utils_textView;
这里@ViewInject(R.id.util_button)的util_button为XML中的控件的id。
2)如果想给控件加点击事件,则可以不用加.setOnClickListener(this);
而是在onCreat()加上
ViewUtils.inject(this);
然后再 ViewUtils.inject(this);的上面加上要加控件点击事件的数组就可以了:
@OnClick({R.id.util_button,R.id.dd})
这样就省略了很多的方法。
DbUtils主要用于对数据库的查询;
可以通过DbUtils实现轻松的数据库查询,
1)新建一个类,例如User。在类的上面@Table(name = “user”)。注意:user为要访问的表格的名称。
2)新建变量,这里的变量名与表格的字段名保持一致。必须在id字段的上面 @Column(column = “id”)这里的id也与表格的名字对应。
3)在Activity的点击事件的查询事件中。输入一下代码:
//建立DbUtils对象,MY_BASE.db为数据库的名字
DbUtils dbUtils= DbUtils.create(this,"MY_BASE.db");
try {
//用DbUtils调用各种方法进行查询,并将查询的结果放到装User的List中。
List<User> users=dbUtils.findAll(com.lidroid.xutils.db.sqlite.Selector.from(User.class));
//对查询的结果进行打印
for(User user:users){
Log.d("aaaaaaaaa","用户名"+user.getName()+"密码"+user.getPassword());
}
} catch (DbException e) {
e.printStackTrace();
}
这要比以前简单的多。
这是在网上找的代码,参考一下:
private void testDb() {
Parent parent = new Parent();
parent.name = "测试";
parent.isVIP = false;
parent.setAdmin(true);
parent.setEmail("[email protected]");
/*Parent parent2 = new Parent(); parent2.name = "测试2"; parent2.isVIP = false;*/
Child child = new Child();
child.name = "child name";
child.parent = parent;
try {
DbUtils db = DbUtils.create(this, true);
try {
Parent test = db.findFirst(parent);//通过entity的属性查找
LogUtils.d("wyouflf :" + test);
} catch (Exception e) {
LogUtils.e(e.getMessage(), e);
}
parent.setTime(new Date());
parent.setTime2(new java.sql.Date(new Date().getTime()));
db.saveBindingId(child);//保存对象关联数据库生成的id
List<Child> children = db.findAll(Selector.from(Child.class));
LogUtils.d("wyouflf size:" + children.size());
if (children.size() > 0) {
LogUtils.d("wyouflf child:" + children.get(children.size() - 1).parent);
}
List<Parent> list = db.findAll(Selector.from(Parent.class).where(WhereBuilder.b("id", "<", 54).append("name","=","测试")).orderBy("id").limit(10));
LogUtils.d("wyouflf size:" + list.size());
if (list.size() > 0) {
LogUtils.d("wyouflf parent:" + list.get(list.size() - 1).toString());
}
//parent.name = "hahaha123";
//db.update(parent);
Parent entity = db.findById(Parent.class, parent.getId());
LogUtils.d("wyouflf parent:" + entity.toString());
List<DbModel> dbModels = db.findDbModelAll(Selector.from(Parent.class).groupBy("name").select("name", "count(name)"));
LogUtils.d("wyouflf:" + dbModels.size());
} catch (DbException e) {
LogUtils.e(e.getMessage(), e);
}
}