Welcome to my home page: juejin.cn/user/330776… ARouter official documentation: github.com/alibaba/ARo… Componentized ARouter series: juejin.cn/post/704852… Componentized ARouter series (two, cross-module API calls) : juejin.cn/post/705339… Componentized ARouter series: juejin.cn/post/705340…
I. Overview of ARouter
ARouter is a framework for componentizing Android apps — enabling routing, communication, and decoupling between modules. Typical application scenarios for ARouter are:
- Mapping from external URLS to internal pages, and parameter passing and parsing;
- Page hopping across modules, decoupling between modules;
- Intercept jump process, deal with logicality such as landing and burying point;
- Cross-module API calls, component decoupling through control inversion;
This article focuses on one of the uses of ARouter: parameter passing and parsing.
2. Basic Usage
Again, the same example as in “Componentized Foundation ARouter (I)” : In MainActivity there is a FirstButton, which opens the FirstActivity in the first_library module. The difference is, This time we need to pass an int (10), a String (“hello”), and a User parameter when opening FirstActivity:
Without using ARouter, the simple implementation is to pass an intent:
public class MainActivity extends Activity implements View.OnClickListener {
@Override
public void onClick(View v) {
if (v.getId() == R.id.main_button) {
User user = new User("jack".18);
Intent intent = new Intent(this, FirstActivity.class);
// 1. Add parameters to the intent
intent.putExtra("intValue".10);
intent.putExtra("strValue"."hello");
intent.putExtra("user", user); startActivity(intent); }}}public class FirstActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
/ /... Omit some code
// 2. Select a parameter from the intent
Intent intent = getIntent();
intValue = intent.getIntExtra("intValue".0);
strValue = intent.getStringExtra("strValue");
user = intent.getParcelableExtra("user");
firstIntTextView.setText("intValue:"+intValue);
firstStrTextView.setText("strValue"+strValue);
firstUserTextView.setText("User:"+user.name+":"+user.age); }}Copy the code
Here is how the ARouter parameter is passed:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
public void onClick(View v) {
if (v.getId() == R.id.main_button) {
User user = new User("jack".18);
// 1. Pass the corresponding type parameters through the with series method: support basic types and Parcelable, Serializable objects
ARouter.getInstance().build("/first/activity")
.withInt("intValue".10)
.withString("strValue"."hello")
.withParcelable("user", user) .navigation(); }}}@Route(path = "/first/activity")
public class FirstActivity extends AppCompatActivity implements View.OnClickListener {
// 2. Parameters that need to be passed by ARouter should be annotated @autowired
@Autowired
public int intValue;
@Autowired
public String strValue;
@Autowired
public User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
The arouter.getInstance ().inject(this) method automatically completes parameter injection
ARouter.getInstance().inject(this);
firstIntTextView.setText("intValue:"+intValue);
firstStrTextView.setText("strValue"+strValue);
firstUserTextView.setText("User:"+user.name+":"+user.age); }}Copy the code
In the example above, the User must be a Parcelable object. In addition, ARouter’s withObject() method can pass non-Parcelable objects directly, but it needs to implement the SerializationService interface itself. I won’t tell you much here. So far, two implementations of parameter passing and parsing are introduced. The following analysis source code implementation.
Third, source code analysis
Here’s how ARouter implements the functionality in Section 2, using the source code. The new API added to this example is “componentized Base ARouter (1)” :
- The @autowired annotation.
- WithParcelable () method;
- Inject () method;
Accordingly, this section is divided into three sections to analyze the main work of each step.
3.1 the @autowired annotation
After adding @autowired annotations to parameters that need to be injected by ARouter in 2.2, ARouter uses the ARouter – Compiler declared in Section 2.1 to process the annotations, automatically generating code and passing parameters on top of that. For basic knowledge of Annotation Processor, see: Annotation Processor Simple Usage. ARouter APT after processing the @autowired automatically generates a class file (in/first_library/build/generated/source/kapt/debug/com/BC/first directory) :
This class implements ISyringe under the com.bc.first package (the same package name as the @AutoWired tag parameter) :
/** * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class FirstActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
FirstActivity substitute = (FirstActivity)target;
substitute.intValue = substitute.getIntent().getIntExtra("intValue", substitute.intValue);
substitute.strValue = substitute.getIntent().getExtras() == null ? substitute.strValue : substitute.getIntent().getExtras().getString("strValue", substitute.strValue);
substitute.user = substitute.getIntent().getParcelableExtra("user"); }}Copy the code
I’ll look at what the auto-generated code is for later.
3.2 it. WithParcelable ()
Arouter.getinstance ().build(“/first/ Activity “) would return the PostCard object in “Componentized Base ARouter (A)”. Then, the main work of the PostCard.WithParcelable () is analyzed:
public final class Postcard extends RouteMeta {
private Bundle mBundle;
public Postcard withInt(@Nullable String key, int value) {
mBundle.putInt(key, value);
return this;
}
public Postcard withParcelable(@Nullable String key, @Nullable Parcelable value) {
mBundle.putParcelable(key, value);
return this; }}Copy the code
The job description is simple: After you assign the Postcard’s Bundle a value to it, the Postcard’s Bundle will be displayed in the navigation() in the Intent to start the Activity (see “Componentization Base ARouter (一)” for the complete process for starting the Activity) :
final class _ARouter {
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = postcard.getContext();
switch (postcard.getType()) {
case ACTIVITY:
final Intent intent = new Intent(currentContext, postcard.getDestination());
// Key code
intent.putExtras(postcard.getExtras());
String action = postcard.getAction();
if(! TextUtils.isEmpty(action)) { intent.setAction(action); } runInMainThread(new Runnable() {
@Override
public void run(a) {
// 3. Start ActivitystartActivity(requestCode, currentContext, intent, postcard, callback); }});break;
}
return null; }}Copy the code
3.3 the inject ()
Arouter.getinstance ().inject(this); The main work of:
final class _ARouter {
static void inject(Object thiz) {
// Get the instance object of the AutowiredService service
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
if (null! = autowiredService) { autowiredService.autowire(thiz); }}}Copy the code
The above code shows that the work is done by AutowiredService.
3.3.1 AutowiredService
The AutowiredService interface inherits the IProvider interface, and ARouter’s IProvider can be understood as a service provider (see analysis in “Componentization basics ARouter (2)”) :
public interface AutowiredService extends IProvider {
/**
* Autowired core.
* @param instance the instance who need autowired.
*/
void autowire(Object instance);
}
Copy the code
3.3.2 rainfall distribution on 10-12 AutowiredServiceImpl
The implementation of AutowiredService is AutowiredServiceImpl, and the autowire() method is then analyzed:
@Route(path = "/arouter/service/autowired")
public class AutowiredServiceImpl implements AutowiredService {
// LruCache holds ISyringe objects
private LruCache<String, ISyringe> classCache;
private List<String> blackList;
@Override
public void init(Context context) {
classCache = new LruCache<>(50);
blackList = new ArrayList<>();
}
@Override
public void autowire(Object instance) {
doInject(instance, null);
}
/** * recursive injection parameters */
private void doInject(Object instance, Class
parent) { Class<? > clazz =null == parent ? instance.getClass() : parent;
ISyringe syringe = getSyringe(clazz);
if (null! = syringe) {Syringe completes injectionsyringe.inject(instance); } Class<? > superClazz = clazz.getSuperclass();// Recursive injection
if (null! = superClazz && ! superClazz.getName().startsWith("android")) { doInject(instance, superClazz); }}In this example, the waiting class is FirstActivity, and the injector class is FirstActivity$$ARouter$$Autowired */
private ISyringe getSyringe(Class
clazz) {
String className = clazz.getName();
try {
if(! blackList.contains(className)) {// 1. Check whether there is a corresponding injector power in LruCache
ISyringe syringeHelper = classCache.get(className);
if (null == syringeHelper) {
SUFFIX_AUTOWIRED = ClassName+SUFFIX_AUTOWIRED = ClassName+SUFFIX_AUTOWIRED
syringeHelper = (ISyringe) Class.forName(clazz.getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
}
// 3. Update LruCache and return to the injector class
classCache.put(className, syringeHelper);
returnsyringeHelper; }}catch (Exception e) {
blackList.add(className); // This instance need not autowired.
}
return null; }}Copy the code
In the above code, the injector class lookup is based on ClassName + SUFFIX_AUTOWIRED, with the suffix fixed to ‘$$ARouter$$Autowired’, thus associated with the auto-generated classes described in 3.1.
3.3.4 ISyringe
Syringe, and the code generated by the @autowired annotation in Section 3.1 is simple:
/** * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class FirstActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
// 1. Whether there is a custom serialization implementation. The default is NULL
serializationService = ARouter.getInstance().navigation(SerializationService.class);
// 2. Get the class to inject
FirstActivity substitute = (FirstActivity)target;
// 3. The injected implementation is actually taken from the intent
substitute.intValue = substitute.getIntent().getIntExtra("intValue", substitute.intValue);
substitute.strValue = substitute.getIntent().getExtras() == null ? substitute.strValue : substitute.getIntent().getExtras().getString("strValue", substitute.strValue);
substitute.user = substitute.getIntent().getParcelableExtra("user"); }}Copy the code
3.4 summarize
- Arouter through Arouter. GetInstance (). Inject (this); Inversion of control;
- Arouter automatically generates the injected code by APT handling the @Autowired annotation;
- Arouter pass arouter.getInstance ().build(“/first/activity”).withParcelable(“user”, user).navigation(); To pass parameters to the Postcard by putting data in the Postcard. When the Postcard.navigation() starts the Activity, place the parameters in the Intent.
- Arouter in Arouter.getInstance().inject(this) calls APT automatically generated code to extract parameters from the intent and assign them to the corresponding variable;
- The main work for inversion of control is done by the AutowiredService in ARouter;