xUtils简介
- xUtils 包含了很多实用的android工具。
- xUtils 最初源于Afinal框架,进行了大量重构,使得xUtils支持大文件上传,更全面的http请求协议支持(10种谓词),拥有更加灵活的ORM,更多的事件注解支持且不受混淆影响...
- xUitls最低兼容android 2.2 (api level 8)
- 目前xUtils主要有四大模块:
-
-
DbUtils模块:
- android中的orm框架,一行代码就可以进行增删改查;
- 支持事务,默认关闭;
- 可通过注解自定义表名,列名,外键,唯一性约束,NOT NULL约束,CHECK约束等(需要混淆的时候请注解表名和列名);
- 支持绑定外键,保存实体时外键关联实体自动保存或更新;
- 自动加载外键关联实体,支持延时加载;
- 支持链式表达查询,更直观的查询语义,参考下面的介绍或sample中的例子。
-
ViewUtils模块:
- android中的ioc框架,完全注解方式就可以进行UI,资源和事件绑定;
- 新的事件绑定方式,使用混淆工具混淆后仍可正常工作;
- 目前支持常用的20种事件绑定,参见ViewCommonEventListener类和包com.lidroid.xutils.view.annotation.event。
-
HttpUtils模块:
- 支持同步,异步方式的请求;
- 支持大文件上传,上传大文件不会oom;
- 支持GET,POST,PUT,MOVE,COPY,DELETE,HEAD,OPTIONS,TRACE,CONNECT请求;
- 下载支持301/302重定向,支持设置是否根据Content-Disposition重命名下载的文件;
- 返回文本内容的请求(默认只启用了GET请求)支持缓存,可设置默认过期时间和针对当前请求的过期时间。
-
BitmapUtils模块:
- 加载bitmap的时候无需考虑bitmap加载过程中出现的oom和android容器快速滑动时候出现的图片错位等现象;
- 支持加载网络图片和本地图片;
- 内存管理使用lru算法,更好的管理bitmap内存;
- 可配置线程加载线程数量,缓存大小,缓存路径,加载显示动画等...
使用xUtils快速开发框架需要有以下权限:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
混淆时注意事项:
- 添加Android默认混淆配置${sdk.dir}/tools/proguard/proguard-android.txt
- 不要混淆xUtils中的注解类型,添加混淆配置:-keep class * extends java.lang.annotation.Annotation { *; }
- 对使用DbUtils模块持久化的实体类不要混淆,或者注解所有表和列名称@Table(name="xxx"),@Id(column="xxx"),@Column(column="xxx"),@Foreign(column="xxx",foreign="xxx");
DbUtils使用方法:
DbUtils db = DbUtils.create(this); User user = new User(); //这里需要注意的是User对象必须有id属性,或者有通过@ID注解的属性 user.setEmail("wyouflf@qq.com"); user.setName("wyouflf"); db.save(user); // 使用saveBindingId保存实体时会为实体的id赋值 ... // 查找 Parent entity = db.findById(Parent.class, parent.getId()); List<Parent> list = db.findAll(Parent.class);//通过类型查找 Parent Parent = db.findFirst(Selector.from(Parent.class).where("name","=","test")); // IS NULL Parent Parent = db.findFirst(Selector.from(Parent.class).where("name","=", null)); // IS NOT NULL Parent Parent = db.findFirst(Selector.from(Parent.class).where("name","!=", null)); // WHERE id<54 AND (age>20 OR age<30) ORDER BY id LIMIT pageSize OFFSET pageOffset List<Parent> list = db.findAll(Selector.from(Parent.class) .where("id" ,"<", 54) .and(WhereBuilder.b("age", ">", 20).or("age", " < ", 30)) .orderBy("id") .limit(pageSize) .offset(pageSize * pageIndex)); // op为"in"时,最后一个参数必须是数组或Iterable的实现类(例如List等) Parent test = db.findFirst(Selector.from(Parent.class).where("id", "in", new int[]{1, 2, 3})); // op为"between"时,最后一个参数必须是数组或Iterable的实现类(例如List等) Parent test = db.findFirst(Selector.from(Parent.class).where("id", "between", new String[]{"1", "5"})); DbModel dbModel = db.findDbModelAll(Selector.from(Parent.class).select("name"));//select("name")只取出name列 List<DbModel> dbModels = db.findDbModelAll(Selector.from(Parent.class).groupBy("name").select("name", "count(name)")); ... List<DbModel> dbModels = db.findDbModelAll(sql); // 自定义sql查询 db.execNonQuery(sql) // 执行自定义sql ...
ViewUtils使用方法
- 完全注解方式就可以进行UI绑定和事件绑定。
- 无需findViewById和setClickListener等。
// xUtils的view注解要求必须提供id,以使代码混淆不受影响。 @ViewInject(R.id.textView) TextView textView; //@ViewInject(vale=R.id.textView, parentId=R.id.parentView) //TextView textView; @ResInject(id = R.string.label, type = ResType.String) private String label; // 取消了之前使用方法名绑定事件的方式,使用id绑定不受混淆影响 // 支持绑定多个id @OnClick({R.id.id1, R.id.id2, R.id.id3}) // or @OnClick(value={R.id.id1, R.id.id2, R.id.id3}, parentId={R.id.pid1, R.id.pid2, R.id.pid3}) // 更多事件支持参见ViewCommonEventListener类和包com.lidroid.xutils.view.annotation.event。 @OnClick(R.id.test_button) public void testButtonClick(View v) { // 方法签名必须和接口中的要求一致 ... } ... //在Activity中注入: @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ViewUtils.inject(this); //注入view和事件 ... textView.setText("some text..."); ... } //在Fragment中注入: @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.bitmap_fragment, container, false); // 加载fragment布局 ViewUtils.inject(this, view); //注入view和事件 ... } //在PreferenceFragment中注入: public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); ViewUtils.inject(this, getPreferenceScreen()); //注入view和事件 ... } // 其他重载 // inject(View view); // inject(Activity activity) // inject(PreferenceActivity preferenceActivity) // inject(Object handler, View view) // inject(Object handler, Activity activity) // inject(Object handler, PreferenceGroup preferenceGroup) // inject(Object handler, PreferenceActivity preferenceActivity)
HttpUtils使用方法:
普通get方法
HttpUtils http = new HttpUtils(); http.send(HttpRequest.HttpMethod.GET, "http://www.lidroid.com", new RequestCallBack<String>(){ @Override public void onLoading(long total, long current, boolean isUploading) { testTextView.setText(current + "/" + total); } @Override public void onSuccess(ResponseInfo<String> responseInfo) { textView.setText(responseInfo.result); } @Override public void onStart() { } @Override public void onFailure(HttpException error, String msg) { } });
使用HttpUtils上传文件 或者 提交数据 到服务器(post方法)
RequestParams params = new RequestParams(); params.addHeader("name", "value"); params.addQueryStringParameter("name", "value"); // 只包含字符串参数时默认使用BodyParamsEntity, // 类似于UrlEncodedFormEntity("application/x-www-form-urlencoded")。 params.addBodyParameter("name", "value"); // 加入文件参数后默认使用MultipartEntity("multipart/form-data"), // 如需"multipart/related",xUtils中提供的MultipartEntity支持设置subType为"related"。 // 使用params.setBodyEntity(httpEntity)可设置更多类型的HttpEntity(如: // MultipartEntity,BodyParamsEntity,FileUploadEntity,InputStreamUploadEntity,StringEntity)。 // 例如发送json参数:params.setBodyEntity(new StringEntity(jsonStr,charset)); params.addBodyParameter("file", new File("path")); ... HttpUtils http = new HttpUtils(); http.send(HttpRequest.HttpMethod.POST, "uploadUrl....", params, new RequestCallBack<String>() { @Override public void onStart() { testTextView.setText("conn..."); } @Override public void onLoading(long total, long current, boolean isUploading) { if (isUploading) { testTextView.setText("upload: " + current + "/" + total); } else { testTextView.setText("reply: " + current + "/" + total); } } @Override public void onSuccess(ResponseInfo<String> responseInfo) { testTextView.setText("reply: " + responseInfo.result); } @Override public void onFailure(HttpException error, String msg) { testTextView.setText(error.getExceptionCode() + ":" + msg); } });
使用HttpUtils下载文件:
- 支持断点续传,随时停止下载任务,开始任务
HttpUtils http = new HttpUtils(); HttpHandler handler = http.download("http://apache.dataguru.cn/httpcomponents/httpclient/source/httpcomponents-client-4.2.5-src.zip", "/sdcard/httpcomponents-client-4.2.5-src.zip", true, // 如果目标文件存在,接着未完成的部分继续下载。服务器不支持RANGE时将从新下载。 true, // 如果从请求返回信息中获取到文件名,下载完成后自动重命名。 new RequestCallBack<File>() { @Override public void onStart() { testTextView.setText("conn..."); } @Override public void onLoading(long total, long current, boolean isUploading) { testTextView.setText(current + "/" + total); } @Override public void onSuccess(ResponseInfo<File> responseInfo) { testTextView.setText("downloaded:" + responseInfo.result.getPath()); } @Override public void onFailure(HttpException error, String msg) { testTextView.setText(msg); } }); ... //调用cancel()方法停止下载 handler.cancel();
BitmapUtils 使用方法
BitmapUtils bitmapUtils = new BitmapUtils(this); // 加载网络图片 bitmapUtils.display(testImageView, "http://bbs.lidroid.com/static/image/common/logo.png"); // 加载本地图片(路径以/开头, 绝对路径) bitmapUtils.display(testImageView, "/sdcard/test.jpg"); // 加载assets中的图片(路径以assets开头) bitmapUtils.display(testImageView, "assets/img/wallpaper.jpg"); // 使用ListView等容器展示图片时可通过PauseOnScrollListener控制滑动和快速滑动过程中时候暂停加载图片 listView.setOnScrollListener(new PauseOnScrollListener(bitmapUtils, false, true)); listView.setOnScrollListener(new PauseOnScrollListener(bitmapUtils, false, true, customListener));
-
下面依次开始介绍这些组件具体的使用。
一、ViewUtils android中得ioc(控制反转)框架,可以完全使用注解的方式来完成UI的绑定和事件绑定。简单的说,ViewUtils的功能就是做这个的,但是可以说,就这么个功能确是能极大的简化我们的代码。下面我们看下具体的代码,顺便对比下注解的方式绑定ID和findViewById之间的差别。
@ViewInject(R.id.btn)
private Button btn;
@ViewInject(R.id.img)
private ImageView img;
@ViewInject(R.id.list)
private ListView list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
ViewUtils.inject(this);}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
btn = (Button) findViewById(R.id.btn);
img = (ImageView) findViewById(R.id.img);
list = (ListView) findViewById(R.id.list);
}
如果项目中得Activity中的控件相当多,那么想象一下代码中累积的那一大坨就真心受不了。通过xUtils的简单注解,就能轻松摆脱无尽坏味道的代码。
注意:在使用注解绑定控件的时候,一定记得在onCreate中调用ViewUtils.inject(this);
下面我们再对比下Android中事件绑定的区别。
@OnClick({ R.id.btn, R.id.img })
public void clickMethod(View v) {
Toast.makeText(SecondActivity.this, "you clicked button!",
Toast.LENGTH_SHORT).show();
}
@OnItemClick(R.id.list)
public void itemClick(AdapterView<?> parent, View view, int position,long id) {
Toast.makeText(SecondActivity.this, "position--->" + position,
Toast.LENGTH_SHORT).show();
}
btn.setOnClickListener(this);
list.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(SecondActivity.this, "position--->" + position,
Toast.LENGTH_SHORT).show();
}
});
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn:
Toast.makeText(SecondActivity.this, "you clicked button!",
Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
原本绑定Button的监听事件要么用丑陋的内部类,要么Activity实现OnClickListener,在复写的onClick方法中去根据id。而xUtils只要通过简单的一句注解就能实现监听事件的功能,而且可以实现多个控件共用一个监听方法。同时xUtils提供onClick、onItemClick、onLongClick等15种事件监听注解。
注意:在使用注解监听事件的时候,监听方法名是自定义的,但是一定要保证方法的访问修饰符为public,同时方法的参数要与Android原来的监听方法参数一致,不仅参数类型,而且要保证参数的顺序。
二、BitmapUtils 加载网络或本地bitmap的时候无需担心再遇到OOM的现象,管理bitmap的内存采用了LRU算法,同时也能避免列表滑动过程中发生图片错位等得现象。加载网络图片时,还可以配置运行线程的数量,缓存路径等。。。通过BitmapUtils的各种构造器,可以很方便的创建出本地缓存路径和缓存的大小,以及内存缓存的大小。 BitmapUtils utils = new BitmapUtils(this);
BitmapDisplayConfig config = new BitmapDisplayConfig(this);
config.setLoadingDrawable(getResources().getDrawable(R.drawable.loading));
config.setLoadFailedDrawable(getResources().getDrawable(R.drawable.failed));
config.setImageLoadCallBack(new ImageLoadCallBack() {
@Override
public void loadFailed(ImageView imageView, Drawable drawable) {
}
@Override
public void loadCompleted(ImageView imageView, Drawable drawable,
BitmapDisplayConfig config) {
}
});
config.setBitmapMaxWidth(480);
config.setBitmapMaxHeight(720);
// utils.display(img, "http://img1.gtimg.com/news/pics/hv1/63/26/1451/94357968.jpg");
utils.display(img, "http://img1.gtimg.com/news/pics/hv1/63/26/1451/94357968.jpg", config);
BitmapUtils在用来加载网络图片时,可以配置加载图片尺寸的大小,加载成功和失败的回调以及加载过程中图片的配置。同时也可以选择不配置。
//bitmapUtils.display(testImageView, "/sdcard/test.jpg"); //支持加载本地图片
// 使用ListView等容器展示图片时可通过PauseOnScrollListener控制滑动和快速滑动过程中时候暂停加载图片
listView.setOnScrollListener(new PauseOnScrollListener(bitmapUtils, false, true));
listView.setOnScrollListener(new PauseOnScrollListener(bitmapUtils, false, true), customListener);
三、
DbUtils操纵数据库的工具类,无论多牛X,总离不开最根本的CRUD,即创建,查询,更新和删除。下面从这四个角度依次介绍xUtils是如何简便持久化数据的。大家都知道,在Android里面如果要存储一个对象,我们需要创建一个SQLiteOpenHelper,然后还得创建一张对应对象各个属性的表,还得继续把我们的对象转换成ContentValues,进而去存储。真心是麻烦的不能再麻烦了,我们现在介绍的DbUtils就能让你轻松解脱麻绳一样的代码。DbUtils在进行save操作的时候,会根据java反射反射出对象的各个字段,然后去查询数据库中是否存在这个对象类型对应的表,如果表已经存在,直接进行插入操作;如果不存在,就先动态的创建的一张对应我们对象的表,再进行插入处理。直接上代码,大家看。
@OnClick(R.id.insert)
public void insert(View v) {
Student stu = null;
for (int i = 0; i < 20; i++) {
stu = new Student();
stu.setAge(10 + i);
stu.setName("jack" + i);
mList.add(stu);
try {
dbUtils.save(stu);
} catch (DbException e) {
e.printStackTrace();
}
}
}
注意:并不是所有的实体对象都快可以通过这种方式去存储,一定要保证对象的类型中有int类型的id或者_id的属性,这就对应数据库表中的主键字段。如果类型中没有id字段,可以通过@Id注解去指定一个int类型的字段作为主键。如果表中的又字段不想被存储在数据库中,也可以通过@Transient去实现忽略。如果直接存储一个对象的列表,这样也是被允许的,达到批量存储的目的。
DbUtils可以帮助对SQL语句不是很熟悉的同学快速的实现查询,而不用去写sql查询语句,而且可以对查询结果进行排序和分页,使用简单,功能强大。大家可以看下,下面的几行代码就能实现复杂的查询功能
dbUtils.findAll(Selector.from(Student.class).where("_id", "<", 10).and("age", ">", 10).orderBy("_id").limit(pageSize).offset(pageSize * pageIndex));
同样的,也可以对数据库中得数据进行便捷的更新。下面演示的是更新Student对应的表中的第一条记录的age这个字段。这个比较简单,就直接上代码看吧。
@OnClick(R.id.update)
public void update(View v){
try {
List<Student> stus = dbUtils.findAll(Selector.from(Student.class));
Student stu = stus.get(0);
stu.setAge(20);
dbUtils.update(stu);
} catch (DbException e) {
e.printStackTrace();
}
}
最后一个就是数据的删除。一个实体对象,一组实体对象,根据条件删除,删除表,删除整个数据库,这些操作都可以通过一句简单的代码来实现,看了代码就明白了。
@OnClick(R.id.delete)
public void delete(View v){
try {
List<Student> stus = dbUtils.findAll(Selector.from(Student.class));
dbUtils.delete(stus.get(0));
dbUtils.deleteAll(stus);
dbUtils.deleteById(Student.class, WhereBuilder.b("age", "==", 20));
dbUtils.dropTable(Student.class);
dbUtils.dropDb();
} catch (DbException e) {
e.printStackTrace();
}
}
四、
HttpUtils是解决日常工作过程中繁杂的上传下载文件以及各种Get和post请求的必备工具类,通过这个类,开源非常方便关注接口的业务,不必再再写那么冗长的代码。下面全文都围绕着四个中心点去依次展开,分别是:HttpGet请求,HttpPost请求,下载文件和上传文件。
HttpGet请求。相信从事Android网络应用的开发的同学对这个一定不会陌生,长期的开发会让大家积累下一个可以复用的工具类。而xUtils就是帮助了我们把那些工具类给抽象整合成一个更具扩展性的帮助类。比如HtttpGet请求,这里只简要介绍下关键的应用方法,具体的细节还请大家自己去下载xUtils的源码去研究研究了,我们介绍的足够应用到我们的应用开发中去了。通常的HttpGet请求会把一系列的请求参数挂在请求地址的后面,拖出一节长长的尾巴,十分的惹人厌,这里可以通过像HttpPost请求的参数那样组成一个类似BasicNameValuePair的对象直接封装到请求方法中去,省去拼接url的麻烦,同时也可以设置超时时间。另外请求的方法中提供了一个回调类,这个类中有处理不同请求结果的回调方法,比如说请求过程中的回调,请求成功的回调以及请求出现错误时的回调。下面直接上代码看下。
RequestParams params = new RequestParams();
params.addQueryStringParameter("method", "info");
params.addQueryStringParameter("access_token",
"3.1042851f652496c9362b1cd77d4f849b.2592000.1377530363.3590808424-248414");
HttpUtils http = new HttpUtils();
http.configCurrentHttpGetCacheExpiry(1000 * 10);
http.send(HttpRequest.HttpMethod.GET,
"https://pcs.baidu.com/rest/2.0/pcs/quota",
params,
new RequestCallBack<String>() {
@Override
public void onStart() {
resultText.setText("conn...");
}
@Override
public void onLoading(long total, long current) {
resultText.setText(current + "/" + total);
}
@Override
public void onSuccess(String result) {
resultText.setText("response:" + result);
}
@Override
public void onFailure(HttpException error, String msg) {
resultText.setText(msg);
}
});
RequestParams params = new RequestParams();
params.addQueryStringParameter("method", "mkdir");
params.addQueryStringParameter("access_token", "3.1042851f652496c9362b1cd77d4f849b.2592000.1377530363.3590808424-248414");
params.addBodyParameter("path", "/apps/测试应用/test文件夹");
HttpUtils http = new HttpUtils();
http.send(HttpRequest.HttpMethod.POST,
"https://pcs.baidu.com/rest/2.0/pcs/file",
params,
new RequestCallBack<String>() {
@Override
public void onStart() {
resultText.setText("conn...");
}
@Override
public void onLoading(long total, long current) {
resultText.setText(current + "/" + total);
}
@Override
public void onSuccess(String result) {
resultText.setText("upload response:" + result);
}
@Override
public void onFailure(HttpException error, String msg) {
resultText.setText(msg);
}
});
HttpHandler handler = http.download(
downloadAddrEdit.getText().toString(),
"/sdcard/fileexplorer.apk",
true, // 如果目标文件存在,接着未完成的部分继续下载。
true, // 如果从请求返回信息中获取到文件名,下载完成后自动重命名。
new RequestCallBack<File>() {
@Override
public void onStart() {
resultText.setText("conn...");
}
@Override
public void onLoading(long total, long current) {
resultText.setText(current + "/" + total);
}
@Override
public void onSuccess(File result) {
resultText.setText("downloaded:" + result.getPath());
}
@Override
public void onFailure(HttpException error, String msg) {
resultText.setText(error.getExceptionCode() + ":" + msg);
}
});
注意:下载过程中如果需要暂停下载,也只需简单的一行代码来实现:mHandler.stop(),如果设置断点下载的话,下次会重新开始的话,会自动从上次下载的断点处继续下载。
最后介绍的功能就是上传文件了,这个也是在项目中也是非常常见的。比如用户上传头像,再比如网盘应用需要把本地文件上传到云端等等。同时HttpUtils也同时为开发者提供了上传过程中和上传结果的各个回调接口。大家在使用HttpUtils上传文件的时候,只要仿照下面的代码去码代码就快可以基本满足业务的需要了。
RequestParams params = new RequestParams();
params.addQueryStringParameter("method", "upload");
params.addQueryStringParameter("path", "/apps/测试应用/test.zip");
// 请在百度的开放access_tokenapi测试页面找到自己的access_token
params.addQueryStringParameter("access_token", "3.1042851f652496c9362b1cd77d4f849b.2592000.1377530363.3590808424-248414");
params.addBodyParameter("file", new File("/sdcard/test.zip"));
HttpUtils http = new HttpUtils();
http.send(HttpRequest.HttpMethod.POST,
"https://pcs.baidu.com/rest/2.0/pcs/file",
params,
new RequestCallBack<String>() {
@Override
public void onStart() {
resultText.setText("conn...");
}
@Override
public void onLoading(long total, long current) {
resultText.setText(current + "/" + total);
}
@Override
public void onSuccess(String result) {
resultText.setText("upload response:" + result);
}
@Override
public void onFailure(HttpException error, String msg) {
resultText.setText(msg);
}
});