Author: Dawish_ big D Jane books: www.jianshu.com/u/40e1f22c2…

(1)Android official MVVM framework to achieve componentization of the overall structure (2)Android official MVVM framework to achieve componentization of ARouter series modules

Github address of Demo: github.com/Dawish/Goog…


0- Demo project MVVM componentized architecture diagram

The ARouter router is a godsend for MVVM componentization and is perfect for componentization. It can easily fetch fragments, jump activities, intercept jumps, start services, and more.

ARouter official address: github.com/alibaba/ARo…

Module_girls, module_news, and lib_commoin all have components that ARouter supports, such as activities, fragments, and services. In MVVM, ARouter is the most convenient way to use it. For more details, please refer to the official instructions and official Demo. The authorities made that very clear.


1, componentized ARouter use configuration considerations

There are seven apps, modules, and lib modules in this demo project. If you use ARouter’s functionality for each of them, then you need to add configuration to build.gradle for each Module.

android { defaultConfig { ... javaCompileOptions { annotationProcessorOptions { arguments = [ moduleName : Project.getname ()]}}}} dependencies { Compile 'com.alibaba:arouter- API: X.X.X 'annotationProcessor 'com.alibaba:arouter-compiler: X.X.X'... }Copy the code

If your module needs to be packaged as an APK or AAR, and you have code obturation, add the keep code to the proguard-rules.pro file for each module:

-keep public class com.alibaba.android.arouter.routes.**{*; } -keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*; } # if byType is used, add the following rule: Protection interface - keep interface * implements com in alibaba. Android. Arouter. The facade. The template. The IProvider # if you are using a single type of injection, neither define interfaces to realize IProvider, Need to add the following rules to protect the implementation - keep class * implements com in alibaba. Android. Arouter. The facade. The template. IProviderCopy the code

Jump and intercept activities

First we need to add a class to the lib_common library to hold the path used by ARouter’s jump:

