CompositeAndroid Documentation

repository·master·Indexed 19 days ago

https://github.com/passsy/compositeandroid

A library that solves the 'BaseActivity' problem in Android by using composition instead of inheritance. It allows developers to inject modular functionality via Plugins into Activities and Fragments, enabling the interception of lifecycle methods without complex inheritance trees. The library includes CompositeActivity and CompositeFragment implementations and provides a generator for creating Blueprints from target classes.

Tokens
1.7K
Snippets
4
Records
7
Agent score
18%

What's inside CompositeAndroid

  1. How Plugins and Composite implementations work together

    master

    CompositeAndroid uses a 'Composition over inheritance' approach to add functionality to Android components without creating deep inheritance hierarchies.

    Instead of extending a BaseActivity that contains everything, you extend a composite implementation (like CompositeActivity) and add specific Plugins via the constructor.

    Key Lifecycle Rules:

    • Registration: Always add plugins in the constructor. Do not add them in onCreate(), as many lifecycle methods are called before onCreate() and the plugin needs to be present to intercept them.
    • Execution: Plugins can intercept every method of the Activity/Fragment. You can choose to execute code before calling super, after calling super, or by explicitly not calling super at all.
    public class MainActivity extends CompositeActivity {
    
        final LoadingIndicatorPlugin loadingPlugin = new LoadingIndicatorPlugin();
    
        public MainActivity() {
            addPlugin(new ViewTrackingPlugin("Main"));
            addPlugin(loadingPlugin);
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // ...
    
            // example usage of the LoadingIndicatorPlugin
            loadingPlugin.showLoadingIndicator();
        }
    }
  2. What are Blueprints in CompositeAndroid

    master
    Blueprints serve as the input files for the CompositeAndroid library generation process. A Blueprint is a class that extends a target class and overrides all of its public and protected methods. When generating a library, the Blueprint also carries over the original method's Javadoc documentation.
  3. Generate input files for the Composite Generator

    master

    To provide the generator with the necessary metadata (method names and JavaDocs) from classes like AppCompatActivity or SupportFragment, you should use Android Studio to generate the source files.

    Follow these steps:

    1. Create a new Java class that extends the target class (e.g., class MyActivity extends AppCompatActivity {}).
    2. Use Android Studio's code generation feature to override all possible methods in that class.
    3. Ensure that the generated methods retain their original JavaDoc comments.

    This approach creates a simplified .java file that the generator can reliably parse using Regular Expressions, bypassing the limitations of reflection which cannot easily access variable names or JavaDoc content.

  4. Extend from CompositeActivity or CompositeFragment

    master

    To use plugins, you must change your class inheritance from the standard Android components to the CompositeAndroid implementations.

    - public class MyActivity extends AppCompatActivity {
    + public class MyActivity extends CompositeActivity {
    - public class MyFragment extends Fragment { // v4 support library
    + public class MyFragment extends CompositeFragment {
  5. Install CompositeAndroid via Gradle

    master

    CompositeAndroid is available via jcenter. It is divided into modules for Activities, Fragments, and a core module.

    Important: You must use the same version number for CompositeAndroid as you are using for your Android support library to ensure compatibility.

    dependencies {
        // it's very important to use the same version as the support library
        def supportLibraryVersion = "25.0.0"
        
        // contains CompositeActivity
        implementation "com.pascalwelsch.compositeandroid:activity:$supportLibraryVersion"
    
        // contains CompositeFragment and CompositeDialogFragment
        implementation "com.pascalwelsch.compositeandroid:fragment:$supportLibraryVersion"
    
    
        // core module (not required, only abstract classes and utils)
        implementation "com.pascalwelsch.compositeandroid:core:$supportLibraryVersion"
    }
  6. Create a custom ActivityPlugin

    master

    To create a new piece of functionality, extend ActivityPlugin. You can override any lifecycle method. The implementation logic remains identical to how you would write it in a standard Activity override.

    public class ViewTrackingPlugin extends ActivityPlugin {
    
        private final String mViewName;
    
        protected ViewTrackingPlugin(final String viewName) {
            mViewName = viewName;
        }
    
        @Override
        public void onResume() {
            Analytics.trackView(mViewName);
        }
    }
  7. Plugin development restrictions and best practices

    master

    When writing plugins, adhere to these rules to ensure stability and correct execution order:

    Accessing Activity methods

    Do not call Activity methods directly on the plugin instance (e.g., this.onResume() or this.getResources()). Doing so breaks the guaranteed call order of plugins. Instead, always access these methods through the attached Activity instance using getActivity().

    • Correct: getActivity().onResume() or getActivity().getResources()
    • Incorrect: this.onResume() or this.getResources()

    Handling NonConfigurationInstances

    If you need to save a NonConfigurationInstance inside a plugin:

    1. Override onRetainNonConfigurationInstance().
    2. Return an instance of CompositeNonConfigurationInstance(key, object).
    3. Retrieve the data using getLastNonConfigurationInstance(key).

    Note: Do not use the standard CompositeActivity#onRetainNonConfigurationInstance() or getLastCustomNonConfigurationInstance() as they are final and reserved for internal use. Use the composite versions instead:

    • CompositeActivity#onRetainCompositeCustomNonConfigurationInstance()
    • CompositeActivity#getLastCompositeCustomNonConfigurationInstance()