优雅的android数据库编程

Android开发——数据库操作 实现基于SQLite数据库的通信录应用,通过单击增加图标打开添加通信录界面,通过单击通信录中的各条信息可删除选中项 立即下载

android 的数据库编程,说白了就是如何操控Sqlite,其实网上帖子一大把,大多都已经能够完成我们的数据开发任务。

今天我要讲的是,如何把这件事情做的优雅点。。

首先,就涉及到如何定义“优雅”。我想对数据库的操作,优雅,就是你去定义个表格,比如播放记录(见谅,我是做视频的,相信大家都喜欢看我司的片,4亿观众总有你)。

当然,出了播放记录还有一些偏好/下载信息等等等等,这些,都可以存储在Sqlite里面。这些表,我们特别希望,就是在android的代码里面定义这些表,跟在数据库表里面定义这个表几乎没有区别,你不再需要各种查询,删除,插入,等等这些繁琐的事情。加一个表,你只需要定义个表即可。。我想,这就是优雅。。

比如,我要加播放记录,PlayRecord,那么,我实际操作过程中,只需要定义个类 PlayRecord{ String mMovieId; String mMovieTitle....}等字段信息即可。


好,定义好了什么叫优雅,我们就来完成这项优雅的事情。。

大家要记住一点,优雅 的让你少干活,不代表没人干这个活,这个活,可能在父类里面干完了。以后的子类,基本就子传父业即可。所以,不要说,怎么这么难。。


第一步,我们需要定义个相对而言很多人不清楚的一个类,标签辅助类。


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DatabaseField {

    public enum DataType {
        INTEGER, REAL, TEXT, BLOB
    };

    String value() default "";

    DataType type() default DataType.TEXT;

    String name() default "";

    boolean isNull() default true;

    boolean isPrimaryKey() default false;

    boolean isAutoIncrement() default false;
}

很困惑是正常的,我们看过interface,这里却冒出个@interface,前辈告诉我,这叫标签类,类似于@override,而我们自定义了一个标签类,它的作用我们后面会讲到。起到一个辅助的作用。。

需要详细了解的同学,可以搜索 anotation interface。


第二步,定义个所有表格类的父类。

public abstract class BaseInfo {

    @DatabaseField(name = "_id", type = DataType.INTEGER, isPrimaryKey = true, isAutoIncrement = true)
    public long id;

    @DatabaseField(name = "created_at", type = DataType.INTEGER, isNull = false)
    public long createdAt;

    @DatabaseField(name = "updated_at", type = DataType.INTEGER, isNull = false)
    public long updatedAt;

    public static String getTableName(Class<?> clazz) {
        String name = clazz.getSimpleName();
        name = name.replaceAll("(?:(?<=[a-z])(?=[A-Z]))|(?:(?<=\\w)(?=[A-Z][a-z]))", "_");
        return name.toLowerCase(Locale.US) + "s";
    }

    public BaseInfo() {
        id = -1;
    }

    public boolean isNewRecord() {
        return id == -1;
    }

    @Override
    public boolean equals(Object o) {
        BaseInfo info = (BaseInfo) o;

        return info != null && info.id == id;
    }
}

第一步的标签类初次出现在这里,这个父类定义了一些大家都需要定义的字段,例如id等。

后续的表格继承这个类。


第三步,定义个操作父类,把所有的数据库操作都封装在里面,然后后面继承它的子类就轻松很多。

public class BaseDao<T extends BaseInfo> {
    private static final Logger LOG = Logger.getLogger(BaseDao.class);

    private Class<T> mClazz;
    private String mTableName;
    private List<Field> mDbFields = new ArrayList<Field>();
    private List<String> mColumnNames = new ArrayList<String>();
    private int mMaxRowCount;
    private DatabaseHelper mOpenHelper;

    public BaseDao(Context context, Class<T> clazz) {
        this(context, clazz, -1);
    }

    public BaseDao(Context context, Class<T> clazz, int maxRowCount) {
        mClazz = clazz;
        mTableName = BaseInfo.getTableName(clazz);
        mMaxRowCount = maxRowCount;
        mOpenHelper = DatabaseHelper.getInstance();

        retrieveFieldInfos();
    }

    public void beginbeginTransaction() {
        mOpenHelper.getWritableDatabase().beginTransaction();
    }

    public void setTransactionSuccessful() {
        mOpenHelper.getWritableDatabase().setTransactionSuccessful();
    }

    public void endTransaction() {
        mOpenHelper.getWritableDatabase().endTransaction();
    }

    public long insert(T data) {
        return insert(data, null);
    }

