android, how to separate functionality of activity over multiple classes? - java

I'm implementing GCM for the first time and the sample app on google provides DemoActivity which deals with GCM functionality. (http://developer.android.com/google/gcm/client.html)
I can copy those gcm related codes over to my MainActivity, but I 'd like to keep things separate, ie. create a separate file for gcm and let MainActivity use it.
In python world, mixin would be great fit here.
But I'm not sure if mixin exists for java and if its the right tool here.
How would one implement the GCM functionality in a separate class in java?
I'm thinking something like the following.
Create GcmHelper.java
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-->
public void onCreate(Bundle savedInstanceState, Activity activity) {
context = getApplicationContext();
this.mActivity = activity;
MainActivity::onCreate creates GcmHelper() and calls GcmHelper.onCreate(bundle, this)
do make similar changes for onResume() and activity related code to use the handed-over activity.

Create a Seperate Class eg. GcmHelper
Make GcmHelper constructor using Context parameter
Copy Every GCM related functions to GcmHelper
Create object of GcmHelper by passing the context in MainActivity->OnCreate
Finally call the required methods...

Related

Passing custom parameters to a parent activity through onCreate method yields confusing linter error

Why does passing custom parameters to a parent activity through an onCreate method, while leaving overriding the root method to the parent, result in the following linter error:
Overriding method should call super.onCreate
Background
For example, I have a MainActivity class that extends from ParentActivity that extends Android's Activity.
UML Generator
In order to make my app more abstract I am trying to handle several things in ParentActivity that the developer need not see in their development and use of MainActivity.
I have several parameters I would like to pass to the ParentActivity, like booleans turning on and off log functionalities, but it seems passing them through the onCreate() method is not ~~possible~~ recommended since this throws linter errors. I will make a separate question regarding the best practices for passing such parameters upward to custom parent classes using custom methods or directly setting parent fields, but I was looking to verify my current understanding of why this is not ~~possible~~ recommended through the existing onCreate method and additional parameters.
What has been tried
As a starting point, if I have some basic MainActivity and ParentActivity:
Code 0.1
public class MainActivity extends ParentActivity{
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
//Do stuff
}
}
public class ParentActivity extends Activity{
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
//Do stuff
}
}
and then try to add further parameters to onCreate, e.g. a boolean to turn on/off some logger functionality within ParentActivity:
Code 0.2
public class MainActivity extends ParentActivity{
boolean logOn = true;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState, logOn);
//Do stuff
}
}
public class ParentActivity extends Activity{
// No longer overriding Activity.onCreate() due to diff in params
protected void onCreate(Bundle savedInstanceState, boolean logOn){
super.onCreate(savedInstanceState);
//Do stuff
}
}
Android Studio first warns me that I am not overriding the parent's method, which makes sense as it has a different parameter count, but then I thought I can just remove the #Override and call it good since I'm still calling super.onCreate(savedInstanceState) in ParentActivity, which will pass the savedInstanceState up to Activity, and I'm still passing the savedInstanceState to ParentActivity from MainActivity. At this point I encountered my first unknown issue: back in MainActivity, I get a linter error that states
Overriding method should call super.onCreate
Whats confusing is that I do call super.onCreate(savedInstanceState, logOn) directly below where I get this error. Although the error message is not too informative, I can get rid of the error by calling super.onCreate(savedInstanceState) directly above the already existing call to super.onCreate(savedInstanceState, logOn), i.e.:
Code 0.3
public class MainActivity extends ParentActivity{
boolean logOn = true;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
super.onCreate(savedInstanceState, logOn);
//Do stuff
}
}
public class ParentActivity extends Activity{
// No longer overriding Activity.onCreate() due to diff in params
protected void onCreate(Bundle savedInstanceState, boolean logOn){
super.onCreate(savedInstanceState);
//Do stuff
}
}
Looking at the tooltip inline doc using Android Studio, I see that super.onCreate(savedInstanceState); is calling the onCreate method from Activity (i.e the parent class of ParentClass) and super.onCreate(savedInstanceState, logOn); is calling the onCreate method of ParentClass. With typical inheritance in mind, and matching parameter lengths and types, this makes sense.
What doesn't make sense to me is why I have to call the onCreate method of Activity in MainActivity. Why does the call to onCreate within ParentActivity not suffice? i.e. why does Code 0.2 throw the linter error:
Overriding method should call super.onCreate
? I note, as per the comment by #greeble31 that Code 0.2 compiles, and runs on my example smartphone, but the linter error remains.
I don't think it's too much to worry about, since the problem is limited to a lint warning. You just did something a little too complicated for the linter to follow; your program is not, in fact, incorrect.
You could suppress the warning (#SupressLint), or simply ignore it.
...I can just remove the #Override and call it good...
Not too sure I agree with you, there... Removing the #Override annotation can't really help anything; the linter/compiler still knows what's an override and what's not. I consider the annotation useful b/c the IDE will tell you if you think you're overriding a method, but you're actually not, i.e. due to a signature mismatch or something (as here).
SUGGESTED APPROACH
FWIW, I would've solved this problem a little differently. (Note that code 0.3 actually results in two calls to the base class onCreate(); that's probably illegal.) I would just change the method name (to reflect a semantic distinction between configuration and creation), and store some state information in the base class:
public class MainActivity extends ParentActivity{
boolean logOn = true;
#Override
protected void onCreate(Bundle savedInstanceState){
configure(logOn); //Required, per base class specification
super.onCreate(savedInstanceState);
//Do stuff
}
}
public abstract class ParentActivity extends Activity{
boolean logOn;
boolean configured = false;
/** Subclasses are obligated to call this before calling super.onCreate() */
protected void configure(boolean logOn)
{
this.logOn = logOn;
this.configured = true;
}
#Override
protected void onCreate(Bundle savedInstanceState){
if(!configured)
throw new IllegalStateException("configure() not called prior to onCreate()");
super.onCreate(savedInstanceState);
//Do stuff
}
#Override
protected void onDestroy() {
configured = false; //(just being pedantic)
}
}
It's really just a matter of taste.
Sometimes people don't realize that Android can spontaneously create Activities on its own, like when it restores the back-stack state of your app. (This is why you want to be able to serialize and deserialize your Activity state to/from the bundle; b/c your Activity might not be re-created using the same workflow that caused its creation the first time).
I don't think that's going to be an issue for you, though, since ParentActivity (which I've declared as abstract) is always going to be instantiated via a concrete subclass, and all subclasses are guaranteed to call configure() in their onCreate() methods. (IOW, ParentActivity wouldn't have a manifest entry, so the system is never going to try to instantiate a base-class ParentActivity by itself.)
Just something to be aware of. (You had logOn set to a constant value in MainActivity, so if you were planning on changing that dynamically based on the app state, before calling configure()/super.onCreate(), just bear in mind that -- if you don't take steps to prevent it -- that information could be lost when your app is restored to the foreground.)
IDE will check #CallSuper annotation when on compilation {#link Activity#onCreate}.

Communication Between Two Android Activities via Reference to Caller or Callee

I have two activities Activity A and Activity B inside of the same application and they need to be able to call methods on each other. A needs to communicate to B at the start (possibly see "code" below).
B will call a lot of methods on A (this means that I cannot use the startActivityForResult method for communication because this would close B (Activity B is a Bluetooth Client and Server because it is a Peer to Peer app)). I do use startActivityForResult to start B for more of a kill signal than anything as the result.
Activity A uses a SupportMapFragment and Activity B cannot be a fragment because I want to be able to switch from B to A and then never use B again.
Originally, I started with one activity and tried switching between views using a ViewFlipper and just a call to setContentView(R.layout.my_layout_A) or setContentView(R.layout.my_layout_B). The fragment of course caused a lot of problems for both.
Using Fragments were confusing. The SupportMapFragment is the code for a Google Map which is a Fragment.
When I click a menu option inside of the MapsActivity (Activity A), I would like to be able to start myBluetoothActivity (Activity B) with a reference to the MapsActivity (Activity A) OR start myBluetoothActivity and then be able to set a reference to the caller inside of myBluetoothActivity (but this option would require having a reference to the BluetoothActivity inside of the MapsActivity or having some way to obtain the started activity from the intent).
//the following code is in Kotlin, but this can easily be converted over to java:
//option A: (pass it inside of the constructor)
var mbta:myBluetoothActivity = myBluetoothActivity(this)
//line for intent that I am unsure of
//intent so that I can start the activity with the pointer to the caller already passed into the new activity
startActivity(mbta)
//option B: (set this reference after obtaining a reference from intent):
var mintent:Intent = Intent(this.applicationContext, myBluetoothActivity::class.java)
startActivity(mintent)
//obtain the reference to the BluetoothActivity from the intent (NOT SURE HOW TO DO THIS???)
mbta.setCallerReference(this)
How can I accomplish this communication between the two activities via reference between the two activities? Should I use an interface for communication? If I should use it, (which I did try) how should I?
In other words, I am trying to access the caller activity (Activity A) from (Activity B) directly via a reference to Activity A inside of B OR trying to get the reference to B from the intent that started it inside of Activity A. I am trying to get a reference to that, so I can use it for communication/method calling/member variable and UI modification purposes.
NOTES: 1. The BluetoothActivity and the MapsActivity are NOT SERIALIZABLE. I tried serializing it and then adding it to the extras Bundle in the Intent and it just crashed saying that that was impossible to serialize due to BroadCastReciever. As this also deals with WIFI. Which I am highly considering separating out to be put with the BluetoothActivity in a future release.
I am also ASSUMING that Activity B will never just be started up by anything other than my MapsActivity class.
I am also new to Kotlin, but I know Java.
When I tried using an interface, I caused a StackOverflow error and I have no idea why.
I have read the documentation for Intents on the website.
I have done some research on here which gave me those ideas above. I am not sure how to implement them.
You are using the wrong approach. The solution requires a bit more work than you would think. The correct approach is to:
First, realize that these activities Activity A and Activity B (and any other activities) are activities that are specific to your application and you want to establish direct communication between them.
Second, realize that you are trying to get the current (or a previous) activity's context. The context will help serve the reference.
Third, you can create your own Activity and Application classes by extending the desired classes. The Application class is a low-level class used for the activities.
From here, you will be able to make use of the getApplicationContext() which will return your custom Application class.
Design: It is inside of your CustomApplication class that you must track the references to the activities that you want. From there all that you have to do is cast the getApplicationContext() to your CustomApplication class and then call your methods that access the Activity(ies). You must of course cast your Activities if you want to access certain instances of a specific activity that you created to its "type." For example:
MapsActivity mact = (MapsActivity)(((MyApplication)(this.getApplicationContext())).getCurrentActivity())
You must of course note that this activity must be already created (the onCreate method was already called) for this to return the current activity. The same of course goes for the other life-cycle methods for the activity as you will make a baseActivity which will deal with these as well as you will also have an Application life-cycle that will help deal with this too.
To answer the question: "How to get the current foreground activity context in android?" I turned to StackOverflow and found user: gezdy 's answer to be exactly what I needed at: How to get current foreground activity context in android?.
(BEGIN QUOTATION FROM: GEZDY)
You should manage activities references. Add the name of the
application in the manifest file :
<application
android:name=".MyApp"
....
</application>
Your application class :
public class MyApp extends Application {
public void onCreate() {
super.onCreate();
}
private Activity mCurrentActivity = null;
public Activity getCurrentActivity(){
return mCurrentActivity;
}
public void setCurrentActivity(Activity mCurrentActivity){
this.mCurrentActivity = mCurrentActivity;
}
}
Create a new Activity :
public class MyBaseActivity extends Activity {
protected MyApp mMyApp;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMyApp = (MyApp)this.getApplicationContext();
}
protected void onResume() {
super.onResume();
mMyApp.setCurrentActivity(this);
}
protected void onPause() {
clearReferences();
super.onPause();
}
protected void onDestroy() {
clearReferences();
super.onDestroy();
}
private void clearReferences(){
Activity currActivity = mMyApp.getCurrentActivity();
if (this.equals(currActivity))
mMyApp.setCurrentActivity(null);
}
}
So, now instead of extending Activity class for your activities, just
extend MyBaseActivity. Now, you can get your current activity from
application or Activity context like that :
Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();
(END OF QUOTATION FROM: GEZDY)
Note: All code is written in java for this answer.

