I am using TapJoys offerwall to give the users a way to get "free" game coins for stuff, however, the offerwall never makes the callback when i return to my activity.
I think i have the relevant implements
public class MainActivity extends BaseGameActivity implements ITimerCallback,
TapjoyAwardPointsNotifier, TapjoyEarnedPointsNotifier, TapjoyNotifier {
and i do connect to the server.
// Enables logging to the console.
TapjoyLog.enableLogging(true);
// Connect with the Tapjoy server.
TapjoyConnect.requestTapjoyConnect(getApplicationContext(), appID, secretKey);
TapjoyConnect.getTapjoyConnectInstance().setEarnedPointsNotifier(MainActivity.this);
I call the offerwall like this
TapjoyConnect.getTapjoyConnectInstance().showOffersWithCurrencyID(
currencyID, false);
And my callback methods that never gets called
#Override
public void earnedTapPoints(int amount) {
displayText = "Earned points: " + amount;
Log.d(TAG, displayText);
gameToast(displayText);
}
#Override
public void getUpdatePoints(String currencyName, int pointTotal) {
displayText = "Earned points: " + pointTotal;
Log.d(TAG, displayText);
gameToast(displayText);
}
#Override
public void getUpdatePointsFailed(String error) {
Log.d(TAG, error);
gameToast(displayText);
}
#Override
public void getAwardPointsResponse(String s, int i) {
displayText = s + i;
Log.d(TAG, displayText);
gameToast(displayText);
}
#Override
public void getAwardPointsResponseFailed(String s) {
Log.d(TAG, s);
gameToast(s);
}
None of the toasts get shown and there is nothing in the log...
in my game I did it to make it work
#Override
protected void onResume()
{
TapjoyConnect.getTapjoyConnectInstance().getTapPoints(this);
super.onResume();
}
https://knowledge.tapjoy.com/en/integration/managed-currency
NOTE: It is best to call getTapPoints(...) on application startup and resume. The callback notifiers for SPEND and AWARD points also return the total virtual currency balance of the user, so use these to update the total amount of currency the user has.
Related
I would like to use PublishProcessor that would work like that:
is fed with some init data (randomly when user scrolls view - Integer in the example),
it does some background job (downloads data based on the init data)
when finish downloading notifies about newly downloaded data type (String in the example)
It should be all the time ready to receive and handle new init data.
I've prepared simple code to test it but it doesn't work.
PublishProcessor processor = PublishProcessor.create();
processor.map(new Function<Integer, String>() {
#Override
public String apply(Integer o) throws Exception {
Thread.sleep(1000);
DevLog.d("test","executing "+o);
return String.valueOf(o)+"aaa";
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<String>() {
Subscription sub;
#Override
public void onSubscribe(org.reactivestreams.Subscription s) {
DevLog.d("test","onsubscribe "+s);
sub = s;
sub.request(1);
}
#Override
public void onNext(String s) {
DevLog.d("test","next "+s);
sub.request(1);
}
#Override
public void onError(Throwable t) {
DevLog.d("test","error "+t);
}
#Override
public void onComplete() {
DevLog.d("test","complete");
}
});
processor.onNext(666);
processor.onNext(123);
processor.onNext(456);
DevLog.d("test","nextsent");
All I get in the logcat is:
onsubscribe 0
nextsent
I would rather expect:
nextsent
executing 666
executing 123
executing 456
next 666aaa
next 123aaa
next 456aaa
Before marking the question as duplicate, read my issue first.
I have map fragment that execute a lot of process using firebase realtime database and the issue that I'm facing is that some callbacks do intensive processing which makes the map very laggy and sometimes crashes the app, so I made a new thread to execute that callback and inside it there's some code that update the UI, so I run it using runOnUiThread.
Everything worked fine once I open the fragment for the first time, after I press back and reopen it again, getActivity keeps coming null always!
I tried this famous workaround
FragmentActivity mActivity;
#Override
public void onAttach(Context context) {
super.onAttach(context);
mActivity = (FragmentActivity) context;
}
#Override
public void onDetach() {
super.onDetach();
mActivity = null;
}
And used mActivity instead of getActivity , but it didn't work..! mActivity keeps coming as null also.!
I don't know why this error happens! when I open the fragment again it added again on the backstack and attached again so the activity shouldn't be null, and why it worked when added on the first time launch only?!
The Code
void updateStudents() {
if (isRecursionEnable) {
return;
}
isRecursionEnable = true;
thread = new Thread(new Runnable() {
#Override
public void run() {
if (!thread.isInterrupted()) {
studentQuery.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(final DataSnapshot snapshot, String s) {
Student_User.add(snapshot.getValue(TestUserStudent.class));
if (getActivity() != null) { // NPE
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
showAllMarkers(snapshot); // show custom markers on the map
}
});
}
}
#Override
public void onChildChanged(final DataSnapshot snapshot, String s) {
Student_User.add(snapshot.getValue(TestUserStudent.class));
if (getActivity() != null) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
showAllMarkers(snapshot);
}
});
}
}
#Override
public void onChildRemoved(DataSnapshot snapshot) {
}
#Override
public void onChildMoved(DataSnapshot snapshot, String s) {
}
#Override
public void onCancelled(DatabaseError error) {
}
});
}
}
}, "");
thread.start();
}
The issue is quite difficult to solve because the UI code is entangled with the data loading logic. I think the best solution here would be to make those things independent. This will most likely solve the problem, or at least make it much easier to find and resolve. Also will improve the feature design in general.
Here is a short code snipit as requested, just to understand my idea. I won't write the whole thing though because this is beyond the question.
Create a state class, e.g. StudentState. The class will also provide a listener interface that will send updates to the fragment.
public class StudentState {
// You might want to store Student_User here instead
// I'm not sure how your data is currently represented.
private DataSnapshot dataSnapshot;
// Can also be a list of listeners if needed.
private StudentStateListener studentStateListener;
public void registerListener(StudentStateListener listener) {
studentStateListener = listener;
}
public void unregisterListener() {
studentStateListener = null;
}
...
public void setDataSnapshot(DataSnapshot dataSnapshot) {
this.dataSnapshot = dataSnapshot;
if (studentStateListener != null) {
studentStateListener.onDataSnapshotLoadFinished(dataSnapshot);
}
}
public interface StudentStateListener {
void onDataSnapshotLoadFinished(DataSnapshot dataSnapshot);
}
}
Implement it in your Fragment:
public class StudentFragment implements StudentStateListener {
// The state can be initialized in multiple ways.
// It can be a singleton, or stored in Application class, or
// defined in your base activity, etc. Up to you. Ideally
// should be injected via dependency injection if you use one, such as Dagger.
private StudentState state;
#Override
public void onCreate() {
// initialize state
// ...
}
#Override
public void onResume() {
state.registerListener(this);
// If the data has already been loaded you might also want the following.
// It's up to you.
if (state.getDataSnapshot() != null) {
showAllMarkers(state.getDataSnapshot());
}
}
#Override
public void onPause() {
state.unregisterListener();
}
...
#Override
public void onDataSnapshotLoadFinished(DataSnapshot dataSnapshot) {
if (!isAdded()) {
return;
}
showAllMarkers(snapshot);
}
public void updateStudents() {
// I'm not quite sure how to work with your API but basically the idea is to load a new data snapshot and store it in the state
// This should happen in background thread.
studentQuery.loadDataSnapshot(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot) {
state.setDataSnapshot(dataSnapshot);
}
});
}
}
I hope this helps.. Good luck!
This problem shows some dependency issues in your app. Perhaps you should find some other way to implment what you want.
Anyway, take a look at:
.setRetainInstance(true)
https://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)
This is not ideal, and you should avoid it. But it might solve your problem if nothing else works.
Hope it helps.
I created an Observable which emits the ViewPager's positionOffset.
public Observable<Float> observable() {
return Observable.create(new Observable.OnSubscribe<Float>() {
#Override
public void call(final Subscriber<? super Float> subscriber) {
if (viewPager == null) {
if (!subscriber.isUnsubscribed()) {
subscriber.onError(new IllegalStateException("viewPager is null"));
}
return;
}
final ViewPager.OnPageChangeListener listener = new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(final int position,
final float positionOffset,
final int positionOffsetPixels) {
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(positionOffset);
}
}
#Override
public void onPageSelected(final int position) {
}
#Override
public void onPageScrollStateChanged(final int state) {
}
};
viewPager.addOnPageChangeListener(listener);
subscriber.add(Subscriptions.create(() -> viewPager.removeOnPageChangeListener(listener)));
}
});
}
It works so far.
But with every subscription it creates a new ViewPager.OnPageChangeListener and adds it to the ViewPager.
Is there a way that all subscriptions share the same Observable that the ViewPager has only one Listener?
You can use Observable.share(), that's good option if you have multiple subscribers and dont mind if late subscribers can loose some notifications.
If you want that all subscribers see same notifications, use Observable.publish() - it will return ConnectableObservable, which starts emitting items once its connect() method is called. So you can do
connectable = originalObservable.publish();
connectable.subscribe(observer1);
connectable.subscribe(observer2);
connectable.connect();
You can use Connectable observables.
For example the code below simulates infinite (never completing) stream of events:
Observable<Float> o = Observable.concat(Observable.just(1f,2f,3f), Observable.never());
You can create a connectable observable using replay with refCount that will cache the last value:
Observable<Float> shared = o.replay(1).refCount();
A first subscriber will make the observable "hot" and will force it to produce items:
shared.subscribe(new Action1<Float>() {
#Override
public void call(Float t) {
System.out.println("o1:" + t);
}
});
Thread.sleep(1000);
A second observable may connect to it after a while, and will start from the latest item emitted:
shared.subscribe(new Action1<Float>() {
#Override
public void call(Float t) {
System.out.println("o2:" + t);
}
});
Thread.sleep(1000);
Output:
o1:1.0
o1:2.0
o1:3.0
o2:3.0
I have a class called PendingRequests. This class is basically a counter for Volley Requests. I have two Volley methods - One called CheckOpen and one called CheckCompleted. Each methods selects either Open or Completed records on my SQLite tablet database and checks them against the server.
In CheckOpen and CheckClose, I use three calls to PendingRequests. At the start I call Initialize(), then as I add a volley request to the queue I call Add() and then as I begin to receive responses I call Subtract(). For both CheckOpen or CheckClose I will very quickly Add() up to 50 and then slowly countdown (Subtract()) back to zero.
Once I countdown to zero, I call onComplete.
Here is the code for PendingRequests
import rx.Observable;
import rx.subjects.PublishSubject;
import rx.subjects.Subject;
/**
* Created by Admin on 6/17/15.
*/
public class PendingRequests {
private static Integer pendingReq;
private static Subject<Integer, Integer> pendingObser = PublishSubject.create();
public static void Initialize(Integer myInt) {
pendingReq = myInt;
pendingObser.onNext(pendingReq);
}
public static void Add() {
pendingReq ++;
pendingObser.onNext(pendingReq);
}
public static void Subtract() {
if (pendingReq == 0) {
return;
}
else
{
pendingReq --;
if (pendingReq == 0) {
pendingObser.onCompleted();
}
else {
pendingObser.onNext(pendingReq);
}
}
}
public static Integer Current() {
// return the current value of pendingReq for testing purposes
return pendingReq;
}
public static Observable<Integer> valueChanged() {
return pendingObser;
}
}
So from above PendingObser is a Subject and valueChanged is the Observer. (Hopefully I understand that correctly).
In my MainActivity I have two buttons. Beside each button is a textview that shows the value coming from pendingObser.
Here is the code for my two buttons
public void checkComplete(View view) {
//Intent intent = new Intent(this, ResendActivity.class);
//startActivity(intent);
PendingRequests.valueChanged().subscribe(new Subscriber<Integer>() {
#Override
public void onCompleted() {
tvPendingTotalClosed.setText("Done");
System.gc();
}
#Override
public void onError(Throwable e) {
tvPendingTotalClosed.setText(e.getMessage());
}
#Override
public void onNext(Integer integer) {
tvPendingTotalClosed.setText(String.valueOf(integer));
}
});
String myResult = seWebServiceUtils.checkCompletedRecord();
Toast.makeText(ApplicationController.getContext(), myResult, Toast.LENGTH_LONG).show();
}
public void checkOpen(View view){
// called directly from button btnCheckOpen
PendingRequests.valueChanged().subscribe(new Subscriber<Integer>() {
#Override
public void onCompleted() {
tvPendingTotalOpen.setText("Done");
System.gc();
}
#Override
public void onError(Throwable e) {
tvPendingTotalOpen.setText(e.getMessage());
}
#Override
public void onNext(Integer integer) {
tvPendingTotalOpen.setText(String.valueOf(integer));
}
});
String myResult = seWebServiceUtils.checkOpenRecord();
Toast.makeText(ApplicationController.getContext(), myResult, Toast.LENGTH_LONG).show();
}
I have two problems:
When I call checkComplete it performs as expected and will end with the word Done in tvPendingTotalClosed. However, when I hit checkOpen BOTH tvPendingTotalClosed and tvPendingTotalOpen count down. So checkComplete is not unsubscribing
After hitting the buttons the first time, if I try to hit the button CheckOpenRecords a second time nothing happens. It is like I can't re-subscribe to PendingRequests a second time.
Thanks, John
I'm looking at retrofit for my networking layer. Is there any way to tell if a particular async request is running at any given moment?
For example, I'd like to know if a request is running so that I can update the user interface at various times. I could do this myself by keeping variables around to track state, but wondering if there's something already in the library for this.
Here is what I would normally do when needing a way to keep track of running requests:
First, using retrofit, as soon as you make the request, you can do the following:
Use EventBus library to post an event to your activity or fragment. Now, this can be done inside onSuccess() method of your Callback or onError() method of the same.
In your activity or fragment's onEvent(EventClassName event) method, you can simply check a variable like [isRunning] from your event to make sure that if the event is still running, you update the UI accordingly and if not, do what you need to do respectively.
When the request is completed, obviously isRunning will be false and you can then update the UI as expected by the user.
I am recommending EventBus here simply because it is much easier to decouple your application code with it; you can send different events that notify the activity of the different statuses of your requests and then update your UI that way.
You can find EventBus here
I hope this helps!
What I personally ended up doing in this case was that I was running the example with Retrofit, Android Priority Jobqueue (from yigit's fork) and Otto eventbus.
public enum SingletonBus {
INSTANCE;
private Bus bus;
private Handler handler = new Handler(Looper.getMainLooper());
private SingletonBus() {
this.bus = new Bus(ThreadEnforcer.ANY);
}
public <T> void postToSameThread(final T event) {
bus.post(event);
}
public <T> void postToMainThread(final T event) {
handler.post(new Runnable() {
#Override
public void run() {
bus.post(event);
}
});
}
public <T> void register(T subscriber) {
bus.register(subscriber);
}
public <T> void unregister(T subscriber) {
bus.unregister(subscriber);
}
}
public interface Interactor {
void injectWith(PresenterComponent presenterComponent);
}
public interface SendCertificateRequestInteractor
extends Interactor {
interface Listener {
void onSuccessfulEvent(SuccessfulEvent e);
void onFailureEvent(FailureEvent e);
}
class SuccessfulEvent
extends EventResult<CertificateBO> {
public SuccessfulEvent(CertificateBO certificateBO) {
super(certificateBO);
}
}
class FailureEvent
extends EventResult<Throwable> {
public FailureEvent(Throwable throwable) {
super(throwable);
}
}
void sendCertificateRequest(String username, String password);
}
Pay attention to the Job here:
public class SendCertificateRequestInteractorImpl
implements SendCertificateRequestInteractor {
private Presenter presenter;
private boolean isInjected = false;
#Inject
public JobManager jobManager;
public SendCertificateRequestInteractorImpl(Presenter presenter) {
this.presenter = presenter;
}
#Override
public void sendCertificateRequest(String username, String password) {
if(!isInjected) {
injectWith(presenter.getPresenterComponent());
isInjected = true;
}
InteractorJob interactorJob = new InteractorJob(presenter, username, password);
long jobId = jobManager.addJob(interactorJob); //this is where you can get your jobId for querying the status of the task if you want
}
#Override
public void injectWith(PresenterComponent presenterComponent) {
presenterComponent.inject(this);
}
public static class InteractorJob
extends Job {
private final static int PRIORITY = 1;
private final static String TAG = InteractorJob.class.getSimpleName();
private String username;
private String password;
#Inject
public MyService myService;
public InteractorJob(Presenter presenter, String username, String password) {
super(new Params(PRIORITY).requireNetwork());
presenter.getPresenterComponent().inject(this);
this.username = username;
this.password = password;
}
#Override
public void onAdded() {
// Job has been saved to disk.
// This is a good place to dispatch a UI event to indicate the job will eventually run.
// In this example, it would be good to update the UI with the newly posted tweet.
}
#Override
public void onRun()
throws Throwable {
String certificate = myService.getCertificate(username, password);
SingletonBus.INSTANCE.postToMainThread(new SuccessfulEvent(certificate));
}
#Override
protected void onCancel() {
// Job has exceeded retry attempts or shouldReRunOnThrowable() has returned false.
Log.e(TAG, "Cancelled job.");
}
#Override
protected boolean shouldReRunOnThrowable(Throwable throwable) {
// An error occurred in onRun.
// Return value determines whether this job should retry running (true) or abort (false).
Log.e(TAG, "Failed to execute job.", throwable);
SingletonBus.INSTANCE.postToMainThread(new FailureEvent(throwable));
return false;
}
}
}
And then
#Subscribe
#Override
public void onSuccessfulEvent(SendCertificateRequestInteractor.SuccessfulEvent e) {
String certificate = e.getResult();
//do things
}
#Subscribe
#Override
public void onFailureEvent(SendCertificateRequestInteractor.FailureEvent e) {
Throwable throwable = e.getResult();
//handle error
}
More about android priority jobqueue here.
This way, technically the async handling is referred to the job queue, while Retrofit itself is using the synchronous interface. It works well as long as you don't need to access the headers of the response. Although to be fair, I was also keeping track of whether the job was running with a boolean instead of the job manager and the id as well..
Also, I haven't figured out how to use dependency injection properly with persisted jobs; nor do I really know how they intended to make that work. Of course, it'd work if it was using the application scoped component rather than a supplied presenter scoped one, but that is irrelevant.
You'll probably need to customize this solution to your own scenario, and use only what you actually need.