0%

主流的ORM框架

greenDAO

介绍

greenDAO是一种Android数据库ORM(object/relational mapping)框架,与OrmLite、ActiveOrm、LitePal等数据库相比,单位时间内可以插入、更新和查询更多的数据,而且提供了大量的灵活通用接口。
源码github
官网

GreenDao 3.0改动:

使用过GreenDao的同学都知道,3.0之前需要通过新建GreenDaoGenerator工程生成Java数据对象(实体)和DAO对象,非常的繁琐而且也加大了使用成本。

GreenDao 3.0最大的变化就是采用注解的方式通过编译方式生成Java数据对象和DAO对象。

类注解

  • @Entity 标记了一个Java类作为一个greenDAO实体
1
2
3
4
@Entity
public class Test {
...
}

基本属性注解

  • @Id 必须是long类型,在数据库作为主键,参数autoincrement是否自增
1
2
@Id(autoincrement = true)
private Long id;
  • @Property 指定改字段的列名,如果不指定将使用默认值(eg:customName对应数据库列名为CUSTOM_NAME)
1
2
@Property(nameInDb = "USER_NAME")
private String customName;
  • @Transient 这个属性将不会作为数据表中的一个字段
1
2
@Transient
private int tempUsageCount;
  • @NotNull 该字段不能为null(作用于long, int, short, byte类型)
1
2
@NotNull
private int repos;
  • @Index 索引(unique唯一)
1
2
@Index(unique = true)
private String name;
  • @Unique 添加唯一键(隐含为其创建了一个索引)
1
@Unique private String name;

问题

知识点:int 是基本类型,直接存数值,而integer是对象,用一个引用指向这个对象
问题描述:在实体类用int等基本类型,默认不标记注解,在建数据库也会全部设置NOT NULL
解决:因此需要在实体类里写对象类型。
2016-11-29 16:18:24 发现使用中文字段名默认生成为ANSI,出现乱码,尽量使用因为的字段别名

主流的ORM框架

greenDAO3.1 安装

配置build.gradle

在model的build.gradle文件添加如下配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.greenrobot:greendao-gradle-plugin:3.1.0'
}
}

apply plugin: 'org.greenrobot.greendao'

dependencies {
compile 'org.greenrobot:greendao:3.1.0'
}

数据库的设置(可选项)

在model的build.gradle文件添加如下配置

1
2
3
4
5
6
7
8
android {
...
}

greendao {
schemaVersion 2
...
}

参数解释

  • schemaVersion: 数据库schema版本,也可以理解为数据库版本号(默认1)
  • daoPackage:设置DaoMaster 、DaoSession、Dao包名(默认为你实体的名字)
  • targetGenDir:设置DaoMaster 、DaoSession、Dao目录(默认build/generated/source/greendao)
  • testsGenSrcDir:设置生成单元测试目录(默认src/androidTest/java)
  • generateTests:设置自动生成单元测试用例

新建实体

添加注解@

1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
public class test {
@Id(autoincrement = true)
private long tes;

public long getTes() {
return tes;
}

public void setTes(long tes) {
this.tes = tes;
}
}

编译运行

  • targetGenDir目录(默认build/generated/source/greendao)下自动生成自动生成DaoMaster.java 、DaoSession.java、Dao.java
  • 实体test.java自动新增如下代码
1
2
3
4
5
6
7
8
@Generated(hash = 838475940)
public test(long tes) {
this.tes = tes;
}

@Generated(hash = 1102163179)
public test() {
}

简单的使用

新建DBManager.java管理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public class DBManager {
private final static String dbName = "test_db";
private static DBManager mInstance;
private DaoMaster.DevOpenHelper openHelper;
private Context context;

public DBManager(Context context) {
this.context = context;
openHelper = new DaoMaster.DevOpenHelper(context, dbName, null);
}
/**
* 获取单例引用
*
* @param context
* @return
*/
public static DBManager getInstance(Context context) {
if (mInstance == null) {
synchronized (DBManager.class) {
if (mInstance == null) {
mInstance = new DBManager(context);
}
}
}
return mInstance;
}

/**
* 获取可读数据库
*/
private SQLiteDatabase getReadableDatabase() {
if (openHelper == null) {
openHelper = new DaoMaster.DevOpenHelper(context, dbName, null);
}
SQLiteDatabase db = openHelper.getReadableDatabase();
return db;
}
/**
* 获取可写数据库
*/
private SQLiteDatabase getWritableDatabase() {
if (openHelper == null) {
openHelper = new DaoMaster.DevOpenHelper(context, dbName, null);
}
SQLiteDatabase db = openHelper.getWritableDatabase();
return db;
}
/**
* 插入一条记录
*
* @param test
*/
public void insertUser(test test) {
DaoMaster daoMaster = new DaoMaster(getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
testDao userDao = daoSession.getTestDao();
userDao.insert(test);
}
/**
* 查询用户列表
*/
public List<test> queryUserList() {
DaoMaster daoMaster = new DaoMaster(getReadableDatabase());
DaoSession daoSession = daoMaster.newSession();
testDao userDao = daoSession.getTestDao();
QueryBuilder<test> qb = userDao.queryBuilder();
List<test> list = qb.list();
return list;
}
}

测试代码

1
2
3
4
5
private void daotest(){
DBManager dbManager=DBManager.getInstance(this);
dbManager.insertUser(new test(10));
Log.i("sssss","ddddd"+dbManager.queryUserList().get(0).getTes());
}

参考

Android数据存储之GreenDao 3.0 详解
官方教程

创建拦截器(Interceptor)

可以分开写两个拦截器一个有网的一个离线的,这里只写了一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//创建拦截器(Interceptor)
Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Response originalResponse = chain.proceed(chain.request());
if (com.xuan.bledemo.util.Utils.isNetworkAvailable(MainActivity.this)) { //判断是否有网络判断
int maxAge = 60; //在线缓存在一分钟内读取
Log.i("缓存测试", "在线");
return originalResponse.newBuilder()
.removeHeader("Pragma") //作用未知
.header("Cache-Control", "public,max-age=" + maxAge)
.build();
} else {
int maxStale = 60 * 60 * 24 * 28; //离线时缓存保存4周
Log.i("缓存测试", "离线");
return originalResponse.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.build();
}
}
};

