Im interested if i can to set some common listeners inside main activity class? For my project i use FirebaseAuth, so i would like to init it in MainActivity onCreate(), setup needed listeners in onStart() and onStop(), and then inherit that class in every other activity class.
Some code to please you :]
MainActivity class [parent]:
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
protected FirebaseAuthentication firebaseAuthentication;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
firebaseAuthentication = new FirebaseAuthentication(FirebaseAuth.getInstance(), FirebaseDatabase.getInstance());
}
#Override
protected void onStart() {
super.onStart();
firebaseAuthentication.addAuthStateListener();
}
#Override
public void onStop() {
super.onStop();
firebaseAuthentication.removeAuthStateListener();
}
}
AuthActivity class [child]:
public class AuthActivity extends MainActivity implements FirebaseAuthentication.OnUserAuthListener {
#BindView(R.id.viewPager) LockableViewPager viewPager;
private String userUID;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_market);
ButterKnife.bind(this);
firebaseAuthentication.setOnUserAuthListener(this);
firebaseAuthentication.isSingedIn(); // check if user is singed in
}
#Override
// response for firebaseAuthentication.isSingedIn() above
public void onAuthSuccess(String userUID) {
this.userUID = userUID;
}
#Override
// response for firebaseAuthentication.isSingedIn() above
public void onAuthFailure(String message) {
snackbar(message);
Intent intent = new Intent(this, AuthActivity.class);
startActivity(intent);
finish(); // TODO mb should to delete it
}
}
Can this implementations bring me errors (maybe NullPointerExeption or what unexpectedly in future)?
Would be great if you provide me some sources to read/watch.
Thank you.
Perfect example of abstraction, but not really a question.
You will not get any nullpointers or other errors by implementing it like this.
Related
I decided to try and make my code more object oriented and avoid repetitive code in another class.
Source code for Activities :
public class EasyMode extends MainActivity {
GameActivityPVP game = new GameActivityPVP();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game_layout_pvp);
game.initializeButtons();
}
}
public class GameActivityPVP extends MainActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game_layout_pvp);
initializeButtons();
}
public void initializeButtons() {
button[0] = (Button) findViewById(R.id.button1);
}
}
The second the program gets to the line where I try to call a method using game.methodName(); the program crashes. No compiling errors or anything.
I am new to programming in general so please take it easy on me and I tried to simplify my code as much as possible.
Android Monitor/logcat :
W/OpenGLRenderer: Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...
and
W/art: Before Android 4.1, method int android.support.v7.widget.ListViewCompat.lookForSelectablePosition(int, boolean) would have incorrectly overridden the package-private method in android.widget.ListView
You can use another class's method by creating object of parent class.
See below example;
Here you want to use method from 'GameActivityPVP' class. So you need to create one object in this class only.
public class GameActivityPVP extends MainActivity {
public static GameActivityPVP mGameActivity;
public GameActivityPVP getInstance(){
return mGameActivity; // assign value in onCreate() method.
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game_layout_pvp);
mGameActivity = this; // Do not forget this, otherwise you'll get Exception here.
initializeButtons();
}
public void initializeButtons() {
button[0] = (Button) findViewById(R.id.button1);
}
}
Now use this Object in another class 'EasyMode' like this;
if(GameActivityPVP.getInstance()!=null){
GameActivityPVP.getInstance().initializeButtons();
}
Try This:
Make one Class Utils:
In Utils:
public class Utils{
private Activity context;
Button button;
public Utils(Activity context) {
this.context=context;
}
public void inititializeButton(Activity context){
button[0]= (Button) context.findViewById(R.id.button_flasher);
}
}
And in your Class use:
public class EasyMode extends MainActivity {
Utils utils;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game_layout_pvp);
utils=new Utils(this);
utils.initializeButtons();
}
}
As already stated, you shouldn't use nested activities, they are not supposed to interact like this. If you want two activities to interact you have to do it through an intent. Regarding the duplicated code, you have few solution presented but my personal opinion is that the OOP rules are not followed. If I had to write that logic, I would create a BaseActivity to hold the common logic of the other two activities and use inheritance to extend them.
public class BaseActivity extends Activity {
protected List<Button> buttons = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game_layout_pvp);
initializeButtons();
}
protected void initializeButtons() {
buttons.add((Button) findViewById(R.id.button1));
}
}
public class EasyMode extends BaseActivity {
// Add here logic that is used only in EasyMode activity
}
public class GameActivityPVP extends BaseActivity {
// Add here logic that is used only in GameActivityPVP activity
}
Note that in this way you don't have to override onCreate again to initialise the buttons and so on. Also, I saw that you used the same layout for both activities, but if you want to use different layouts you can do it as usual and then call initializeButtons.
I have espresso test which verifies that text is displayed:
public class InformationActivityTests {
#Rule
public ActivityTestRule<InformationActivity_> mInformationActivityTestRule =
new ActivityTestRule<InformationActivity_>(InformationActivity_.class) {
#Override
protected Intent getActivityIntent() {
Intent i = new Intent(getTargetContext(), InformationActivity_.class);
i.putExtra("INFORMATION", "Espresso");
return i;
}
};
#Test
public void textIsDisplayed() {
onView(withText("Espresso")).check(matches(isDisplayed()));
}
}
This test passes when Activity has following code:
#EActivity
public class InformationActivity extends AppCompatActivity {
#ViewById(R.id.information_view)
TextView informationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_information);
Intent intent = getIntent();
String information = intent.getStringExtra("INFORMATION");
informationView.setText(information);
}
}
but fails when I "move" setContentView to #EActivity annotation:
#EActivity(R.layout.activity_information)
public class InformationActivity extends AppCompatActivity {
#ViewById(R.id.information_view)
TextView informationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
String information = intent.getStringExtra("INFORMATION");
informationView.setText(information);
}
}
Error is:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.edu/com.edu.InformationActivity_}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
Am I doing something wrong or is it an issue with espresso / android-annotations?
I've checked code generated by android-annotations when using #EActivity(R.layout.activity_information) and this is how onCreate looks like in generated class (InformationActivity_):
public void onCreate(Bundle savedInstanceState) {
OnViewChangedNotifier previousNotifier = OnViewChangedNotifier.replaceNotifier(onViewChangedNotifier_);
init_(savedInstanceState);
super.onCreate(savedInstanceState);
OnViewChangedNotifier.replaceNotifier(previousNotifier);
setContentView(R.layout.activity_information);
}
the problem is that it first calls super.onCreate (so onCreate from InformationActivity) where I handle intent and TextView and then it calls setContentView and this can't work like that.
The solution for this is what Be_Negative suggested, so using #AfterViews annotation. It also simplifies code a bit (I could remove onCreate method):
#EActivity(R.layout.activity_information)
public class InformationActivity extends AppCompatActivity {
#ViewById(R.id.information_view)
TextView informationView;
#AfterViews
void handleIntent() {
Intent intent = getIntent();
String information = intent.getStringExtra("INFORMATION");
informationView.setText(information);
}
}
Your problem is that you are never initializing informationview
public class InformationActivity extends AppCompatActivity {
#ViewById(R.id.information_view)
TextView informationView; //<-- Null
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
String information = intent.getStringExtra("INFORMATION");
informationView.setText(information); //<--STILL NULL
}
}
So you first will have to initialize that view.
I am using Espresso and Mockito for testing an Activity. Is it possible to mock a method before an activity is created.
public class MyActivity extends Activity {
public int i;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
callSomeMethod();
callAnotherMethod();
}
protected void callSomeMethod() {
//do some work
}
protected void callAnotherMethod() {
//do some work
}
}
My test class looks like
public class ActivityTest{
#Rule
public MyActivityRule testRule = new MyActivityRule(MyActivity.class);
public void test_preconditions(){
assertNotNull(testRule.getActivity())
}
}
But i need to mock callSomeMethod() and callAnotherMethod() method. Thanks in advance.
I have implemented an otto bus example. It works fine, but ONLY on the second time I visit the activity.
For example, when I load the app and hit the secret message button I am taken to the activity but the toast does not show. Then I hit the back button to return to the MainActivity and hit the show secret message button again and when I am taken to the secret message activity the toast is displayed. I realize it works the second time because I have created a leak by not unregistering the event.
Is there something I am missing about the logic?
MainActivity:
public class MainActivity extends AppCompatActivity {
Button buttonSecretMessage;
Intent intentToMessage;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentToMessage = new Intent(MainActivity.this, SecretMessageActivity.class);
buttonSecretMessage = (Button) findViewById(R.id.buttonSecretMessage);
buttonSecretMessage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EventBus.getInstance().post(new MakeMySecretMessageEvent());
startActivity(intentToMessage);
}
});
}
}
Secret Message Activity:
public class SecretMessageActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_secret_message);
}
#Subscribe
public void getMySecretMessage(MakeMySecretMessageEvent event){
Toast.makeText(this, event.getMessage(), Toast.LENGTH_SHORT).show();
}
#Override
protected void onStart(){
super.onStart();
EventBus.getInstance().register(this);
}
#Override
protected void onStop() {
super.onStop();
//EventBus.getInstance().unregister(this);
}
}
MakeMySecretMessageEvent:
public class MakeMySecretMessageEvent {
public MakeMySecretMessageEvent() {
}
public String getMessage() {
String message = "YOU ARE AWESOME!";
return message;
}
}
EventBus:
public final class EventBus extends Bus{
private static final EventBus Bus = new EventBus();
public static Bus getInstance() {
return Bus;
}
private EventBus() {
}
}
You can send sticky event using EventBus library. It allows you to send events to component which is not created yet.
You`ll find more info here.
Here EventBus has applied in wrong scenario, when you can simply send data via intent or bundle. Which is more reliable in communication with one activity with another. You will never ever receive event on first click, as event fire is instant and your activity creation will take some time accordingly.
So try to use bundle or intent to setup communication b/w to activity one after another.
Thanks to contributors I now have a better understanding of the activity life cycle and how it fits in with event bus. That is you cannot send an event from the MainActivity to its children, but the other way around instead. Below reflects how to implement an otto event bus to pass a simple object from an activity back to the main activity. Hopefully someone else can find this useful :) And if this can be improved upon please comment. Thanks.
Main Activity:
public class MainActivity extends AppCompatActivity {
Button buttonSecretMessage;
Intent intentToMessage;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getInstance().register(this);
intentToMessage = new Intent(MainActivity.this, SecretMessageActivity.class);
buttonSecretMessage = (Button) findViewById(R.id.buttonSecretMessage);
buttonSecretMessage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(intentToMessage);
}
});
}
public MakeMySecretMessageEvent event;
#Subscribe
public void getMySecretMessage(MakeMySecretMessageEvent event) {
Toast.makeText(this, event.getMessage(), Toast.LENGTH_SHORT).show();
}
protected void onStop() {
super.onStop();
if(event != null ){
EventBus.getInstance().unregister(this);
}
}
}
SecretMessageActivity (this is where the secret message is created)
public class SecretMessageActivity extends AppCompatActivity {
Button buttonClickToMeToSeeMessage;
Intent intentToMain;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_secret_message);
intentToMain = new Intent(SecretMessageActivity.this, MainActivity.class);
buttonClickToMeToSeeMessage = (Button) findViewById(R.id.buttonClickToMeToSeeMessage);
buttonClickToMeToSeeMessage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MakeMySecretMessageEvent makeMySecretMessageEvent = new MakeMySecretMessageEvent();
EventBus.getInstance().post(makeMySecretMessageEvent);
startActivity(intentToMain);
}
});
}
}
MakeMySecretMessageEvent
public class MakeMySecretMessageEvent {
public MakeMySecretMessageEvent() {
}
public String getMessage() {
String message = "YOU ARE AWESOME!";
return message;
}
}
EventBus:
public final class EventBus extends Bus{
private static final EventBus Bus = new EventBus();
public static Bus getInstance() {
return Bus;
}
private EventBus() {
}
}
Maybe this question is duplicate but the answers used static references to archive this. I know that static variables can cause memory leak so we should avoid using them.
How can I do that without using static method or references?
public class FirstActivity extends Activity
{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);
}
// Utility.method() used somewhere in FirstActivity
}
public class Utility {
public static void method()
{
}
}
public class SecondActivity extends Activity
{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);
Utility.method();
}
}