Sending information from one fragment to another Android

I am having this issue where I have nested fragments. I need one to be able to send information to the other.
For example, if I press a button in the inner fragment I want the outer fragment to perform an action.
Any help would be great!
Edit: I forgot to mention that both of these are live fragments. As far as I know using bundles only allows me to set arguments when creating a fragment but not after it has been created.
Well there are plenty of ways to manage this. It really depends on the architecture you want to go with.
You could go lazy and use a Bus library that uses sub/pub modeling and reflection for notifying generic objects that registered and have matching signature methods of changes, However, this is frowned upon to use reflection for regular coding practices that could be implemented without it.
I would say you can either create an interface that represents the calls that would go back and forth like IMainActivityCallbacks with methods like dataChange(myObject obj)
.
Then in your onAttach of fragment you cast the context as IMainActivityCallbacks. Make sure the MainActivity implements the interface.
Then simply call the mIMainActivityCallbacks.dataChange(someObject);
This will call the method in the parent activity and allow him to pass it to the child fragment that needs it with mMyOtherFragment.publicMethod(newDataJustReceived).
You could also get crazier if you want and pass an interface into the child fragment that references the outer fragment so when MainActivity news up the children fragment, the outer one could implement an interface of doStuff and then be passed as an interface to the inner child fragment. So the child fragment can easily say if "mParentInterface" is not null then mParentInterface.doStuff.
There are millions of ways to skin a cat, but prefer to use interfaces rather then Reflection based libraries typically. Hope that helps.
Use interface in this case. 1) Define interface in you first fragment(in which you click something and waiting something to happen in a second fragment) and use callback method you define in your interface. For example:
OnButtonClickListener mCallback;
......your code
public interface OnButtonClickListener {
void onButtonSelected(int position);
}
......your code
then you call your callback method
.....
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
mCallback.onButtonSelected(position);
}
});
2) Then implement this interface in host activity you have for nested fragments.
For example:
public class MainActivity extends AppCompatActivity implements YourFragment.OnButtonClickListener{....
3) Define callback method of your interface in this activity. For example:
public void onButtonSelected(int position) {
...Here just change something in your second fragment.....
Hope this will be helpful.
I use greenrobot EventBus in that cases.
You need to add this library to your build gradle:
compile 'org.greenrobot:eventbus:3.0.0'
That you can send message like this:
EventBus.getDefault().post(new ActionEvent("test action"));
And another fragment can catch it this way:
#Subscribe(threadMode = ThreadMode.MAIN)
public void onActionEvent(ActionEvent event) {
//do something
}
You can sending different actions, if you want, same way.
Don`t forget to according to fragment lifecycle:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}

Cannot set the view from activity android

I am facing a strange problem suddenly in android. I am creating layout .xml file in res>layout but from a class that extends activity, I can not access it. I have done this several a time but I can not figure out this strange problem. I restarted the eclipse, even the computer but no. When I go for setting the contentView it says something as below :
The method onCreate(Bundle) of type MainActivity must override or implement a supertype method
That is let I have created an xml file at res>layout named "whynot.xml" and from a activity when I try to set it like
setContentView(R.layout.whynot);
It shows red mark below and the above message.
you have to extends Activity and call super.onCreate(savedInstanceState) in the onCreate. For each callback of the Activity's lifecycle is mandatory to call its super
As the error says,
must override or implement a supertype method
you need the super call like
#Override
protected void onCreate(Bundle bundle)
{
super.onCreate(bundle); // here
setContentView(...);
...
}

Google Play Game Services - Listener null pointer exception

I am still fairly new to Android and I am trying to implement Achievements inside my app. I basically want to replicate the achievements implemented in the "Type-a-Number Challenge" sample app given on the Google play developer site here.
I have a first activity that contains the methods and classes to handle the achievements, and a second activity where I have the variables that would be forwarded to the first activity for "processing". I copied the code that I believed was necessary for doing this, but I am always getting a null pointer exception when calling the listener inside the second class.
Here is my listener in the second activity:
public interface Listener {
public void onEnteredScore(int score);
}
Listener mListener = null;
public void setListener(Listener l) {
mListener = l;
}
The null pointer exception is flagged here when I call the listener as such (where mRequestedScore is different to 0):
mListener.onEnteredScore(mRequestedScore);
The first activity's class implements the second activity listener like this:
public class FirstActivity extends BaseGameActivity
implements SecondActivity.Listener
And includes the onEnteredScore method as such
#Override
public void onEnteredScore(int requestedScore) {
checkForAchievements(requestedScore);
pushAccomplishments();
}
I am not entirely sure if the error appears because the listener is expecting a click or some action by the user, or the "linkage" is not being established properly between both activities.
I looked around for similar issues but haven't found anything yet.
Apologies if the mistake is obvious.
Thanks in advance for any help provided.
Your "linkage" isn't established, hence the null.
If you want to pass data between Activities (not fragments), you'll need to use the Intent framework - see How do I pass data between Activities in Android application?
The Interface method that you are following in the Type a Number Challenge is useful for passing data between Fragments and the parent Activity. That is what it is being used for in that example. You don't appear to be using Fragments.
For achievements, you are probably better served using a class attached to your FirstActivity. That class can then implement your Listener interface (if the interface is even useful at that point). Using a separate activity is far more heavyweight than is required.

Categories