In the last article, I explained why componentization, the concept of componentization, and the creation of componentization framework; This article will detail some cases about Android componentized development, which integrates a number of 10 project modules……
catalogue
1. Actual development cases
-
1.1 Open source projects for componentized practices
-
1.1 How do I Create a Module
-
1.2 How to Establish a Dependency
-
1.3 How can I Unify The Configuration File
-
1.4 Componentized base library
-
1.5 How do I Switch between Component Mode and Integration Mode
-
1.6 Componentization Resolves repeated dependencies
-
1.7 Componentization Pay attention to key points
-
1.8 Resource name Conflicts during Componentization
-
1.9 Problems encountered in componentized development
2. Communication between components
-
2.1 Select the open source routing library
-
2.2 Basic principles of Ali Arouter
-
2.3 Precautions for Using Arouter
0. Componentized development case open source address
Github.com/yangchong21…
1. Actual development cases
1.1 Open source projects for componentized practices
- About componentization development a feeling
- There are many blogs on the web about componentization, explaining what it is, why it is, and the benefits of componentization. Most of the articles provided componentization ideas, which made it a lot easier for me to start componentization development. Thank you for your sharing! Although there were some gains, few articles were able to present an overall and effective solution, or even a concrete Demo.
- But after all, reading blogs is also a preparation for practice. When we started to revise the previous open source cases into component-based cases, a lot of problems emerged and some problems were solved. The main thing is to learn some componentized development process.
- Most companies start with componentization slowly. In small companies, it’s good for people who haven’t done componentization before to try it; In a big company, somebody just goes in and does a module, maybe it’s just starting to componentize and somebody else has done it, and it’s faster to componentize the learning practice. Amateur practice, revision of the previous open source project, wrote this blog, cost me a lot of time, if there is some help for you, then I will be very happy. Because I am also a small person, so write bad, do not spray, welcome to put forward suggestions!
- About componentized open source projects
- The overall architecture mode of the project adopts: componentization +MVP+Rx+Retrofit+ Design +Dagger2+VLayout+X5
- Modules included: wanAndroid [Kotlin] + dry goods concentration camp + Zhihu Daily + Tomato Todo+ selected news + Douban music movie novel + novel reading + simple notepad + funny video + classic games + more etc
- This project is a part-time training project. The interface data are all from the Internet. If there is any infringement, please inform us immediately. This project is only for learning and communication. The API data content belongs to the original company. Please do not use it for other purposes.
- Open source componentization project address: github.com/yangchong21…
1.1 How do I Create a Module
- You can see this from the previous article 3.3 Architecture Design diagram
- The main project:
- There is no business code except for some global configuration and the main Activity. Some are also called shell apps, which rely on business components to run.
- Business components:
- At the top level of the business, each component represents a complete line of business, independent of each other. In principle: there can be no direct dependencies between business components! All business components need to be able to run independently. For testing, integration testing depends on the functions of multiple business components. App shell can be used for multi-component dependency management running.
- This case is divided into: working concentration camp, playing Android, Zhihu Daily, wechat news, headlines, funny videos, Baidu Music, my Notepad, Douban music, reading movies, game components and so on.
- Functional components:
- This case is divided into share component, comment feedback component, payment component, gallery component and so on. Also be aware that there may be multiple business components that depend on a functional component!
- Basic components:
- The underlying business services that support the operation of the upper-layer business components. This component provides basic functional support for upper-layer business components.
- In this case, there are network request, picture loading, communication mechanism, tool class, sharing function, payment function and so on in the basic component library. Of course, I put some public third party libraries into the base component!
1.2 How to Establish a Dependency
- The component dependency structure diagram in the project is shown below
- Business module complete configuration code
// Control component patterns and integration patternsif (rootProject.ext.isGankApplication) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
compileSdkVersion rootProject.ext.android["compileSdkVersion"]
buildToolsVersion rootProject.ext.android["buildToolsVersion"]
defaultConfig {
minSdkVersion rootProject.ext.android["minSdkVersion"]
targetSdkVersion rootProject.ext.android["targetSdkVersion"]
versionCode rootProject.ext.android["versionCode"]
versionName rootProject.ext.android["versionName"]
if(rootProject. Ext. IsGankApplication) {/ / component mode setting applicationId applicationId"com.ycbjie.gank"
}
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}} //jdk1.8 compileOptions {sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets {
main {
if (rootProject.ext.isGankApplication) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
jniLibs.srcDirs = ['libs']
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':library')
annotationProcessor rootProject.ext.dependencies["router-compiler"]}Copy the code
1.3 How can I Unify The Configuration File
-
Because there are so many modules in the componentization practice, you need to consider simplifying when configuring Gradle and adding dependent libraries. So how do you do that?
-
The first step is to create a yc.gradle file in the project root directory. In actual development, you only need to change the version information in this file.
- The vast majority of cases I see on the Internet are through a switch control component mode and integration mode switch, but here I configured the switch of multiple components, respectively to control the corresponding component switching state.
ext {
isApplication = false //false: exists as a Lib component,true: exists as application isAndroidApplication =false// Play Android module switch,false: exists as a Lib component,true: exists isLoveApplication = as applicationfalse// Love expression module switch,false: exists as a Lib component,true: exists as application isVideoApplication =false// Video module switch,false: exists as a Lib component,true: exists as application isNoteApplication =false// Switch the notepad module,false: exists as a Lib component,true: exists as application isBookApplication =false//book module switch,false: exists as a Lib component,true: Exists isDouBanApplication = as applicationfalse//false: exists as a Lib component,true: exists as application isGankApplication =false// Dry goods module switch,false: exists as a Lib component,true: exists as application isMusicApplication =false// Music module switch,false: exists as a Lib component,true: exists as application isNewsApplication =false// Switch the news module,false: exists as a Lib component,true: isToDoApplication = exists as applicationfalse// Todo module switch,false: exists as a Lib component,trueIsZhiHuApplication = exists as applicationfalse// Switch of zhihu module,false: exists as a Lib component,true: Exists as application isOtherApplication =false// Other module switches,false: exists as a Lib component,trueAndroid = [compileSdkVersion: 28, buildToolsVersion:"28.0.3",
minSdkVersion : 17,
targetSdkVersion : 28,
versionCode : 22,
versionName : "1.8.2"// must be int orfloat, otherwise affect online upgrade] version = [androidSupportSdkVersion:"28.0.0",
retrofitSdkVersion : "2.4.0",
glideSdkVersion : "4.8.0",
canarySdkVersion : "1.5.4",
constraintVersion : "1.0.2"
]
dependencies = [
//support
"appcompat-v7" : "com.android.support:appcompat-v7:${version["androidSupportSdkVersion"]}"."multidex" : "Com. Android. Support: multidex: 1.0.1." ",
//network
"retrofit" : "com.squareup.retrofit2:retrofit:${version["retrofitSdkVersion"]}"."retrofit-converter-gson" : "com.squareup.retrofit2:converter-gson:${version["retrofitSdkVersion"]}"."retrofit-adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava2:${version["retrofitSdkVersion"]}", // omit some code here]}Copy the code
- Step 2, then add the code to lib in the project, as shown below
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.android["compileSdkVersion"]
buildToolsVersion rootProject.ext.android["buildToolsVersion"]
defaultConfig {
minSdkVersion rootProject.ext.android["minSdkVersion"]
targetSdkVersion rootProject.ext.android["targetSdkVersion"]
versionCode rootProject.ext.android["versionCode"]
versionName rootProject.ext.android["versionName"]
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
api rootProject.ext.dependencies["appcompat-v7"]
api rootProject.ext.dependencies["design"]
api rootProject.ext.dependencies["palette"]
api rootProject.ext.dependencies["glide"]
api (rootProject.ext.dependencies["glide-transformations"]){
exclude module: 'glide'
}
annotationProcessor rootProject.ext.dependencies["glide-compiler"]
api files('libs/tbs_sdk_thirdapp_v3. 2.0. The jar')
api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"// omit some code}Copy the code
-
Third, add dependencies to the other models
- Implementation Project (‘: Library ‘).
1.4 Componentized base library
-
Base library component encapsulation
- The base library component package library mainly includes some commonly used frameworks for development. Can directly see my project more intuitive!
- 1. Network request (Retrofit+RxJava framework), interceptor
- 2. Image loading (Strategy mode, Glide and Picasso can be toggle)
- 3, communication mechanism (RxBus), routing ARouter simple encapsulation tool class (communication between different models)
- MVP framework, common base classes, such as BaseActivity, BaseFragment and so on
- 5, general tools, such as rounded corners, animation tools and so on
- 6, custom view(including dialog box, ToolBar layout, circular image and other view customization)
- 7, common shape, drawable, layout, color and other resources file
- 8, global initialization asynchronous thread pool encapsulation library, each component can use
-
Component initialization
- For example, if you switch the news component in this case to a standalone app, the news jump detail page needs to be initialized because it uses the X5 WebView. To start, configure a separate application for each component that can be switched to app, and then initialize some tasks that need to be initialized for that component. But there’s a downside to this, it’s not very easy to manage. Later, after reading the componentization practice scheme of Zhihu, the scheme proposed that a set of multi-thread initialization framework was developed. Each component only needed to create several start Task classes and declare the dependency relationship in the Task. But specific how to use the code in the late to be realized!
-
How to simplify componentization for people unfamiliar with quickly adapt to components running independently Set multiple components switch, need to switch the component change that. If you set a switch that either cuts all the components into integration mode or all the components into component mode, it’s a little bit problematic. Read more below!
-
Strictly limit the growth of the common base Components As development continues, be careful not to add too much to the common base components. It should be reduced in volume! If the underlying component is too large, then running the component is also slow!
1.5 How do I Switch between Component Mode and Integration Mode
-
Play with build.gradle file under Android components, other components are similar.
- The module, if a library, uses the com.android.library plugin. If it is an application, use the com.android.application plug-in
// Control component patterns and integration patternsif (rootProject.ext.isAndroidApplication) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
Copy the code
-
The integration pattern is shown below
- The first step is to set isApplication=false in the yc.gradle file. After Sync, the component is found to be library
ext {
isAndroidApplication = false //false: exists as a Lib component,true: exists as an applicationCopy the code
-
The component pattern is shown below
- The first step is to set isApplication=true in the yc.gradle file. After being logged in to Sync, the component is found to be an application and can be run against the module
ext {
isAndroidApplication = true //false: exists as a Lib component,true: exists as an applicationCopy the code
-
One thing to watch out for, this is important
- First look at the vast majority of online practices, thank you very much for the selfless dedication of these gods! But I think multiple components can be controlled by a single switch, but sourceSets can switch to component app directly without the following trouble, can reuse Java and RES files.
-
Here’s how I did it:
-
The following configuration is important. When the playing Android component switches from Library to Application, since it can run as a standalone app, set the applicationId and configure the manifest file, as shown below!
-
To switch between Library and application, the manifest file also needs to provide both
android {
defaultConfig {
if(rootProject. Ext. IsAndroidApplication) {/ / component mode setting applicationId applicationId"com.ycbjie.android"}}sourceSets {
main {
if (rootProject.ext.isAndroidApplication) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
jniLibs.srcDirs = ['libs']}}}Copy the code
- Details in the project are as follows
-
Duplicate dependency problem description
-
The problem of duplicate dependencies is A common one in development, where your project implements an A, then you implement A B in the library, and then you implement the same B in your project, and you rely on it twice.
-
By default, if it is an AAR dependency, Gradle will automatically find the new version of the library for us and discard duplicate dependencies from the old version. However, if you are using a project dependency, Gradle does not eliminate the redo, and you end up with duplicate classes in your code.
-
-
Solutions, for example
api(rootProject.ext.dependencies["logger"]) {
exclude module: 'support-v4'// Exclude group from component name:'android.support.v4'// exclude by package name}Copy the code
1.7 Componentization Pay attention to key points
-
The linkage between service components leads to serious coupling
-
For example, in real development, a shopping cart and a home page item are two separate components. However, when there is a product demand, such as the need to do an activity during a festival or issue a shopping voucher, the components will often be linked because there are activities in the shopping cart and the product details page. Without adequate preparation, over time, the code boundaries of the lines of business will deteriorate as the main project did before componentization, and coupling will become more severe.
-
The first solution: Use sourceSets to put different business code in different folders, but the problem with sourceSets is that it doesn’t prevent sourceSets from referring to each other, so it’s not very friendly!
-
The second solution is to extract requirements as utility classes, and achieve the invocation relationship by transferring values from different components. In this way, the requirements can be changed only by changing the utility classes. But this only meets the requirements, but is used in a different module scenario.
-
-
Database separation for componentized development
- For example, the video module I’m developing now needs to be used by others, but I need to rely on a large third party database because I need a database for caching and so on. But using the system’s native SQL database is not too convenient, how to do? I haven’t found a way yet…
####1.8 Resource name Conflicts during componentization
-
What are the resource name conflicts?
-
For example, color, Shape, drawable, image resources, layout resources, or anim resources can cause resource name conflicts. This is why, sometimes people are responsible for different modules, if not named according to the uniform specification, this problem can occur occasionally.
-
Especially if string, color and Dimens resources are distributed in various corners of the code, it is very tedious to remove them one by one. You don’t have to do that. Because Android merges and shrinks resources during build. Res/values under the various documents (styles. Should pay attention to XML) eventually will only be put to use in intermediate/res/merged /.. /valus.xml, all useless will be deleted automatically. And finally we can use Lint to automatically delete. So this place doesn’t take too much time.
-
-
The solution
- This issue is not new, and is encountered by most third party SDKS. It can be avoided by setting resourcePrefix. After setting this value, all of your resource names must be prefixed with the specified string, otherwise an error will be reported. However, the resourcePrefix value only applies to resources in the XML, not to image resources, all of which still require you to manually change the resource name.
-
Personal advice
- Put color, Shape, and so on in the base library component, because all business components depend on the base library. Note in styles. XML that you must prefix the attribute name with a qualifier. If it is not added, it may be packaged as an AAR and used by other modules, there will be conflicts of attribute name repetition. Why? Because the name BezelImageView simply won’t appear in the intermediate/res/merged /.. /valus.xml, so don’t assume this is a qualifier for the attribute!
1.9 Problems encountered in componentized development
-
How can componentized modules get global context
-
Scene reappearance
- For example, at the beginning, the online project was created in the main project of app with simple profit, so the context in the application class of the main project could not be obtained if it was divided into components in lib or later stage. This time is fine
-
The solution
- Write a Utils utility class in lib and initialize utils.init (this) in the main project application to get the global context in lib and all business components that already depend on the common base component library.
-
-
ButterKnife usage issues
- Although there are a number of blogs on the web that address butterKnife references between different components. However, I encountered a compilation error problem when switching between component mode and integration mode during actual development. If anyone solves the butterKnife reference problem in componentization, let me know, thank you very much!
-
When componentization is lib
- Instead of using switch (R.id.xx), use if.. Else instead.
-
Don’t send bus messages indiscriminately
-
If you use EventBus a lot in your project, you’ll see a class with a lot of onEventMainThread() methods that are fun to write and painful to read.
-
Although it is very convenient and simple to use EventBus or RxBus to send messages to realize communication between components in the early stage, with the increase of business and continuous update in the later stage, some of them have been modified by multiple programmers, which will reduce the code reading. There are many places in the project where this Event is sent and many places in the project where it is received. In the late want to improve to componentized development, and the code split, do not dare to make a rash move, for fear of which events are not received.
-
-
Page hopping problems exist
-
If a page requires login status to be viewed, the same logic will be written if (isLogin()) {// jump to page}else{// jump to login page} for each operation.
-
Native startActivity jump, unable to listen to the status of the jump, such as jump error, success, exception and other problems.
-
Later, the background will control to jump to different pages from clicking the button [in different scenarios]. If the background configuration information is wrong or parameters are missing, the jump may fail or cause a crash, which also does not have a good processing mechanism.
-
Ali launched the open source framework Arouter, it can solve the page jump problem, you can add interception, or even if the background configuration parameters are wrong, when listening to jump abnormal or jump error state, can directly default jump to the home page. That’s what I did in the open source case!
-
-
About jump parameters
- Let’s take a look at this code. This is fine, but in multiplayer development, if someone wants to jump to a page in your development module, it’s easy to pass the wrong value. It is recommended to write the value of key as a static constant and put it in a special class. Convenience for yourself and convenience for others.
/ / jump intent. SetClass (this, CommentActivity. Class); intent.putExtra("id",id);
intent.putExtra("allNum",allNum);
intent.putExtra("shortNum",shortNum);
intent.putExtra("longNum",longNum); startActivity(intent); Intent Intent = getIntent(); int allNum = intent.getExtras().getInt("allNum");
int shortNum = intent.getExtras().getInt("shortNum");
int longNum = intent.getExtras().getInt("longNum");
int id = intent.getExtras().getInt("id");
Copy the code
2. Communication between components
2.1 Select the open source routing library
* More representative component-based open source frameworks have been DDComponentForAndroid, Ali Arouter, Jumei Router and so on.
-
Get DDComponentForAndroid: a complete and effective Android componentialization program, support component components of complete isolation, separate debugging, integrated debugging, component interaction, UI jump, dynamic loading and unloading functions.
-
Ali Arouter: middleware that provides routing functions for pages and services. It is simple and easy to use. There are many online introduction blogs.
-
Router: a single, componentized, plug-in fully supported routing framework
2.2 Basic principles of Ali Arouter
-
This is just the basic idea
-
The @route annotation added to the code will generate some class files that store the path and activityClass mappings at compile time through APT. Then when the app starts, it will get these class files and read the data of these mappings into memory (stored in map). The build() method is then used to pass in the route address to the page during the route jump.
- Add the @Route annotation and compile to generate this class, and then look at the class. As follows:
-
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$video implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/video/VideoActivity", RouteMeta.build(RouteType.ACTIVITY, VideoActivity.class, "/video/videoactivity"."video", null, -1, -2147483648)); }}Copy the code
-
Class (activity.class = map.get(path))) and then new Intent(), When ARouter’s withString() method is called, it calls intent.putextra (String name, String Value) internally, and calls navigation(), Its internal call startActivity(Intent) to jump, so that two modules that do not depend on each other can successfully start each other’s activities.
- Look at the _navigation method code in _ARouter on line 345.
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = null == context ? mContext : context;
switch (postcard.getType()) {
case ACTIVITY:
// Build intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// Set flags.
int flags = postcard.getFlags();
if(1! = flags) { intent.setFlags(flags); }else if(! (currentContext instanceof Activity)) { // Non activity, need less one flag. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } // Set Actions String action = postcard.getAction();if(! TextUtils.isEmpty(action)) { intent.setAction(action); } // Navigationin main looper.
runInMainThread(new Runnable() {
@Override
public void run() { startActivity(requestCode, currentContext, intent, postcard, callback); }});break;
casePROVIDER: // Omit the code herecase BOARDCAST:
case CONTENT_PROVIDER:
caseFRAGMENT: // omit code herecase METHOD:
case SERVICE:
default:
return null;
}
return null;
}
Copy the code
2.3 Precautions for Using Arouter
-
Use Ali route extraction tool class, convenient later maintenance!
- First of all, there’s a way to write it on the Internet.
// First add the following code @route (path =) via annotations"/test/TestActivity"Public class TestActivity extends BaseActivity {} // redirect arouter.getInstance ().inject()"/test/TestActivity");
Copy the code
-
The optimized writing method
- The following approach is convenient for later maintenance.
Public class ARouterConstant {// Jump to the video page public static final String ACTIVITY_VIDEO_VIDEO ="/video/VideoActivity"; // insert all the routes into the database, Public class ARouterUtils {/** * simple jump page * @param string String Target interface corresponding path */ public static void navigation(string) string){if (string==null){
return; } ARouter.getInstance().build(string).navigation(); }} @route (Path = arouterconstant.activity_video_video) public Class VideoActivity extends BaseActivity {} ARouterUtils.navigation(ARouterConstant.ACTIVITY_VIDEO_VIDEO);Copy the code
I Java development 4 years Android development 5 years, regularly share Android advanced technology and experience to share, welcome everyone to pay attention to ~ (like the article points a praise to encourage the next speaker)
The reader’s welfare
-
Android cutting-edge technology – componentized framework design
-
BAT Mainstream Android advanced architecture technical outline + learning route + material sharing
Architecture technology details, learning routes and information sharing are in this blog article “the winter is not over”, Ali P9 architecture share Android essential technology points, let you get offer soft! (Including custom controls, NDK, architecture design, React Native (Weex), performance optimization, full commercial project development, etc.)
-
Ali P8 Level Android architect technical brain map
-
A full set of systematic high-level architecture videos; Seven mainstream technology modules, video + source + notes