Is that the correct way of creating realm module? - android

Hi i have a android project that use another android project as a module. I used realm for offline data storage. both the project uses realm data base. when i try to run the project it shows error.
class RealmModel is not part of the schema for this Realm
i used this link to fix that error
In that above url, they asked to create RealmModule class with #RealmModule annotation. This is my class,
#RealmModule
public class MessageRealmModule implements RealmModule {
#Override
public boolean library() {
return true;
}
#Override
public boolean allClasses() {
return false;
}
#Override
public Class<?>[] classes() {
return new Class<?>[0];
}
#Override
public Class<? extends Annotation> annotationType() {
return null;
}
}
After this line got this error.
java.lang.IllegalArgumentException: com.anubavam.message.MessageRealmModule is not a RealmModule. Add #RealmModule to the class definition.

No, you need to do it in the annotation parameters like so:
#RealmModule(library = true, classes = { MyModelClass.class })
public class MessageRealmModule {
}
See also https://realm.io/docs/java/latest/#schemas

Related

Realm accessed from incorrect thread, with Dagger

I am currently learning Dagger, RxJava, Realm and MVP in a simple project.
Basically what this app can do is it can view, add, delete and update data from database, which I'm using Realm.
I have decided to follow MVP architecture and applied repository pattern as well for data manipulation at the back end layer.
For an extra learning, I added Dagger for the dependency injection in the architecture.
Before this, I have developed an app without applying MVP nor repository pattern, not even Dagger and RxJava in mind. All seems to work well without any errors from Realm threading system. Maybe because I tied everything in a single class.
So, now that I'm moving away from that approach, I'm now having trouble implementing it in the new approach, which I think is more loosely coupled and should be better if implemented correctly.
Enough of introduction, let's get back to the topic.
The issue I'm facing right now is Realm always giving me this error:
Exception has been thrown: Realm accessed from incorrect thread.
I was suspecting that my Dagger graph isn't properly managed (especially on providing Realm instance), thus whenever I make query for data, it gives me the error.
So, my Dagger component looks like this:
#Singleton
#Component(modules = {ContextModule.class, RepositoryModule.class, PresenterModule.class})
public interface AppComponent {
/* Inejct application */
void inject(FourdoApp fourdoApp);
/* Realm Helper */
void inject(DatabaseRealm databaseRealm);
/* Activity */
void inject(MainActivity mainActivity);
void inject(TaskDetailActivity taskDetailActivity);
/* Presenter*/
void inject(MainPresenter mainPresenter);
void inject(TaskDetailPresenter taskDetailPresenter);
/* Model repository*/
void inject(TaskRepositoryImpl taskRepository);
}
Inside RepositoryModule.class;
#Module
public class RepositoryModule {
#Provides
#Singleton
Repository<Task> provideTaskRepository() {
return new TaskRepositoryImpl();
}
// Here I provide DatabaseRealm class instance
#Provides
#Singleton
public DatabaseRealm provideDatabaseRealm() {
return new DatabaseRealm();
}
}
Not sure whether I did this correctly or not. You can view the source for DI here.
For the data request to happen, inside MainActivity, I injected MainPresenter and call onRequestData interface to request it from the Presenter. From there, Presenter will make the call to Repository for the said data.
public class MainActivity extends BaseActivity implements MainContract.View {
#Inject
MainPresenter mainPresenter;
// ...
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Injecting MainActivity class
Injector.getAppComponent().inject(this);
mainPresenter.attachView(this);
// Requesting for data from Presenter
mainPresenter.onRequestData();
}
// ...
#Override
public void onRequestDataSuccess(List<String> taskList) {
doAdapter.addAll(taskList);
doAdapter.notifyDataSetChanged();
}
}
Inside MainPresenter, I injected Repository interface to make request from TaskRepositoryImpl for the real data from database.
public class MainPresenter extends BasePresenter<MainContract.View> implements MainContract.Presenter {
#Inject
Repository<Task> taskRepository;
public MainPresenter() {
Injector.getAppComponent().inject(this);
}
#Override
public void onRequestData() {
requestData();
}
private void requestData() {
taskRepository.findAll()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.map(this::mapToStringList)
.subscribe(new Observer<List<String>>() {
#Override
public void onNext(List<String> strings) { // Error in this line
if (strings.size() > 0) {
mView.onRequestDataSuccess(strings);
} else {
mView.showEmpty();
}
}
});
}
}
Inside TaskRepositoryImpl, here is how I did the findAll and it should return data from DatabaseRealm:
#Override
public Observable<List<Task>> findAll() {
return Observable.create(subscriber -> {
try {
List<Task> models = databaseRealm.findAll(Task.class);
subscriber.onNext(models);
subscriber.onComplete();
} catch (Exception e) {
subscriber.onError(e);
}
});
}
Code for DatabaseRealm are as follows:
public class DatabaseRealm {
#Inject
Context context;
RealmConfiguration realmConfiguration;
public DatabaseRealm() {
Injector.getAppComponent().inject(this);
}
public void setup() {
if (realmConfiguration == null) {
Realm.init(context);
realmConfiguration = new RealmConfiguration.Builder()
.deleteRealmIfMigrationNeeded()
.build();
Realm.setDefaultConfiguration(realmConfiguration);
} else {
throw new IllegalStateException("Realm already configured");
}
}
public Realm getRealmInstance() {
return Realm.getDefaultInstance();
}
public <T extends RealmObject> T add(T model) {
Realm realm = getRealmInstance();
realm.beginTransaction();
realm.copyToRealm(model);
realm.commitTransaction();
return model;
}
public <T extends RealmObject> T update(T model) {
Realm realm = getRealmInstance();
realm.beginTransaction();
realm.copyToRealmOrUpdate(model);
realm.commitTransaction();
return model;
}
public <T extends RealmObject> T remove(T model) {
Realm realm = getRealmInstance();
realm.beginTransaction();
realm.copyToRealm(model);
realm.deleteAll();
realm.commitTransaction();
return model;
}
public <T extends RealmObject> List<T> findAll(Class<T> clazz) {
return getRealmInstance().where(clazz).findAll();
}
public void close() {
getRealmInstance().close();
}
}
Full source code for this flawed code is right here.
I'd like to make it clear that I have limited knowledge on Realm instances being used in Dagger.
I followed this tutorial for the Repository Design Pattern with Realm, but it doesn't include Dagger for its dependency injection.
Can someone guide me on why it is always telling I'm calling Realm from incorrect thread?
I think you get this error because of this:
#Override
public Observable<List<Task>> findAll() {
return Observable.create(subscriber -> {
try {
List<Task> models = databaseRealm.findAll(Task.class);
subscriber.onNext(models);
subscriber.onComplete();
} catch (Exception e) {
subscriber.onError(e);
}
});
}
You are subscribing to io Thread but you inject your databaseRealm in Main Thread.
if you get instance in your observable's create you'll not get this error.
#Override
public Observable<List<Task>> findAll() {
return Observable.create(subscriber -> {
try {
Realm realm = getRealmInstance();
List<Task> models = realm.findAll(Task.class);
subscriber.onNext(models);
subscriber.onComplete();
} catch (Exception e) {
subscriber.onError(e);
}
});
}
You need to setup RealmConfigration only once in the Application class and use the Realm.getDeafultInstance() method to access Realm Database
With Dagger you need to Pass only realm instance in constructor
You can follow this Example and fork it
Its not exactly the same code you posted here.But it might help to understand dagger better with MVP ,RxJava and Realm

