九月裸辞从长沙跑到上海,跑了一个月的面试,本月中旬终于拿到了爱奇艺的高级工程师offer。
做Android开发整4年有余,但是这一年才是最充实的,我花一年时间努力,送给了自己一个完美的蜕变!
我普通本科毕业,在长沙待了四年,其中只换过两家公司。去年这个时候,公司业绩不好要裁人。主管平时跟我关系比较好,说我本来也在被裁的考率名额中的,他帮我跟领导说了一堆好话才没事。我请主管吃了一顿火锅。
但是我无法像以前那么心安理得混日子了。因为我去年首付买了房。原本想着每个月稳定税后拿个8k的工资。挺满足的(8k在长沙算中高水平了)
想不到我要求这么低还差点被淘汰了??
同样以之前的Person作为父类,我们再定一个衣服类Clothes。(这里先省略Dao,Database,Room步骤,后面会细讲)
Clothes:
@Entity(foreignKeys = @ForeignKey(entity = Person.class,parentColumns = “uid”,childColumns = “father_id”))
public class Clothes {
@PrimaryKey(autoGenerate = true)
private int id;
private String color;
private int father_id;
//…省略get和set
}
好多人不知道外键约束是什么意思,这里我们先往里面插数据,然后我们看看db里的数据:
第一步:我们往Person里面插入2填数据
1、(uid =1 name = 岩浆 age =18)
2、(uid =2 name = 小学生 age=10);
第二部:我们往衣服里面插入3条数据
1、(id = 1 color = 红色 father_id = 1)
2、(id = 2 color = 黑色 father_id = 1)
3、(id = 3 color = 红色 father_id = 2)
这里其实显而易见,可以先认为,person岩浆有2件衣服,红色和黑色的衣服;person小学生有1件衣服,红色的衣服。我们看看表是怎么样的。意思就是用parentColumns = “uid”(person的uid字段)作为childColumns = “father_id”(clothes的father_id字段)。这里就相当于约束到了。先不急,我们看看2张表。
person表(后面会有教程,教你怎么看db数据库):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kEafRvv0-1649750630762)(https://user-gold-cdn.xitu.io/2019/10/11/16dba6ad66ce7b6c?imageView2/0/w/1280/h/960/ignore-error/1)]
clothes表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-96k90PMX-1649750630763)(https://user-gold-cdn.xitu.io/2019/10/11/16dba6bc2f868066?imageView2/0/w/1280/h/960/ignore-error/1)]
那么为什么说是外键约束呢?当然这里有操作。如下:
@Entity(foreignKeys = @ForeignKey(onDelete = CASCADE,onUpdate = CASCADE,entity = Person.class,parentColumns = “uid”,childColumns = “father_id”))
public class Clothes {
}
这里我加了2个动作,在删除和更新的时候用了onDelete = CASCADE,onUpdate = CASCADE。这里动作有以下:
现在是不是很清楚了。很多博客都带过。我也费力讲清楚了。给博主个赞把。文章demo没有做处理,在观察时,记得请按顺序观察。
//省略部分代码,便于理解
public class Person {
//person当然不需要符合主键,我们可以直接这样默认uid为主键
//想要自增长那么这样@PrimaryKey(autoGenerate = true)
@PrimaryKey
private int uid;
}
我们都知道,Person里的属性值名就是表里的字段名。假如不像用属性名当字段名,可以这样
//省略部分代码,便于理解
public class Person {
//那么这个时候我的主键在表里的key就是uid_
@ColumnInfo(name = “uid_”)
private int uid;
}
如果不想要属性值作为表里的字段,那么忽略掉
//省略部分代码,便于理解
public class Person {
//让我们忽略调钱,人要钱干嘛。。
@Ignore
private int money;
}
实体类中引用其他实体类。这样的话Address里属性也成为了表person的字段。
//省略部分代码,便于理解
public class Person {
@Embedded
private Address address;
}
我们Address里有2个字段,city,street,所以我们的表也是
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nEVSdDaJ-1649750630763)(https://user-gold-cdn.xitu.io/2019/10/11/16dba6ad66ce7b6c?imageView2/0/w/1280/h/960/ignore-error/1)]
这里有个特殊的地方,比如说这个人很有钱(刚刚才忽略掉钱),有2个家,有2个Address类,那么怎么办呢,
//@Embedded(prefix = “one”),这个是区分唯一性的,比如说一这个人可能有2个地址类似于tag,那么在数据表中就会以prefix+属性值命名
@Embedded(prefix = “one”)
private Address address;
@Embedded(prefix = “two”)
private Address address;
这里直接上代码,相关标注是:
@Dao
public interface PersonDao {
//查询所有数据
@Query(“Select * from person”)
List getAll();
//删除全部数据
@Query(“DELETE FROM person”)
void deleteAll();
//一次插入单条数据 或 多条
// @Insert(onConflict = OnConflictStrategy.REPLACE),这个是干嘛的呢,下面有详细教程
@Insert
void insert(Person… persons);
//一次删除单条数据 或 多条
@Delete
void delete(Person… persons);
//一次更新单条数据 或 多条
@Update
void update(Person… persons);
//根据字段去查找数据
@Query(“SELECT * FROM person WHERE uid= :uid”)
Person getPersonByUid(int uid);
//一次查找多个数据
@Query(“SELECT * FROM person WHERE uid IN (:userIds)”)
List loadAllByIds(List userIds);
//多个条件查找
@Query(“SELECT * FROM person WHERE name = :name AND age = :age”)
Person getPersonByNameage(String name, int age);
}
这里唯一特殊的就是@Insert。其有一段介绍:对数据库设计时,不允许重复数据的出现。否则,必然造成大量的冗余数据。实际上,难免会碰到这个问题:冲突。当我们像数据库插入数据时,该数据已经存在了,必然造成了冲突。该冲突该怎么处理呢?在@Insert注解中有conflict用于解决插入数据冲突的问题,其默认值为OnConflictStrategy.ABORT。对于OnConflictStrategy而言,它封装了Room解决冲突的相关策略。
这里比如在插入的时候我们加上了OnConflictStrategy.REPLACE,那么往已经有uid=1的person表里再插入uid =1的person数据,那么新数据会覆盖就数据。如果我们什么都不加,那么久是默认的OnConflictStrategy.ABORT,重复上面的动作,你会发现,程序崩溃了。也就是上面说的终止事务。其他大家可以自己试试
直接上代码
//注解指定了database的表映射实体数据以及版本等信息(后面会详细讲解版本升级)
@Database(entities = {Person.class, Clothes.class}, version = 1)
public abstract class AppDataBase extends RoomDatabase {
public abstract PersonDao getPersonDao();
public abstract ClothesDao getClothesDao();
}
Room创建我们的AppDataBase,我们把它封装成单例,省的每次都去执行一遍,耗性能
public class DBInstance {
//private static final String DB_NAME = “/sdcard/LianSou/room_test.db”;
private static final String DB_NAME = “room_test”;
public static AppDataBase appDataBase;
public static AppDataBase getInstance(){
if(appDataBasenull){
synchronized (DBInstance.class){
if(appDataBasenull){
appDataBase = Room.databaseBuilder(MyApplication.getInstance(),AppDataBase.class, DB_NAME)
//下面注释表示允许主线程进行数据库操作,但是不推荐这样做。
//我这里是为了Demo展示,稍后会结束和LiveData和RxJava的使用
.allowMainThreadQueries()
.build();
}
}
}
return appDataBase;
}
}
做完这一切,那么我们的准备工作就做完了。让我们来插入一条数据
Person person_ = new Person(“Room”, 18);
DBInstance.getInstance().getPersonDao().insert(person_);
这里怎么查看db数据呢?首先我们把db文件存在手机内存里,记得打开存储权限,就是在上面代码里指定路径
private static final String DB_NAME = “/sdcard/LianSou/room_test.db”;
插入数据后,就会在手机内存卡生成db文件。
拿到db文件,怎么办呢。用插件!!Database Navigator,[插件教程](()
这里的意思比如我已经往person表存里数据。但是我要增加字段,或者是增加索引。如果你直接写上去,你会发现,你再使用数据库的时候,会直接崩溃。怎么办呢,用过greendao的人都知道,我们要升级数据库版本
@Entity
public class Person {
//…省略部分代码,便于理解。
//这里给Person加上一个儿子
}
然后来到我们的Database类里,把版本信息改下,并增添一个Migration 类,告诉Room是哪张表改了什么东西
//修改版本信息为2
@Database(entities = {Person.class, Clothes.class}, version = 2)
public abstract class AppDataBase extends RoomDatabase {
public abstract PersonDao getPersonDao();
public abstract ClothesDao getClothesDao();
//数据库变动添加Migration,简白的而说就是版本1到版本2改了什么东西
public static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
//告诉person表,增添一个String类型的字段 son
database.execSQL(“ALTER TABLE person ADD COLUMN son TEXT”);
}
};
}
关于版本更新的execSQL里的用法,可以参考[Room升级](()。也可以自行度娘,网上很多
最后来到我们的Room里:
public class DBInstance {
// private static final String DB_NAME = “/sdcard/LianSou/room_test.db”;
private static final String DB_NAME = “room_test”;
public static AppDataBase appDataBase;
public static AppDataBase getInstance(){
if(appDataBasenull){
synchronized (DBInstance.class){
if(appDataBasenull){
return Room.databaseBuilder(MyApplication.getInstance(),AppDataBase.class, DB_NAME)
.allowMainThreadQueries()
//加上版本升级信息
.addMigrations(AppDataBase.MIGRATION_1_2)
.build();
}
}
}
return appDataBase;
}
}
做完以上操作后,我们来运行下项目看看。成功,打开数据看看(本文demo里,我把升级代码注释了,想测试的可自行打开):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jdjrSG1D-1649750630764)(https://user-gold-cdn.xitu.io/2019/10/12/16dbdc21d8f62c0e?imageView2/0/w/1280/h/960/ignore-error/1)]
首先看我们DBInstance里的Room创建我们的AppDataBase,这句代码
//下面注释表示允许主线程进行数据库操作,但是不推荐这样做。
.allowMainThreadQueries()
我们,把这句代码注释掉,其他不变,运行下代码,看看。结果会报错,报错信息如下
Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
这个时候,我们来结合RxJava来使用下,这样数据操作可以放在子线程,回调可以切换到主线程更改UI。首先是引入我们的依赖
implementation ‘android.arch.persistence.room:rxjava2:2.1.4’
这份资料我从春招开始,就会将各博客、论坛。网站上等优质的Android开发中高级面试题收集起来,然后全网寻找最优的解答方案。每一道面试题都是百分百的大厂面经真题+最优解答。包知识脉络 + 诸多细节。
节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
《960页Android开发笔记》
《1307页Android开发面试宝典》
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。
《507页Android开发相关源码解析》
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
《1307页Android开发面试宝典》
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。
[外链图片转存中…(img-THfwQMSn-1649750630765)]
《507页Android开发相关源码解析》
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
[外链图片转存中…(img-IJDbmAIT-1649750630765)]
Android开发不会这些?如何面试拿高薪!