Android activeandroid使用

activeandroid是一个开源的数据库框架,使我们操作数据库更方便,简单。

1:添加依赖:

a:在项目的build.gradle文件添加:

allprojects {
    repositories {
        google()
        jcenter()
        mavenCentral()
        maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
    }
}

b:在app的build.gradle文件添加依赖:

implementation 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT'

2:配置数据库的名字和版本号。

a:让你自己的Application对象继 承自com.activeandroid.app.Application而不是android.app.Application。如果你需要继承其他库 的Application,则需要在Application中初始化和处理ActiveAndroid。

public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        ActiveAndroid.initialize(this);
    }
    @Override
    public void onTerminate() {
        super.onTerminate();
        ActiveAndroid.dispose();
    }
}

b:在AndroidManifest.xml文件中配置数据库名称和数据库版本号。

<meta-data
<manifest ...>
    <application android:name=".MyApp" ...>
 
        ...
        
<meta-data
    android:name="AA_DB_NAME"
    android:value="test-aa.db" />
<meta-data
    android:name="AA_DB_VERSION"
    android:value="1" />
    </application>
</manifest>

3:创建自己的表,也就是我们的实体类。你的实体类必须继承自Model,这样你的类名就是你的表名。如果不想使用类名做表名,则可以使用@Table定义表名。@Column用于定义列名。

@Table(name = "MyPerson",id = "_id")
public class Person extends Model {
    @Column(name = "person_id")
    private int personId;
    //private int id;
    @Column(name = "person_name")
    private String name;
    @Column(name = "person_describe")
    private String describe;

    public int getPersonId() {
        return personId;
    }

    public void setPersonId(int personId) {
        this.personId = personId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescribe() {
        return describe;
    }

    public void setDescribe(String describe) {
        this.describe = describe;
    }

    @Override
    public String toString() {
        return "<PersonId: " + personId +
            ", Name: '" + name + '\'' +
            ", Describe: '" + describe + '\'' +
            '>';
    }
}

MyPerson就是表名,activeAndroid 会为每一个表分配一个使用自增长的ID作为主键。这个自增长的列表默认叫Id,我们可以为这个列起名字。

@Table(name = "MyPerson",id = "_id")

当然我们也可以指定我们的parson_id为主键。(因为如果我们用框架分配的Id为主键的话,我们会发现,同一个personId的人会被多次插入表中,造成冗余的数据。)

@Column(name = "person_id",unique = true)
private int personId;

指定某个列可以被索引,在其注解中添加:index = true。

@Column(name = "person_id",unique = true,index = true)
private int personId;

其他属性可以查询依赖包中com.activeandroid.annotation.Column。

另外model里面有个方法叫getId,所以我们的实体类里面的属性不能有个属性叫id,因为如果有个属性叫id,在重写get,set方法的时候,会和父类的方法有冲突。那么如果原始的json里面就是id,例如;{"id":"1","name":"zmm","describe":"zmm是个好人"}

我们在使用Gson解析的时候,定义实体类Person的时候,就必须写id了,而这个框架又不能用id,肿么办,莫慌,其实Gson给我们提供了一个注解:@SerializedName("id"),即把json里面的id转成我们实体类的personId;

@SerializedName("id")
@Column(name = "person_id")
private int personId;

这样就可以正常解析了。

4:在 这一步是优化ActiveAndroid的启动速度(可有可无)

AndroidManifest.xml文件里,添加标签:

<meta-data