设置缓存文件

问题?关于缓存文件在手机上的地方,目前未找到

1
2
3
4
//设置缓存文件
File cacheFile=new File(this.getCacheDir(),"xuanCache");
Log.i("缓存测试","缓存目录"+this.getCacheDir().getPath());
Cache cache=new Cache(cacheFile,1024*1024*100); //100mb

创建httpclient

1
2
3
4
5
6
7
8
//创建httpclient
OkHttpClient okHttpClient=new OkHttpClient.Builder()
.cache(cache)
.addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR) //添加有网过滤器
.addInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR) //添加无网络过滤器,可以分别定义
.retryOnConnectionFailure(true) //出现错误时重新连接
.connectTimeout(5, TimeUnit.SECONDS) //设置超时时间
.build();

将httpclient添加到retrofit

1
2
3
4
5
6
//将http client添加到retrofit
Retrofit retrofit=new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create()) //添加gson包
.client(okHttpClient) //添加自定义的httpclient
.baseUrl("http://api.zdoz.net/") //添加网址头,注意‘/’结尾
.build();

请求数据

第一次必须有网络,后面无网络就是请求缓存

  • enqueue 异步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Itest itest=retrofit.create(Itest.class); //接口
Call<Test> getCall= itest.getTest(108.2345); //传接口参数
//异步网络请求json数据
getCall.enqueue(new Callback<Test>() {
@Override
public void onResponse(Call<Test> call, Response<Test> response) {
Log.e("缓存测试","请求成功"+response.body().getD());
}
@Override
public void onFailure(Call<Test> call, Throwable t) {
Log.e("缓存测试","请求失败"+t.toString());

}
});

问题,未验证

  • 相当与数据库的 POST(创建)、PUT(更新)、GET(查看)、DELETE(删除)
  • 缓存根据查找的资料,好像只有GET可以缓存

总结

  • 上述缓存,是同一缓存的配置,如果要单个请求配置,可以设置接口的head,在里面传参数,然后在统一的缓存配置中用参数动态变化没次的不同缓存策略及时间。

认识

当前的网络开源库有许多,如volley,okhttp,retrofit等,这三个库当前是比较火的,其中,okhttp和retrofit由square团队开发。

  • okhttp是高性能的http库,等同于httpclient,6.0将替换httpclient
  • 简化了网络请求流程,同时自己内部对OkHtttp客户端做了封装
  • gson库是为了将返回数据转化为实体类

搭建环境

Retrofit

1
compile 'com.squareup.retrofit2:retrofit:2.1.0'

gson

1
2
//将请求结果转换成json的json转换包,如果导入了这个依赖,就不用再导入Gson包,因为这个已经包含了Gson包
compile 'com.squareup.retrofit2:converter-gson:2.1.0'

网络权限

1
2
<!-- 用于访问网络,网络定位需要上网 -->
<uses-permission android:name="android.permission.INTERNET" />

创建json对应得实体类

测试网址:http://api.zdoz.net/DDD2DMS.aspx?gps=108.2345

  • 新建一个实体类Test.java,把json数据通过gsonFormat插件生成对应得属性方法

创建接口

  • 新建一个接口类Itest.java
1
2
3
4
public interface Itest {
@GET("DDD2DMS.aspx")
Call<Test> getTest(@Query("gps") double gps);
}

