I'm new to Room and i'm trying to query my database to get a row from it. I attempted doing so by querying it with the primary key which is id but the problem is i don't know how to return the target object from the repository.
This is the Dao
#Query("SELECT * FROM targets WHERE id = :id LIMIT 1")
Targets findTargetById(int id);
THis is the Repository class
public Targets findTarget (int id) {
new findTargetByIDAsyncTask(mTargetsDao).execute(id);
}
private static class findTargetByIDAsyncTask extends AsyncTask<Integer, Void, Targets> {
private TargetsDao mAsyncTaskDao;
findTargetByIDAsyncTask(TargetsDao dao) {
mAsyncTaskDao = dao;
}
#Override
protected Targets doInBackground(Integer... integers) {
return mAsyncTaskDao.findTargetById(integers[0]);
}
#Override
protected void onPostExecute(Targets targets) {
super.onPostExecute(targets);
}
}
You have two ways to return a result.
The first way is to call AsyncTask.get() method, but it will still hold a MainThread what leads to ANR if a task will longer than 5 seconds:
public Targets findTarget (int id) {
return new findTargetByIDAsyncTask(mTargetsDao).execute(id).get();
}
The second way is more complicated but it will not hold the MainThread. You should add a Callback class:
public interface Callback {
void onSuccess(Targets targets);
}
Each method of your repository will look like that:
public void findTarget (Callback callback, int id) {
new findTargetByIDAsyncTask(mTargetsDao, callback).execute(id);
}
And AsynTask will look like that:
private static class FindTargetByIDAsyncTask extends AsyncTask<Integer, Void, Targets> {
private final TargetsDao mAsyncTaskDao;
private final Callback callback;
FindTargetByIDAsyncTask(TargetsDao dao, Callback callback) {
mAsyncTaskDao = dao;
this.callback = callback;
}
#Override
protected Targets doInBackground(Integer... integers) {
return mAsyncTaskDao.findTargetById(integers[0]);
}
#Override
protected void onPostExecute(Targets targets) {
callback.onSuccess(targets);
}
}
The point is to get the data/object from a background thread. You can use Android's AsyncTask or a ExecutorService. A simple example is if you want to get a String of a user name the method will be:
private String getName() {
String name = null;
try {
name = Executors.newSingleThreadExecutor().submit(() ->
userDao.fetchUserName()).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return name;
}
Related
I've tried using the below code to load some data from my database and display them in an activity:
PhoneNumberViewModel phoneNumberViewModel =
new ViewModelProvider(WorksideContactCard.this).get(PhoneNumberViewModel.class);
phoneNumberViewModel
.getPhoneNumbersById(contactID)
.observe(this,
numbers -> {
phoneNumberList = numbers;
});
To be precise, I needn't observe the data changing, as the data will be refreshed if the activity is resumed. But the above code freezes my application, although I am accessing the DB using an AsyncTask in my other class.
Why could this be so?
EDIT:
PhoneNumberViewModel.class:
public class PhoneNumberViewModel extends AndroidViewModel {
private PhoneNumberRepository phoneNumberRepository;
private LiveData<List<PhoneNumber>> allPhoneNumbers;
public PhoneNumberViewModel(#NonNull Application application) {
super(application);
phoneNumberRepository = new PhoneNumberRepository(application);
allPhoneNumbers = phoneNumberRepository.getAllPhoneNumbersLive();
}
public void insert(PhoneNumber phoneNumber) {
System.out.println("[PhoneNumberRepository] Adding new phoneNumber");
phoneNumberRepository.insert(phoneNumber);
}
public void update(PhoneNumber phoneNumber) {
phoneNumberRepository.update(phoneNumber);
}
public void delete(PhoneNumber phoneNumber) {
phoneNumberRepository.delete(phoneNumber);
}
public LiveData<List<PhoneNumber>> getAllPhoneNumbers() {
return allPhoneNumbers;
}
public LiveData<List<PhoneNumber>> getPhoneNumbersById(long contactId)
throws ExecutionException, InterruptedException {
return phoneNumberRepository.getPhoneNumbersByContactId(contactId);
}
PhoneNumberRepository.class
public class PhoneNumberRepository {
private PhoneNumberDao phoneNumberDao;
private LiveData<List<PhoneNumber>> allPhoneNumbers;
private LiveData<List<PhoneNumber>> phoneNumbersByIdList;
public PhoneNumberRepository(Application application) {
WorksideDatabase database = WorksideDatabase.getInstance(application);
phoneNumberDao = database.phoneNumberDao();
allPhoneNumbers = phoneNumberDao.getAllPhoneNumbers();
}
...
public LiveData<List<PhoneNumber>> getAllPhoneNumbersLive() {
return allPhoneNumbers;
}
public void deleteAllPhoneNumbers() {
new DeleteAllPhoneNumbersAsyncTask(phoneNumberDao).execute();
}
public LiveData<List<PhoneNumber>> getPhoneNumbersByContactId(long id)
throws ExecutionException, InterruptedException {
return new SelectPhoneNumberByIdAsyncTask(phoneNumberDao, id).get();
}
private static class SelectPhoneNumberByIdAsyncTask
extends AsyncTask<Long, Void, LiveData<List<PhoneNumber>>> {
private PhoneNumberDao phoneNumberDao;
private Long ID;
private SelectPhoneNumberByIdAsyncTask(PhoneNumberDao phoneNumberDao, Long contactId) {
this.phoneNumberDao = phoneNumberDao;
ID = contactId;
}
#Override
protected LiveData<List<PhoneNumber>> doInBackground(Long... contactId) {
ID = contactId[0];
return phoneNumberDao.getPhoneNumbersById(ID);
}
}
...
}
PhoneNumberDao.class:
#Dao
public interface PhoneNumberDao {
#Insert
void insert(PhoneNumber phoneNumber);
#Update
void update(PhoneNumber phoneNumber);
#Delete
void delete(PhoneNumber phoneNumber);
// Probably uneeded method
#Query("DELETE FROM phone_numbers_table")
void deleteAllPhoneNumbers();
// Delete entry/entries by ID
#Query("DELETE FROM phone_numbers_table WHERE id = :phoneNumberId")
void deleteByPhoneNumberId(long phoneNumberId);
// Retrieve entry/entries by contact ID
// #Query("SELECT * FROM phone_numbers_table WHERE contact_id = :contactId")
// List<PhoneNumber> getPhoneNumbersById(long contactId);
// Retrieve all saved phone numbers in LiveData format
#Query("SELECT * FROM phone_numbers_table")
LiveData<List<PhoneNumber>> getAllPhoneNumbers();
#Query("SELECT * FROM phone_numbers_table WHERE contact_id = :contactId")
LiveData<List<PhoneNumber>> getPhoneNumbersById(long contactId);
#Query("SELECT * FROM phone_numbers_table")
List<PhoneNumber> getAll();
}
return new SelectPhoneNumberByIdAsyncTask(phoneNumberDao, id).get();
this here is your problem ,.get() call blocks the main thread
I would suggest switching to Kotlin and using Coroutines or here you can handle this using callbacks and not using AsyncTask.get() which blocks the main thread.Also BTW Async Task is going to be deprecated last I heard ,So keep that in mind too.
This is one solution the problem
public class Repository implements
SelectPhoneNumberByIdAsyncTask.OnPhoneNumberFound {
#Override
public void onPhoneNumberFound(LiveData<List<Object>> list) {
//you can get the data you want here
}
}
class SelectPhoneNumberByIdAsyncTask extends AsyncTask<Long, Void,
LiveData<List<PhoneNumber//Correct this>>> {
interface OnPhoneNumberFound{
void onPhoneNumberFound(LiveData<List<PhoneNumberDao>> list);
}
OnPhoneNumberFound mListener;
private PhoneNumberDao phoneNumberDao;
private Long ID;
private SelectPhoneNumberByIdAsyncTask(Object phoneNumberDao, Long contactId) {
this.phoneNumberDao = phoneNumberDao;
ID = contactId;
}
#Override
protected LiveData<List<Object>> doInBackground(Long... contactId) {
ID = contactId[0];
mListener.onPhoneNumberFound(phoneNumberDao.getPhoneNumbersById(ID));
return null;
}
}
use .execute() call now instead of .get()
I successfully created a multiple photo uploader. It works as planned. Except I would like the ability to cancel the upload if I need to.
I know I can call cancel() on an AsyncTask if I need to cancel it.
What I would like to do is keep a reference to the AsyncTask in my photo upload object, so it can be referenced to cancel if necessary.
Doing the below works, but I can't get a reference to the async task.
UploadItem item = new UploadItem();
item.setItemFilePath(imageLocation);
item.setTaskIdentifier(UUID.randomUUID().toString());
new UploadTask().execute(item);
But I want to keep a reference to the task should I need to cancel it. So I did something like this but it crashes the app.
//Create new upload item
UploadItem item = new UploadItem();
item.setItemFilePath(imageLocation);
item.setTaskIdentifier(UUID.randomUUID().toString());
item.setAsyncTask(new UploadTask());
//Start the upload
item.getAsyncTask().execute(this);
How do you keep a reference to an Async Task in a custom object?
My custom upload object.
public class UploadItem implements Serializable {
public AsyncTask mAsyncTask;
public String itemFilePath = "";
public String taskIdentifier = "";
public boolean isUploading = false;
public AsyncTask getAsyncTask() {
Log.d("Upload Item", "Getting upload task");
return mAsyncTask;
}
public void setAsyncTask(AsyncTask asyncTask) {
Log.d("Upload Item", "Setting upload task");
this.mAsyncTask = asyncTask;
}
public String getItemFilePath() {
return itemFilePath;
}
public void setItemFilePath(String itemFilePath) {
this.itemFilePath = itemFilePath;
}
public boolean isUploading() {
return isUploading;
}
public void setUploading(boolean uploading) {
isUploading = uploading;
}
public String getTaskIdentifier() {
return taskIdentifier;
}
public void setTaskIdentifier(String taskIdentifier) {
this.taskIdentifier = taskIdentifier;
}
}
The background task:
public class UploadTask extends AsyncTask<UploadItem, Integer, HashMap<String, String>> {
#Override
protected void onPreExecute() { }
#Override
protected void onProgressUpdate(Integer... values) { }
#Override
protected void onPostExecute(HashMap<String, String> r) { }
}
I have these two methods declared:
private Result mResult;
private void setResult(Result result){
this.mResult = result;
}
private Result getResult(){
new Executor(new OnResultListener() {
#Override
public void onResult(Result result) {
setResult(result);
}
}).execute();
return mResult;
}
Im using an interface while an AsyncTask is executing. What my problem is, is that I want to return the Result object of the onResult method as an object to the getResult() method.
As shown above, I tried to set it through a setter, but it seems that this is not working.
How can I succeed that?
Thanks in advance!
You have two options here. The bad one is to wait until the new thread will finish. let's don't do that). the better way is to use a callback for:
public static interface OnResultCallback {
void onResult(Result result);
}
private void getResult(final OnResultCallback callback){
new Executor(new OnResultListener() {
#Override
public void onResult(Result result) {
setResult(result);
callback.onResult(result);
}
}).execute();
}
You could provide an instance of OnResultListener as part of the constructor of your AsyncTask, which the caller has to implement. E.g.
private Result mResult;
private OnResultListener mListener;
private void setResult(Result result, OnResultListener listener){
this.mResult = result;
mListener = listener;
}
private Result getResult(){
new Executor(new OnResultListener() {
#Override
public void onResult(Result result) {
if (mListener != null) {
mListener.onResult(result);
}
setResult(result);
}
}).execute();
return mResult;
}
or you could directly provide mListener to new Executor
public class CustomOnResultListener extends OnResultListener{
Callback callback ;
public CustomOnResultListener(Callback callback){}
this.callback =callback; // use this callback to send result
}
public interface Callback{public void onCallback(Result result);};
I'm using a multiplayer Game Client that's called AppWarp (http://appwarp.shephertz.com), where you can add event listeners to be called back when event's happen, let's assume we'll be talking about the Connection Listener, where you need to implement this interface:
public interface ConnectionRequestListener {
void onConnectDone(ConnectEvent var1);
void onDisconnectDone(ConnectEvent var1);
void onInitUDPDone(byte var1);
}
My goal here is to mainly create a Reactive version of this client to be used in my Apps Internally instead of using the Client itself directly (I'll also rely on interfaces later instead of just depending on the WarpClient itself as in the example, but that's not the important point, please read my question at the very end).
So what I did is as follows:
1) I introduced a new event, named it RxConnectionEvent (Which mainly groups Connection-Related events) as follows:
public class RxConnectionEvent {
// This is the original connection event from the source client
private final ConnectEvent connectEvent;
// this is to identify if it was Connection / Disconnection
private final int eventType;
public RxConnectionEvent(ConnectEvent connectEvent, int eventType) {
this.connectEvent = connectEvent;
this.eventType = eventType;
}
public ConnectEvent getConnectEvent() {
return connectEvent;
}
public int getEventType() {
return eventType;
}
}
2) Created some event types as follows:
public class RxEventType {
// Connection Events
public final static int CONNECTION_CONNECTED = 20;
public final static int CONNECTION_DISCONNECTED = 30;
}
3) Created the following observable which emits my new RxConnectionEvent
import com.shephertz.app42.gaming.multiplayer.client.WarpClient;
import com.shephertz.app42.gaming.multiplayer.client.events.ConnectEvent;
import rx.Observable;
import rx.Subscriber;
import rx.functions.Action0;
import rx.subscriptions.Subscriptions;
public class ConnectionObservable extends BaseObservable<RxConnectionEvent> {
private ConnectionRequestListener connectionListener;
// This is going to be called from my ReactiveWarpClient (Factory) Later.
public static Observable<RxConnectionEvent> createConnectionListener(WarpClient warpClient) {
return Observable.create(new ConnectionObservable(warpClient));
}
private ConnectionObservable(WarpClient warpClient) {
super(warpClient);
}
#Override
public void call(final Subscriber<? super RxConnectionEvent> subscriber) {
subscriber.onStart();
connectionListener = new ConnectionRequestListener() {
#Override
public void onConnectDone(ConnectEvent connectEvent) {
super.onConnectDone(connectEvent);
callback(new RxConnectionEvent(connectEvent, RxEventType.CONNECTION_CONNECTED));
}
#Override
public void onDisconnectDone(ConnectEvent connectEvent) {
super.onDisconnectDone(connectEvent);
callback(new RxConnectionEvent(connectEvent, RxEventType.CONNECTION_DISCONNECTED));
}
// not interested in this method (for now)
#Override
public void onInitUDPDone(byte var1) { }
private void callback(RxConnectionEvent rxConnectionEvent)
{
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(rxConnectionEvent);
} else {
warpClient.removeConnectionRequestListener(connectionListener);
}
}
};
warpClient.addConnectionRequestListener(connectionListener);
subscriber.add(Subscriptions.create(new Action0() {
#Override
public void call() {
onUnsubscribed(warpClient);
}
}));
}
#Override
protected void onUnsubscribed(WarpClient warpClient) {
warpClient.removeConnectionRequestListener(connectionListener);
}
}
4) and finally my BaseObservable looks like the following:
public abstract class BaseObservable<T> implements Observable.OnSubscribe<T> {
protected WarpClient warpClient;
protected BaseObservable (WarpClient warpClient)
{
this.warpClient = warpClient;
}
#Override
public abstract void call(Subscriber<? super T> subscriber);
protected abstract void onUnsubscribed(WarpClient warpClient);
}
My question is mainly: is my implementation above correct or should I instead create separate observable for each event, but if so, this client has more than 40-50 events do I have to create separate observable for each event?
I also use the code above as follows (used it in a simple "non-final" integration test):
public void testConnectDisconnect() {
connectionSubscription = reactiveWarpClient.createOnConnectObservable(client)
.subscribe(new Action1<RxConnectionEvent>() {
#Override
public void call(RxConnectionEvent rxEvent) {
assertEquals(WarpResponseResultCode.SUCCESS, rxEvent.getConnectEvent().getResult());
if (rxEvent.getEventType() == RxEventType.CONNECTION_CONNECTED) {
connectionStatus = connectionStatus | 0b0001;
client.disconnect();
} else {
connectionStatus = connectionStatus | 0b0010;
connectionSubscription.unsubscribe();
haltExecution = true;
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
fail("Unexpected error: " + throwable.getMessage());
haltExecution = true;
}
});
client.connectWithUserName("test user");
waitForSomeTime();
assertEquals(0b0011, connectionStatus);
assertEquals(true, connectionSubscription.isUnsubscribed());
}
I suggest you avoid extending the BaseObservable directly since it's very error prone. Instead, try using the tools Rx itself gives you to create your observable.
The easiest solution is using a PublishSubject, which is both an Observable and a Subscriber. The listener simply needs to invoke the subject's onNext, and the subject will emit the event. Here's a simplified working example:
public class PublishSubjectWarpperDemo {
public interface ConnectionRequestListener {
void onConnectDone();
void onDisconnectDone();
void onInitUDPDone();
}
public static class RxConnectionEvent {
private int type;
public RxConnectionEvent(int type) {
this.type = type;
}
public int getType() {
return type;
}
public String toString() {
return "Event of Type " + type;
}
}
public static class SimpleCallbackWrapper {
private final PublishSubject<RxConnectionEvent> subject = PublishSubject.create();
public ConnectionRequestListener getListener() {
return new ConnectionRequestListener() {
#Override
public void onConnectDone() {
subject.onNext(new RxConnectionEvent(1));
}
#Override
public void onDisconnectDone() {
subject.onNext(new RxConnectionEvent(2));
}
#Override
public void onInitUDPDone() {
subject.onNext(new RxConnectionEvent(3));
}
};
}
public Observable<RxConnectionEvent> getObservable() {
return subject;
}
}
public static void main(String[] args) throws IOException {
SimpleCallbackWrapper myWrapper = new SimpleCallbackWrapper();
ConnectionRequestListener listner = myWrapper.getListener();// Get the listener and attach it to the game here.
myWrapper.getObservable().observeOn(Schedulers.newThread()).subscribe(event -> System.out.println(event));
listner.onConnectDone(); // Call the listener a few times, the observable should print the event
listner.onDisconnectDone();
listner.onInitUDPDone();
System.in.read(); // Wait for enter
}
}
A more complex solution would be to use one of the onSubscribe implementations to create an observable using Observable.create(). For example AsyncOnSubscibe. This solution has the benefit of handling backperssure properly, so your event subscriber doesn't become overwhelmed with events. But in your case, that sounds like an unlikely scenario, so the added complexity is probably not worth it.
I have RPC service that returns an object of type GameEvent that
extends from Event (abstract). When I get the object on the client
side, all the properties inherited from Event (eventId, copyEventId,
gameTimeGMT) are set to null whereas on the server side, these
properties have values.
public class GameEvent extends Event implements IsSerializable {
private String homeTeam;
private String awayTeam;
public GameEvent() {
}
}
// Annotation are from the twig-persist framework which should not
// impact the serialization process.
public abstract class Event implements IsSerializable {
#Key
protected String eventId;
#Index
protected String copyEventId;
protected Date gameTimeGMT;
protected Event() {
}
}
Update: I use the gwt-platform framework (MVP implementation). Here is the call to the service client side. The result.getGE() returns the GameEvent object but with null properties.
dispatcher.execute(
new GetFormattedEventAction(
id),
new AsyncCallback<GetFormattedEventResult>() {
#Override
public void onFailure(Throwable caught) {
caught.printStackTrace();
}
#Override
public void onSuccess(
GetFormattedEventResult result) {
FormattedGameEvent formattedGameEvent = new FormattedGameEvent(
result.getGE());
}
});
The action handler:
public class GetFormattedEventActionHandler implements
ActionHandler<GetFormattedEventAction, GetFormattedEventResult> {
#Override
public GetFormattedEventResult execute(GetFormattedEventAction action,
ExecutionContext context) throws ActionException {
GameEvent gameEvent = null;
QueryResultIterator<GameEvent> rs = datastore.find().type(
GameEvent.class).addFilter("copyEventId", FilterOperator.EQUAL,
action.getEventId()).returnResultsNow();
if (rs.hasNext()) {
gameEvent = rs.next();
}
return new GetFormattedEventResult(gameEvent);
}
}
The Result:
public class GetFormattedEventResult implements Result {
private GameEvent e;
#SuppressWarnings("unused")
private GetFormattedEventResult() {
}
public GetFormattedEventResult(GameEvent gameEvent) {
e = gameEvent;
}
public GameEvent getGE() {
return e;
}
}
I'll try to take a stab.
Verify that the Event class is in the GWT serialization whitelist (the .gwt.rpc file that is generated for each service interface). If it's not, you may have to trick GWT into adding it.