    android:name="AA_MODELS"
    android:value="com.example.zongm.testapplication.bean.Person" />

至此你就可以对你的数据进行增删改查了。

5:数据处理

A:增删改查

你可以新建一个Manager类,对数据的增删改查都放该类里面。

保存:这个save方法有一个返回值,这个返回值表示当前插入的数据的id。

public boolean add(Person person) {
    if (person == null)
        return false;
    return person.save() > 0;
}

批量保存:

public boolean add(List<Person> items) {
    Log.d(TAG, "Add Items : " + (items == null ? "null" : items.size()));

    if (items == null || items.size() == 0)
        return false;

    boolean success = true;
    ActiveAndroid.beginTransaction();
    try {
        for (Person item : items) {
            success &= add(item);
        }
        ActiveAndroid.setTransactionSuccessful();
        success &= true;
    } catch (Exception e) {
        e.printStackTrace();
        success &= false;
        Log.e(TAG, "addAll : error : " + e.getClass());
    } finally {
        ActiveAndroid.endTransaction();
    }

    return success;
}

当然也可以循环,单个保存,但是那样更慢,开个事务进行保存,1可以加快速度,2:万一有一个保存失败了,可以回滚。当然其他的操作,例如删除,更新,只要是批量的操作,都可以开启事务来执行。

删除:

// 第一种删除
public void delete(Person item) {
    Log.d(TAG, "Delete : " + item);
    if (item == null) {
        return;
    }
    item.delete();
}

//第二种删除

public void delete(int personId){
    new Delete().from(Person.class).where("person_id=?",personId).execute();
}

修改:(同理也有两种方法)

public void update(String personName, String newPersonName) {
    new Update(Person.class).set("person_name = ?", newPersonName).where("person_name = ?", personName);
}

查询:

//1:把person里面的所有数据都查出来。

@NonNull
public List<Person> getAll() {
    return getAll(Integer.MAX_VALUE);
}

private List<Person> getAll(int count) {

    return new Select().from(Person.class).limit(count).execute();
}

//2:条件查询。

//a:根据id查一个person

public Person getPersonById(int id) {
    return new Select().from(Person.class).where("person_id=?", id).executeSingle();
}

//b;根据一组id拼成的ids,查出一组person。注意,这里ids格式必须是:1,2,3,4,

public List<Person> getPersonListByIds(String ids) {

    if (TextUtils.isEmpty(ids))
        return null;

    List<Person> items =
        new Select().from(Person.class).where(" person_id in (" + ids + ")").execute();
    return items;
}

其实这个框架简化了不少我们的工作,查询做的非常像SQLite的原生查询语句,几乎涵盖了所有的指令,基本所有的条件查询也都支持,只要你的sql语句记得牢,where里面可以放任何你可以写出来的sql语句,当然也支持关联查询,具体的使用方法可以参照github上的测试用例去了解。

B:类型序列

ActiveAndroid默认处理多种类型。如果需要处理自定义数据类型,继承TypeSerializer,重写其方法。比如我们要将Date类型转换为long类型保存,读取的时候又直接得到Date类型:

1:

/**
 * @author zongm on 2019/8/15
 */
public class DateUtilSerializer extends TypeSerializer {
    /**
     *返回序列的类
     */
    @Override
    public Class<?> getDeserializedType() {
        return Date.class;
    }
    /**
     *返回存储到数据库中的类型
     */
    @Override
    public Class<?> getSerializedType() {
        return long.class;
    }
    /**
     *将我们所使用的数据类型转换为ActiveAndroid存储的数据类型
     */
    @Override
    public Object serialize(Object data) {
        if (data == null) {
            return null;
        }

        return ((Date) data).getTime();
    }
    /**
     *使存储的数据转换成使用的数据类型
     */
    @Override
    public Object deserialize(Object data) {
        if (data == null) {
            return null;
        }

        return new Date((Long) data);
    }
}

2:然后注册自定义的序列化类型,在AndroidManifest.xml声明它们:

<meta-data
    android:name="AA_SERIALIZERS"
    android:value="com.example.zongm.testapplication.DateUtilSerializer"/>

其次ActiveAndroid自带的TypeSerializer:

C:对数据库升级(适用于:A:在原来数据表的基础上增加字段,而又顾忌用户原来保存的数据,不想删除原来的数据表再重新建表,B:在原来的数据库上,增加了一个新的表)

A:使用A情况

1、首先,你需要更改AndroidManifest.xml中数据库版本号AA_DB_VERSION(必须比上一版本号大的正整数)

2、其次,你需要在assest目录里面创建sql文件,目录结构(app/main/assets/migrations/升级后的版本号.sql),文件里面你需要写上你变动数据库的sql语句(一行一句sql语句),比如我们想在MyPerson表里增加age字段,我们需要这样写:

ALTER TABLE MyPerson ADD COLUMN person_age INTEGER;


3、最后,你需要在Person对象里面添加的对应字段(如果有增加字段的话,添加规则参考上面创建数据库模型

@Column(name = "person_age")
private int age;

B:使用B情况。

1、同A情况的第一步

2、只需要在AndroidManifest.xml里面的 AA_MODELS标签下增加你增加的表名即可。

<meta-data
    android:name="AA_MODELS"
    android:value="com.example.zongm.testapplication.bean.Person,
    com.example.zongm.testapplication.bean.Person2" />

最后说下使用过程中遇到的问题

1:在save的时候报错:

 Caused by: java.lang.SecurityException: Failed to find provider null for user 0; expected to find a valid ContentProvider for this authority

原因:

当我们在 8.0 或 8.1 系统上使用 26 或以上的版本的 SDK 时,调用 ContentResolver 的 notifyChange 方法通知数据更新,或者调用 ContentResolver 的 registerContentObserver 方法监听数据变化时,会出现异常:java.lang.SecurityException: Failed to find provider < authority > for user 0,而在 SDK 26 之前的版本上这两个方法都是可以正常运行的。

具体原因请移步这位大哥的博客:https://blog.csdn.net/weixin_37077539/article/details/80067073

解决办法:找到AndroidManifest.xml文件,在application结点下面添加以下代码:

<provider
    android:name="com.activeandroid.content.ContentProvider"
    android:authorities="${applicationId}"
    android:enabled="true"
    android:exported="false"></provider>

2:当你的实体类有一个带参的构造方法时,你再调用save的时候,报错:

Caused by: java.lang.RuntimeException: Your model com.example.zongm.testapplication.bean.Person does not define a default constructor. The default constructor is required for now in ActiveAndroid models, as the process to populate the ORM model is : 1. instantiate default model 2. populate fields

原因:因为ActiveAndroid 的Model的有一个无参的构造函数,我们的Person是Model的子类,我们写了一个带参的构造方法,则默认的无参构造方法就不存在了,我们看下ActiveAndroid 的源码,SQLiteUtils的processCursor():

public static <T extends Model> List<T> processCursor(Class<? extends Model> type, Cursor cursor) {
   TableInfo tableInfo = Cache.getTableInfo(type);
   String idName = tableInfo.getIdName();
   final List<T> entities = new ArrayList<T>();

   try {
      Constructor<?> entityConstructor = type.getConstructor();

      if (cursor.moveToFirst()) {
               /**
                * Obtain the columns ordered to fix issue #106 (https://github.com/pardom/ActiveAndroid/issues/106)
                * when the cursor have multiple columns with same name obtained from join tables.
                */
               List<String> columnsOrdered = new ArrayList<String>(Arrays.asList(cursor.getColumnNames()));
         do {
            Model entity = Cache.getEntity(type, cursor.getLong(columnsOrdered.indexOf(idName)));
            if (entity == null) {
               entity = (T) entityConstructor.newInstance();
            }

            entity.loadFromCursor(cursor);
            entities.add((T) entity);
         }
         while (cursor.moveToNext());
      }

   }
   catch (NoSuchMethodException e) {
      throw new RuntimeException(
               "Your model " + type.getName() + " does not define a default " +
               "constructor. The default constructor is required for " +
               "now in ActiveAndroid models, as the process to " +
               "populate the ORM model is : " +
               "1. instantiate default model " +
               "2. populate fields"
           );
   }
   catch (Exception e) {
      Log.e("Failed to process cursor.", e);
   }

   return entities;
}

首先  判断LruCache缓存中是否存在Model类.如果不存在,就去反射创造一个实体出来,

entityConstructor.newInstance();点进去也就是在Class.class。的newInstance()方法是个native方法,具体怎么写的我们看不到,但是看注释,解释说的大概就是根据一个无参的构造方法,获得一个对象。

故:我们需要通过一个无参的构造方法来反射得到一个对象。因为我们写了一个带参的构造方法,默认的无参的构造方法就不存在了,所以需要我们手动写上。

解决办法:

在我们的Person实体类里面显示的定义一个无参的构造函数。

即:

public Person() {
}

public Person(int personId) {
    this.personId = personId;
}

3:数据总是重复保存到数据库。

这个的原因在上面已经讲过了,可能因为你没有定义主键。把你的实体类里面具有唯一性的属性设置为主键,一般都是实体类的id。

4:因为数据重复保存,所有有的小阔爱就在保存之前那,先去查询有没有,如果有的话,先删除,再保存,导致报错:
 

注意:不要将Delete配合executeSingle使用,因为这样使用不可避免的会碰到两个坑:

  1. SQLite默认不支持DELETE和LIMIT并存的操作.而executeSingle会使用Limt 1语句.
  2. 使用Delete和executeSingle配合,其实是先执行SELETE操作,然后再执行Model的delte操作.但是ActiveAndroid源码中没有判空,会导致空指针

详细请移步:http://ju.outofmemory.cn/entry/341803

5:这是gson解析的问题。Gson解析json数据时,如果属性值为null时报异常错误

解决办法就是:用GsonBuilder创建Gson实例,而不是new Gson();

Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT,
    Modifier.STATIC, Modifier.FINAL).create();

还有很多GsonBuilder的使用方法,请自行搜索。

每日语录:一切有为法,如梦幻泡影,如露亦如电,应作如是观。

单曲循环《只爱西经》

猜你喜欢

转载自blog.csdn.net/androidzmm/article/details/99626542