请求json数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Retrofit retrofit=new Retrofit.Builder()
//如果是json数据必须加这句
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://api.zdoz.net/")
.build();
Itest itest=retrofit.create(Itest.class);
Call<Test> testCall= itest.getTest(108.2345);
testCall.enqueue(new Callback<Test>() {
@Override
public void onResponse(Call<Test> call, Response<Test> response) {
Log.e("得到得json数据",""+response.body().getD());
}
@Override
public void onFailure(Call<Test> call, Throwable t) {

}
});

参数详解

  • enqueue 异步请求
  • execute 同步请求
  • baseUrl参数以’/‘结束

写数据(发送数据)

前提找到可写的characteristic,在onServicesDiscovered回调里查找

1
2
3
characteristic_W.setValue("string或者byte[]等");
//写成功issucceed返回true
boolean issucceed= mBluetoothGatt.writeCharacteristic(characteristic_W)

写完之后onCharacteristicWrite回调返回写的消息

1
2
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)

其它的写同理

写的对应关系如下:
写的操作 —> 对应的回调
writeCharacteristic—>onCharacteristicWrite
writeDescriptor —>onDescriptorWrite

接收数据(使能通知)

1
2
3
4
5
6
7
8
9
10
11
//characteristic_R接收数据的uuid
//使能characteristic_R的通知
mBluetoothGatt.setCharacteristicNotification(characteristic_R, true);
//未知,测试发现不能改
String UUIDDes = "00002902-0000-1000-8000-00805f9b34fb";
//从接收里获取descriptor
BluetoothGattDescriptor descriptor = characteristic_R.getDescriptor(UUID.fromString(UUIDDes));
//写数据到descriptor
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
//发送给底层
mBluetoothGatt.writeDescriptor(descriptor);

使能接收数据每步都有状态返回,注意做好状态的判断,以确定是那一步失败
设置成功后,onDescriptorWrite会有回调消息
然后在onCharacteristicChanged的回调里旧可以接收到外围蓝牙设备发送的广播消息了

注意

向外围蓝牙设备写(发送)消息,_同一时间只能写一次_,最好是在在上次写完成之后才开始第二次

断开连接

1
mBluetoothGatt.disconnect();

蓝牙连接

BluetoothGattCallback 回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
android.bluetooth.BluetoothGattCallback BluetoothGattCallback = new BluetoothGattCallback() {
//连接状态
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)

//遍历Services和characteristic
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)

//接收的数据改变时
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)

//信号强度
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status)

//写descriptor时的回调,status=0写成功
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)

//写Characteristic时的回调,status=0写成功
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)

}

连接

1
2
3
private BluetoothGatt mBluetoothGatt;
//连接服务 ,第二个参数设为true为自动连接,false不自动连接
mBluetoothGatt = mBluetoothDevice().connectGatt(context, true,BluetoothGattCallback);

找到需要的characteristic,descriptor

在onServicesDiscovered回调里遍历所有Services,Characteristics

1
2
3
4
5
6
7
8
9
10
11
//获得服务
List<BluetoothGattService> serviceList = mBluetoothGatt.getServices();
//遍历服务
for (BluetoothGattService gattService : serviceList) {
//获得Characteristics
List<BluetoothGattCharacteristic> characteristicList = gattService.getCharacteristics();
//遍历Characteristics
for (BluetoothGattCharacteristic characteristic : characteristicList) {
//判断存储自己需要的characteristic
}
}

读写UUID

如何找到读写的UUID号,可以用蓝牙助手,或者查看外围蓝牙设备文档
或者从权限判断。

添加权限

1
2
3
4
5
6
7
<!-- 低功耗蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- 是否只允许BLE蓝牙 -->
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="false" />

蓝牙初始化

检测是否支持蓝牙,返回true支持

1
getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)

打开蓝牙

1
2
3
4
5
6
7
8
//获取蓝牙
final BluetoothManager bluetoothManager =(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// 2.Enable Bluetooth 检测用户是否打开蓝牙并提示用户打开
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

搜索蓝牙

Ble蓝牙搜索功能在 android5.1(api 21)时发生了变化,有过滤,有扫描设置等很不错的功能

api 21:

可以配置过滤器,设置搜索模式

1
2
startScan(List<ScanFilter> filters, ScanSettings settings, ScanCallback callback)
stopScan(ScanCallback callback)

三个回调,如果不关闭搜索onScanResult一直回返回结果
要进入onBatchScanResults回调,必须设置搜索时间setReportDelay(5000),时间到了一起返回

1
2
3
4
5
6
@Override
public void onScanResult(int callbackType, ScanResult result)
@Override
public void onBatchScanResults(List<ScanResult> results)
@Override
public void onScanFailed(int errorCode)

api 18:

1
2
startLeScan(BluetoothAdapter.LeScanCallback callback)
stopLeScan(BluetoothAdapter.LeScanCallback callback)

一个回调

1
2
@Override
public void onLeScan(BluetoothDevice bluetoothDevice, int rssi, byte[] bytes)

注意

不管是新api搜索,还是旧的都要注意停止搜索,还有最好做好兼容,两种搜索都写,版本判断

1
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)