folks! I wrote a Book class which has Chapter objects (a one to many relationship). It implements the method public List chapters(), as stated in the docs. This is the Book.java
#Table(name = "Books")
public class Book extends Model implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "Name", unique = true, onUniqueConflict = Column.ConflictAction.IGNORE)
public String name;
#Column(name = "Sort")
public int sort;
public Book() {
super();
}
public Book(String name, int sort) {
super();
this.name = name;
this.sort = sort;
}
public List<Chapter> chapters() {
return getMany(Chapter.class, "Book");
}
#Override
public String toString() {
return name;
}
}
On the main activity I can get the Chapter objects successfully. However, I have to pass a book object to another activity, which has a fragment, and though I get the object's stated attributes (String name and int sort) it throws an exception when I call to chapters():
Bundle bundle = getIntent().getExtras();
Book book = (Book) bundle.getSerializable("BOOK");
// This line is executed successfully
Log.d("TAGGED", "Recovered book: " + book.name + " has " + book.sort + " as its sort");
// This is the line that throws an exception
ArrayList<Chapter> chapters = book.chapters();
the thrown exception is the following:
05-06 15:21:59.701: E/AndroidRuntime(9647): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.hanovit.libraria/com.hanovit.libraria.chapter.ChapterActivity}: java.lang.NullPointerException
05-06 15:21:59.701: E/AndroidRuntime(9647): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2180)
05-06 15:21:59.701: E/AndroidRuntime(9647): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
05-06 15:21:59.701: E/AndroidRuntime(9647): Caused by: java.lang.NullPointerException
05-06 15:21:59.701: E/AndroidRuntime(9647): at com.activeandroid.query.From.getArguments(From.java:207)
05-06 15:21:59.701: E/AndroidRuntime(9647): at com.activeandroid.query.From.execute(From.java:183)
05-06 15:21:59.701: E/AndroidRuntime(9647): at com.activeandroid.Model.getMany(Model.java:266)
Any ideas what is wrong? Thanks!!!
Submitted an issue in Github and now is fixed (https://github.com/melvin7/ActiveAndroid)
On the other hand, putting into the extras bundle only the ID of the column also works. Then I can get the full object with Model.load(Book.class, id)
Related
I'm trying to get myself into RxJava so I read some posts about it. I think I understood how it works but I would like to submit you a theorical code to ensure my good understanding of the library.
Let's imagine I have an API enabling to retrieve a list of produceur and for each of them the artists they produce, their albums and their songs.
My model would be the following
public class Produceur {
private Integer id;
private String name;
private String pictureUrl;
}
public class Artist {
private Integer id;
private String name;
private String lastname;
private String sceneName;
private String pictureUrl;
}
public class Album {
private Integer id;
private int year;
private String name;
private String style;
private String pictureUrl;
private Integer artistId;
}
public class Song {
private Integer id;
private String title;
private int duration;
private String pictureUrl;
private Integer albumId;
}
With my Retrofist services
#GET("myUrl/produceurs")
Observable<List<ProduceurResponse>> getProduceurs();
#GET("myUrl/produceurs/{produceurId}")
Observable<List<ArtisteResponse>> getArtistForProduceur(#Path("produceurId") Integer produceurId);
#GET("myUrl/picture/{id}")
Observable<ResponseBody> getPicture(#Path("id") Integer id);
And the response objects
public class ProduceurResponse {
private Integer id;
private String name;
private String pictureUrl;
}
public class ArtisteResponse {
private Integer id;
private String name;
private String lastname;
private String sceneName;
private String pictureUrl;
private List<AlbumResponse> albums;
}
public class AlbumResponse {
private Integer id;
private int year;
private String name;
private String style;
private String pictureUrl;
private Integer artistId;
private List<SongResponse> songs;
}
public class SongResponse {
private Integer id;
private String title;
private int duration;
private String pictureUrl;
private Integer albumId;
}
I would like to retrieve all the produceur and for each the artists and save all the images in the local memory in the same sequence.
I thought about the following code where for each produceur we will retrieve the artists, their albums and song and insert them into our base.
And once we've finished retrieveing the produceurs and artists we can download images (we stored in a list every id with a picture).
getProduceurs().flatMap(produceurs -> Observable.from(produceurs))
.doOnNext(produceur -> insertProduceurInBase(produceur))
.subscribe(produceur -> Observable.from(getArtistForProduceur(produceur.getId())
.flatMap(artists -> Observable.from(artists))
.doOnNext(artist -> insertArtistInBase(artist)))
.subscribe(),
e -> showError(e),
() -> Observable.from(listOfIdsToDownload)
.doOnNext(id -> getPicture(id))
.subscribe(response -> createImage(response),
e -> showError(e),
() -> isFinished()
)
);
Would this code work (I think so but I'm not sure) ? Is this the best way to do it ?
Now, what if my getProduceurs service return me a list of ProduceurResponse containing produceur artists ids and I got a service to retrieve the artist profil and another one to retrieve its albums.
public class ProduceurResponse {
private Integer id;
private String name;
private String pictureUrl;
List<Integer> artistsIds;
}
public class ArtisteProfileResponse {
private Integer id;
private String name;
private String lastname;
private String sceneName;
private String pictureUrl;
}
#GET("myUrl/artist/{artistId}")
Observable<List<ArtisteProfileResponse>> getArtistProfile(#Path("artistId") Integer artistId);
#GET("myUrl/artist/{artistId}/detail")
Observable<List<AlbumResponse>> getArtistAlbums(#Path("artistId") Integer artistId);
I could to use the .zip to make the getArtistProfile() and getArtistAlbums() call simultaneous with something like
getProduceurs().flatMap(produceurs -> Observable.from(produceurs))
.doOnNext(produceur -> insertProduceurInBase(produceur))
.subscribe(produceur -> Observable.from(produceur.getArtistIds())
.zip(
getArtistProfile(),
getArtistAlbums(),
(artistProfil, albumList) -> insertArtistInBase()
)
.subscribe(),
e -> showError(e),
() -> Observable.from(listOfIdsToDownload)
.doOnNext(id -> getPicture(id))
.subscribe(response -> createImage(response),
e -> showError(e),
() -> isFinished()
)
);
But I'm really not sure I'm using the zip the right way. Is zip well used in this piece of code ? Would this work ? Is this the best way to do it ?
EDIT
So I tried to implement something similar to my original idea with the google books api.
I've got a Retrofit interface
public interface IBookService {
#GET("volumes?q=robot+subject:fiction")
Observable<BookSearchResult> getFictionAuthors(#Query("category") String key);
#GET("volumes")
Observable<BookSearchResult> getBooksForAuthor(#Query("q") String author, #Query("category") String key);
}
With
public class BookSearchResult {
public List<BookResult> items;
}
public class BookResult {
public String id;
public String selfLink;
public VolumeInfoResult volumeInfo;
public SaleInfoResult saleInfo;
}
And I try to retrieve the books of fiction with a the string robot (getFictionAuthors) with return me a BookSearchResult containing a list of BookResult. For each of the book results I retrieve all the books of the author with getBooksForAuthor. My code is below
Observable<BookResult> observable = mWebService.getFictionAuthors(API_KEY)
.flatMap(new Func1<BookSearchResult, Observable<BookResult>>() {
// Parse the result and build a CurrentWeather object.
#Override
public Observable<BookResult> call(final BookSearchResult data) {
return Observable.from(data.items);
}
})
.concatMap(new Func1<BookResult, Observable<BookSearchResult>>() {
// Parse the result and build a CurrentWeather object.
#Override
public Observable<BookSearchResult> call(final BookResult data) {
return mWebService.getBooksForAuthor("=inauthor:" + data.volumeInfo.authors.get(0), API_KEY);
}
})
.flatMapIterable(new Func1<BookSearchResult, List<BookResult>>() {
// Parse the result and build a CurrentWeather object.
#Override
public List<BookResult> call(final BookSearchResult data) {
return data.items;
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<BookResult>() {
#Override
public void onNext(final BookResult book) {
Log.e("Book","Book is " + book.volumeInfo.title + " written by " + book.volumeInfo.authors.get(0));
}
#Override
public void onCompleted() {
Log.e("Book","Book list completed");
}
#Override
public void onError(final Throwable error) {
Log.e("Book","Book list error");
}
}
This code is working but there is something weird that I don't understand. In my logs I have at first the return from the getBooksForAuthor request for the first author and then the log from each book of that author. After that I've got the result of the request for the second author and part of the log from his books. Follow the results of the request of the other authors and then the end of the list of books for the second author and the list of books for all the other authors.
To illustrate it, my logs looks like
- > Return from request for Author 1
- > Book 1 from author 1
...
- > Book 10 from author 1
- > Return from request for Author 2
- > Book 1 from author 2
...
- > Book 5 from author 2
- > Return from request for Author 3
- > Return from request for Author 4
...
- > Return from request for Author 10
- > Book 6 from author 2
..
- > Book 10 from author 2
- > Book 1 from author 3
..
- > Book 10 from author 3
...
- > Book 1 from author 10
..
- > Book 10 from author 10
When I expected
- > Return from request for Author 1
- > Book 1 from author 1
...
- > Book 10 from author 1
- > Return from request for Author 2
- > Book 1 from author 2
...
- > Book 10 from author 2
...
- > Return from request for Author 10
- > Book 1 from author 10
...
- > Book 10 from author 10
Does someone have an explanation or understand what I'm missing ?
You should avoid nested subscription (check out the flatmap operator). It's offen an indicator of a code smell.
To avoid nested subscription, you can use the operator flatMap (do not garanti resulting events order) or concatMap (garanti resulting events order).
I notice too that you're using another Observable in the completed callback : you can concat observables in this case.
So the rework of your code using this sort of operators :
getProduceurs().flatMap(produceurs -> Observable.from(produceurs))
.doOnNext(produceur -> insertProduceurInBase(produceur))
// call getArtistForProduceur and emit results in order
.concatMap(produceur -> getArtistForProduceur(produceur.getId()))
// emits items of the list
.flatMapIterable(artists -> artists)
.doOnNext(artist -> insertArtistInBase(artist)))
// don't care about elements. But will wait for the completion of the previous observable
.ignoreElements()
// perform jobs after the previous observable complete
.concatWith(Observable.from(listOfIdsToDownload)
.doOnNext(id -> getPicture(id))
.doOnNext(response -> createImage(response)))
// show an error if an error occur in the downstream
.doOnError(e -> showError(e))
// call isFinished when everything is finished.
.doOnCompleted(() -> isFinished())
.subscribe()
I have an android application where user will input data about car. When they are done, they will press a button to sync their data. That is, on pressing the button it will call a rest webservice (Jersey) to synchronize the data with the server which will store the data in mysql.
I'm using hibernate/JPA/spring 4/jdk1.8 for the server side.
So basically both entity (android and server) should be same so that synchronization can take place. I'm having an issue when I'm synchronizing date. Both entity is using the same library java.util.date. Am getting this error :
Can not deserialize instance of java.util.Date out of START_OBJECT token
If both side is string , I don't have any issue. I also try to put the date field to type String on server, and on the android to type Date. It throws this exception:
Can not deserialize instance of java.lang.String out of START_OBJECT token
Any solution for this? I think the java.util on android and the one use in server side are not same.
Android Code: (am using google api client for json)
public class CarClient extends AbstractGoogleJsonClient {
public static final String DEFAULT_ROOT_URL = "";
public static final String DEFAULT_SERVICE_PATH = "..";
public static final String TOKEN_URL = DEFAULT_ROOT_URL + "....";
public static final String DEFAULT_BASE_URL = DEFAULT_ROOT_URL + DEFAULT_SERVICE_PATH;
public CarClient(com.google.api.client.http.HttpTransport transport,
com.google.api.client.json.JsonFactory jsonFactory,
com.google.api.client.http.HttpRequestInitializer httpRequestInitializer) {
this(new Builder(transport, jsonFactory, httpRequestInitializer));
}
public class MergeCar extends
CarRequest<Car> {
public MergeCar(Car cars) throws IOException {
super(CarClient.this,
HttpMethods.PUT,
DEFAULT_BASE_URL + "car_sync",
cars,
Car.class);
}
}
}
Android Car Entity:
#DatabaseTable(tableName = "CAR_TABLE")
public class Car implements Serializable {
private static final long serialVersionUID = 1L;
#Key("carId")
#DatabaseField(id = true, unique = true)
private String CAR_ID;
#Key("carName")
#DatabaseField
private String CAR_NAME;
#Key("createdDate")
#DatabaseField
private DATE CREATED_DATE;
//Getters and Setters .....
}
I followed the tutorial given here to develop my code.
Since the whole code is given at the above link as well as here I wont type it all here to avoid cluttering.
On the app engine app, I have a class like this :
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
#Entity
public class Quote {
#Id
Long id;
String who;
String what;
public Quote() {}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getWho() {
return who;
}
public void setWho(String who) {
this.who = who;
}
public String getWhat() {
return what;
}
public void setWhat(String what) {
this.what = what;
}
}
And since I automatically generated my Endpoints class from the above class, it looks (partly) like this (As opposed to the one shown in the example)
#Api(
name = "quoteApi",
version = "v1",
resource = "quote",
namespace = #ApiNamespace(
ownerDomain = "QuoteApi.com",
ownerName = "QuoteApi.com",
packagePath = ""
)
)
public class QuoteEndpoint {
private static final Logger logger = Logger.getLogger(QuoteEndpoint.class.getName());
private static final int DEFAULT_LIST_LIMIT = 20;
static {
// Typically you would register this inside an OfyServive wrapper. See: https://code.google.com/p/objectify-appengine/wiki/BestPractices
ObjectifyService.register(Quote.class);
}
/**
* Returns the {#link Quote} with the corresponding ID.
*
* #param id the ID of the entity to be retrieved
* #return the entity with the corresponding ID
* #throws NotFoundException if there is no {#code Quote} with the provided ID.
*/
#ApiMethod(
name = "get",
path = "quote/{id}",
httpMethod = ApiMethod.HttpMethod.GET)
public Quote get(#Named("id") Long id) throws NotFoundException {
logger.info("Getting Quote with ID: " + id);
Quote quote = ofy().load().type(Quote.class).id(id).now();
if (quote == null) {
throw new NotFoundException("Could not find Quote with ID: " + id);
}
return quote;
}
}
Now in my Android App, I do this. This goes in an AsyncTask, doinbackground
try {
Long id = Long.getLong("9649299534313129");
Quote q1= endpointBldr.get(id).execute();
}catch (IOException e){
return e.getMessage();
}
I should mention, the "id" that I am asking to get, is accurate, and exists in the datastore.
Also, the 'list' and 'insert' and 'delete' ApiMethods work just fine. Just this 'get' is giving me trouble.
All of which are auto-generated methods.
And finally the Error I get , that stops my app is:
/com.blah.QuoteApi E/AndroidRuntime﹕ FATAL EXCEPTION: AsyncTask #1
Process: com.blah.QuoteApi, PID: 16820
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:300)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.NullPointerException: Required parameter id must be specified.
at com.google.api.client.repackaged.com.google.common.base.Preconditions.checkNotNull(Preconditions.java:208)
at com.google.api.client.util.Preconditions.checkNotNull(Preconditions.java:140)
at com.blah.QuoteApi$Get.<init>(Quote.java:151)
at com.blah.QuoteApi.QuoteApi.get(QuoteApi.java:129)
at com.blah.QuoteApi.EndpointsAsyncTask.doInBackground(EndpointsAsyncTask.java:72)
at com.blah.QuoteApi.EndpointsAsyncTask.doInBackground(EndpointsAsyncTask.java:22)
at android.os.AsyncTask$2.call(AsyncTask.java:288)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Can someone please help me resolve this. Not sure where it is getting a null pointer , I give the assigned Long id, as argument to the get.
Also, should point out, the ApiMethod works as it should in the online Google Api Explorer (AppSpot)
I must admit, it was a silliest mistake.
Long.getLong is not the right one to use. Long.parseLong should have been the one.
getLong was obviously gonna give null in this case.
I'm coding a RESTful client for my AndroidApp. My rest webservice returns me a json and I pasrse it with springfreamwork into java class members. In this way my code is ok.
I need to pass parameters from main activity to another one, so I implemented that class(Clinica.class see below) as PARCELABLE following the guide lines. Now app returns me this error
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: No suitable constructor found for type [simple type, class it.stasbranger.clinigomobile.model.Clinica]: can not instantiate from JSON object (need to add/enable type information?)
at [Source: libcore.net.http.ChunkedInputStream#41346758; line: 1, column: 3]; nested exception is org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple type, class it.stasbranger.clinigomobile.model.Clinica]: can not instantiate from JSON object (need to add/enable type information?)
at [Source: libcore.net.http.ChunkedInputStream#41346758; line: 1, column: 3]
this is my Clinica.class
public class Clinica implements Parcelable {
#JsonProperty
private Integer idclinica;
#JsonProperty
private String nome;
#JsonProperty
private Long dataRegistrazione;
#JsonProperty
private Long version;
#JsonProperty
private String referente;
#JsonProperty
private String indirizzo;
#JsonProperty
private String cap;
#JsonProperty
private String telefono;
#JsonProperty
private String email;
#JsonProperty
private String sitoWeb;
#JsonProperty
private Boolean abilitata;
#JsonProperty
private Integer valutazione;
#JsonProperty
private Double rank;
#JsonProperty
private String nomeFatturazione;
//getters and setters
.......
public Clinica (Parcel p){
boolean[] booleans = new boolean[1];
this.cap=p.readString();
this.email=p.readString();
this.indirizzo=p.readString();
this.nome=p.readString();
this.nomeFatturazione=p.readString();
this.referente=p.readString();
this.sitoWeb=p.readString();
this.telefono=p.readString();
this.idclinica=p.readInt();
this.valutazione=p.readInt();
this.dataRegistrazione=p.readLong();
this.version=p.readLong();
this.rank=p.readDouble();
p.readBooleanArray(booleans);
this.abilitata=booleans[0];
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
boolean[] booleans = new boolean[1];
Arrays.fill(booleans, abilitata);
dest.writeString(cap);
dest.writeString(email);
dest.writeString(indirizzo);
dest.writeString(nome);
dest.writeString(nomeFatturazione);
dest.writeString(referente);
dest.writeString(sitoWeb);
dest.writeString(telefono);
dest.writeInt(idclinica);
dest.writeInt(valutazione);
dest.writeLong(dataRegistrazione);
dest.writeLong(version);
dest.writeDouble(rank);
dest.writeBooleanArray(booleans);
}
public static final Parcelable.Creator<Clinica> CREATOR = new Creator<Clinica>() {
public Clinica[] newArray(int size) {
return new Clinica[size];
}
public Clinica createFromParcel(Parcel source) {
return new Clinica(source);
}
};
}
and this is my Async call to make request
......
Clinica data[] = restTemplate.getForObject(urls[0], Clinica[].class, vars);
Any suggestions? Thanks in advance.
The answer is simple: remove the constructor (also in parent class, if there is any inside) and everything will work! Maybe there is some annotation like #JsonIgnore, but it not work with constructors.
I have these classes:
public class AllegationContent {
#DatabaseField(generatedId = true)
protected long id;
#DatabaseField(canBeNull = true, foreign = true)
private AllegationItem allegationItem;
#DatabaseField()
protected String content;
...
}
And:
public class AllegationItem {
#DatabaseField(generatedId = true)
protected long id;
#ForeignCollectionField(eager = false)
protected ForeignCollection<AllegationContent> allegationContent;
...
}
How can I persist both?
I am trying this:
allegationContentDao = getAllegationContentDao();
AllegationContent allegationContent = new AllegationContent();
allegationContent.setContent("allegation content");
allegationContentDao.create(allegationContent);
allegationItemDao = getAllegationItemDao();
AllegationItem allegationItem = new AllegationItem();
AllegationContent allegationContent2 = allegationContentDao.queryForSameId(allegationContent); //is that wrong?
allegationItem.getAllegationContent().add(allegationContent2);
allegationItemDao.create(allegationItem);
but I got this error:
04-24 10:41:57.128: ERROR/AndroidRuntime(802): java.lang.RuntimeException: Unable to start activity ComponentInfo{br.com.project/br.com.project.DaoTestActivity}: java.lang.NullPointerException
I solved. The table can't create because don't have atributes, only id (what's another problem). The NPE happen because getAllegationContent return null, so first I need create a ForeignCollection list, set using setAllegationContent and then get.