Skip to content

How does it really work

Marcin Moskała edited this page Mar 23, 2017 · 6 revisions

Lot's of code generation examples are contained here, where they serve as test cases (first code class and under that what has expected the result from the processor).

Basically, the processor is generating Starter class for each annotated Activity, Service etc. (or one with at least one annotated field). This Starter contains all methods, we use to start or get Intent for this class. It also always creates fill method. This method can be fund in ActivityStarter and it is started when we use ActivityStarter.fill(this).

Here are few examples of code and what is generated from it. For:

@MakeActivityStarter
public class MainActivity extends Activity {}

Will be generated:

public final class MainActivityStarter {

  public static void fill(MainActivity activity, Bundle savedInstanceState) {
  }

  public static void save(MainActivity activity, Bundle bundle) {
  }

  public static Intent getIntent(Context context) {
    Intent intent = new Intent(context, MainActivity.class);
    return intent;
  }

  public static void start(Context context) {
    Intent intent = getIntent(context);
    context.startActivity(intent);
  }

  public static void startWithFlags(Context context, int flags) {
    Intent intent = getIntent(context);
    intent.addFlags(flags);
    context.startActivity(intent);
  }
}

Activities are quite heavy because of 3 possible starters. For Service:

@MakeActivityStarter
public class SomeService extends Service {

    @Arg String name = "";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

Will be generated:

public final class SomeServiceStarter {
  public static void fill(SomeService service, Intent intent) {
    if(intent.hasExtra("nameStarterKey")) service.name = intent.getStringExtra("nameStarterKey");
  }

  public static Intent getIntent(Context context, String name) {
    Intent intent = new Intent(context, SomeService.class);
    intent.putExtra("nameStarterKey", name);
    return intent;
  }

  public static void start(Context context, String name) {
    Intent intent = getIntent(context, name);
    context.startService(intent);
  }
}

Note, that there is still only one getIntent and start method. It is because an argument is not optional. Here is an example of the result of optional arguments:

public class MainFragment extends Fragment {
    @Arg @Optional String name;
    @Arg @Optional int id;
}

Will be generated:

public final class MainFragmentStarter {
  public static void fill(MainFragment fragment) {
    Bundle arguments = fragment.getArguments();
    if(arguments.containsKey("nameStarterKey")) fragment.name = arguments.getString("nameStarterKey");
    if(arguments.containsKey("idStarterKey")) fragment.id = arguments.getInt("idStarterKey", -1);
  }

  public static MainFragment newInstance(String name, int id) {
    MainFragment fragment = new MainFragment();
    Bundle args = new Bundle();
    args.putString("nameStarterKey", name);
    args.putInt("idStarterKey", id);
    fragment.setArguments(args);
    return fragment;
  }

  public static MainFragment newInstance(String name) {
    MainFragment fragment = new MainFragment();
    Bundle args = new Bundle();
    args.putString("nameStarterKey", name);
    fragment.setArguments(args);
    return fragment;
  }

  public static MainFragment newInstance(int id) {
    MainFragment fragment = new MainFragment();
    Bundle args = new Bundle();
    args.putInt("idStarterKey", id);
    fragment.setArguments(args);
    return fragment;
  }

  public static MainFragment newInstance() {
    MainFragment fragment = new MainFragment();
    return fragment;
  }
}

Another important feature is a possibility to save instance state. Let's look at the code generated from Activity. Here is simple Activity:

public class MainActivity extends Activity {
    @Arg String name;
}

And this is how generated starter looks like: (only when possibility to save is not turned off by NonSavable Activity annotation)

public final class MainActivityStarter {

    public static void fill(MainActivity activity, Bundle savedInstanceState) {
        Intent intent = activity.getIntent();
        if (savedInstanceState != null && savedInstanceState.containsKey("com.example.activitystarter.nameStarterKey")) {
            activity.name = savedInstanceState.getString("com.example.activitystarter.nameStarterKey");
        } else {
            if (intent.hasExtra("com.example.activitystarter.nameStarterKey"))
                activity.name = intent.getStringExtra("com.example.activitystarter.nameStarterKey");
        }
    }

    public static void save(MainActivity activity, Bundle bundle) {
        bundle.putString("com.example.activitystarter.nameStarterKey", activity.name);
    }

    public static Intent getIntent(Context context, String name) {
        Intent intent = new Intent(context, MainActivity.class);
        intent.putExtra("com.example.activitystarter.nameStarterKey", name);
        return intent;
    }

    public static void start(Context context, String name) {
        Intent intent = getIntent(context, name);
        context.startActivity(intent);
    }

    public static void startWithFlags(Context context, String name, int flags) {
        Intent intent = getIntent(context, name);
        intent.addFlags(flags);
        context.startActivity(intent);
    }
}

Note, that is a possibility to save Activity, and when it is used then each saved argument will be read during restore. If save method won't be used then arguments will be taken from intent like Activity arguments.

To use it, there must be saved method called in onSaveInstanceState Activity function:

public class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityStarter.fill(this, savedInstanceState);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        ActivityStarter.save(this, outState);
    }
}