Is this the correct way of adding parammeters to constructor in Dagger 2?

Context
Recenty I started investigating about dependency injection and Dagger 2. It looks a pretty good library but it seems a bit confusing to me. There are some situations in which I don't know exactly how to proceed.
What have I tried
I have created a simple Android app that creates a Client and its Dependency and do some (dummy) work. These are the classes:
Client.java
public class Client {
private Dependency dep;
#Inject
public Client(Dependency dep) {
this.dep = dep;
}
public void work() {
System.out.println("Client working");
dep.doWork();
}
}
Dependency.java
public class Dependency {
#Inject
public Dependency() {
}
public void doWork() {
System.out.println("Dependency working");
}
}
Following some tutorials I created a couple of Module classes:
DependencyModule.java
#Module
public class DependencyModule {
#Provides
Dependency provideDependency() {
return new Dependency();
}
}
ClientModule.java
#Module
public class ClientModule {
#Provides
Client provideClient(Dependency dep) {
return new Client(dep);
}
}
And also the Component interface:
#Component(modules = {ClientModule.class})
public interface ClientComponent {
Client provideClient();
}
This works fine. From my activity I can do the following and it works:
ClientComponent clientComp = DaggerClientComponent
.builder()
.clientModule(new ClientModule())
.build();
Client client = clientComp.provideClient();
client.work();
Problem
I understand how to inject dependencies in a client (at least I think so). But how I add parameters into the constructor of a client/dependency?
I mean, what if I would wanted to add some int parameters to my objects? Something as simple as this:
Client.java
public class Client {
int id;
Dependency dep;
#Inject
public Client(int id, Dependency dep) {
this.id = id;
this.dep = dep;
}
public void work() {
System.out.println("id: " + id + " Client working");
dep.doWork();
}
}
Dependency.java
public class Dependency {
private int id;
#Inject
public Dependency(int id) {
this.id = id;
}
public void doWork() {
System.out.println("id: " + id + " Dependency working");
}
}
NOTE:
The following code is what I've tried. So I'm not sure about its correctness.
So, as the objects has new parameters in their constructor the Modules have to change:
DependencyModule.class
public class DependencyModule {
#Provides
Dependency provideDependency() {
return new Dependency(id);
}
}
ClientModule.class
#Module
public class ClientModule {
#Provides
Client provideClient(int id, Dependency dep) {
return new Client(id, dep);
}
}
Question
How do I use that new Modules? I haven't found a way to pass the id to that methods. The only way I get it to work is by passing it in the Module constructor and removing it from the provide method. This way:
#Module
public class ClientModule {
private int id;
public ClientModule(int id) {
this.id = id;
}
#Provides
Client provideClient(Dependency dep) {
return new Client(id, dep);
}
}
Same approach in the DependencyModule.java.
This way, adding the DependencyModule.class in the ClientComponent interface I can do something like:
ClientComponent clientComp = DaggerClientComponent
.builder()
.clientModule(new ClientModule(clientId))
.dependencyModule(new DependencyModule(dependencyId))
.build();
Client client = clientComp.provideClient();
client.work();
Is that the correct way of doing that?
Is there a better way of getting the same effect?
Am I committing crimes against DI principle?
There are two basic ways to get Dagger to provide an instance of a class:
Add #Inject to a constructor, and put the class's dependencies in as constructor arguments.
Add a #Provides-annotated method to a #Module-annotated class, and install that module into your #Component.
You only need to use one method for each class. So in your first example, Client and Dependency are fine as is; you don't also need ClientModule and DependencyModule.
Once you add the int dependency, now you do need a module, because there's no class to #Inject. The module just needs to provide that int, so something like this would work:
#Module
public class ClientIdModule {
private final clientId;
public ClientIdModule(int clientId) {
this.clientId = clientId;
}
#Provides
static int clientId() {
return clientId;
}
}
Now if you install ClientIdModule into your component, you'll be able to get a Client which has the right ID, and its Dependency will as well.

