Use Android Annotations with Parceler - android

I'm using Android Annotations in my Android Project. As implementing Parcelable is a lot of work I want to use Parceler and the #Parcel annotation.
The problem is that if I want to use the #FragmentArg annotation by Android Annotations it doesn't (for obvious reasons) recognize that the class will be generated with the Parcelable interface implemented.
I now have two questions:
Where does Parceler put the generated classes so that I could work with these? On parceler.org it is stated: "To use the generated code, you may reference the generated class directly, or via the Parcels utility class"
Is there another way to use Parceler or any library which generates the Parcelable boilerplate code with Android Annotations?
Until now my code for the Fragment looks like:
#EFragment(R.layout.fragment_faq)
public class FaqFragment extends ListFragment {
#FragmentArg
ArrayList<FaqItemImpl> faqItems;
// ...
}
The generated POJO class is annotated with #Parcel:
#Parcel
public class FaqItemImpl implements FaqItem {
protected String iconURL;
protected String title;
protected String question;
protected String answer;
protected ArrayList<FaqItemImpl> faqChildren;
// ...
}
In the generated FaqFragment_ the interesting part is:
// ...
public FaqFragment_.FragmentBuilder_ faqItems(ArrayList<FaqItemImpl> faqItems) {
args.putSerializable(FAQ_ITEMS_ARG, faqItems);
return this;
}
// ...
As you can see the generated class treads the POJO as Serializable...

One approach you can use is to let AA handle the hand-off of the Parcelable and let Parceler perform the serialization/deserialization. One nice feature about Parceler is it will handle collection serialization for you, so AA should just have to deal with a single Parcelable. This would effectively avoid any reference to generated code, besides AA's underscore class of course.
Here's what I mean:
#EFragment(R.layout.fragment_faq)
public class FaqFragment extends ListFragment {
#FragmentArg
Parcelable faqParcelable;
public void useFaq(){
List<FaqItemImpl> faqItems = Parcels.unwrap(faqParcelable);
// ...
}
}
Then when you're ready to build FaqFragment, you would just have to have Parceler wrap your List:
FaqFragment_.builder()
.faqParcelable(Parcels.wrap(faqItems))
.build();
Yes, this approach is not as nice as AA making the wrap/unwrap call for you, but it should work.
Edit:
Working with the Android Annotation team we've added Parceler support to #Extra, #FragmentArg and #SavedInstanceState annotated fields. This means the OP's desired functionality is in place. This should work:
#EFragment(R.layout.fragment_faq)
public class FaqFragment extends ListFragment {
#FragmentArg
ArrayList<FaqItemImpl> faqItems;
// ...
}

Unfortunetaly you cannot use #Parcelable objects with #FragmentArg (or other AA bundle injection annotations). Since you are using FaqItemImpl which itself does not implement Parcelable, AA cannot know how to deal with it. An (ugly) solution would be using the generated class:
#FragmentArg
ArrayList<FaqItemImpl.Parcelable> faqItems;
Actually there was an attempt to integrate parceler into AndroidAnnotations but was rejected due to some reasons.
There are plans to add Parcelable boilerplate generator into AA directly, unfortunetaly it needs more initial work.

Related

Is there better way for handle kotlinx serialization?

I use kotlinx.serialization on Kotlin native project, I a defined Super class for my models and all of the models extends from it.
I defined a function to called toJSON() for serialize variables and fields inside model that all of class models have it.
#Serializable
open class Model {
fun toJSON(): String = JSON.stringify(this);
}
And I created a subclass
class Me : Model() {
var name:String = "Jack";
}
but when I invoke JSON.stringify(this), IDE get a Warning to me:
This declaration is experimental and its usage must be marked with '#kotlinx.serialization.ImplicitReflectionSerializer' or '#UseExperimental(kotlinx.serialization.ImplicitReflectionSerializer::class)'
I paid attention and I used #ImplicitReflectionSerializer annotation while not worked.
Where is my problem?
This is discussed here. It's the particular overload you're using which is still experimental. So your options are either to use the other overload (which takes in a serializer) or to use one of the annotations mentioned in the error message. If you look at the answer to the question I linked (and the comments following it), you'll see it talks about using #UseExperimental and where it should be used.

How to inject using interface in dagger2

Considering the following structure:
public class WaterWorld implements IWorld {
...
#Inject
CreationMode creationMode;
#Override
public final void init() {
WorldModule.getComponent().inject(this);
}
...
}
Is it possible for WaterWorld to get dependencies if the Component has following structure:
void inject(IWorld world);
I am getting null this way. However, if I try to do it in the following
void inject(WaterWorld world);
then it works. However, I have many classes implementing IWorld. I wanted to do it this way: void inject(IWorld world); How to do this or is there some other generic solution?
No, Dagger will always inject the class specified in the inject(Foo foo) method. It will inject objects in the parent types, but it will not inject objects in subclasses.
Dagger uses the type specified to generate code for the injection, but if you don't specify the specific class then Dagger simply doesn't know about it and won't inject its fields.
Note: You don't give any specific example, but it looks like you could be very well using constructor injection instead, which has no need for manual injection or inject methods in your component. If you have multiple implementations of the same interface you might even take a look at multi bindings with Dagger.

