领域实例和多线程

时间:2016-11-02 09:22:10

标签: android multithreading realm

我正在尝试对某些内容进行编码以使某些内容可以在以后观看。此内容由jsons,images,vidéos和pdfs组成:

{
  "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,有时我使用已经关闭的域实例。

这是我想的吗? 谢谢,

我做了什么

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

2 个答案:

答案 0 :(得分:2)

  

这是正确的方法吗?保持领域引用和其线程的处理程序引用。

没必要。只要关闭实例,Realm就可以在任何后台线程上使用。

Realm已经管理了UI线程及其自动更新的处理程序,因此您无需亲自手动执行此操作。

你有一个非常简单的问题。您应该只在后台线程上而不是在UI线程上同步执行Retrofit。

像这样:

protected ExecutorService executor = Executors.newSingleThreadExecutor();

//...


public static void bar(final Context ctx, final String id) {
    MainThingToDownload mainThingToDownload = mRealm.where(MainThingToDownload.class)
                                                   .equalTo("id", id)
                                                   .findFirst(); 
                                       // assuming there is a UI thread Realm
    if (mainThingToDownload != null) {
        processMain(mainThingToDownload);
    } else {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                Response<MainThingToDownload> response = Api.getInstance()
                                                            .backendRepresentation
                                                            .getMainThing(id)
                                                            .execute();
                MainThingToDownload mainThingToDownload = response.body();
                Realm realm = null;
                try {
                    realm = Realm.getDefaultInstance();
                    realm.executeTransaction(new Realm.Transaction() {
                        @Override
                        public void execute(Realm realm) {
                            realm.insertOrUpdate(mainThingToDownload);
                        }
                    }
                } finally {
                    if(realm != null) {
                        realm.close();
                    }
                }
            }
       });
}

有关更多常规信息,请查看the guide on the basics of Realmthis very simple gist

(这是要点:

public class NewsActivity extends AppCompatActivity {
  // ...
  private RealmChangeListener<RealmResults<NewsPost>> realmChangeListener;
  private RealmResults<NewsPost> listenerSet;
  private long postId;

  private Realm realm;

  @Override
  protected void onCreate(Bundle bundle) {
    super.onCreate(bundle);
    Injector.get().inject(this);
    postId = getIntent().getLongExtra("postId");
    setContentView(R.layout.activity_news);
    ButterKnife.bind(this);

    realm = RealmManager.getRealm();

    realmChangeListener = new RealmChangeListener<RealmResults<NewsPost>>() {
      @Override
      public void onChange(RealmResults<NewsPost> element) {
        NewsPost newsPost = realm.where(NewsPost.class)
                                    .equalTo(NewsPostFields.ID, postId)
                                    .findFirst();
        if(newsPost != null) { // if news post was downloaded on background thread, initalize view
          initView(newsPost);
        }
      }
    };
    listenerSet = realm.where(NewsPost.class)
                       .findAll();
    listenerSet.addChangeListener(realmChangeListener); // listen to changes in `NewsPost` table

    NewsPost newsPost = realm.where(NewsPost.class)
                                .equalTo(NewsPostFields.ID, postId)
                                .findFirst();
    if(newsPost == null) {
      // download news post if not available
      getNewsPostInteractor.getNewsPost(postId);
    } else {
      initView(newsPost);
    }
  }

  private void initView(NewsPost newsPost) {
    // set views
  }
}

哦,you should close any Realm instance that you open with getDefaultInstance()。我可以看到你没有这样做。

答案 1 :(得分:0)

嗯,在这种情况下最好的解决方案 - 使用功能反应式编程(rxJava库为android)。通过这样做,您可以在每次更新数据时(通过使用PublishSubject)创建触发器,发出事件并轻松处理并发。 如果你这样做,使用async ..方法,因为同步将通过重定向rx'管道'内的事件流来完成。 如果这些对象是从另一个线程创建的,则Realm无法从另一个线程访问对象,因此,对于多线程环境,分配新的realm实例,执行事务和释放实例。这不是强制性的,您仍然可以使用异步方法,但它会使交互更加全面。