Dagger 2: No implementation generated for component interface

I have created a demo Android Lib project and used dagger 2.0 with the following steps:
Added the following jars to /libs folder:
dagger-2.0.jar
dagger-compiler-2.0.jar
dagger-producers-2.0-beta.jar
guava-18.0.jar
javawriter-2.5.1.jar
javax.annotation-api-1.2.jar
javax.inject-1.jar
Project -> Properties -> Java Compiler -> Annotation Processing (Enabled annotation processing)
Project -> Properties -> Java Compiler -> Annotation Processing - Factory path: Added all the above mentioned jars.
Created the following classes:
public class Car {
private Engine engine;
#Inject
public Car(Engine engine) {
this.engine = engine;
}
public String carDetails(){
String engineName = this.engine.getName();
int engineNumber = this.engine.getNumber();
return "This car has the following details: \n" + engineName + "----" + engineNumber;
}
}
public interface Engine {
public String getName();
public int getNumber();
}
public class Toyota implements Engine{
#Override
public String getName() {
return "This is toyota engine";
}
#Override
public int getNumber() {
return 1234567890;
}
}
#Component(modules = EngineModule.class)
public interface EngineComponent {
void inject();
}
#Module
public class EngineModule {
public EngineModule(DemoApplication demoApplication) {
}
#Provides
Engine provideEngine(){
return new Toyota();
}
}
But inside /.apt-generated folder there are only two files:
Car_Factory.java EngineModule_ProvideEngineFactory.java
DaggerEngineComponent.java is not there for me to build the component.
Could someone please help?
I'm guessing the annotation processor is encountering an error and Eclipse is not showing you the log. If you have log output in the Output view, you may want to paste that into the question.
Specifically, I think it's erroring out on void inject(), which isn't a format descibed in the #Component docs. Those docs describe three types of methods:
Parameterless factory methods that return an injectable type Dagger creates and injects, like Engine createEngine(), or
Single-parameter void methods that receive an instance created elsewhere and apply method and field injection, like void injectEngine(Engine) or Engine injectEngine(Engine).
Subcomponent-returning methods that combine your Component's bindings with those from another module.
Because your void inject() doesn't match any of those formats, Dagger is likely erroring out and refusing to create a DaggerEngineComponent.