    public long insert(T data, SQLiteDatabase database) {
        long result = -1;
        SQLiteDatabase db;
        if (database == null) {
            db = mOpenHelper.getWritableDatabase();
        } else {
            db = database;
        }

        // 超过条数限制则复用最后一个记录
        if (mMaxRowCount > 0 && getRowCount(db) >= mMaxRowCount) {
            data.id = last().id;
            data.createdAt = getCurrentTimestamp();

            update(data);
        } else {
            try {
                ContentValues values = new ContentValues();

                data.createdAt = getCurrentTimestamp();
                data.updatedAt = data.createdAt;

                for (Field field : mDbFields) {
                    DatabaseField dbFieldAnnotation = field.getAnnotation(DatabaseField.class);
                    if (dbFieldAnnotation != null) {
                        if (!(field.getName().equals("id"))) {
                            setFieldValue(data, values, field, dbFieldAnnotation);
                        }
                    }
                }

                result = db.insert(mTableName, null, values);
            } catch (IllegalAccessException e) {
                LOG.warn(e);
            }
        }

        return result;
    }

    public T first() {
        T result = null;

        List<T> values = find(null, null, null, null, "`updated_at` DESC");
        if (!(values.isEmpty())) {
            result = values.get(0);
        }

        return result;
    }

    public T last() {
        T result = null;

        List<T> values = find(null, null, null, null, "`updated_at` ASC");
        if (!(values.isEmpty())) {
            result = values.get(0);
        }

        return result;
    }

    public int getRowCount() {
        return getRowCount(null);
    }

    public boolean isMaxRowCount() {
        return getRowCount() >= mMaxRowCount;
    }

    public int getRowCount(SQLiteDatabase database) {
        SQLiteDatabase db = null;
        if (database == null) {
            db = mOpenHelper.getReadableDatabase();
        } else {
            db = database;
        }
        return db.query(mTableName, null, null, null, null, null, null).getCount();

    }

    public int delete(long id) {
        return deleteBy("_id", Long.toString(id));
    }

    public int deleteBy(String columnName, String value) {
        return mOpenHelper.getWritableDatabase()
                .delete(mTableName, escapeColumnName(columnName) + " = ?", new String[] { value });
    }

    public int update(T data) {
        int result = 0;

        BaseInfo info = (BaseInfo) data;

        try {
            SQLiteDatabase db = mOpenHelper.getWritableDatabase();
            ContentValues values = new ContentValues();

            data.updatedAt = getCurrentTimestamp();

            for (Field field : mDbFields) {
                DatabaseField annotation = field.getAnnotation(DatabaseField.class);
                if (annotation != null) {
                    setFieldValue(data, values, field, annotation);
                }
            }

            result = db.update(mTableName, values, "`_id` = ?", new String[] { Long.toString(info.id) });
        } catch (IllegalAccessException e) {
            LOG.warn(e);
        }

        return result;
    }

    public T find(int id) {
        T instance = null;

        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
        Cursor c = db.query(mTableName, null, "`_id` = ?", new String[] { Long.toString(id) }, null, null, null);
        if (c != null && c.moveToNext()) {
            instance = this.fillData(c);
        }
        if (c != null) {
            c.close();
        }

        return instance;
    }

    public List<T> findAll() {
        return find("", null, null, null, null);
    }

    public List<T> findAllOrderByUpdatedAt() {
        return find("", null, null, null, "`updated_at` DESC");
    }

    public T find(String selection, String[] selectArgs, String sortOrder) {
        List<T> dataList = find(selection, selectArgs, null, null, sortOrder);

        return dataList.isEmpty() ? null : dataList.get(0);
    }

    public T findBy(String columnName, String value) {
        List<T> dataList = find(escapeColumnName(columnName) + " = ?", new String[] { value }, null, null, null);

        return dataList.isEmpty() ? null : dataList.get(0);
    }

    public List<T> find(String selection, String[] selectArgs, String groupBy, String having, String sortOrder) {
        List<T> dataList = null;

        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
        Cursor c = db.query(mTableName, null, selection, selectArgs, groupBy, having, sortOrder);
        if (c != null) {
            dataList = fillList(c);
            c.close();
        }

        return dataList;
    }

    public Cursor query(String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
        return mOpenHelper.getReadableDatabase()
                .query(mTableName, null, selection, selectionArgs, groupBy, having, orderBy);
    }

    public boolean exist(String selection, String[] selectionArgs) {
        boolean result = false;

        Cursor cursor = mOpenHelper.getReadableDatabase()
                .query(mTableName, null, selection, selectionArgs, null, null, null);
        result = cursor.getCount() > 0;
        cursor.close();

        return result;
    }

    public void touch(T record) {
        update(record);
    }

    public String getTableName() {
        return mTableName;
    }

    public static String getColumnName(Field field, DatabaseField annoation) {
        String name = annoation.name();
        if (TextUtils.isEmpty(name)) {
            name = field.getName();
        }

        return name;
    }