Android annotations not injecting RestService

Hi I can't inject the RestService anywhere in my code.
I'm working test driven so I am using Robolectric to test this code. I hope it isn't a problem with AA+Robolectric, I don't have any experience with this.
The weird thing is that in my tests I can manually insert the generated RestClient_, but it doesn't get inserted automatically.
So I can do this:
RestClient rest = new RestClient_(activity.getApplicationContext());
but the following doesn't work:
#RestService
RestClient restClient;
I get a NullPointerException on restClient.
I also didn't forget the #EBean tag
#EBean
public class Player {
#RestService
RestClient restClient;
private int playerId;
public Player() {
}
public int getPlayerId() {
return playerId;
}
public List<Card> getHand() {
return restClient.getHand(playerId);
}
}
In the log I can see that Android Annotations has processed everything correctly.
This is my first project with Android Annotations and I can't grasp why the dependency injection doesn't work. No dependency injection removes almost all the benefit of using Android Annotations.
Thanks in advance!
Some extra information: I am instantiating my Player object in an Android Annotations-annotated REST Service. A code snippet of the method creating the Player object.
#Get(value = "/players/createAnonymous")
#Accept(MediaType.APPLICATION_JSON)
public Player createAnonymousPlayer();
Ok so here is the answer to my question. I hope it can be of use to other people who ask themselves the same as I did. Thanks to WonderCsabo for pointing me in the right direction.
Android Annotations generates the annotated classes at compile time. So Player becomes Player_. When using #Inject, Android Annotations will inject an instance of Player_ in the annotated field. However in my case the RestClient_ will return Player and not Player_ (So the player object without the processed annotations, so without the injected RestClient_).
It's not possible (not that I know of) to make the RestClient return Player_. This will not compile because the Player_ class doesn't exist yet at compile time.
Now I also understand the drawback that Android Annotations has. You can't easily test the code because you can't use android annotations in your tests. You can't swap the testclass with the generated class anywhere. You can't mock the injected classes either without using a runtime mock generator as PowerMock. So if you need dependency injection and easy testing, you should look elsewhere. (Dagger for example)

Using Dagger for dependency injection on constructors