package google.architecture.common.base; /** * Created by danxx on 2017/11/27. * Route path ** Aty: Activity * Fgt: Fragment ** / /** * Created by danxx on 2017/11/27. * Route ** Aty: Activity * Fgt: Fragment ** / public class ARouterPath {/ / public static final String GirlsListAty = "/girls/aty/list"; Public static final String DynaGirlsListAty = "/girls/dynaty/list"; static final String DynaGirlsListAty = "/girls/dynaty/list"; Public static final String NewsListAty = "/news/aty/list"; Public static final String GirlsListFgt = "/girls/aty/ FGT /list"; Public static final String NewsListFgt = "/news/ FGT /list"; / / public static final String NewsListFgt = "/news/ FGT /list"; Public static final String AboutFgt = "/about/ FGT/Fragment "; }Copy the code

Add a Router annotation to an Activity that needs to be jumped to declare its own path:

@Route(path = ARouterPath.GirlsListAty) public class ActivityGirls extends BaseActivity { GirlsAdapter girlsAdapter; ActivityGirlsBinding activityGirlsBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setTitle("Module_ActivityGirls"); getSupportActionBar().setDisplayHomeAsUpEnabled(true); activityGirlsBinding = DataBindingUtil.setContentView(ActivityGirls.this,R.layout.activity_girls); GirlsViewModel girlsViewModel = new GirlsViewModel(ActivityGirls.this.getApplication()); girlsAdapter = new GirlsAdapter(girlItemClickCallback); activityGirlsBinding.setRecyclerAdapter(girlsAdapter); subscribeToModel(girlsViewModel); } GirlItemClickCallback girlItemClickCallback = new GirlItemClickCallback() { @Override public void onClick(GirlsData.ResultsBean fuliItem) { Toast.makeText(ActivityGirls.this, fuliItem.getDesc(), Toast.LENGTH_SHORT).show(); }}; /** * Subscribe to data changes to refresh the UI * @param model */ private void subscribeToModel(final GirlsViewModel model){// Observe data changes to refresh the UI model.getLiveObservableData().observe(this, new Observer<GirlsData>() { @Override public void onChanged(@Nullable GirlsData girlsData) { Log.i("danxx", "subscribeToModel onChanged onChanged"); model.setUiObservableData(girlsData); girlsAdapter.setGirlsList(girlsData.getResults()); }}); } @Override protected void onResume() { super.onResume(); }}Copy the code

Take a look at the ActivityGirls layout file for those interested:

<? The XML version = "1.0" encoding = "utf-8"? > <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" tools:context=".ActivityGirls"> <data> <import type="android.view.View"/> <variable name="girlsViewModel" type="google.architecture.coremodel.viewmodel.GirlsViewModel"/> <variable name="recyclerAdapter" type="android.support.v7.widget.RecyclerView.Adapter"/> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/cardview_light_background" android:orientation="vertical"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/girls_list_wrapper"> <android.support.v7.widget.RecyclerView android:id="@+id/girls_list" android:contentDescription="girls list" android:layout_width="match_parent" android:layout_height="match_parent" app:adapter="@{recyclerAdapter}" app:layoutManager="LinearLayoutManager" /> </FrameLayout> </FrameLayout> </LinearLayout> </layout>Copy the code

ARouter intercepts the IInterceptor interface by writing a class that implements the IInterceptor interface. Use the Interceptor comment to specify the Interceptor level and name. There is no need to register the Interceptor. For more information on the use of Java AOP annotations, see my previous article “AOP’s Love-hate Relationship with Android”

/** * Created by danxx on 2017/11/27. ** A classic application is to handle login events during a jump, so there is no need to repeat login checks on the target page. * * Interceptors are executed in order of priority. * * Interceptors of multiple levels can be set to execute at once. * Creating a class that implements the IInterceptor interface is an interceptor. */ Interceptor(priority = 8, Name = "Test Interceptor ") public Class RouterInterceptor implements IInterceptor {@Override public void Process (Postcard postcard, InterceptorCallback callback) { if(postcard.getPath().contains(ARouterPath.GirlsListAty)){ Log.d("danxx", "Intercept redirect to ActivityGirls "); Else {log.d ("danxx", "non-intercepting jump execute path: "+ postcars.getPath ()); } callback.onContinue(postcard); // Callback. OnInterrupt (new RuntimeException(" I feel something unusual ")); } @override public void init(Context Context) {// When the interceptor is initialized, this method is called when the SDK initializes. Log.d("danxx", "RouterInterceptor init") is called only once; }}Copy the code

It can support a lot of parameters to carry, and the jump of the Activity can also be automatically animated with transitions:

private ItemClick itemClick = new ItemClick() { @Override public void onClick(int id) { switch (id){ case R.id.toGirls: Log.i("danxx", "onClick toGirls"); GirlsActivity arouter.getInstance ().build(arouterpath.girlslistaty .withTransition(R.anim.activity_up_in, R.anim.activity_up_out) .navigation(ActivityMain.this); break; case R.id.toNews: Log.i("danxx", "onClick toNews"); // Jump to NewsActivity arouter.getInstance ().build(arouterPath.newslistaty) /** Can target jump jump animation */ .withTransition(R.nim.activity_up_in, R.nim.activity_up_out) /** Set the jump back to */. Navigation (activitymain. this, 2, new NavigationCallback() { @Override public void onFound(Postcard postcard) { Log.i("danxx", "ARouter onFound "); } @override public void onLost(Postcard) {log. I ("danxx", "ARouter onLost is not matched to the jump path "); } @override public void onArrival(Postcard) {log. I ("danxx", "ARouter onArrival Successful hop "); } @override public void onInterrupt(Postcard) {log. I ("danxx", "ARouter onInterrupt jump is interrupted "); }}); break; case R.id.toDynamic: Log.i("danxx", "onClick toNews"); / / jump to ActivityDynamicGirls (simulated dynamic urls) ARouter. GetInstance (). The build (ARouterPath. DynaGirlsListAty). WithString (" fullUrl ", "http://gank.io/api/data/%E7%A6%8F%E5%88%A9/20/1") .withTransition(R.anim.activity_up_in, R.anim.activity_up_out) /** Supports requestCode and calls onActivityResult */. Navigation (activitymain. this, 3); break; }}};Copy the code

3. Obtaining fragments

Look at the Fragment that needs to be retrieved:

/** * @Desc FragmentGirls */ @Route(path = ARouterPath.GirlsListFgt) public class FragmentGirls extends BaseFragment { FragmentGirlsBinding girlsBinding; GirlsAdapter girlsAdapter; private static final String ARG_PARAM1 = "param1"; private static final String ARG_PARAM2 = "param2"; private String mParam1; private String mParam2; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() ! = null) { mParam1 = getArguments().getString(ARG_PARAM1); mParam2 = getArguments().getString(ARG_PARAM2); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { ARouter.getInstance().inject(FragmentGirls.this); girlsBinding = DataBindingUtil.inflate(inflater,R.layout.fragment_girls,container,false); girlsAdapter = new GirlsAdapter(girlItemClickCallback); girlsBinding.setRecyclerAdapter(girlsAdapter); final GirlsViewModel girlsViewModel = new GirlsViewModel(getActivity().getApplication()); subscribeToModel(girlsViewModel); return girlsBinding.getRoot(); } GirlItemClickCallback girlItemClickCallback = new GirlItemClickCallback() { @Override public void onClick(GirlsData.ResultsBean fuliItem) { Toast.makeText(getContext(), fuliItem.getDesc(), Toast.LENGTH_SHORT).show(); }}; /** * Subscribe to data changes to refresh the UI * @param model */ private void subscribeToModel(final GirlsViewModel model){// Observe data changes to refresh the UI model.getLiveObservableData().observe(FragmentGirls.this, new Observer<GirlsData>() { @Override public void onChanged(@Nullable GirlsData girlsData) { Log.i("danxx", "subscribeToModel onChanged onChanged"); model.setUiObservableData(girlsData); girlsAdapter.setGirlsList(girlsData.getResults()); }}); }}Copy the code

Getting fragments is also very simple:

      
BaseFragment fragmentNews = (BaseFragment) ARouter.getInstance().build(ARouterPath.NewsListFgt).navigation();

BaseFragment fragmentGirls = (BaseFragment) ARouter.getInstance().build( ARouterPath.GirlsListFgt).navigation();

BaseFragment fragmentAbout = (BaseFragment) ARouter.getInstance().build( ARouterPath.AboutFgt ).navigation();

Copy the code

ARouter’s so-called service acquisition

ARouter’s Service is a class that implements the IProvider interface provided by ARouter, either directly or indirectly.

First, we can write an interface that inherits the IProvider interface. We can add the methods we want to our interface:

** * Created by Danxx on 2017/11/28. ** ARouter is a class that implements the IProvider interface provided by ARouter directly or indirectly TestService extends IProvider {/** adds a method you want */ String sayHello(String name); }Copy the code

To implement this interface:

/** * Created by danxx on 2017/11/28. * @route (path = "/service/test", Public class implements TestService {@override public String sayHello(String name) { Log.d("danxx", "TestServiceImpl sayHello : "+name); return "TestServiceImpl TestServiceImpl TestServiceImpl"; } @Override public void init(Context context) { Log.d("danxx", "TestServiceImpl TestServiceImpl init"); }}Copy the code

It can be used in a variety of ways:

Service Statement:


    @Autowired(name = "/service/test")
    TestService testService1;

    TestService testService2;
    TestService testService3;

Copy the code

Service usage:

Inject (activitymain.this); inject(activitymain.this); inject(activitymain.this); // Annotated method testService1.sayHello("Autowired invoke 233"); TestService2 = arouter.getInstance ().navigation(testService.class); testService2.sayHello("navigation invoke 233"); TestService3 = (TestService) arouter.getInstance ().build("/service/test").navigation(); testService3.sayHello("build invoke 233");Copy the code

The official recommendation annotation way, more reliable some.

Summary of the advantages of using ARouter in MVVM

  1. More low coupling between modules, modules do not need to pay attention to each other, to develop their own functions on the line.

  2. Activity jumps, fragments, and services that ARouter calls have all become implicit, without knowing who they are, as long as they register the corresponding path.

  3. Support the interception of the jump of the Activity, I test does not seem to support the interception of the Fragment, some regret, in the condition that does not meet the conditions of the module can be external shielding.

  4. Support URL jump, convenient H5 and native mixed development.

.

5. Shameless previews

The next article will cover the use of DataBinding in MVVM. Please refer to the official description and Demo for more ARouter:

ARouter Github address: github.com/alibaba/ARo…

Example project Demo address: github.com/Dawish/Goog…