    @SuppressWarnings("unchecked")
    public Class<T> getClassT() {
        Type type = getClass().getGenericSuperclass();
        ParameterizedType parameterizedType = (ParameterizedType) type;

        return (Class<T>) parameterizedType.getActualTypeArguments()[0];
    }

    public List<T> fillList(Cursor cursor) {
        List<T> dataList = new ArrayList<T>();
        while (cursor.moveToNext()) {
            T entry = fillData(cursor);
            if (entry != null) {
                dataList.add(entry);
            }
        }

        return dataList;
    }

    public Object getFieldValue(Cursor cursor, DatabaseField annoation, String columnName) {
        Object result = null;
        final int columnIndex = cursor.getColumnIndex(columnName);
        if (columnIndex != -1) {
            switch (annoation.type()) {
            case TEXT:
                result = cursor.getString(columnIndex);
                break;

            case BLOB:
                break;

            case INTEGER:
                result = cursor.getLong(columnIndex);
                break;

            case REAL:
                result = cursor.getFloat(columnIndex);
                break;

            default:
                break;
            }
        }
        return result;
    }

    public T fillData(Cursor cursor) {
        T instance = null;
        try {
            instance = mClazz.newInstance();
        } catch (IllegalAccessException e) {
            LOG.warn(e);
        } catch (InstantiationException e) {
            LOG.warn(e);
        }
        for (Field field : mDbFields) {
            DatabaseField annoation = field.getAnnotation(DatabaseField.class);
            if (annoation != null) {
                String columnName = getColumnName(field, annoation);
                try {
                    Object value = getFieldValue(cursor, annoation, columnName);
                    if (value != null) {
                        if (field.getType().equals(int.class)) {
                            field.set(instance, ((Long) value).intValue());
                        } else {
                            field.set(instance, value);
                        }
                    }
                } catch (IllegalArgumentException e) {
                    LOG.warn(e);
                } catch (IllegalAccessException e) {
                    LOG.warn(e);
                }
            }
        }

        return instance;
    }

    private void retrieveFieldInfos() {
        Field[] fields = mClazz.getFields();
        for (Field field : fields) {
            DatabaseField annotation = field.getAnnotation(DatabaseField.class);
            if (annotation != null) {
                mDbFields.add(field);
                mColumnNames.add(getColumnName(field, annotation));
            }
        }
    }

    private void setFieldValue(T data, ContentValues values, Field field, DatabaseField annotation)
            throws IllegalAccessException {
        String columnName = escapeColumnName(getColumnName(field, annotation));
        switch (annotation.type()) {
        case TEXT:
            values.put(columnName, String.valueOf(field.get(data)));
            break;

        case BLOB:
            break;

        case INTEGER:
            Object o = field.get(data);
            if (o instanceof Integer) {
                values.put(columnName, (Integer) o);
            } else {
                values.put(columnName, (Long) o);
            }
            break;

        case REAL:
            values.put(columnName, (Float) field.get(data));
            break;

        default:
            break;
        }
    }

    private String escapeColumnName(String columnName) {
        return "`" + columnName + "`";
    }

    private long getCurrentTimestamp() {
        return (new Date()).getTime();
    }
}

对数据库的一些改变表的,例如删除,更新的操作放到一个单独 的类中。


public class DatabaseHelper extends SQLiteOpenHelper {
    private static final Logger LOG = Logger.getLogger(DatabaseHelper.class);

    private static final String DATABASE_NAME = "anime.db";
    private static final int DATABASE_VERSION = 3;

    private static List<Class<? extends BaseInfo>> sTables = new ArrayList<Class<? extends BaseInfo>>();

    private static DatabaseHelper sInstance = null;
    private SQLiteDatabase mDatabase = null;

    public static void init(Context context) {
        assert (sInstance == null);

        LOG.info("init.");

        registerDatabaseTable(LocalPlayRecord.class);
        registerDatabaseTable(PlayRecord.class);
        registerDatabaseTable(MovieUpdateInfo.class);
        registerDatabaseTable(DownloadInfo.class);

        registerDatabaseTable(SearchKeyword.class);
        registerDatabaseTable(SearchResult.class);
        registerDatabaseTable(SearchRecord.class);
        registerDatabaseTable(Favorite.class);
        registerDatabaseTable(TestTable.class);

        sInstance = new DatabaseHelper(context);
    }

    public static void fini() {
        LOG.info("fini.");

        if (sTables.size() > 0) {
            sTables.clear();
        }
        sInstance.close();
        sInstance = null;
    }

    public static DatabaseHelper getInstance() {
        return sInstance;
    }

