1. 依赖配置和初始化
- project level build.gradle:
buildscript {
...
dependencies {
...
classpath "io.realm:realm-gradle-plugin:5.9.0"
}
}
...
- app module level build.gradle:
apply plugin: 'realm-android'
...
dependencies {
implementation 'io.realm:android-adapters:2.1.1'
...
}
android-adapters 这个包分装了一个RecycleView.Adpter,监听了数据的变化 处理了增删改的notifyChange事件,你可以自己分装一个.
2. 全局初始化
- 在你的自定义application初始化:
private void initRealm() {
Realm.init(libInterface.provideContext());
RealmConfiguration.Builder builder = new RealmConfiguration.Builder()
.name("app.realm")
.schemaVersion(1) // TODO: 数据库版本号
.migration(new MyMigration());
if (BuildConfig.DEBUG) {
builder.deleteRealmIfMigrationNeeded();
}
Realm.setDefaultConfiguration(builder.build());
}
class MyMigration implements RealmMigration {
@Override
public void migrate(@NonNull DynamicRealm realm, long oldVersion, long newVersion) {
}
}
MyMigration这个类是数据库升级的时候的迁移类用来改变数据库的schema ,通过**schemaVersion(1)**来改变数据库升级.
当你增加RealmObject或者增加 删减 所有和RealmObject相关的配置改变的时候都需要处理是迁移呢还是配置**builder.deleteRealmIfMigrationNeeded()**来直接删除全部数据库文件来重新创建.
一般情况,开发的时候配置 :
if (BuildConfig.DEBUG) {
builder.deleteRealmIfMigrationNeeded();
}
直接删除数据库文件重新创建.
如果是升级app的时候,缓存的数据库数据是必须保留的,比如登录的用户信息userId ,token,那么你需要配置你的MyMigration类,来进行数据库升级.
3. 配置全局realm实例和全局数据库监听
- 在application中:
mGlobalRealm = Realm.getDefaultInstance();
保留一份realm实例,为了ui线程的使用.(Realm实例只能在创建realm实例的本线程中使用,ui线程中就创建这一个全局使用就行了,没必要一直创建);
realm实例创建后,使用完毕必须关闭.
对于application中创建的实例:
@Override
public void onTerminate() {
mGlobalRealm.close();
super.onTerminate();
}
- 全局监听数据库,处理上传参数请求失败的重试和下载数据成功的监听
上传参数请求(如微信聊天记录):
//这是微信消息体,需要上传到服务器的一些数据参数,图片语音视频 涉及到文件的上传需求是先上传文件,把返回的md5或者Url路径放到消息的一个参数里面再上传消息体
//其中有重试次数和成功失败的一些标志位.
//当接收到微信消息的时候,直接realm.insert()进去 (realm的所有写操作都要开启事务哦).
public class WxMessageEntity extends RealmObject {
// 收消息
public static final int GET = 0;
// 发消息
public static final int POST = 1;
public static final int NORMAL_TXT = 1;
public static final int VISIBLE_SELF_ONLY = 10000;
public static final int REVOKE_MESSAGE = 10002;
public static final int READ_MESSAGE = -10002;
public static final int PIC = 3;
public static final int VOICE = 34;
public static final int BUSINESS_CARD = 42;
public static final int VIDEO = 43;
public static final int FACE = 47;
public static final int LOCATION = 48;
public static final int FILE = 49;
public static final int LINK = -49;
public static final int TRANS_MONEY = 419430449;
public static final int LUCKY_MONEY = 436207665;
public static final int CHATROOM_SYSTEM_MESSAGE = 570425393;
public static final int SHARE_LOCATION = -1879048186;
private long msgId;
private long msgSvrId;
private String talker;
private String userId;
private String chatRoomTalker;
private int isSend;
private int type;
private String content;
private String imgPath;//0b01
private String videoImgPath;//0b10
private String imgPathMd5;
private String videoImgPathMd5;
private int fileNeedUpLoadFlag;//两个全部需要是 0b11 不需要是0
private long createTime;
private int retry;
//0未进行 1进行中 2完成 3失败
private int processStatus;
private long timeStrap;
private int retryFile;
//0未进行 1进行中 2完成 3失败
private int processStatusFile;
private long timeStrapFile;
}
在application中全局监听WxMessageEntity:
//这是在监听微信消息中需要先上传文件的
RealmResults<WxMessageEntity> allWxMessageNeedUploadFile = mGlobalRealm
.where(WxMessageEntity.class)
.notEqualTo("fileNeedUpLoadFlag",0)
.notEqualTo("processStatusFile", ProcessStatus.STATUS_FAIL_LOCAL_FILR_NOT_EXIST.getCode())
.findAllAsync();
allWxMessageNeedUploadFile.addChangeListener(list -> WxFileUploadService.startSelf(this));
//这是 上传微信消息中的文件的service 同步上传
public class WxFileUploadService extends IntentService {
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*/
public WxFileUploadService() {
super("WxFileUploadService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (!NetUtil.isNetConnect()) {
return;
}
Realm realm = Realm.getDefaultInstance();
WxMessageEntity first = realm.where(WxMessageEntity.class)
.notEqualTo("fileNeedUpLoadFlag", 0)
.and()
.notEqualTo("processStatusFile", ProcessStatus.STATUS_DOING.getCode())
.and()
.notEqualTo("processStatusFile", ProcessStatus.STATUS_DONE.getCode())
.and()
.notEqualTo("processStatusFile", ProcessStatus.STATUS_FAIL_LOCAL_FILR_NOT_EXIST.getCode())
.and()
.lessThan("retryFile", 1)
.findFirst();
if (first != null) {
uploadFile(realm, first);
}
realm.close();
}
private void uploadFile(Realm realm, WxMessageEntity first) {
realm.beginTransaction();
first.setRetryFile(first.getRetryFile() + 1);
first.setProcessStatusFile(ProcessStatus.STATUS_DOING.getCode());
first.setTimeStrapFile(System.currentTimeMillis());
realm.commitTransaction();
String path = null;
int flagCurrent = 0b00;
int flag = first.getFileNeedUpLoadFlag();
if ((flag & (flagCurrent = 0b01)) > 0) {
path = first.getImgPath();
} else if ((flag & (flagCurrent = 0b10)) > 0) {
path = first.getVideoImgPath();
}
if (!TextUtils.isEmpty(path)) {
File file = new File(path);
if (!file.exists()) {
//不存在 直接失败
realm.beginTransaction();
first.setProcessStatusFile(ProcessStatus.STATUS_FAIL_LOCAL_FILR_NOT_EXIST.getCode());
first.setTimeStrapFile(System.currentTimeMillis());
realm.commitTransaction();
//网络资源 去下载
if (path.startsWith("http")) {
WxMessageHttpResourceDownloadService.startSelf(getApplicationContext() ,path);
}
return;
}
String fileMD5String = MD5Utils.getFileMD5String(file);
if (TextUtils.isEmpty(fileMD5String)) {
//获取md5失败
realm.beginTransaction();
first.setProcessStatusFile(ProcessStatus.STATUS_FAIL_OBTAIN_MD5_EXCEPTION_OCCOUR.getCode());
first.setTimeStrapFile(System.currentTimeMillis());
realm.commitTransaction();
return;
}
try {
//校验服务器是否存在
Response<StatusEntity> execute = H.with().fileExist(new SimpleBody().id(fileMD5String)).execute();
if (execute != null && execute.body() != null) {
if (ApiRequestCode.API_LOGIN_OK.equals(execute.body().getCode())) {
//存在
L.e("服务器文件存在:" + path);
realm.beginTransaction();
int fileNeedUpLoadFlag = flag ^ flagCurrent;
first.setFileNeedUpLoadFlag(fileNeedUpLoadFlag);
first.setProcessStatusFile(fileNeedUpLoadFlag == 0 ? ProcessStatus.STATUS_DONE.getCode() : ProcessStatus.STATUS_UNSTART.getCode());
if (flagCurrent == 0b01) {
first.setImgPathMd5(fileMD5String);
} else {
first.setVideoImgPathMd5(fileMD5String);
}
realm.commitTransaction();
return;
} else {
//不存在
L.e("服务器文件不存在:" + path);
}
}
//同步上传文件
int fileType;
switch (first.getType()) {
case WxMessageEntity.PIC:
fileType = MessageServerType.PIC;
break;
case WxMessageEntity.VOICE:
fileType = MessageServerType.VOICE;
break;
case WxMessageEntity.VIDEO:
fileType = MessageServerType.VIDEO;
break;
case WxMessageEntity.FILE:
fileType = MessageServerType.FILE;
break;
default:
fileType = -1;
}
RequestBody md5RequestBody = RequestBody.create(MediaType.parse("multipart/form-data"), fileMD5String);
RequestBody fileTypeRequestBody = RequestBody.create(MediaType.parse("multipart/form-data"), String.valueOf(fileType));
MultipartBody.Part fileRequestBody = MultipartBody.Part.createFormData("file", file.getName(), RequestBody.create(MediaType.parse("*/*"), file));
Response<DataStringEntity> execute1 = H.with().uploadWxFile(md5RequestBody, fileTypeRequestBody, fileRequestBody).execute();
if (execute1 != null && execute1.body() != null && ApiRequestCode.API_LOGIN_OK.equals(execute1.body().getCode())) {
String successMd5 = execute1.body().getData();
realm.beginTransaction();
int fileNeedUpLoadFlag = flag ^ flagCurrent;
first.setFileNeedUpLoadFlag(fileNeedUpLoadFlag);
first.setProcessStatusFile(fileNeedUpLoadFlag == 0 ? ProcessStatus.STATUS_DONE.getCode() : ProcessStatus.STATUS_UNSTART.getCode());
if (flagCurrent == 0b01) {
first.setImgPathMd5(successMd5);
} else {
first.setVideoImgPathMd5(successMd5);
}
realm.commitTransaction();
} else {
realm.beginTransaction();
first.setProcessStatusFile(ProcessStatus.STATUS_FAIL.getCode());
realm.commitTransaction();
}
} catch (Exception e) {
realm.beginTransaction();
first.setProcessStatusFile(ProcessStatus.STATUS_FAIL.getCode());
realm.commitTransaction();
e.printStackTrace();
}
}
}
public static void startSelf(Context context) {
if (!ServiceUtil.isServiceRunning(context, WxFileUploadService.class.getName())) {
context.startService(new Intent(context, WxFileUploadService.class));
}
}
}
//这是拿没有上传 和 上传失败需要重试的微信记录
RealmResults<WxMessageEntity> allAsync =
mGlobalRealm
.where(WxMessageEntity.class)
.notEqualTo("processStatus",ProcessStatus.STATUS_DONE.getCode())
.and()
.equalTo("0)
.findAllAsync();
allAsync.addChangeListener(list -> WxMessageUploadService.startSelf(this));
//这个是上传微信记录的service,继承intentservice,同步http请求,这样就可以一条一条的上传啦
public class WxMessageUploadService extends IntentService {
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*/
public WxMessageUploadService() {
super("WxMessageUploadService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (!NetUtil.isNetConnect()) {
return;
}
Realm realm = Realm.getDefaultInstance();
WxMessageEntity first = realm.where(WxMessageEntity.class)
.notEqualTo("processStatus", ProcessStatus.STATUS_DONE.getCode())
.and()
.notEqualTo("processStatus", ProcessStatus.STATUS_DOING.getCode())
.and()
.equalTo("fileNeedUpLoadFlag", 0)
.and()
.lessThan("retry", 1).findFirst();
if (first != null) {
upload2Server(realm, first);
}
realm.close();
}
private static void upload2Server(Realm realm, WxMessageEntity first) {
realm.beginTransaction();
first.setRetry(first.getRetry() + 1);
first.setProcessStatus(ProcessStatus.STATUS_DOING.getCode());
first.setTimeStrap(System.currentTimeMillis());
realm.commitTransaction();
Response<StatusEntity> execute = HttpSyncRequestWrapper.excute(H.with().uploadWxMessage(WxMessageEntity.obtainUploadBody(first)));
if (execute != null) {
realm.beginTransaction();
first.setProcessStatus(ProcessStatus.STATUS_DONE.getCode());
realm.commitTransaction();
} else {
realm.beginTransaction();
first.setProcessStatus(ProcessStatus.STATUS_FAIL.getCode());
realm.commitTransaction();
}
}
public static void startSelf(Context context) {
if (!ServiceUtil.isServiceRunning(context, WxMessageUploadService.class.getName())) {
context.startService(new Intent(context, WxMessageUploadService.class));
}
}
}
然后我这边用一个定时service来定时清理retry的值,来达到触发重试上传的目的.
/**
* 守护服务 给微信发消息 和 上传微信消息
*
*/
public class DeamonService extends Service {
//servie 自启动的间隔时间 10分钟
private static final long DELAY_TIME = 10 * 60 * 1000;
//超时时间 10分钟
private static final long ACTION_TIME_OUT = 10 * 60 * 1000;
//最小的被启动的时间间隔 5分钟
private static final long MIN_START_INTERVAL = 5 * 60 * 1000;
private static long timeStrap;
private Handler mHandler;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
mHandler = new Handler(Looper.getMainLooper());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
L.e(DeamonService.class.getSimpleName() + " 启动...");
mHandler.removeCallbacksAndMessages(null);
if (Calendar.getInstance().get(Calendar.HOUR_OF_DAY) > 6 && SocketApp.accountAvalible() && NetUtil.isNetConnect()) {
//6 - 24点 账户可用 网络可用 才去处理失败的请求
//删除三天前的socket日志
if (SocketLib.get().mGlobalRealm.where(SocketLogEntity.class).greaterThan("timeStrap", System.currentTimeMillis() - 24 * 60 * 60 * 1000).count() > 100) {
//最近三天的日志超过100条 才去删除之前的
RealmResults<SocketLogEntity> socketLogLimit1000 = SocketLib.get().mGlobalRealm.where(SocketLogEntity.class).lessThan("timeStrap", System.currentTimeMillis() - 24 * 60 * 60 * 1000).limit(10000).findAll();
SocketLib.get().mGlobalRealm.beginTransaction();
socketLogLimit1000.deleteAllFromRealm();
SocketLib.get().mGlobalRealm.commitTransaction();
}
//超时十分钟 重置状态 未开始
RealmResults<WxMessageEntity> all = SocketLib.get().mGlobalRealm
.where(WxMessageEntity.class)
.notEqualTo("processStatus", ProcessStatus.STATUS_DONE.getCode())
.and()
.equalTo("fileNeedUpLoadFlag", 0)
.and()
.lessThan("timeStrap", System.currentTimeMillis() - ACTION_TIME_OUT)
.findAll();
SocketLib.get().mGlobalRealm.beginTransaction();
for (WxMessageEntity wxMessageEntity : all) {
wxMessageEntity.setRetry(0);
wxMessageEntity.setProcessStatus(ProcessStatus.STATUS_UNSTART.getCode());
}
SocketLib.get().mGlobalRealm.commitTransaction();
RealmResults<WxMessageEntity> allWxFile = SocketLib.get().mGlobalRealm
.where(WxMessageEntity.class)
.notEqualTo("fileNeedUpLoadFlag", 0)
.and()
.notEqualTo("processStatusFile", ProcessStatus.STATUS_DONE.getCode())
.and()
.lessThan("timeStrapFile", System.currentTimeMillis() - ACTION_TIME_OUT)
.findAll();
SocketLib.get().mGlobalRealm.beginTransaction();
for (WxMessageEntity wxMessageEntity : allWxFile) {
wxMessageEntity.setRetryFile(0);
wxMessageEntity.setProcessStatusFile(ProcessStatus.STATUS_UNSTART.getCode());
}
SocketLib.get().mGlobalRealm.commitTransaction();
RealmResults<PushEntity> allUnCommitPushResult = SocketLib.get().mGlobalRealm
.where(PushEntity.class)
.equalTo("needUploadAckResult", true)
.and()
.notEqualTo("processStatus", ProcessStatus.STATUS_DONE.getCode())
.and()
.notEqualTo("ack", AckStatus.ACK_NO_RESPONSE)
.and()
.lessThan("timeStrap", System.currentTimeMillis() - ACTION_TIME_OUT)
.findAll();
SocketLib.get().mGlobalRealm.beginTransaction();
for (PushEntity wxMessageEntity : allUnCommitPushResult) {
wxMessageEntity.setRetry(0);
wxMessageEntity.setProcessStatus(ProcessStatus.STATUS_UNSTART.getCode());
}
SocketLib.get().mGlobalRealm.commitTransaction();
}
mHandler.postDelayed(() -> DeamonService.startSelf(this), DELAY_TIME);
return START_STICKY;
}
@Override
public void onDestroy() {
startService(new Intent(this, DeamonService.class));
super.onDestroy();
}
public static void startSelf(Context context) {
if (System.currentTimeMillis() - timeStrap > MIN_START_INTERVAL) {
timeStrap = System.currentTimeMillis();
context.startService(new Intent(context, DeamonService.class));
}
}
public static void startSelfDelay(Handler handler, Context context) {
handler.postDelayed(() -> startSelf(context), 30 * 1000);
}
}
下篇介绍realm和recycleView的搭配.