Android工具类之XUtils应用分析



一、引言

大多数情况下,工具的好坏会影响到项目的进程,对于工具的使用说好听点是不想重复造轮子,不好听点就是拿来主义。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()); }

如何创建数据库,如何创建表,如何给表进行添加一列,如何对表进行增删查改的操作。

四、总结

如果有应用是基于上述模块,那么只要处理得当,可以简化了很多的开发步骤,它会让你彻底的摆脱各种工具类和重复代码的困扰。