    public void close() {
        dropTable(mDatabase, SearchKeyword.class);
        dropTable(mDatabase, SearchResult.class);
        createTable(mDatabase, SearchKeyword.class);
        createTable(mDatabase, SearchResult.class);

        mDatabase.close();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        LOG.debug("onCreate.");

        createTables(db);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        LOG.info("onUpgrade. {} => {}", oldVersion, newVersion);

        if (oldVersion < 3) {
            upgrade(db, oldVersion);
        } else {
            createTables(db);
        }
    }

    private void upgrade(SQLiteDatabase db, int oldVersion) {
        try {
            String tableName = null;
            String tempTableName = null;
            String sql;
            db.beginTransaction();
            if (oldVersion == 1) {
                tableName = BaseInfo.getTableName(Favorite.class);
                sql = "ALTER TABLE es RENAME TO " + tableName;
                db.execSQL(sql);

                tableName = BaseInfo.getTableName(MovieUpdateInfo.class);
                sql = "ALTER TABLE gs RENAME TO " + tableName;
                db.execSQL(sql);

                tableName = BaseInfo.getTableName(SearchKeyword.class);
                sql = "ALTER TABLE ks RENAME TO " + tableName;
                db.execSQL(sql);

                tableName = BaseInfo.getTableName(SearchRecord.class);
                sql = "ALTER TABLE ms RENAME TO " + tableName;
                db.execSQL(sql);

                tableName = BaseInfo.getTableName(SearchResult.class);
                sql = "ALTER TABLE os RENAME TO " + tableName;
                db.execSQL(sql);

                tableName = BaseInfo.getTableName(PlayRecord.class);
                tempTableName = tableName + "_temp";
                sql = "ALTER TABLE iis RENAME TO " + tempTableName;
                db.execSQL(sql);

                createTables(db);

                StringBuilder columnsBuilder = new StringBuilder();
                Field[] fields = PlayRecord.class.getFields();
                for (Field field : fields) {
                    DatabaseField annotation = field.getAnnotation(DatabaseField.class);
                    if (annotation != null) {
                        String columnName = BaseDao.getColumnName(field, annotation);

                        if (!"_id".equals(columnName) && !"source".equals(columnName)) {
                            columnsBuilder.append(columnName).append(',');
                        }
                    }
                }
                columnsBuilder.deleteCharAt(columnsBuilder.length() - 1);

                String columns = columnsBuilder.toString();
                sql = "INSERT INTO " + tableName + "(" + columns + ")"
                        + " SELECT " + columns + " FROM " + tempTableName;
                db.execSQL(sql);

                dropTable(db, tempTableName);
            } else if (oldVersion == 2) {
                createTables(db);

                tableName = BaseInfo.getTableName(LocalPlayRecord.class);
                StringBuffer sqlBuffer = new StringBuffer();
                Field[] localFields = LocalPlayRecord.class.getFields();
                for (Field field : localFields) {
                    DatabaseField annotation = field.getAnnotation(DatabaseField.class);
                    if (annotation != null) {
                        String fieldName = BaseDao.getColumnName(field, annotation);
                        if (fieldName.equals("total_episode")) {
                            DataType tableType = annotation.type();
                            sqlBuffer.append(" " + fieldName + " " + tableType.toString() + " ");
                            if (!(annotation.isNull())) {
                                sqlBuffer.append("NOT NULL ");
                            }
                            if (annotation.isPrimaryKey()) {
                                sqlBuffer.append("PRIMARY KEY ");
                            }
                            if (annotation.isAutoIncrement()) {
                                sqlBuffer.append("autoincrement");
                            }

                            break;
                        }
                    }
                }
                sql = "ALTER TABLE " + tableName + " ADD COLUMN " + sqlBuffer.toString();
                db.execSQL(sql);
            }

            db.setTransactionSuccessful();
        } catch (SQLException e) {
            LOG.warn(e);
        } catch (Exception e) {
            LOG.warn(e);
        } finally {
            db.endTransaction();
        }
    }

    private static void registerDatabaseTable(Class<? extends BaseInfo> tableClass) {
        LOG.info("register {}.", tableClass.getName());

        if (!(sTables.contains(tableClass))) {
            sTables.add(tableClass);
        }
    }

