一、引言
大多数情况下,工具的好坏会影响到项目的进程,对于工具的使用说好听点是不想重复造轮子,不好听点就是拿来主义。xUtils 包含了很多实用的Android工具,且是目前比较火的工具类框架,用处也是比较多。在了解或使用之后,你就会觉得XUtils功能还是比较强大。
二、xUtils简介
xUtils 最初源于Afinal框架,后面进行了大量重构,使得xUtils支持大文件上传,更全面的http请求协议支持(10种谓词),拥有更加灵活的ORM,更多的事件注解支持且不受混淆影响...
目前XUtils里面总共有四大模块:
- DbUtils模块:用于数据库的操作,也是需要注解方式指定表名和列名
- ViewUtils模块:注解方式就可以进行UI,资源和事件绑定
- HttpUtils模块:用于网络数据的请求
- BitmapUtils模块:用于图片的下载和绑定视图
2.1 DbUtils模块
- android中的orm框架,一行代码就可以进行增删改查
- 支持事务,默认关闭
- 可通过注解自定义表名,列名,外键,唯一性约束,NOT NULL约束,CHECK约束等(需要混淆的时候请注解表名和列名)
- 支持绑定外键,保存实体时外键关联实体自动保存或更新
- 自动加载外键关联实体,支持延时加载
- 支持链式表达查询,更直观的查询语义,参考下面的介绍或sample中的例子
2.2 ViewUtils模块
- android中的ioc框架,完全注解方式就可以进行UI,资源和事件绑定
- 新的事件绑定方式,使用混淆工具混淆后仍可正常工作
- 目前支持常用的20种事件绑定,参见ViewCommonEventListener类和包com.lidroid.xutils.view.annotation.event
2.3 HttpUtils模块
- 支持同步,异步方式的请求
- 支持大文件上传,上传大文件不会oom
- 支持GET,POST,PUT,MOVE,COPY,DELETE,HEAD,OPTIONS,TRACE,CONNECT请求
- 下载支持301/302重定向,支持设置是否根据Content-Disposition重命名下载的文件
- 返回文本内容的请求(默认只启用了GET请求)支持缓存,可设置默认过期时间和针对当前请求的过期时间
2.4 BitmapUtils模块
- 加载bitmap的时候无需考虑bitmap加载过程中出现的oom和android容器快速滑动时候出现的图片错位等现象
- 支持加载网络图片和本地图片
- 内存管理使用lru算法,更好的管理bitmap内存
- 可配置线程加载线程数量,缓存大小,缓存路径,加载显示动画等...
GitHub :xUtils
三、XUtils的使用:
说明:开发中,用到项目中的唯有HttpUtils模块,其他模块的功能还未用上。
3.1 添加依赖
compile 'org.xutils:xutils:3.3.36' //V3.5.0
项目中使用的是V3.3.36,但最新的已是V3.5.0。
3.2 初始化
在application的onCreate中初始化
@Override
public void onCreate() {
super.onCreate();
x.Ext.init(this);
x.Ext.setDebug(BuildConfig.DEBUG); // 是否输出debug日志, 开启debug会影响性能.
...
}
3.3 网络请求模块使用
xUtils3网络模块大大方便了在实际开发中网络模块的开发,xUtils3网络模块大致包括GET请求、POST请求、上传文件、下载文件等功能。
Get请求
RequestParams params = new RequestParams(Constants.HTTP_BASE + "...");
x.http().get(params, new Callback.CommonCallback<String>() {
@Override
public void onSuccess(String result) {
subscriber.onNext(result);
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
subscriber.onError(ex);
}
@Override
public void onCancelled(CancelledException cex) {
subscriber.onError(cex);
}
@Override
public void onFinished() {
subscriber.onCompleted();
}
});
Post请求
String timestamp = System.currentTimeMillis() + "";
final String signature = md5(Constants.HTTP_FIELDS_ENCRYPTION + Constants.APPKEY + timestamp).toUpperCase();
entity.addHeader("timestamp", timestamp);
entity.addHeader("signature", signature);
String userToken = MyApp.getInstance().getUserToken();
if (!TextUtils.isEmpty(userToken)) {
entity.addHeader("token", userToken);
}
x.http().post(entity, new Callback.CommonCallback<String>() {
@Override
public void onSuccess(final String result) {
BaseResponse response = gson.fromJson(result, new TypeToken<BaseResponse>() {
}.getType());
//TODO
if (!response.success()) {
MyApp.getInstance().showToast(response.getMsg());
}
callback.onSuccess(result);
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
callback.onError(ex, isOnCallback);
}
@Override
public void onCancelled(CancelledException cex) {
callback.onCancelled(cex);
}
@Override
public void onFinished() {
callback.onFinished();
}
});
3.4 注解模块使用
Activity的注解
@ContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
@ViewInject(R.id.viewpager)
ViewPager viewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
x.view().inject(this);
...
}
...
}
Fragment的注解
@ContentView(R.layout.fragment_http)
public class HttpFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return x.view().inject(this, inflater, container);
}
@Override
public void onViewCreated(View v, @Nullable Bundle savedInstanceState) {
super.onViewCreated(v, savedInstanceState);
}
...
}
ViewHolder的注解
public class MyAdapter extends BaseAdapter{
ImageOptions options = new ImageOptions.Builder().setFadeIn(true).build();
@Override
public int getCount() {
return listUrl.size();
}
@Override
public String getItem(int position) {
return listUrl.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder=null;
if(convertView == null){
convertView=LayoutInflater.from(BitmapUtilsActivity.this).inflate(R.layout.itemone, null);
holder = new ViewHolder();
x.view().inject(holder,convertView);
convertView.setTag(holder);
} else {
holder=(ViewHolder) convertView.getTag();
}
x.image().bind(holder.image, getItem(position), options);
return convertView;
}
class ViewHolder{
@ViewInject(R.id.image)
private ImageView image;
}
}
点击事件的注解
@ViewInject(R.id.button1)
private Button button1;
/**
* 单击事件
* type默认View.OnClickListener.class,故此处可以简化不写,@Event(R.id.bt_main)
*/
@Event(type = View.OnClickListener.class,value = R.id.bt_main)
private void testInjectOnClick(View v){
Snackbar.make(v,"OnClickListener",Snackbar.LENGTH_SHORT).show();
}
/**
* 长按事件
*/
@Event(type = View.OnLongClickListener.class,value = R.id.bt_main)
private boolean testOnLongClickListener(View v){
Snackbar.make(v,"testOnLongClickListener",Snackbar.LENGTH_SHORT).show();
return true;
}
/**
* 同时为多个按钮添加点击事件,type用默认
*/
@Event(value = {R.id.button1, R.id.button2, R.id.button3})
private void getEvent(View view){
switch(view.getId()){
case R.id.button1:
Intent it1 = new Intent(MainActivity.this, HttpUtilsActivity.class);
startActivity(it1);
break;
case R.id.button2:
Intent it2 = new Intent(MainActivity.this, BitmapUtilsActivity.class);
startActivity(it2);
break;
case R.id.button3:
Intent it3 = new Intent(MainActivity.this, DbUtilsActivity.class);
startActivity(it3);
break;
}
}
强调:使用注解模块一定要注意初始化视图注解框架。
3.5 图片模块使用
xUtils3图片模块,重点在于加载图片的4个bind方法,loadDrawable与loadFIle用法和ImageOptions用法。
ImageOptions用法
//通过ImageOptions.Builder().set方法设置图片的属性
ImageOptions imageOptions= new ImageOptions.Builder().setFadeIn(true).build(); //淡入效果
//ImageOptions.Builder()的一些其他属性:
.setCircular(true) //设置图片显示为圆形
.setSquare(true) //设置图片显示为正方形
.setCrop(true).setSize(200,200) //设置大小
.setAnimation(animation) //设置动画
.setFailureDrawable(Drawable failureDrawable) //设置加载失败的动画
.setFailureDrawableId(int failureDrawable) //以资源id设置加载失败的动画
.setLoadingDrawable(Drawable loadingDrawable) //设置加载中的动画
.setLoadingDrawableId(int loadingDrawable) //以资源id设置加载中的动画
.setIgnoreGif(false) //忽略Gif图片
.setParamsBuilder(ParamsBuilder paramsBuilder) //在网络请求中添加一些参数
.setRaduis(int raduis) //设置拐角弧度
.setUseMemCache(true) //设置使用MemCache,默认true
bind方法
@ViewInject(R.id.image01)
ImageView image01;
//network
x.image().bind(imageView, "www.xmamiga.com/a.jpg", imageOptions);
// assets file
x.image().bind(imageView, "assets://test.gif", imageOptions);
// local file
x.image().bind(imageView, new File("/sdcard/test.gif").toURI().toString(), imageOptions);
x.image().bind(imageView, "/sdcard/test.gif", imageOptions);
x.image().bind(imageView, "file:///sdcard/test.gif", imageOptions);
x.image().bind(imageView, "file:/sdcard/test.gif", imageOptions);
x.image().bind(imageView, url, imageOptions, new Callback.CommonCallback<Drawable>() {
@Override
public void onSuccess(Drawable result) {
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
}
loadDrawable方法
加载图片
x.image().loadDrawable(url, imageOptions, new Callback.CommonCallback<Drawable>() {
@Override
public void onSuccess(Drawable result) {
imageView.setImageDrawable(result);
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
}
loadFile方法
通过bind()或者loadDrawable()方法加载了一张图片,它会保存到本地文件中,那当我需要这张图片时,就可以通过loadFile()方法进行查找。
x.image().loadFile(url,imageOptions,new Callback.CacheCallback<File>(){
@Override
public boolean onCache(File result) {
//在这里可以做图片另存为等操作
Log.i("JAVA","file:"+result.getPath()+result.getName());
return true; //相信本地缓存返回true
}
@Override
public void onSuccess(File result) {
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
}
3.6 数据库模块使用
数据库的操作一般有:
- 如何创建数据库和删除数据库
- 如何创建删除一张表
- 如何对表进行增删查改操作
- 如何建立一表对一表,多表对一表,多表对多表的外键操作
创建数据库和删除数据库
首先在项目Application中进行初始化配置DaoConfig(与onCreate方法同级目录下)
/**
* 初始化DaoConfig配置
*/
DbManager.DaoConfig daoConfig = new DbManager.DaoConfig()
//设置数据库名,默认xutils.db
.setDbName("cchip.db")
//设置数据库路径,默认存储在app的私有目录
.setDbDir(new File("/mnt/sdcard/"))
//.setDbDir(new File(Environment.getExternalStorageDirectory().getAbsolutePath()))
//设置数据库的版本号
.setDbVersion(2)
//设置数据库打开的监听
.setDbOpenListener(new DbManager.DbOpenListener() {
@Override
public void onDbOpened(DbManager db) {
//开启数据库支持多线程操作,提升性能,对写入加速提升巨大
db.getDatabase().enableWriteAheadLogging();
}
})
//设置数据库更新的监听
.setDbUpgradeListener(new DbManager.DbUpgradeListener() {
@Override
public void onUpgrade(DbManager db, int oldVersion, int newVersion) {
}
})
//设置表创建的监听
.setTableCreateListener(new DbManager.TableCreateListener() {
@Override
public void onTableCreated(DbManager db, TableEntity<?> table){
Log.i("JAVA", "onTableCreated:" + table.getName());
}
});
//设置是否允许事务,默认true
//.setAllowTransaction(true)
DbManager db = x.getDb(daoConfig);
db.dropDb();//删除数据库
DbManager这个类主要做以下几件事情:
- getDaoConfig 获取数据库的配置信息
- getDatabase 获取数据库实例
- saveBindingId saveOrUpdate save 插入数据的3个方法(保存数据)
- replace 只有存在唯一索引时才有用 慎重
- delete操作的4种方法(删除数据)
- update操作的2种方法(修改数据)
- find操作6种方法(查询数据)
- dropTable 删除表
- addColumn 添加一列
- dropDb 删除数据库
创建表和删除表
创建数据库表ChildInfo的实体类
/**
* onCreated = "sql":当第一次创建表需要插入数据时候在此写sql语句
*/
@Table(name = "child_info",onCreated = "")
public class ChildInfo {
/**
* name = "id":数据库表中的一个字段
* isId = true:是否是主键
* autoGen = true:是否自动增长
* property = "NOT NULL":添加约束
*/
@Column(name = "id",isId = true,autoGen = true,property = "NOT NULL")
private int id;
@Column(name = "c_name")
private String cName;
public ChildInfo(String cName) {
this.cName = cName;
}
//默认的构造方法必须写出,如果没有,这张表是创建不成功的
public ChildInfo() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getcName() {
return cName;
}
public void setcName(String cName) {
this.cName = cName;
}
@Override
public String toString() {
return "ChildInfo{"+"id="+id+",cName='"+cName+'\''+'}';
}
}
//用集合向child_info表中插入多条数据
ArrayList<ChildInfo> childInfos = new ArrayList<>();
childInfos.add(new ChildInfo("zhangsan"));
childInfos.add(new ChildInfo("lisi"));
childInfos.add(new ChildInfo("wangwu"));
childInfos.add(new ChildInfo("zhaoliu"));
childInfos.add(new ChildInfo("qianqi"));
childInfos.add(new ChildInfo("sunba"));
//db.save()方法不仅可以插入单个对象,还能插入集合
db.save(childInfos);
删除表
db.dropTable(ChildInfo.class);
新增表中的数据
ChildInfo childInfo = new ChildInfo("zhangsan123");
db.save(childInfo);
删除表中的数据
//第一种写法:
db.delete(ChildInfo.class); //child_info表中数据将被全部删除
//第二种写法,添加删除条件:
WhereBuilder b = WhereBuilder.b();
b.and("id",">",2); //构造修改的条件
b.and("id","<",4);
db.delete(ChildInfo.class, b);
修改表中的数据
//第一种写法:
ChildInfo first = db.findFirst(ChildInfo.class);
first.setcName("zhansan2");
db.update(first,"c_name"); //c_name:表中的字段名
//第二种写法:
WhereBuilder b = WhereBuilder.b();
b.and("id","=",first.getId()); //构造修改的条件
KeyValue name = new KeyValue("c_name","zhansan3");
db.update(ChildInfo.class,b,name);
//第三种写法:
first.setcName("zhansan4");
db.saveOrUpdate(first);
查询表中的数据
//查询数据库表中第一条数据
ChildInfo first = db.findFirst(ChildInfo.class);
Log.i("JAVA",first.toString());
//添加查询条件进行查询
List<ChildInfo> all = db.selector(ChildInfo.class).where("id",">",2).and("id","<",4).findAll();
for(ChildInfo childInfo :all){
Log.i("JAVA",childInfo.toString());
}
如何创建数据库,如何创建表,如何给表进行添加一列,如何对表进行增删查改的操作。
四、总结
如果有应用是基于上述模块,那么只要处理得当,可以简化了很多的开发步骤,它会让你彻底的摆脱各种工具类和重复代码的困扰。