Map enums to Dagger injected classes (MapBinder equivalent)

I'm trying to inject a class at runtime using a factory to map enums to class types, But I'm running into an issue where it will throw an error when I try to inject it.
java.lang.IllegalArgumentException: No inject registered for members/com.example.android.push.DefaultPushHandlerStrategy. You must explicitly add it to the 'injects' option in one of your modules.
public class PushFactory {
private Map<PushType, Class<? extends PushHandlerStrategy>> classMap = new HashMap<PushType, Class<? extends PushHandlerStrategy>>();
#Inject
public PushFactory() {
classMap.put(PushType.DEFAULT, DefaultPushHandlerStrategy.class);
classMap.put(PushType.VIDEOS, VideosPushHandlerStrategy.class);
classMap.put(PushType.MESSAGE, MessagePushHandlerStrategy.class);
}
public PushHandlerStrategy getPushHandlerStategy(PushType type){
Class<? extends PushHandlerStrategy> klazz = classMap.get(type);
if(klazz == null){
klazz = DefaultPushHandlerStrategy.class;
}
ObjectGraph graph = App.getApplication().getObjectGraph();
return graph.get(klazz); // this line throws the exception
}
}
Basically, what I'm trying to achieve is instantiating a strategy based on some data that comes in a GCM push.
I DO have the following registered in a module.
#Module(
injects = {
PushFactory.class,
PushBroadcastReceiver.class
},
complete = false,
library = false
)
public class PushModule {
}
Any ideas what's wrong with my approach?
Edit:
I was able to achieve what I wanted by injecting providers, but it seems a bit cumbersome. Any way around this?
public class PushFactory {
private Map<PushType, Provider<? extends PushHandlerStrategy>> providerMap = new HashMap<PushType, Provider<? extends PushHandlerStrategy>>();
#Inject
public PushFactory(Provider<DefaultPushHandlerStrategy> d, Provider<VideosPushHandlerStrategy> v, Provider<MessagePushHandlerStrategy> m) {
providerMap.put(PushType.DEFAULT, d);
providerMap.put(PushType.VIDEOS, v);
providerMap.put(PushType.MESSAGE, m);
}
public PushHandlerStrategy getPushHandlerStrategy(PushType type){
Provider<? extends PushHandlerStrategy> provider = providerMap.get(type);
if(provider == null){
provider = providerMap.get(PushType.DEFAULT);
}
return provider.get();
}
}
Your original solution should be achievable but it seems like you're probably missing the injects definition of those class in your PushModule. Since you're creating those objects using objectGraph.get(class) directly and not through field or constructor injection, without adding those classes to the injects Dagger cannot know that those classes are needed and will not create any plumbing for them and therefore will fail at runtime.
#Module(
injects = {
PushFactory.class,
PushBroadcastReceiver.class,
DefaultPushHandlerStrategy.class,
VideosPushHandlerStrategy.class,
MessagePushHandlerStrategy.class
},
complete = false,
library = false
)
public class PushModule {
}

Roboguice 2.0 (android): POJO injection error (always null)

My base POJO class:
public class BaseDao {
public BaseDao() {
}
// ...
}
My extends POJO class:
public class KelvinDao extends BaseDao {
public KelvinDao () {
super();
}
// ...
}
I want to use KelvinDao in a service like that:
public class HanKelvinHandler extends HttpRequestHandler {
#Inject
private KelvinDao mKelvinDao;
public void treatGet() {
mKelvinDao.blabla(); !!! mKelvinDao is always NULL
}
It's really simple but it doesn't work :(
Thank you guys for your help!
How are you creating HanKelvinHandler? If you're doing it within a subclass of a RoboGuice class, such as RoboActivity, then it should just work. Example:
public class MyActivity extends RoboActivity
{
#Inject
private HanKelvinHandler m_handler;
[...]
}
Otherwise (i.e., you're creating it within another POJO), you're in regular Guice land, and I believe you will need to use the injector to get an instance of it. Example:
public class MyClass
{
public void doSomething()
{
Injector injector = Guice.createInjector( new YourGuiceBindings() );
HanKelvinHandler handler = injector.getInstance( HanKelvinHandler.class );
handler.treatGet(); // mKelvinDao should be good now
}
}
If you haven't seen the use of the injector before, or you don't know what to put for YourGuiceBindings(), then you may need to read the following:
https://github.com/roboguice/roboguice/wiki/Simple-Custom-Binding
https://code.google.com/p/google-guice/wiki/GettingStarted
It seems like there should be a way to do this without using the injector, but I don't know.

Categories

Resources