    private DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);

        mDatabase = getWritableDatabase();

        LOG.debug("construction. database.version={}", DATABASE_VERSION);
    }

    private void dropTable(SQLiteDatabase db, Class<? extends BaseInfo> tableClass) {
        dropTable(db, BaseInfo.getTableName(tableClass));
    }

    private void dropTable(SQLiteDatabase db, String tableName) {
        db.execSQL("DROP TABLE IF EXISTS " + tableName);
    }

    private void createTable(SQLiteDatabase db, Class<? extends BaseInfo> tableClass) {
        StringBuffer sqlBuffer = new StringBuffer();
        sqlBuffer.append("CREATE TABLE IF NOT EXISTS " + BaseInfo.getTableName(tableClass) + " ( ");
        Field[] fields = tableClass.getFields();

        if (fields != null && fields.length > 0) {
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                if (field != null) {
                    DatabaseField annoation = field.getAnnotation(DatabaseField.class);
                    if (annoation != null) {
                        String fieldName = BaseDao.getColumnName(field, annoation);
                        DataType tableType = annoation.type();
                        sqlBuffer.append("`" + fieldName + "` " + tableType.toString() + " ");
                        if (!(annoation.isNull())) {
                            sqlBuffer.append("NOT NULL ");
                        }
                        if (annoation.isPrimaryKey()) {
                            sqlBuffer.append("PRIMARY KEY ");
                        }
                        if (annoation.isAutoIncrement()) {
                            sqlBuffer.append("autoincrement");
                        }
                        sqlBuffer.append(",");
                    }
                }
            }
        }
        // 删除最后一个逗号,
        sqlBuffer.deleteCharAt(sqlBuffer.length() - 1);

        sqlBuffer.append(" )");
        String sql = sqlBuffer.toString();
        LOG.info("create table. sql={}", sql);
        db.execSQL(sql);
    }

    private void createTables(SQLiteDatabase db) {
        for (Class<? extends BaseInfo> tableClass : sTables) {
            createTable(db, tableClass);
        }
    }
}

后续,假如我们需要定义播放记录表。。则,变得非常便捷。。


1)表类

public class PlayRecord extends BaseInfo {

    @DatabaseField(type = DataType.TEXT, isNull = false)
    public String name;

    @DatabaseField(type = DataType.INTEGER, isNull = false)
    public int type;

    @DatabaseField(name = "movie_id", type = DataType.INTEGER, isNull = false)
    public int movieId;

    @DatabaseField(type = DataType.TEXT)
    public String source;

    @DatabaseField(type = DataType.INTEGER, isNull = false)
    public int episode;

    @DatabaseField(name = "total_episode", type = DataType.INTEGER, isNull = false)
    public int totalEpisode;

    @DatabaseField(type = DataType.INTEGER, isNull = false)
    public int position;

    @DatabaseField(type = DataType.INTEGER, isNull = false)
    public int duration;

    public boolean isFinished() {
        return duration > 0 && position == duration;
    }

    @Override
    public String toString() {
        return "[name=" + name + " movieId=" + movieId + " type=" + type + " source=" + source + " totalEpisode="
                + totalEpisode + " episode=" + episode + " position=" + position + " duration=" + duration + "]";
    }
}


2)操作类

public class PlayRecordDao extends BaseDao<PlayRecord> {
    @SuppressWarnings("unused")
    private static final Logger LOG = Logger.getLogger(PlayRecordDao.class);

    private static final int MAX_ROW_COUNT = 20;

    public PlayRecordDao(Context context) {
        super(context, PlayRecord.class, MAX_ROW_COUNT);
    }

    public boolean exist(int movieId) {
        return findByMovieId(movieId) != null;
    }

    public PlayRecord getPlayRecord(int movieId) {
        PlayRecord record = findByMovieId(movieId);

        if (record == null) {
            record = new PlayRecord();
            record.movieId = movieId;
        }

        return record;
    }

    public PlayRecord getPlayRecord(int movieId, int episode) {
        PlayRecord record = getPlayRecord(movieId);

        if (record.isNewRecord()) {
            record.episode = episode;
        } else if (record.episode != episode) {
            record.episode = episode;
            record.position = 0;
        }

        return record;
    }

    public PlayRecord save(PlayRecord record) {
        return save(record, null);
    }

    private PlayRecord save(PlayRecord record, SQLiteDatabase db) {
        if (record.isNewRecord()) {
            record.id = insert(record, db);
        } else {
            update(record);
        }

        return record;
    }

    public int deleteByMovieId(long movieId) {
        return deleteBy("movie_id", String.valueOf(movieId));
    }

    public PlayRecord findByMovieId(int movieId) {
        return findBy("movie_id", Integer.toString(movieId));
    }

}


举一反三。

假如我们要定义用户偏好的一个表。也是如此进行。非常简单。

1)表格类定义

public class Favorite extends BaseInfo {
    @DatabaseField(type = DataType.TEXT, isNull = false)
    public String name;

    @DatabaseField(name = "movie_id", type = DataType.INTEGER, isNull = false)
    public int movieId;

    @DatabaseField(name = "poster_url", type = DataType.TEXT, isNull = false)
    public String posterUrl;

    @DatabaseField(name = "release_date", type = DataType.TEXT, isNull = false)
    public String releaseDate;

    @DatabaseField(name = "tags", type = DataType.TEXT, isNull = false)
    public String tags;

