I have a simple question but can't find the solution in the references anywhere.
When I create an instance of a certain class where the instance is to be propagated to a couple of places everytime it is created again the most obvious place where to put the code is in the creation method of the instance itself. This is how it would look like:
public void MYTest() {
public MYTEST() {
ANOTHER_CLASS.myTest = this; // <-------- can I already use the new this and assign it ?
}
}
Can I put the assignments of this just newly created instance to other variables already in the creation method or do I have to write an extra "super" creation method like this and put this also in MYTEST:
public void MYTest() {
public MYTEST() {
ANOTHER_CLASS.myTest = this; // <-------- can I already use the new this and assign it ?
}
public static MYTEST superCreationMethodForMYTEST() {
x = new MYTEST(); // <- here for sure the creation of MYTEST instance is finished BEFORE the assgnment happens
ANOTHER_ClASS.myTest = x;
.... all the other assignments for x.....
}
}
and then call it like
x = MYTEST.superCreationMethodForMYTEST();
instead of:
x = new MYTEST();
Thanks
You can assign the this reference anywhere in the constructor. However, it may not be wise, depending on your situation. The main problem is that while the object exists at the time of the constructor, it is not completely constructed and therefore may not be in a state to handle method calls correctly. If the assigned reference is used by an unsuspecting thread, for example, (or by a method called by your constructor) before your constructor (and any subclass constructors) finish, bad things may happen.
Edited to add: Your problem, as I understand it, is to make sure that a reference to the object is stored in particular places before whoever requested the creation of the object receives the newly created object. One common pattern is to do basically what you surmise in your question, namely having a factory method (a static method) that uses new to construct the object and then stores it in the appropriate places. This ensures that only references to a fully constructed object is stored. At the same time, the actual constructor is declared private or protected to prevent accidental use of new outside the factory method.
Related
I am creating a Listener class that a couple instances of a custom button in different Activities/Fragments are using. This class has listener methods that will update the respective ViewModel for that Activity/Fragment.
How do you define a ViewModel in a non-activity/fragment class? The documentation says to implement ViewModelStoreOwner, but I'm not really sure on how and what I should be implementing. I'm assuming if I don't implement it correctly, I'll have some sort of memory leak...
public class Listeners implements View.OnClickListener, ViewModelStoreOwner {
#NonNull
#org.jetbrains.annotations.NotNull
#Override
public ViewModelStore getViewModelStore() {
return // what do I do here, and how do I tell it to close the scope appropriately
// when the context is destroyed?
}
// Implement OnClick...
}
Am I just trying to abstract too much here? Does Android really just revolve around Activities and Fragments thus requiring me to have annoyingly long files? The above class is my attempt to reduce redundant implementations of a button listener between two activity/fragments
EDIT:
Is it wrong to just pass the store owner of the activity that this listener instance will eventually reside in? For example:
// Custom class constructor
public Listeners(ViewModelStoreOwner storeOwner) {
mModel = new ViewModelProvider(storeOwner).get(Model.class);
}
// Calling/parent activity/fragment/context
Listeners listeners = new Listeners(this);
mButton.setOnClickListener(listeners);
Unless someone posts an answer to this that says otherwise (and that this is a bad idea), I ended up utilizing the latter solution I updated my question with.
I passed the store owner into the custom Listener class as a parameter, then used this value to initialize my ViewModelProvider inside the custom class.
I believe this is safe, since the class is instantiated within the scope of that parent Fragment/Activity anyway.
So for instance, if you were calling this class from an activity/fragment:
// Calling from Activity
Listeners listeners = new Listeners(this);
// Calling from Fragment
Listeners listeners = new Listeners(requireActivity());
And the relevant class definition:
public Listeners(ViewModelStoreOwner storeOwner) {
mModel = new ViewModelProvider(storeOwner).get(Model.class);
}
I have a MVVM architecture in my Android app. In an activity, I invoke a method to try to create something from service/repository and return it. I am using RxJava.
Here is the flow:
I click something in view, it invokes method in the Activity.
Method in Activity invokes method in ViewModel.
Method in ViewModel invokes method in Interactor(/use-case).
Interactor has access to service and tries to create something from that service.
Here is the code for this:
Activity:
#Override
public void onCreateWalletClick(String password) {
addWalletViewModel.createWallet(password);
}
ViewModel:
public class AddWalletViewModel extends BaseViewModel {
private AddWalletInteractor addWalletInteractor;
private final MutableLiveData<Wallet> newWallet = new MutableLiveData<Wallet>();
private final MutableLiveData<ErrorCarrier> newWalletError = new MutableLiveData<ErrorCarrier>();
public LiveData<Wallet> newWallet() {
return newWallet;
}
public AddWalletViewModel(AddWalletInteractor addWalletInteractor) {
this.addWalletInteractor = addWalletInteractor;
}
public Single<Wallet> createWallet(String password){
return addWalletInteractor.addWallet(password)
.subscribe(wallet -> newWallet.postValue(wallet), this::addErrorToLiveData);
}
private void addErrorToLiveData(Throwable throwable){
newWalletError.postValue(new ErrorCarrier());
}
}
Interactor:
public class AddWalletInteractor {
private final KeyStoreServiceInterface keyStoreServiceInterface;
public AddWalletInteractor(KeyStoreServiceInterface keyStoreServiceInterface) {
this.keyStoreServiceInterface = keyStoreServiceInterface;
}
public Single<Wallet> addWallet(String password){
return keyStoreServiceInterface.
createWalletAndReturnWallet(password);
}
}
Service:
#Override
public Single<Wallet[]> getAllWallets() {
return Single.fromCallable(()-> {
Accounts accounts = keyStore.getAccounts();
int amount = (int) accounts.size();
Wallet[] wallets = new Wallet[amount];
for (int i = 0; i<amount; i++){
org.ethereum.geth.Account gethAccount = accounts.get(i);
wallets[i] = new Wallet(gethAccount.getAddress().getHex().toLowerCase());
}
return wallets;
}).subscribeOn(Schedulers.io());
}
Problem is I can not manage to get this to work by tweaking the code. Right now it forces me to cast to (Single) in the return of the createWallet() method in the viewmodel. When running the app, it crashes in that method with:
java.lang.ClassCastException:
io.reactivex.internal.observers.ConsumerSingleObserver cannot be cast
to io.reactivex.Single
at addwallet.AddWalletViewModel.createWallet(AddWalletViewModel.java:31)
Please keep in mind I am new to RxJava, I am still trying to figure it out. Any suggestions here?
The cast performed in the createWallet method will always fail.
Solution 1
The simplest way to fix the crash is to change the return type of that method to io.reactivex.disposables.Disposable, assuming you're using RxJava 2. If you're using RxJava 1, then have it return rx.Subscription. The code you presented that calls the createWallet method doesn't seem to use the returned value so it shouldn't make a difference.
Solution 2
If you really do need the return type to be Single and you want to keep the same behavior, then an alternate solution would be to change the createWallet method as follows:
public Single<Wallet> createWallet(String password) {
return addWalletInteractor.addWallet(password)
.doOnSuccess(wallet -> newWallet.postValue(wallet))
.doOnError(this::addErrorToLiveData);
}
The method now returns a new Single that does whatever the Single returned from addWallet does and additionally invokes the appropriate lambda function when a value is successfully emitted or an error occurs. You would also need to modify the call site for the method as follows:
#Override
public void onCreateWalletClick(String password) {
addWalletViewModel.createWallet(password).subscribe();
}
That subscribe call is needed to have the Single start emitting values. It takes no parameters because you already do all of the interesting work in the createWallet method itself. Both snippets were written with RxJava 2 in mind, but I believe they will also work in RxJava 1 as is.
If you haven't already done so, you should check out the official Rx website as it provides a ton of information on how reactive streams work and how to use them.
Since you're new to RxJava and the documentation is so vast, here's a brief overview of the subscription concept and how it applies to your situation.
RxJava and other stream-based libraries like it have two main components: producers and consumers. Producers supply values and consumers do something with those supplied values.
Single is a kind of producer that only produces one value before terminating. In your case, it produces a reference to the newly created wallet. In order to do something with that reference, it needs to be consumed. That's what the subscribe method on the Single class does. When the Single returned by the addWallet method produces a value, the lambda passed to the subscribe method is invoked and the wallet parameter in that lambda is set to the produced value.
The return type of the subscribe method is NOT itself a Single. When a consumer and a producer are coupled together by the subscribe method, it forms a connection which is represented by the Disposable class. An instance of that class has methods to cancel the connection before the producer is done producing values or to check if the connection has been cancelled. It is this connection object that is returned by the subscribe method.
Note that until this connection is made via one of the subscribe overloads, the producer will not start producing items. I.e., a Single that is never subscribed to will never do anything. It's analogous to a Runnable whose run method is never called.
I am trying to pass an object from an activity to another activity. Here is what i do:
MyApplication.db= dbToOpen;
Intent i = new Intent(mContext, OpenDbActivity.class);
i.putExtra("PARENT_GROUP", dbToOpen.root);
mContext.startActivity(i);
Here, MyApplication is the class that extends application, and db object is a static object. My extra object dbToOpen.root is an object of the class DBGroupv1.
Then i get this extra in onCreate method of OpenDbActivity class:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_opendb);
db = MyApplication.db;
groupToOpen = (DBGroupv1) getIntent().getSerializableExtra("PARENT_GROUP");
}
Then i try this boolean expression:
MyApplication.db.root == groupToOpen
and it returns false. When i look at the objects dbToOpen.root and groupToOpen, every single value of the variables inside those objects are the same. But they are are different objects. Why is this happening? Is it because of casting, or does Intent.putextra() method passes a copy of an object, not a reference? If that is the case how can i pass the object as a reference?(Except using static variables)
Thanks
You should use the .equals()-method to compare instances of objects. if you use == you will only get true if the two objects are exactly the same reference. Since the instance in your intent is newly created when deserialized from the bundle, it is no longer a reference to the same instance (although the two objects contains the same data).
So, instead of
MyApplication.db.root == groupToOpen //bad
use
MyApplication.db.root.equals(groupToOpen) //good
Also make sure that if you made the root-object, you implement the equals method properly, so it takes all appropriate variables into consideration.
You can read a bit more here: What is the difference between == vs equals() in Java?
What is the correct way to implement a constructor in android?
It seems that in an Activity or Service 'onCreate()' is where the magic happens.
The reason I ask is because I would like to be sure I'm doing the right thing declaring
attributes in the top of my classes (Context in particular) and then setting the attribute values inside onCreate.
// Activity launched via an Intent, with some 'extras'
public class SomeActivity extends Activity {
private Context context;
private String foo;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set the object attribute for later use, good or Bad to do this?
context = getApplicationContext();
Intent fooIntent = getIntent();
foo = fooIntent.getStringExtra("foo");
}
private void someMethodThatNeedsContext() {
// For example:
Cursor c = this.context.getContentResolver().query(foo, xxx, xxx);
// Or is it better practice to:
// A) Pass the context as a local variable to this method
// B) Use getApplicationContext() locally when needed
}
}
Maybe either of these options is ok, and I'm over thinking it?
Any specific reading and/or suggestions you may have would greatly be helpful to me.
Yes, you are correct that initialization is supposed to take place in onCreate(). You don't really need neither to store a reference to a context, nor to call getApplicationContext(). Your activity is a context itself, so you just use wherever you need a context. For example, making a toast within an activity:
Toast.makeToast(this, "Some text", Toast.LENGTH_LONG).show();
Option B - Since you can call getApplicationContext() from any non-static methods in your Activity class.
In fact, Activity is derived from Context too (Somewhere in the inheritance tree..) so you can just do:
Cursor c = getContentResolver()....
You don't have to keep a reference to a context. Especially not static, that can cause problems.
And you are correct - since you usually don't create your own constructor for Activities, you put the code for construction in onCreate.
You are writing a method inside your activity, so you can call getApplicationContext() anywhere in your code, you don't need to use a local variable :
Cursor c = getApplicationContext().getContentResolver().query(foo, xxx, xxx);
Also remember that the activity itself is a context (the Activity class is derived from Context), so generally you can use this whenever you need to provide a context ( for example when creating an Intent : new Intent(this, ...)).
I need to find a solution that holds and accesses large chunks of complex global data and methods. It has to be accessible from within activities and normal instance variables of various data classes.
This is how I have done it. I would just like to know if there is anything wrong with it or if there is a better/cleaner way.
First I extend Application like recommended many times...
public class MainDataManager extends Application{
public ... large chunks of data in arrays, lists, sets,....
//static variable for singleton access from within instance variables of other classes
public static MainDataManager mainDataManager;
//create and init the global data, and store it in the static variable of the class
#Override
public void onCreate() {
super.onCreate();
//in case it should get called more than once for any reason
if (mainDataManager == null) {
init();
mainDataManager = this;
}
}
Now accessing it from within activities like everywhere recommended...
MainDataManager mainDataManager = (MainDataManager)getApplicationContext();
And since I need to access it from normal instances of data classes ...
public class MyDataClass {
public MainDataManager mainDataManager;
public String name;
public MyDataClass(String namex) {
this.name = namex;
//this is why I defined the static variable within MainDataManager, so
//one has access to it from within the instance of MyDataClass
this.mainDataManager = MainDataManager.mainDataManager;
}
public void examplesForAccessing() {
//some examples on how to access the global data structure and associated methods
mainDataManager.someMethodAccess();
xyz = mainDataManager.someDataAccess;
mainDataManager.someIndirectMethodAccess.clear();
mainDataManager.someOtherData = false;
}
}
Since I have not done this so far, I would like to know if there is anything wrong with this. Memory, efficiency, ...
Thanks very much!
May I add a little sidenote?
I could also have just used a class MainDataClass and access by MainDataClass.var or MainDataClass.method(). Is there any REAL disadvantage?
Is the data in both cases held in heap/stack?
You haven't given much detail about your "large chunks of data" but keep in mind that the onCreate method is the first things that runs when your application is starting and it runs on the main/UI thread. This means that if you do long tasks in your init() method your UX will be poor, not to mention that you are risking an ANR exception.
The solution for that is simple:
Keep your onCreate short
Create a BG thread and use it to run all initialization code
Show a "Splash"/"Welcome" screen with the a proper progressbar while the BG thread is running.