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
Related
I have a problem with my onPostExecute() method in AsyncTask class.
I have an SignupActivity:
public class SignupActivity extends AppCompatActivity implements SignupListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.signup_activity);
//new task, i pass context and interface to it
signup = new Signup(getApplicationContext(), this);
signupButon.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
if(validate()) {
try {
//new task every click
Signup newSignup = new Signup(signup);
//here start AsyncTask
newSignup.execute(name, email, password).get();
} catch (Exception e) {
Toast.makeText(ERROR);
}
// if sign up succes, == true;
if(signupValid) {
Toast.makeText(SUCCES);
finish();
} else {
Toast.makeText(ERROR);
}
}
}
});
}
// my own interface for getting result as bool from onPostExecute
#Override
public void onSignupPerformed(Boolean result){ this.signupValid = result; }
That implements my interface to catching result from onPostExecute():
public interface SignupListener{
void onSignupPerformed(Boolean result);
}
Now, AsyncTask that i trigger in code:
public class Signup extends AsyncTask<String, Boolean, Boolean> {
public Signup(Context context, SignupListener listener){
db = ApplicationDatabase.getDatabase(context);
this.context = context;
this.listener = listener;
}
public Signup(Signup signup){
//constructor to make new task based on first task
db = signup.db;
context = signup.context;
listener = signup.listener;
}
protected Boolean doInBackground(String... body){
try {
user = db.userDao().getUser(body[0], body[1], body[2]);
if (user == null) {
// user is null, so we can add new one to DB
db.userDao().insertUser(new User(body[0], body[1], body[2]));
return Boolean.TRUE; //signup go good, return true
} else {
return Boolean.FALSE; //signup go bad, return false
}
} catch(Exception e) { }
return null;
}
protected void onPostExecute(Boolean result) {
//catching result from doInBackground
listener.onSignupPerformed(result);
}
My question is, why when i first click on button, func return Boolean.TRUE but in SignupActivity signupValid variable is false (signup form not exit, but user is added to DB), but when i click signup button second time, ofc signup fail (because we make new user seconds ago) but signupValid change to true and Signup Form pass? I need to click SignupButton two times to finally exit form. Thanks for finding error in my code
EDIT:
I replaced .get() with Progress Dialog to block UI, but now i get Toast with not valid form even before AsyncTask for Signup do his job. And still, in first click signupValid is false even when from doInBackground() i get TRUE, on second click AsyncTask return FALSE but signupValid is changed to true
My UserDAO:
#Dao
public interface UserDao {
#Query("SELECT * FROM users WHERE email = :email AND password = :password AND username = :username")
User getUser(String username, String email, String password);
}
And ApplicationDatabase:
public abstract class ApplicationDatabase extends RoomDatabase {
public abstract UserDao userDao();
public static ApplicationDatabase getDatabase(final Context context){
if(INSTANCE == null){
synchronized (ApplicationDatabase.class){
if(INSTANCE == null){
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), ApplicationDatabase.class, "database").build();
}
}
}
return INSTANCE;
}
private static volatile ApplicationDatabase INSTANCE;
If I understood the problem correctly - there is a race condition that makes the SignupActivity to fire the toast before the execution of Signup task is completed. Therefore:
signupButon.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
if(validate()) {
try {
//new task every click
Signup newSignup = new Signup(signup);
//here start AsyncTask
newSignup.execute(name, email, password).get();
} catch (Exception e) {
Toast.makeText(ERROR);
}
}
}
});
While these lines:
// if sign up succes, == true;
if(signupValid) {
Toast.makeText(SUCCES);
finish();
} else {
Toast.makeText(ERROR);
}
Should be a part of the listener (right now it seems that these lines are executed BEFORE the completion of your async task)
To clarify myself:
#Override
public void onSignupPerformed(Boolean result)
{
if(result) {
Toast.makeText(SUCCES);
finish();
} else {
Toast.makeText(ERROR);
}
}
I am trying to make example of Play Billing application described here
In Last step they have described
To clean all the resources and unregister the observer, you just need to call BillingClient.endConnection. So define a method with this call inside BillingManager and then call it from GamePlayActivity.onDestroy:
according to above information I have made function called destroy like this in BillingManagerjava class.
public void destroy() {
mBillingClient.endConnection();
}
My Full BillingManager Class is like below
public class BillingManager implements PurchasesUpdatedListener {
private final BillingClient mBillingClient;
private final Activity mActivity;
private static final String TAG = "BillingManager";
public BillingManager(Activity activity) {
mActivity = activity;
mBillingClient = BillingClient.newBuilder(mActivity).setListener(this).build();
mBillingClient.startConnection(new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(#BillingClient.BillingResponse int billingResponse) {
if (billingResponse == BillingClient.BillingResponse.OK) {
Log.i(TAG, "onBillingSetupFinished() response: " + billingResponse);
} else {
Log.w(TAG, "onBillingSetupFinished() error code: " + billingResponse);
}
}
#Override
public void onBillingServiceDisconnected() {
Log.w(TAG, "onBillingServiceDisconnected()");
}
});
}
public void startPurchaseFlow(final String skuId, final String billingType) {
// Specify a runnable to start when connection to Billing client is established
Runnable executeOnConnectedService = new Runnable() {
#Override
public void run() {
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setType(billingType)
.setSku(skuId)
.build();
mBillingClient.launchBillingFlow(mActivity, billingFlowParams);
}
};
// If Billing client was disconnected, we retry 1 time
// and if success, execute the query
startServiceConnectionIfNeeded(executeOnConnectedService);
}
#Override
public void onPurchasesUpdated(#BillingClient.BillingResponse int responseCode,
List<Purchase> purchases) {
Log.d(TAG, "onPurchasesUpdated() response: " + responseCode);
}
private static final HashMap<String, List<String>> SKUS;
static
{
SKUS = new HashMap<>();
SKUS.put(BillingClient.SkuType.INAPP, Arrays.asList("gas", "premium"));
SKUS.put(BillingClient.SkuType.SUBS, Arrays.asList("gold_monthly", "gold_yearly"));
}
public List<String> getSkus(#BillingClient.SkuType String type) {
return SKUS.get(type);
}
public void querySkuDetailsAsync(#BillingClient.SkuType final String itemType,
final List<String> skuList, final SkuDetailsResponseListener listener) {
// Specify a runnable to start when connection to Billing client is established
Runnable executeOnConnectedService = new Runnable() {
#Override
public void run() {
SkuDetailsParams skuDetailsParams = SkuDetailsParams.newBuilder()
.setSkusList(skuList).setType(itemType).build();
mBillingClient.querySkuDetailsAsync(skuDetailsParams,
new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(int responseCode,
List<SkuDetails> skuDetailsList) {
listener.onSkuDetailsResponse(responseCode, skuDetailsList);
}
});
}
};
// If Billing client was disconnected, we retry 1 time
// and if success, execute the query
startServiceConnectionIfNeeded(executeOnConnectedService);
}
private void startServiceConnectionIfNeeded(final Runnable executeOnSuccess) {
if (mBillingClient.isReady()) {
if (executeOnSuccess != null) {
executeOnSuccess.run();
}
} else {
mBillingClient.startConnection(new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(#BillingClient.BillingResponse int billingResponse) {
if (billingResponse == BillingClient.BillingResponse.OK) {
Log.i(TAG, "onBillingSetupFinished() response: " + billingResponse);
if (executeOnSuccess != null) {
executeOnSuccess.run();
}
} else {
Log.w(TAG, "onBillingSetupFinished() error code: " + billingResponse);
}
}
#Override
public void onBillingServiceDisconnected() {
Log.w(TAG, "onBillingServiceDisconnected()");
}
});
}
}
public void destroy() {
mBillingClient.endConnection();
}
}
And My GamePlayActivity is like below
public class GamePlayActivity extends FragmentActivity implements BillingProvider {
#Override
protected void onDestroy() {
super.onDestroy();
// I want call method here
}
}
Now I want call above function in my game play activity. I have no idea how to call it.
As it mentioned in documentation
call it from GamePlayActivity.onDestroy
but you defined your own method.
Override onDestroy method of GamePlayActivity and put mBillingClient.endConnection(); into it.
#Override
protected void onDestroy() {
mBillingClient.endConnection();
}
I assume your Activity already has an instance of the BillingManager
public class GamePlayActivity extends FragmentActivity implements BillingProvider {
BillingManager bm; // assign this in onCreate
#Override
protected void onDestroy() {
super.onDestroy();
bm.destroy();
}
}
I want to sent report to the server which means how long user Use the application in single day..I can achieve using this to method
#Override
protected void onResume() {
super.onResume();
//commonclassMethod.getInstance(UserForground);
}
#Override
protected void onStop() {
super.onStop();
//commonclassMethod.getInstance(UserBackground);
}
What happen i need to call call this method in every activity....
What i need,is there any possible to find user forground background method in single java class or activity..
Thanks in Advance.
You can can achieve this just by adding an method isAppIsInBackground(Context context) in class which is extending Application class
In that class define that method:
public static boolean isAppIsInBackground(Context context) {
boolean isInBackground = true;
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
List<ActivityManager.RunningAppProcessInfo> runningProcesses = am.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
for (String activeProcess : processInfo.pkgList) {
if (activeProcess.equals(context.getPackageName())) {
isInBackground = false;
}
}
}
}
} else {
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
ComponentName componentInfo = taskInfo.get(0).topActivity;
if (componentInfo.getPackageName().equals(context.getPackageName())) {
isInBackground = false;
}
}
return isInBackground;
}
It will true if app is in background
Or another better approach would be just extend your each Activity by an BaseActivity, in this BaseActivity's override methods
protected void onResume() {
super.onResume();
//commonclassMethod.getInstance(UserForground);
}
protected void onStop() {
super.onStop();
//commonclassMethod.getInstance(UserBackground);
}
You can implement callback method to solve your case.
For example:
You create an interface first, then define a method, which would act as a callback. In this example we would have two classes, one classA and another classB
Interface:
public interface OnCustomEventListener{
public void onEvent(); //method, which can have parameters
}
the listener itself in classB (we only set the listener in classB)
private OnCustomEventListener mListener; //listener field
//setting the listener
public void setCustomEventListener(OnCustomEventListener eventListener) {
this.mListener=eventListener;
}
in classA, how we start listening for whatever classB has to tell
classB.setCustomEventListener(new OnCustomEventListener(){
public void onEvent(){
//do whatever you want to do when the event is performed.
}
});
how do we trigger an event from classB (for example on button pressed)
if(this.mListener!=null){
this.mListener.onEvent();
}
Here is some nice tutorials link1,link2,link3 which describes callbacks and the use-case well.
Create a class extending application and use registerActivityLifecycleCallbacks() to get the activity lifecycle
public class MyApp extends Application {
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
if(activity.getClass().getSimpleName().equalsIgnoreCase(MainActivity.class.getSimpleName())){
//Do the required thing here
}
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
Also dont forget to register activity in manifest
<application
android:name=".MyApp"
You have two options.
1) Do a abstract BaseActivity and make all Activities extend it. This way you only write the code in one BaseActivity and all children activities consume it.
2) Use a custom counter class to monitor app to foreground. Here is my implementation of it if you would like to copy it.
/**
* Created by App Studio 35 on 6/23/17.
*/
public class AppLifeCycleTracker implements Application.ActivityLifecycleCallbacks {;
/*///////////////////////////////////////////////////////////////
// METHODS
*////////////////////////////////////////////////////////////////
private static final String TAG = Globals.SEARCH_STRING + AppLifeCycleTracker.class.getSimpleName();
private static AppLifeCycleTracker INSTANCE;
private static int numActivitiesInMemory = 0;
private ArrayList<IAppToForegroundListener> mAppToForegroundListeners;
private boolean isRefreshing;
private Object lockAccess = new Object();
private AlertDialog mAlertDialog = null;
/*///////////////////////////////////////////////////////////////
// PROPERTIES
*////////////////////////////////////////////////////////////////
private ArrayList<IAppToForegroundListener> getAppToForegroundListeners(){
return mAppToForegroundListeners == null ? mAppToForegroundListeners = new ArrayList<IAppToForegroundListener>() : mAppToForegroundListeners;
}
public boolean getIsRefreshing(){
return isRefreshing;
}
public boolean getAppIsInBackground(){
return numActivitiesInMemory < 1;
}
/*///////////////////////////////////////////////////////////////
// CONSTRUCTOR
*////////////////////////////////////////////////////////////////
private AppLifeCycleTracker(){
}
public synchronized static AppLifeCycleTracker getInstance(){
if(INSTANCE == null){
INSTANCE = new AppLifeCycleTracker();
}
return INSTANCE;
}
/*///////////////////////////////////////////////////////////////
// LIFE CYCLE OVERRIDES
*////////////////////////////////////////////////////////////////
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
#Override
public void onActivityStarted(final Activity activity) {
//App went into background, so set a flag to avoid loading while we refresh
if(numActivitiesInMemory == 0 && !(activity instanceof SplashScreenActivity) && !(activity instanceof CreateAccountActivity)){
A35Log.v(TAG, "App Returned to Foreground, refreshing Token");
//first load on splash it goes from 0 to 1 so hold off on splash
synchronized (lockAccess) {
isRefreshing = true;
}
if (DeviceInfo.getInstance(activity).getIsConnectedToInternet()) {
CognitoManager.refreshToken(activity, new GenericHandler() {
#Override
public void onSuccess() {
A35Log.v(TAG, "Token Refresh Complete, notifying listeners");
//we are good, keep going
for(IAppToForegroundListener listener : getAppToForegroundListeners()){
listener.onRefreshTokenComplete();
}
synchronized (lockAccess) {
isRefreshing = false;
}
}
#Override
public void onFailure(Exception exception) {
//boot them to login screen
if(activity instanceof LoginActivity || activity instanceof SplashScreenActivity){
return;
}
startLoginActivity(activity);
synchronized (lockAccess) {
isRefreshing = false;
}
}
});
} else {
showInternetRequiredDialog(activity);
}
}
numActivitiesInMemory++;
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
numActivitiesInMemory--;
//if numActivities == 0 then you are in the background
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
/*///////////////////////////////////////////////////////////////
// METHODS
*////////////////////////////////////////////////////////////////
public void addAppToForegroundListener(IAppToForegroundListener listener){
getAppToForegroundListeners().add(listener);
}
public void removeAppToForegroundListener(IAppToForegroundListener listener){
getAppToForegroundListeners().remove(listener);
}
private void startLoginActivity(final Activity activity){
((AMApplication) activity.getApplication()).logoutCurrentUser(activity, false, false, null, true, null);
}
/*///////////////////////////////////////////////////////////////
// INTERFACES
*////////////////////////////////////////////////////////////////
public interface IAppToForegroundListener {
/*///////////////////////////////////////////////////////////////
// METHODS
*////////////////////////////////////////////////////////////////
void onRefreshTokenComplete();
}
private void showInternetRequiredDialog(Activity activity){
final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle("Error").setMessage("Internet is required to use this app").setNegativeButton(R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if(mAlertDialog != null && mAlertDialog.isShowing()) {
mAlertDialog.dismiss();
}
}
});
mAlertDialog = builder.create();
mAlertDialog.show();
}
}
Of course this does a little more then you are looking for as mine manages refreshing the token with cognito and forcing refresh on returning from background and things like that, so just disregard that piece. But the rest is all the same still. Hope that helps.
I'm assuming you don't need an example of a BaseActivity so I won't patronize you by pasting that.
Startup in Application class
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(AppLifeCycleTracker.getInstance());
}
Then you ONLY need to access from BaseActivity or BaseFragment IF you need to be notified when the app is in foreground or background at an Activity or fragment level. Which for your situation is not the case.
But if you ever wanted to use it, simply do this:
#Override
public void onAttach(Context context) {
super.onAttach(context);
AppLifeCycleTracker.getInstance().addAppToForegroundListener(this);
}
#Override
public void onDetach() {
super.onDetach();
AppLifeCycleTracker.getInstance().removeAppToForegroundListener(this);
}
But again, I must emphasize, this part is ONLY if you care to make your activity or fragment be aware of when the app comes back to foreground to force refresh or other behaviors. Replace onDetach with onDestroy if using Activity, but for your scenario you can skip that whole last section of code, you don't need it.
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
I have a function which makes a http request and parses the response json data. The function is called in AsyncTask class. I have a function defined to check if there is connectivity before asynctask is invoked. But once the connection checker function returns true...my function runs within the asynctask class and the device loses connectivity the application force closes.
private void parseJson()
{
// HTTP request and JSON parsing done here
}
class getData extends AsyncTask <Void,Void,Void>
{
#Override
protected Void onPreExecute(Void...arg0)
{
super.onPreExecute();
//progress dialog invoked here
}
#Override
protected Void doInBackground(Void...arg0)
{
parseJSON();
return null;
}
#Override
protected Void onPostExecute(Void...arg0)
{
super.onPostExecute();
//UI manipulated here
}
}
how do i notify the user about the exception occuring in the doInBackground() method and handle exception properly since doInBackground() doesn't allow things like firing a toast message.
Do this Way
class getData extends AsyncTask <Void,Void,Boolaen>
{
#Override
protected Void onPreExecute(Void...arg0)
{
super.onPreExecute();
//progress dialog invoked here
}
#Override
protected Boolaen doInBackground(Void...arg0)
{
try{
parseJSON();
return true;
}catch(Exception e){
e.printStackStrace();
}
return false;
}
#Override
protected Void onPostExecute(Boolaen result)
{
super.onPostExecute(result);
if(result){
//success
}else{
// Failure
}
//UI manipulated here
}
}
My approach looked like this. Introducing a generic AsyncTaskResult, where you can either store your real return value (if you need one) or the exception which occured in doInBackground(). In onPostExecute you can check if an exception has occured and notify your user (or process your return value).
AsyncTaskResult:
public class AsyncTaskResult<T> {
private T mResult;
private Exception mException = null;
public AsyncTaskResult() {
}
public AsyncTaskResult(T pResult) {
this.mResult = pResult;
}
public AsyncTaskResult(Exception pException) {
this.mException = pException;
}
public T getResult() {
return mResult;
}
public boolean exceptionOccured() {
return mException != null;
}
public Exception getException() {
return mException;
}
}
AsyncTask:
public class RessourceLoaderTask extends AsyncTask<String, String, AsyncTaskResult<String>> {
public RessourceLoaderTask() {
}
#Override
protected AsyncTaskResult<String> doInBackground(String... params) {
try {
// Checked Exception
} catch (Exception e) {
return new AsyncTaskResult<String>(e);
}
return new AsyncTaskResult<String>();
}
#Override
protected void onPostExecute(AsyncTaskResult<String> pResult) {
if (!pResult.exceptionOccured()) {
//...
} else {
// Notify user
}
}
}
Make a field in getData class. Set it in doBackground, check it in onPostExecute.