    public Favorite() {
    }

    public Favorite(String name, int movieId, String posterUrl, long releaseDate, String tags) {
        this.name = name;
        this.movieId = movieId;
        this.posterUrl = posterUrl;
        this.releaseDate = String.valueOf(releaseDate);
        this.tags = tags;
    }

    @Override
    public String toString() {
        return "[name=" + name + " movieId=" + movieId + " posterUrl=" + posterUrl + "]";
    }

}

2)操作定义

public class FavoriteDao extends BaseDao<Favorite> {
    public static final int MAX_COUNT = 100;

    public FavoriteDao(Context context) {
        super(context, Favorite.class, MAX_COUNT);
    }

    public long save(Favorite favorite) {
        if (favorite.isNewRecord()) {
            favorite.id = insert(favorite, null);
        } else {
            update(favorite);
        }

        return favorite.id;
    }

    public boolean exist(Favorite favorite) {
        return findByMovieId(favorite.movieId) != null;
    }

    public int delete(Favorite favorite) {
        return deleteBy("movie_id", String.valueOf(favorite.movieId));
    }

    public Favorite findByMovieId(int movieId) {
        return findBy("movie_id", String.valueOf(movieId));
    }

}


以此类推。。。。。

写父类抽象的人很累,但是,一劳永逸。。





Android数据库编程源码(包含登录注册功能) 自己写的关于SQLite数据库的应用,供学习之用。主要涉及登录、注册。SQLite数据库增删改查,SharedPreferences的使用 立即下载

相关推荐

《Android数据库程序设计》 PDF

《Android数据库程序设计》详细阐述了与Android数据库程序设计相关的基本解决方案,主要包括在Android系统中存储数据、使用SQLite数据库、SQLite查询、使用Content Providers、表查询操作、UI关联、Android数据库应用、外部数据库、数据的收集与存储以及综合示例等内容。此外,本书还提供了相应的示例、代码,以帮助读者进一步理解相关方案的实现过程。 本书适合作为高等院校计算机及相关专业的教材和教学参考书,也可作为相关开发人员的自学教材和参考手册。

Android进阶:步骤三:Android常用框架:OrmLite数据库框架

Orm(对象关系模型) OrmLite框架介绍 对比主流的Orm框架 OrmLite基本用法 一、OrmLite 框架介绍: Ormlite 框架是第三方对数据库操作的封装的一个框架,为了提高开发效率,尤其是对某些 数据库操作特别频繁的 app,建议使用 OrmLite 框架。它是一个非常轻量级的数据库操作框架, 它的底层是根据反射机制来实现的。 Why OrmLite? 大多...

犟驴的博客 1921

android:文件存取、数据库编程(中文)

...........................................................................................................

Android 数据库ORMLite用法

步骤: 1.配置 首先去ORMLite官网下载jar包,对于Android为:ormlite-android-4.48.jar 和 ormlite-core-4.48.jar ; 2.创建Bean类 有了jar,我们直接新建一个项目为:zhy_ormlite,然后把jar拷贝到libs下。 然后新建一个包:com.zhy.zhy_ormlite.bean专门用于存放项目中的Bea

zwyAndroid的博客 954

NFT图片批量合成及稀有度计算工具

一、制作背景 -当前NFT作品制作的大多方式都是使用部件拼接的形式,然后从大量的可能性结果取优; 二、解决问题 拼接合成的组合数量十分巨大,手工合成费时费力; 合成中对各个部件的使用次数不容易控制,部位出现的概率对作品的稀有度及评分影响较大; 对于生成的作品各个部位的信息(包括来源和使用率)统计繁琐,不利于对作品的介绍中体现; 三、程序功能介绍 1、功能自定义部位稀有度批量合成NFT作品 步骤一:创建各个合成部件文件夹,将图片放到文件夹内 步骤二:软件上设置各个部件对于的图片文件夹及出现的概率 步骤二

Keeping111的博客 4638

cutlass2.x源码解读之PitchLinearStripminedThreadMap

TensorCoord:使用layout::PitchLinearCoord作为张量坐标。Shape:使用模板参数Shape_作为tile的形状,kThreads:线程总数,定义为模板参数Threads。kElementsPerAccess:每次访问的元素数,定义为模板参数ElementsPerAccess。ThreadAccessShape:线程访问的形状,使用layout::PitchLinearShape定义。内部实现细节:ShapeVec。

jiangjkd的专栏 790

注解和反射

反射注解

smiledawen的博客 437

ComfyUI新手必看:JoyCaption 2插件安装全流程(附百度网盘资源)

本文为ComfyUI新手提供了JoyCaption 2插件的完整安装与部署指南。详细介绍了从环境准备、插件获取、模型文件配置到核心节点使用的全流程,并重点解析了其集成的LLM反推模式,帮助用户高效实现从图片到精准提示词的转换。