So, I'm currently redesigning an Android app of mine to use Dagger. My app is large and complicated, and I recently came across the following scenario:
Object A requires a special DebugLogger instance which is a perfect candidate for injection. Instead of passing around the logger I can just inject it through A's constructor. This looks something like this:
class A
{
private DebugLogger logger;
#Inject
public A(DebugLogger logger)
{
this.logger = logger;
}
// Additional methods of A follow, etc.
}
So far this makes sense. However, A needs to be constructed by another class B. Multiple instances of A must be constructed, so following Dagger's way of doing things, I simple inject a Provider<A> into B:
class B
{
private Provider<A> aFactory;
#Inject
public B(Provider<A> aFactory)
{
this.aFactory = aFactory;
}
}
Ok, good so far. But wait, suddenly A needs additional inputs, such as an integer called "amount" that is vital to its construction. Now, my constructor for A needs to look like this:
#Inject
public A(DebugLogger logger, int amount)
{
...
}
Suddenly this new parameter interferes with injection. Moreover, even if this did work, there would be no way for me to pass in "amount" when retrieving a new instance from the provider, unless I am mistaken. There's several things I could do here, and my question is which one is the best?
I could refactor A by adding a setAmount() method that is expected to be called after the constructor. This is ugly, however, because it forces me to delay construction of A until "amount" has been filled in. If I had two such parameters, "amount" and "frequency", then I would have two setters, which would mean either complicated checking to ensure that construction of A resumes after both setters are called, or I would have to add yet a third method into the mix, like so:
(Somewhere in B):
A inst = aFactory.get();
inst.setAmount(5);
inst.setFrequency(7);
inst.doConstructionThatRequiresAmountAndFrequency();
The other alternative is that I don't use constructor-based injection and go with field-based injection. But now, I have to make my fields public. This doesn't sit well with me, because now I am obligated to reveal internal data of my classes to other classes.
So far, the only somewhat elegant solution I can think of is to use field-based injection for providers, like so:
class A
{
#Inject
public Provider<DebugLogger> loggerProvider;
private DebugLogger logger;
public A(int amount, int frequency)
{
logger = loggerProvider.get();
// Do fancy things with amount and frequency here
...
}
}
Even still, I'm unsure about the timing, since I'm not sure if Dagger will inject the provider before my constructor is called.
Is there a better way? Am I just missing something about how Dagger works?
What you are talking about is known as assisted injection and is not currently supported by Dagger in any automatic fashion.
You can work around this with the factory pattern:
class AFactory {
#Inject DebugLogger debuggLogger;
public A create(int amount, int frequency) {
return new A(debuggLogger, amount);
}
}
Now you can inject this factory and use it to create instances of A:
class B {
#Inject AFactory aFactory;
//...
}
and when you need to create an A with your 'amount' and 'frequency' you use the factory.
A a = aFactory.create(amount, frequency);
This allows for A to have final instances of the logger, amount, and frequency fields while still using injection to provide the logger instance.
Guice has an assisted injection plugin which essentially automates the creation of these factories for you. There have been discussion on the Dagger mailing list about the appropriate way for them to be added but nothing has been decided upon as of this writing.
What Jake's post says is perfectly true. That said, we (some of the Google folk who work with Guice and Dagger) are working on an alternative version of "assisted injection" or automatic-factory generation which should be usable by Guice or Dagger or stand-alone - that is, it will generate factory class source code for you. These factory classes will (if appropriate) be injectable as any standard JSR-330 class would. But it is not yet released.
Pending a solution like this, Jake Wharton's approach is advisable.
You're having a problem because you are mixing injectables and non injectables in your constructor. The general rules for injection that will save you tons of heartache and keep your code clean are:
Injectables can ask for other injectables in their constructor, but not for newables.
Newables can ask for other newables in their constructor but not for injectables.
Injectables are service type objects, ie objects that do work such as a CreditCardProcessor, MusicPlayer, etc.
Newables are value type objects such as CreditCard, Song, etc.
Jake's post is great, but there is more simple way. Google created AutoFactory library for creating factory automatically at compile time.
First, create class A with #AutoFactory annotation and #Provided annotation for injecting arguments:
#AutoFactory
public class A {
private DebugLogger logger;
public A(#Provided DebugLogger logger, int amount, int frequency) {
this.logger = logger;
}
}
Then library creates AFactory class at compile time. So you need just inject the factory to a constructor of B class.
public class B {
private final AFactory aFactory;
#Inject
public B(AFactory aFactory) {
this.aFactory = aFactory;
}
public A createA(int amount, int frequency) {
return aFactory.create(amount, frequency);
}
}
I just want to add that years passed after this question has been posted and now there is a library called
AssistedInject which has been created by Jake and friends at Square,
to solve the exact same problem and is fully compatible with Dagger 2.
You can find it here: https://github.com/square/AssistedInject
Dagger 2 now has support for assisted injection which should help solve your usecase.
https://dagger.dev/dev-guide/assisted-injection
You can wrap your class like this:
#AssistedInject
public A(DebugLogger logger, #Assisted int amount)
{
...
}
Create a factory for this class.
#AssistedFactory
public interface MyDataFactory {
A create(int amount);
}
and in your client, you can use:
#Inject MyDataFactory dataFactory;
void setupA(int amount) {
A a = dataFactory.create(config);
// ...
}

ListActivity ormlite problem?

I have a rather large amount of code written when I decided to use
ORMLite.
After reading the doc I found that I would need to extend like:
MyClass extends OrmLiteBaseActivity<DatabaseHelper>
but I have already extended it with ListActivity.
Is is possible to do it without extending OrmLiteBaseActivity?
Tnx in advance.
It is not a requirement to extend OrmLiteBaseActivity. You'll just need to manage more of the utility functions yourself.
Your best option would be to create your own DatabaseHelper inside your activity and to manage how many users there are of it and to discard it when it is done being used. Generally speaking, this is the utility that the OrmLiteBaseActivity gives to you. A mechanism which will manage your database objects for you. It's just a convenience.
Example:
private static Dao<Agent, Object> agentDao = null;
public void someMethod() {
if(agentDao == null){
helper = (MyDBHelper) OpenHelperManager.getHelper(getContext());
try {
agentDao = helper.getAgentDao();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
I had implemented a method to return my DAOs on the class MyDBHelper. Take a look at the ORMLite Android Javadoc as well as the more general ORMLite Core Javadoc. There are lots of good examples out there.
[ #Nick's answer is fine but I thought I'd add more information. ]
ORMLite is missing a OrmLiteBaseListActivity class that was added in version 4.10 -- sorry about the miss. In the meantime, you can easily create your own version of this class by copying the OrmLiteBaseTabActivity class changing the class that it extends from TabActivity to ListActivity. Then change all of your list activity classes to extend this new class. Once 4.10 is out then you can go back and remove the class.
For example:
public abstract class OrmLiteBaseListActivity<H extends OrmLiteSqliteOpenHelper>
extends ListActivity {
// insert contents of the OrmLiteBaseTabActivity class here
}

Categories

Resources