【发布时间】:2016-11-02 09:22:10
【问题描述】:
我正在尝试编写一些代码以使某些可以稍后观看的内容脱机。此内容由 json、图像、视频和 pdf 组成:
{
"elements": [
{
"id":"3b4c4f3da8bf9d1527010c5242e037b7",
"type":"media"
},
...
],
"id":"58088318ef0b4832f6c0e70b",
"content": "Hello World"
}
所以基本上我的问题是我在异步网络调用和领域数据库更新之间切换,我不知道如何构建它。
我首先获取上述 Json 必须将其存储在领域中,然后我为每个元素调用第二条路由以获取 DetailedElement 并将其存储。当一个元素包含一个可下载的文档时,我必须下载它并将其路径添加为DetailedElement 的成员。问题是,我的线程不正确。
理想情况下,我想要一个带有这个签名的方法:
FooBar.prefetch(Context ctx, String id, Callback cb)
我的第一步应该是确保它在非 UI Looper 线程中启动:我不知道该怎么做
public static void Bar(final Context ctx, final String id) {
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
final Realm realm = Realm.getDefaultInstance();
final Handler handler = new Handler();
MainThingToDownload mainThingToDownload = realm.where(MainThingToDownload.class).equalTo("id", id).findFirst();
if (mainThingToDownload != null) {
processMain(mainThingToDownload, realm, handler);
} else {
Api.getInstance().backendRepresentation.getMainThing(id).enqueue(new CustomRetrofitCallBack<>(null) {
@Override
public void onResponseReceived(final MainThingToDownload mainThingToDownload) {
handler.post(new Runnable() {
@Override
public void run() {
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.copyToRealmOrUpdate(mainThingToDownload);
processMain(mainThingToDownload, realm, handler);
}
});
}
});
}
});
}
}
});
}
这是正确的方法吗?保留其线程的领域引用和处理程序引用。然后我总是在我的网络回调中做类似的事情:
handler.post(new Runnable() {
@Override
public void run() {
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
//... add downloaded object to realm or modifies one
}
});
}
});
我的代码有很多不同的错误。大多数情况下,我不会在应该使用 Realm 的线程上使用 Realm,有时我会使用已经关闭的领域实例。
这是我应该做的吗? 谢谢,
我做了什么
一个Program 包含一个Module 列表,每个Module 包含一个Element 列表。
ProgramOfflineManager
public class ProgramOfflineManager extends AbstractOfflineManager {
private final static String TAG = "ModuleOfflineManager";
public ProgramOfflineManager(Context ctx, String id, ErrorDisplayerInterface errorDisplayerInterface) {
super(ctx, id, errorDisplayerInterface);
}
@Override
protected void makeOffline() throws IOException {
super.makeOffline();
ProgramOfflineManager.makeOffline(id, ctx);
}
@Override
protected void removeOffline() {
super.removeOffline();
ProgramOfflineManager.removeOffline(id, ctx);
}
public static void removeOffline(String programId, Context ctx) {
Log.i(TAG, "to be deleted");
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
//To be deleted
final Program program = realm.where(Program.class).equalTo("id", programId).findFirst();
Log.i("renaud", "courseDetailed matching is is not null : (detailed!=null)=>" + (program != null));
if (program != null) {
for (Module module : program.getModules()) {
CourseDetailed courseDetailed = realm.where(CourseDetailed.class).equalTo("id", module.getCourse()).equalTo("downloaded", true).findFirst();
if (courseDetailed != null) {
ModuleOfflineManager.removeOffline(module.getCourse(), ctx);
}
}
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
program.deleteFromRealm();
Log.i(TAG, "course has been deleted");
}
});
}
} finally {
if (realm != null) {
realm.close();
}
realm = null;
}
}
public static void makeOffline(final String programId, final Context ctx) throws IOException {
Api.Service360Interface backend = Api.getInstance().backend;
Response<Program> programResponse = backend.getProgram(programId).execute();
if (programResponse.isSuccessful()) {
final Program program = programResponse.body();
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
program.setDownloaded(true);
realm.copyToRealmOrUpdate(program);
}
});
for (final Module module : program.getModules()) {
long count = realm.where(CourseDetailed.class).equalTo("id", module.getCourse()).equalTo("downloaded", true).count();
if (count == 0) {
ModuleOfflineManager.makeOffline(module.getCourse(), ctx);
}
}
} finally {
if (realm != null) {
realm.close();
}
realm = null;
}
}
}
}
ModuleOfflineManager
public class ModuleOfflineManager extends AbstractOfflineManager {
private final static String TAG = "ModuleOfflineManager";
public ModuleOfflineManager(Context ctx, String id, ErrorDisplayerInterface errorDisplayerInterface) {
super(ctx, id, errorDisplayerInterface);
}
@Override
protected void makeOffline() throws IOException {
super.makeOffline();
ModuleOfflineManager.makeOffline(id, ctx);
}
@Override
protected void removeOffline() {
super.removeOffline();
ModuleOfflineManager.removeOffline(id, ctx);
}
public static void removeOffline(String courseId, Context ctx) {
Log.i(TAG, "to be deleted");
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
//To be deleted
final CourseDetailed detailed = realm.where(CourseDetailed.class).equalTo("id", courseId).findFirst();
Log.i("renaud", "courseDetailed matching is is not null : (detailed!=null)=>" + (detailed != null));
if (detailed != null) {
for (Element element : detailed.getElements()) {
Log.i(TAG, "next Element to suppress : " + element.getId());
final CourseElement courseElement = realm.where(CourseElement.class).equalTo("id", element.getId()).findFirst();
if (courseElement.getCollection() != null && courseElement.getCollection() == PostCollectionType.MEDIAS) {
Log.i(TAG, "it's a Media, erasing from db");
MediaDownloadUtils.eraseMedia(ctx, courseElement, realm);
}
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
courseElement.deleteFromRealm();
Log.i(TAG, "element has been deleted");
}
});
}
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
detailed.deleteFromRealm();
Log.i(TAG, "course has been deleted");
}
});
}
} finally {
if (realm != null) {
realm.close();
}
realm = null;
}
}
public static void makeOffline(final String courseId, final Context ctx) throws IOException {
Api.Service360Interface backend = Api.getInstance().backend;
Response<CourseDetailed> response = backend.getCourse(courseId).execute();
if (response.isSuccessful()) {
final CourseDetailed courseDetailedResponse = response.body();
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
courseDetailedResponse.saveEnums();
courseDetailedResponse.setDownloaded(true);
realm.copyToRealmOrUpdate(courseDetailedResponse);
}
});
for (final Element element : courseDetailedResponse.getElements()) {
Call<CourseElement> call = Api.getInstance().getCourseElement(element.getCollection(), element.getId(), courseId);
Response<CourseElement> courseElementResponse = call.execute();
if (courseElementResponse.isSuccessful()) {
final CourseElement courseElement = courseElementResponse.body();
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
courseElement.setCourseElementType(CourseElementTypes.valueOf(element.getCollection()));
courseElement.saveEnums();
courseElement.setDownloaded(true);
realm.copyToRealmOrUpdate(courseElement);
MediaDownloadUtils.prefechMedia(ctx, courseElement);
}
});
}
}
} finally {
if (realm != null) {
realm.close();
}
realm = null;
}
}
}
}
AbstractOfflineManager
public abstract class AbstractOfflineManager implements OfflineManagerInterface {
private final static String TAG = "AbstractOfflineManager";
final protected Context ctx;
final protected String id;
final protected ErrorDisplayerInterface errorDisplayerInterface;
protected boolean status;
public AbstractOfflineManager(Context ctx, String id, ErrorDisplayerInterface errorDisplayerInterface) {
this.ctx = ctx;
this.id = id;
this.errorDisplayerInterface = errorDisplayerInterface;
}
protected void makeOffline() throws IOException {
//implementations in children
}
protected void removeOffline() {
//implementations in children
}
@Override
public CompoundButton.OnCheckedChangeListener getClickListener() {
return new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, final boolean isChecked) {
Log.i(TAG, "clic ! isChecked : " + isChecked);
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
status = isChecked;
if (isChecked) {
try {
makeOffline();
} catch (IOException e) {
e.printStackTrace();
errorDisplayerInterface.popError(null, e);
}
} else {
removeOffline();
}
}
}).start();
}
};
} }
现在我将创建一个ElementOfflineManager
【问题讨论】:
-
每个线程都必须有自己的 Realm 实例。您应该在完成后立即关闭它,而不是之前。
-
例如你的 onResponseReceived 需要它自己的 Realm 实例,因为它运行在一个单独的线程上
-
这就是我使用处理程序的原因,我认为“Realm.getDefaultInstance()”很重,我们应该尝试保留对它及其线程的引用。像这样的一项操作会导致数十次异步网络调用和尽可能多的数据库更新。我只是想在一切都结束后关闭它。
-
考虑将
toBeDeleted之后的所有逻辑移动到单个事务中,目前这在 Realm 上相当繁重 - 你不需要Looper.prepare()。 -
如果没有 Looper.prepare(),我不会有例外吗?我只需要它来获取实时更新吗?我认为我不能将逻辑移动到单个事务中,因为要构建一个 ElementOfflineManager 来处理它自己的事务。
标签: android multithreading realm