mango的博客 303

android 本地数据库持久化框架,android数据库持久化框架, ormlite框架,

前言Android中内置了SQLite,但是对于数据库操作这块,非常的麻烦.其实可以试用第3方的数据库持久化框架对之进行结构上调整, 摆脱了访问数据库操作的细节,不用再去写复杂的SQL语句.虽然这样会在数据库操作上损失一点性能,但基于xxxx对数据库操作不频繁性能要求不高可以使用,所带来的好处即使有一个良好的统一的数据库操作以及降低代码维护成本.适合与android的数据库持久化框架主流的有2种:...

weixin_33508411的博客 527

【VeriStand】从零构建HIL测试平台:核心概念、硬件选型与实战配置指南

本文详细介绍了如何从零构建HIL测试平台,涵盖核心概念、硬件选型与实战配置指南。重点解析了VeriStand生态系统的优势,包括开箱即用特性、与LabVIEW的黄金搭档配合及Python支持。通过实战案例和硬件选型建议,帮助工程师高效搭建可靠的HIL测试平台,提升测试效率与安全性。

weixin_29250573的博客 329

【JESD79-5之】11 IDD, IDDQ, IPP规格参数和测试条件(IDD, IDDQ, IPP Specification Parameters and Test conditions)

【代码】【JESD79-5之】11 IDD, IDDQ, IPP规格参数和测试条件(IDD, IDDQ, IPP Specification Parameters and Test conditions)

m0_61126667的博客 4861

Android的常用框架(1)

为什么很多程序员做不了架构师?1、良好健康的职业规划很重要,但大多数人都忽略了2、学习的习惯很重要,持之以恒才是正解。3、编程思维没能提升一个台阶,局限在了编码,业务,没考虑过选型、扩展4、身边没有好的架构师引导、培养。所处的圈子对程序员的成长影响巨大。金九银十面试季,跳槽季,整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

2401_84123294的博客 460

复制幻灯片到另一个PPT编程

请确保将源PPT文件和目标PPT文件的文件名修改为你实际使用的文件名。运行上述代码后,目标PPT将包含源PPT中的所有幻灯片和形状。这里假设源PPT文件名为"source.pptx",目标PPT文件名为"target.pptx"。方法将每个幻灯片的布局添加到目标PPT中。是一个强大的库,可以用于创建、修改和操作PPT文件。要将一个PPT中的幻灯片复制到另一个PPT,可以使用Python的。这段代码遍历源PPT的每个幻灯片,并使用。步骤 3: 打开源PPT和目标PPT。这将保存修改后的目标PPT文件。

ByteSparkX的博客 1322

高效Android开发架构深度解析:掌握MVP模式与现代化工具链的最佳实践

对于有一定Android开发经验的中级开发者而言,构建健壮、可维护且高效的应用程序架构始终是挑战。Ribot团队的Android Boilerplate项目提供了一个经过实战检验的参考架构,集成了MVP模式、RxJava、Dagger2等现代化开发工具,帮助开发者快速搭建高质量的Android应用。本文将深度解析这一架构的核心模块,并提供实战应用指南。 ## 项目价值与定位:为什么选择Andro

gitblog_00554的博客 405

基于Cesium的简易淹没分析效果

淹没分析是通过模拟水体扩散过程来评估洪水、海平面上升等情景下可能影响的区域,其核心作用是为防灾减灾、规划设计和风险管理提供科学依据。预测淹没的范围、水深和持续时间,识别高风险区域,帮助政府制定应急预案和疏散路线,减少人员伤亡和财产损失。

星云数海的博客,一个专注GIS十多年的GISer人。 531

ONNX Runtime C# YOLO全链路压测:从300ms到22ms的15个底层优化技巧

摘要:本文分享了工业质检场景下C#调用YOLOv8s ONNX模型的性能优化实战经验。原始单帧推理耗时300ms无法满足产线需求,通过全链路分析定位瓶颈(预处理85ms、推理180ms、后处理30ms)。重点优化措施包括:使用OpenCVSharp CUDA加速预处理(85ms→12ms),开启ONNX Runtime ALL级别图优化,选用TensorRT执行提供程序(180ms→22ms),以及优化内存管理和线程调度。最终实现单帧总耗时22ms±2ms,GPU利用率提升至85%,完整满足产线120块板/

2601_94871597的博客 402

探讨SQLiteDatabase的优雅使用

不少人在使用SQLiteDatabase数据库时都很纠结,到底如何使用才算优雅?本文就此问题进行探讨。    使用SQLiteDatabase,不外乎下面几种方式: 1. 在每个CRUD方法开始时调用getWritableDatabase()取得数据库,方法结束时close db 2. 在每个activity、service等生命周期开始时new一个新db对象,取得数据库连接;然后在de

