1

I am wondering when @ViewById-annotated views are injected in AndroidAnnotations. Basically, I want to know if it is safe to access one of these views during onResume? I assume they are injected during onCreate but would like confirmation.

Thank you.

stkent
  • 19,772
  • 14
  • 85
  • 111
Snake
  • 14,228
  • 27
  • 117
  • 250
  • You are definitely safe to use a view annotated with `@ViewById` in `onResume`. If you need more precise information, I can investigate tomorrow. – stkent Apr 19 '19 at 03:12
  • Yea I would like to understand when initialization take place so I have better control in future. For example if I have a method with @AfterViews and the OnResume.. Which one executes first? If you can get me the info in an answer so I can accept then it would be great! – Snake Apr 19 '19 at 03:17

1 Answers1

4

The easiest way to figure out exactly when injection happens is to inspect the code that AndroidAnnotations generates. For your examples, I made a simple Activity and Fragment as below:

@EActivity(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {

    @ViewById(R.id.textView)
    TextView textView;

    @AfterViews
    public void activityTestMethod() {

    }

}
@EFragment(R.layout.fragment_main)
public class MainFragment extends Fragment {

    @ViewById(R.id.imageView)
    ImageView imageView;

    @AfterViews
    public void fragmentTestMethod() {

    }

}

and then ran ./gradlew app:assembleDebug to force AndroidAnnotations to generate the corresponding classes MainActivity_ and MainFragment_. Let's look at MainActivity_ first (irrelevant code omitted):

public final class MainActivity_
    extends MainActivity
    implements HasViews, OnViewChangedListener
{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        OnViewChangedNotifier previousNotifier = OnViewChangedNotifier.replaceNotifier(onViewChangedNotifier_);
        init_(savedInstanceState);
        super.onCreate(savedInstanceState);
        OnViewChangedNotifier.replaceNotifier(previousNotifier);
        setContentView(R.layout.activity_main);
    }

    private void init_(Bundle savedInstanceState) {
        OnViewChangedNotifier.registerOnViewChangedListener(this);
    }

    @Override
    public void setContentView(int layoutResID) {
        super.setContentView(layoutResID);
        onViewChangedNotifier_.notifyViewChanged(this);
    }

    @Override
    public void onViewChanged(HasViews hasViews) {
        this.textView = hasViews.internalFindViewById(R.id.textView);
        activityTestMethod();
    }

}

The sequence of events that results in our views being bound and our @AfterViews methods being called is as follows:

  • In onCreate, the MainActivity_ instance is registered as an OnViewChangedNotifier.
  • onCreate calls setContentView.
  • setContentView calls notifyViewChanged, which triggers a (synchronous) call to onViewChanged.
  • onViewChanged binds all fields annotated with @ViewById, then calls all methods annotated with @AfterViews.

Therefore, @ViewById-annotated views are bound and available for use after onCreate has been called, and @AfterViews-annotated methods will be executed at the end of onCreate and before any other Activity lifecycle method.

The story is similar for MainFragment_:

public final class MainFragment_
    extends com.stkent.aatest.MainFragment
    implements HasViews, OnViewChangedListener
{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        OnViewChangedNotifier previousNotifier = OnViewChangedNotifier.replaceNotifier(onViewChangedNotifier_);
        init_(savedInstanceState);
        super.onCreate(savedInstanceState);
        OnViewChangedNotifier.replaceNotifier(previousNotifier);
    }

    private void init_(Bundle savedInstanceState) {
        OnViewChangedNotifier.registerOnViewChangedListener(this);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        onViewChangedNotifier_.notifyViewChanged(this);
    }

    @Override
    public void onViewChanged(HasViews hasViews) {
        this.imageView = hasViews.internalFindViewById(R.id.imageView);
        fragmentTestMethod();
    }
}

The sequence of events that results in our views being bound and our @AfterViews methods being called is as follows:

  • In onCreate, the MainFragment_ instance is registered as an OnViewChangedNotifier.
  • onViewCreated calls notifyViewChanged, which triggers a (synchronous) call to onViewChanged.
  • onViewChanged binds all fields annotated with @ViewById, then calls all methods annotated with @AfterViews.

Therefore, @ViewById-annotated views are bound and available for use after onViewCreated has been called, and @AfterViews-annotated methods will be executed at the end of onViewCreated and before any other Fragment lifecycle method.

In both our examples, all view binding is performed in a lifecycle method that occurs much earlier than onResume, so you are safe to access them there :)

stkent
  • 19,772
  • 14
  • 85
  • 111