i'm making unit test for my application
my unit test class has this method
#Before
public void initialize() {
mContext = InstrumentationRegistry.getTargetContext();
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(mContext).name("realmTest").inMemory().build();
Realm.setDefaultConfiguration(realmConfiguration);
mWorkoutsModel = new WorkoutsModel(mContext);
mRealm = Realm.getInstance(realmConfiguration);
mWorkoutsModel.registerListener(this);
}
#Test
public void getWorkouts() throws Exception {
mWorkoutsModel.onStart();
mLock.await();
mWorkoutsModel.onStop();
}
#After
public void deInitialize() {
mWorkoutsModel.unRegisterListener();
mRealm.close();
}
and my model
#Override
public void onStart() {
mRealm = Realm.getDefaultInstance();
getDataFromApi();
}
private boolean getDataFromApi() {
Constants.AllAPIs.ALLWorkouts allWorkouts = new Constants.AllAPIs.ALLWorkouts();
if (Permissions.isInternetConnectionExist(mContext)) {
mApiHandler.downLoadDataFromApi(AllWorkouts.class, allWorkouts.getBaseUrl(),
new APIHandler.StringResponseHandler<AllWorkouts>() {
#Override
public void onResponse(AllWorkouts response) {
insertWorkouts(response.getWorkouts());
},
new APIHandler.ErrorResponseHandler() {
#Override
public void onErrorResponse(VolleyError error) {
}
}, TAG);
return true;
} else {
return false;
}
}
private void insertWorkouts(final List<Workout> workouts) {
mCurrentInsertTransaction = mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm bgRealm) {
bgRealm.copyToRealmOrUpdate(workouts);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
}
});
}
my problem that the unittest calls onStart which create realm object in the model in test thread but volley force onResponse to run on UIThread which makes realm throw exception Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
the code runs perfect in normal, but in test it fails
does anyone faced same problem or can solve it ?
i solved my problem by run the test in handler
new Handler(mContext.getMainLooper()).post(new Runnable() {
#Override
public void run() {
try {
mWorkoutsModel.onStart();
mLock.await();
mWorkoutsModel.onStop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
hope that help somebody
Related
i want to call api request every x second using Runnable and read live response to multiple fragment on every api call.
i try bellow code but if start runnable from FragmentA then can't get response on FragmentB even if runnable already run api call in background.
when i run RunnableDemo(mContext, this, JsonRpc.Method.TELL_ACTIVE).run(); in fragmentA and start runnable however can get onDataSuccess in fragmentB.
and if i use RunnableDemo(mContext, this, JsonRpc.Method.TELL_ACTIVE).run(); in FragmentB then run two time API call for FragmentA and FragmentB.
so, how can i run api call every x second once and receive response on multiple fragments?
thank you,
RunnableDemo.java
public class RunnableDemo implements Runnable{
RunnableDemo.RunnableDemoListener listener;
public void run() {
if (myRunnable == null) {
myHandler.postDelayed(myRunnable = new Runnable() {
public void run() {
if(shouldRun) {
running = true;
//do something
loop();
}
myHandler.postDelayed(myRunnable, updateInterval);
}
}, updateInterval);
}
}
private void loop() {
...retrofit....
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(#NonNull Call<ResponseBody> call, #NonNull Response<ResponseBody> response)
{
listener.onDataSuccess(response);
}
#Override
public void onFailure(#NonNull Call<ResponseBody> call, #NonNull Throwable t)
{
listener.onDataError("error");
}
});
}
public void stop() {
if (listener != null) {
listener.onStopped();
}
running = false;
shouldRun = false;
}
public final boolean isRunning() {
return running;
}
public final boolean safeStop() {
if (listener != null) {
listener.onStopped();
}
shouldRun = false;
running = false;
return true;
}
// Listener defined earlier
public interface RunnableDemoListener {
public void onDataError(String title);
public void onDataSuccess(Response<ResponseBody> responseBody);
public void onStopped();
}
}
FragmentA.java
public class FargmentA extends Fragment implements RunnableDemo.RunnableDemoListener{
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
RunnableDemo runnableDemo = new RunnableDemo(mContext, this, JsonRpc.Method.TELL_ACTIVE);
if(!runnableDemo.isRunning()) {
runnableDemo.run();
}
}
#Override
public void onDataError(String title) {
Log.i("TEST", "TestRunnable onResponse receive error Files: "+ title);
}
#Override
public void onDataSuccess(Response<ResponseBody> responseBody) {
Log.i("TEST", "TestRunnable onResponse receive success Files: "+ responseBody.code());
}
#Override
public void onStopped() {
Log.i("TEST", "TestRunnable onResponse receive onStopped Files: ");
}
}
FragmentB.java
public class FargmentB extends Fragment implements RunnableDemo.RunnableDemoListener{
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
RunnableDemo runnableDemo = new RunnableDemo(mContext, this, JsonRpc.Method.TELL_ACTIVE);
if(!runnableDemo.isRunning()) {
runnableDemo.run();
}
}
#Override
public void onDataError(String title) {
Log.i("TEST", "TestRunnable onResponse receive error Files: "+ title);
}
#Override
public void onDataSuccess(Response<ResponseBody> responseBody) {
Log.i("TEST", "TestRunnable onResponse receive success Files: "+ responseBody.code());
}
#Override
public void onStopped() {
Log.i("TEST", "TestRunnable onResponse receive onStopped Files: ");
}
}
I am using MVVM pattern in which I am using SwipeRefresh layout to refresh recycler view in my layout.When I am pulling it then it continue to refresh even after method completed successfully.
Below is my code:
MainActivity.java
refresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
userRepository.getUserList();
}
});
UserRepository.java
public void getUserList(){
Retrofit retrofit = RetrofitClient.getInstance();
ApiService apiService = retrofit.create(ApiService.class);
Call<List<User>> userList = apiService.getUser();
userList.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, final Response<List<User>> response) {
Completable.fromAction(new Action() {
#Override
public void run() throws Exception {
if(response.body() != null) {
List<User> list = response.body();
for (int i = 0; i < list.size(); i++) {
String id = list.get(i).get_id();
String names = list.get(i).getName();
String age = list.get(i).getAge();
User user = new User(id,names,age);
userDb.userDao().Insert(user);
}
}
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onComplete() {
Toast.makeText(context,"Data inserted",Toast.LENGTH_SHORT).show();
}
#Override
public void onError(Throwable e) {
Toast.makeText(context,e.getMessage(),Toast.LENGTH_LONG).show();
}
});
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
Toast.makeText(context,t.getMessage(),Toast.LENGTH_LONG).show();
}
});
}
Method fetching list is in another class UserRepository and I am calling method in another activity MainActivity.I am not getting any way how can I stop refreshing process.Someone please let me know a way to stop refreshing process.
Any help would be appreciated.
THANKS
To disable the progress dialog add this,
swipeLayout.setRefreshing(false);
I am new to Realm (and Android development) and I would like to use a Singleton class to simplify Realm data management so it's easier for my friends to use in our group project.
EpicPandaForce have written a singleton class called RealmManager here, but I couldn't find an example in implementing it, so this is what I have tried:
public class RealmManager {
private static RealmManager instance;
private final ThreadLocal<Realm> localRealm = new ThreadLocal<>();
RealmManager(){}
public synchronized static RealmManager getInstance(){
if(instance == null){
instance = new RealmManager();
}
return instance;
}
public Realm openLocalInstance() {
Realm realm = Realm.getDefaultInstance();
if(localRealm.get() == null) {
localRealm.set(realm);
}
return realm;
}
public Realm getLocalInstance() {
Realm realm = localRealm.get();
if(realm == null) {
throw new IllegalStateException("No open Realms were found on this thread.");
}
return realm;
}
public void closeLocalInstance() {
Realm realm = localRealm.get();
if(realm == null) {
throw new IllegalStateException(
"Cannot close a Realm that is not open.");
}
realm.close();
if(Realm.getLocalInstanceCount(Realm.getDefaultConfiguration()) <= 0) {
localRealm.set(null);
}
}
public void storePreferenceDao(int userID, String rank){
final PreferenceDao preferenceDao = new PreferenceDao();
preferenceDao.setUserID(userID);
preferenceDao.setRank(rank);
openLocalInstance();
getLocalInstance().executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.copyToRealmOrUpdate(preferenceDao);
}
}, new Realm.Transaction.OnSuccess(){
#Override
public void onSuccess(){
System.out.println("Data is stored successfully!");
}
}, new Realm.Transaction.OnError(){
#Override
public void onError(Throwable error){
System.out.println("There is an error in storePreferenceDao()");
}
});
closeLocalInstance();
}
So when my friends want to store some data, they can just use:
RealmManager.getInstance().storePreferenceDao(123, "Alpaca");
Is this how it is supposed to be used or is it redundant? How can I make it more efficient?
Actually in this case, that method can still be called only from UI thread, and the local instance should be closed in the transaction callback (otherwise the onSuccess/onError won't be called)
You could make a method that is able to execute on bg thread if able, and on current thread if already on a bg thread
// method in RealmManager
public final void runTransaction(Realm.Transaction transaction) {
runTransaction(transaction, null, null);
}
public final void runTransaction(Realm.Transaction transaction, Realm.Transaction.OnSuccess onSuccess) {
runTransaction(transaction, onSuccess, null);
}
public final void runTransaction(Realm.Transaction transaction, Realm.Transaction.OnSuccess onSuccess, Realm.Transaction.OnError onError) {
Realm realm = openLocalInstance();
if(realm.isAutoRefresh()) {
realm.executeTransactionAsync(transaction, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
try {
if(onSuccess != null) {
onSuccess.onSuccess();
}
} finally {
closeLocalInstance();
}
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable e) {
try {
if(onError != null) {
onError.onError(e);
}
} finally {
closeLocalInstance();
}
}
});
} else {
try {
realm.executeTransaction(transaction);
if(onSuccess != null) {
onSuccess.onSuccess();
}
} catch(Exception e) {
if(onError != null) {
onError.onError(e);
}
throw new RuntimeException(e);
} finally {
closeLocalInstance();
}
}
}
If you add this method, then you can now execute a transaction that will either be executed on background thread via async transaction method if possible, using synchronous transaction method if not on a looper thread (f.ex. background thread)
This way you can now do
public void storePreferenceDao(int userID, String rank) {
final PreferenceDao preferenceDao = new PreferenceDao();
preferenceDao.setUserID(userID);
preferenceDao.setRank(rank);
runTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.copyToRealmOrUpdate(preferenceDao);
}
}, new Realm.Transaction.OnSuccess(){
#Override
public void onSuccess(){
System.out.println("Data is stored successfully!");
}
}, new Realm.Transaction.OnError(){
#Override
public void onError(Throwable error){
System.out.println("There is an error in storePreferenceDao()");
}
});
}
Or just
public void storePreferenceDao(int userID, String rank) {
final PreferenceDao preferenceDao = new PreferenceDao();
preferenceDao.setUserID(userID);
preferenceDao.setRank(rank);
runInTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.copyToRealmOrUpdate(preferenceDao);
}
});
}
You know, I always felt I should add a runTransaction() method to that example. Whether it should default to using executeTransactionAsync by default if able or not is up for debate, though.
A simple Singleton example,
public class MySingleton {
private static MySingleton sMySingleton;
//private constructor.
private MySingleton() {
if (sMySingleton != null){
throw new RuntimeException("Use getInstance() for the instance");
}
}
public synchronized static MySingleton getInstance() {
if (sMySingleton == null){
sMySingleton = new MySingleton();
}
return sMySingleton;
}
}
Hope it helps!
This can be not the best answer but here how i am using realm in my applications
public class BaseActivity extends AppCompatActivity {
public Realm realm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base);
realm = Realm.getDefaultInstance();
}
}
Extend BaseActivity in other Activities according to your use
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Here you can directly access realm object and perform your task
realm.where()//just example
}
#Override
protected void onDestroy() {
super.onDestroy();
if(realm!=null)
realm.close();
//Don't forget to close realm object
}
}
I am not saying this is the best way but it one the best way. using this way i can easily manage my realm class and can reduce errors related to realm.For fragments You can make BaseFragment and extend it in other fragments.
Hope this will help you...Let me know if you get any other better way for this
When I had my logic inside a Runnable it worked fine except I could not interact with the UI Thread. So I am trying to put everything inside a class that extends Task and it works Except the task is only executed once. No errors and I get a succeeded message form the Task succeeded method.
I have also tried making the task return Boolean true in the call method but that did not help.
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception{
SyncTask syncTask = new SyncTask();
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(syncTask, 0, 10, TimeUnit.SECONDS);
Label syncEngineLabel = centralController.getScheduleTabMessageLabel();
syncEngineLabel.textProperty().bind(syncTask.messageProperty());
}
class SyncTask extends Task<Void>{
private Schedule schedule = null;
public SyncTask() {}
#Override
protected Void call() throws Exception {
System.out.println("we are in the task...");
if (getScheduleFromApi()){
updateMessage("New Schedule Retrieved...");
}
return null;
}
#Override protected void succeeded() {
super.succeeded();
System.out.println("succeeded");
}
#Override protected void cancelled() {
super.cancelled();
System.out.println("cancelled");
}
#Override protected void failed() {
super.failed();
System.out.println("failed");
}
private Boolean getScheduleFromApi(){
Gson gson = new GsonBuilder().serializeNulls().create();
ApiGet api = new ApiGet("schedule/get-schedule-by-room", parameters);
api.sendRequest();
if (api.isSuccess()){
schedule = gson.fromJson(api.response(), Schedule.class);
if (schedule.getStatus().equals("200")){
return true;
}
else{
updateMessage(schedule.getMsg());
return false;
}
}
else {
updateMessage("Failed to process API call to ASI Server.");
return false;
}
}
}
}
Please note that this code actually exists inside a controller but I put it in Main here to try and provide self contained code.
Thanks!
The ScheduledExecutorService will simply treat the task you provide as a Runnable, and try to reuse the same task instance every time it runs, which is explicitly forbidden in the documentation.
Use a ScheduledService instead:
#Override
public void start(Stage primaryStage) throws Exception{
ScheduledService<Void> scheduledService = new ScheduledService<Void>() {
#Override
protected Task<Void> createTask() {
return new SyncTask();
}
};
scheduledService.setPeriod(Duration.seconds(10));
scheduledService.start();
Label syncEngineLabel = centralController.getScheduleTabMessageLabel();
scheduledService.stateProperty().addListener((obs, oldState, newState) -> {
if (newState == Worker.State.RUNNING) {
syncEngineLabel.setText("Sync in progress");
} else if (newState == Worker.State.FAILED) {
syncEngineLabel.setText("Sync error");
} else {
syncEngineLabel.setText("Sync complete");
}
});
}
I am facing with the problem. As far as I know zip method from RxJava waits for all observables to complete.
But am I getting another behaviour.
Here is my code snippet
private PublishSubject<Void> firstSubject;
private PublishSubject<Void> secondSubject;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loadData();
mDrawerHeaderView.postDelayed(new Runnable() {
#Override
public void run() {
// getSecondSubject().onNext(null);
}
}, 1000);
mDrawerHeaderView.postDelayed(new Runnable() {
#Override
public void run() {
getFirstSubject().onCompleted();
}
}, 1000);
}
protected PublishSubject<Void> createFirstSubject() {
firstSubject = PublishSubject.create();
return firstSubject;
}
protected PublishSubject<Void> createSecondSubject() {
secondSubject = PublishSubject.create();
return secondSubject;
}
protected PublishSubject<Void> getFirstSubject() {
return firstSubject;
}
protected PublishSubject<Void> getSecondSubject() {
return secondSubject;
}
private void loadData() {
Observable<Void> firstSubject = createFirstSubject();
Observable<Void> secondSubject = createSecondSubject();
Observable<Boolean> allDataTask = Observable.zip(firstSubject, secondSubject, new Func2<Void, Void, Boolean>() {
#Override
public Boolean call(Void aVoid, Void aVoid2) {
return true;
}
});
allDataTask
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Boolean>() {
#Override
public void onCompleted() {
Notifications.showSuccessMessage(getApplicationContext(), "COMPLETE");
}
#Override
public void onError(Throwable e) {
Notifications.showErrorMessage(getApplicationContext(), "ERROR");
}
#Override
public void onNext(Boolean aBoolean) {
Notifications.showSuccessMessage(getApplicationContext(), "NEXT");
}
});
}
In this case I got COMPLETE message, but I was expecting to get nothing because the second subject is not completed.
What I am doing wrong ?
Please help me to get desired behaviour.
Yes, it works as expected. It makes perfect sense to receive the onCompleted() here, because if one stream is done, as long as all the elements it emitted are "zipped", there's no way to "zip" anything more, so it's "completed". You can also play with the sequence here.