JavaSecret的专栏 1902

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

阿里技术协会(ATA) 201702系列精选文集

阿里巴巴提倡社区型的“共享+共建”理念,特别鼓励集团技术工程师之间的交流和合作,以此促进技术发展和创新。阿里巴巴技术协会(Alibaba Technology Association,简称ATA)这个阿里巴巴内部技术交流第一平台,便由此理念应运而生。 在云栖社区的优质技术文章中,有很多是来自阿里巴巴技术协会(Alibaba Technology Ass...

weixin_34304013的博客 524

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

android sqlite 分析,Android源码分析------SQLiteDatabase(1)

看了SQLiteDatabase的replace接口的源码,感觉写的很好,有些可以借鉴的地方,记录如下:publiclongreplace(Stringtable,StringnullColumnHack,ContentValuesinitialValues){try{returninsertWithOnConflict(table,nullColumnHack,initi...

weixin_29521135的博客 217

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

我们为什么要在Android中使用RxJava

本文翻译来自–>Why should we use RxJava on Android 另外: 微凉一季 再另外: 微凉一季 感觉RxJava最近风生水起,不学习一下都不好意思了,洒家也是初学RxJava,也是感觉代码好像更复杂更难懂了,看了一篇外文感同身受,简单翻译一下。本文简单介绍使用RxJava优势所在。但可能需要有一点RxJava基础,推荐先看一下抛物线的那篇经典的匠心写作。

微凉一季的专栏 5394

Android使用Db4o数据库的方法

db4o是一个被广泛欢迎的面向对象数据库,起初它基于Java平台,而后发布了.Net平台的版本,db4o具有双授权(GPLv2和商业授 权),所以在很多开源项目中都可以看到db4o的身影。面向对象数据库的出现是为了更好的适合面向对象方式开发应用程序,因为传统的关系型数据库与OO模 型具有“阻抗不匹配”问题,目前广被接受的方案是使用ORM(对象关系映射)框架来解决。但是Java中的Hibernat...

Infoq博客 212

最新安卓版本对照表,API对照表(持续更新,更新到安卓17)

话不多说,直接上表,持续更新(更新到安卓16)。 列出了所有记录的可能会影响应用开发者的功能和行为变更。请使用此列表查找影响您的变更,然后点击相应的链接以阅读文档。下表列出了所有记录的可能会影响应用开发者的功能和行为变更。请使用此列表查找影响您的变更,然后点击相应的链接以阅读文档。Java 和 OpenJDK 是 Oracle 及/或其关联公司的商标或注册商标。

雨季余静的博客 9万+

推荐开源项目:The-Movie-DB-Kotlin - 高级的电影数据库应用开发框架

推荐开源项目:The-Movie-DB-Kotlin - 高级的电影数据库应用开发框架 The-Movie-DB-KotlinThe Movie DB app using Kotlin with updated Android features项目地址:https://gitcode.com/gh_mirrors/th/The-Movie-DB-Kotlin 1. 项目介绍 The-Movie-...

gitblog_00086的博客 1075

【03】AI辅助编程完整的安卓二次商业实战-本地构建运行并且调试-二次开发改注册登陆按钮颜色以及整体资源结构熟悉-优雅草伊凡

【03】AI辅助编程完整的安卓二次商业实战-本地构建运行并且调试-二次开发改注册登陆按钮颜色以及整体资源结构熟悉-优雅草伊凡

一颗优雅草科技-央千澈的优快云博客~只想无穷无尽的学习~作息早8-晚8 925

【04】AI辅助编程完整的安卓二次商业实战-寻找修改替换新UI首页图标-菜单图标-消息列表图标-优雅草伊凡

【04】AI辅助编程完整的安卓二次商业实战-寻找修改替换新UI首页图标-菜单图标-消息列表图标-优雅草伊凡

一颗优雅草科技-央千澈的优快云博客~只想无穷无尽的学习~作息早8-晚8 1159

推荐项目:The-Movie-DB-Kotlin,用Kotlin打造的电影数据库应用

推荐项目:The-Movie-DB-Kotlin,用Kotlin打造的电影数据库应用 项目介绍 在浩瀚的数字娱乐世界中,寻找一部好电影常让人陷入选择困难。而The-Movie-DB-Kotlin正是为解决这一难题而来,它是一款利用Kotlin优雅语法构建的电影探索应用程序。此应用通过集成The Movie Database API,让用户轻松获取丰富多样的电影信息,从最新上映到经典回顾,一网打尽。...

gitblog_00141的博客 1038
上一篇: AsyncTask学习
下一篇: Android开发大牛博客
minimicall
博客等级 码龄18年 407粉丝 174原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值