分享读者
作者2013年java转到Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。
被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!
我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。
主要包括腾讯,以及字节跳动,阿里,华为,小米,等一线互联网公司主流架构技术。
如果你觉得自己学习效率低,缺乏正确的指导,可以一起学习交流!
我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。
35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
android:icon=“@drawable/me”
android:title=“组件C” />
navigation.xml里调用的icon图片分别放到drawable和drawable-24目录里,最终主APP的MainActivity界面如下图:
8、MainActivity.java的代码如下:
package net.zy13.module.demo;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import android.os.Bundle;
import android.view.MenuItem;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import net.zy13.module.modulea.fragment.ModuleAFragment;
import net.zy13.module.moduleb.fragment.ModuleBFragment;
import net.zy13.module.modulec.fragment.ModuleCFragment;
public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {
//定义碎片集合
private Fragment[] fragments = new Fragment[3];
//当前显示的fragment的索引位置
private int currentIndex = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFragment();
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(this);
}
/**
- 初始化Fragment碎片
*/
private void initFragment() {
if (fragments[0] == null) {
fragments[0] = new ModuleAFragment();
getSupportFragmentManager().beginTransaction().add(R.id.content, fragments[0], “moduleA”).commit();
} else {
getSupportFragmentManager().beginTransaction().show(fragments[0]);
}
}
/**
-
导航选择事件
-
@param item
-
@return
*/
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_a:
if (currentIndex == 0) return true;//如果已经是当前的fragment,不用切换
FragmentTransaction transition0 = getSupportFragmentManager().beginTransaction();
hideAndShow(0,transition0);
return true;
case R.id.navigation_b:
if (currentIndex == 1) return true;//如果已经是当前的fragment,不用切换
FragmentTransaction transition1 = getSupportFragmentManager().beginTransaction();
if (fragments[1] == null) {
fragments[1] = new ModuleBFragment();
transition1.add(R.id.content, fragments[1], “moduleB”);
}
hideAndShow(1,transition1);
return true;
case R.id.navigation_c:
if (currentIndex == 2) return true;//如果已经是当前的fragment,不用切换
FragmentTransaction transition2 = getSupportFragmentManager().beginTransaction();
if (fragments[2] == null) {
fragments[2] = new ModuleCFragment();
transition2.add(R.id.content, fragments[2], “modulec”);
}
hideAndShow(2,transition2);
return true;
}
return false;
}
/**
-
除了指定的fragment不hide,其他fragment全hide
-
@param expectIndex 指定的fragment在fragments中的位置
-
@param transition
*/
private void hideAndShow(int expectIndex,FragmentTransaction transition) {
for (int i = 0; i < fragments.length; i++) {
if (i != expectIndex && fragments[i] != null) {
transition.hide(fragments[i]);
}
}
transition.show(fragments[expectIndex]);
transition.commit();
currentIndex = expectIndex;
}
}
9、Run运行项目,最终效果如下图,点击导航,可以切换到对应的组件上:
七、各个组件单独开发(测试)
前面我们把各个组件集成到main组件中,现在我们把组件拆分出来,单独开发,开发测试完成后,再把组件集成到main组件中,最后发布。组件单独出来开发的方法就是:在build.gradle文件中,把apply plugin: ‘com.android.library’,改成apply plugin: ‘com.android.application’,也就是把其library模式改为application模式,因为只有application才可以单独运行,library必须依靠application才能运行。
那么问题来了?
组件单独开发时,我们需要改build.gradle的apply plugin模式,等要集成到main组件时,又得改回来,如果这样子手工去改,组件一多,修改起来比较麻烦,也不优雅。优雅的解决办法就是设置一个开关,打开时,就是application模式,可以单独开发;关闭时,就是library模式,可以集成到main组件中。现在按我下面的步骤来实现:
1、在项目根目录下,有一个build.gradle文件,在这个文件最末尾添加一个ext {}配置,然后在ext配置里设定一个常量isDebug,值设为true
ext {
/**
-
组件调试模式
-
isDebug = true 是组件模块,说明是单独的App
-
isDebug = false是集成模式,说明是依赖Lib
-
每次更改“isDebug”的值后,需要点击 “Sync Project” 按钮
*/
isDebug = true
}
2、build.gradle里设置了isDebug常量后,我们项目中的其他build.gradle文件都可以把这个常量读取出来,所以我们可以在其他组件的build.gradle文件中,读取该常量的值,动态设置apply plugin,代码如下:
if(isDebug){
apply plugin: ‘com.android.application’
}else{
apply plugin: ‘com.android.library’
}
3、这样子设置之后,当我们需要切换模式时,只需要修改项目根目录下build.gradle文件中isDebug常量的值,修改完成之后,点击Project sync按钮同步一下即可。如果有报错,那么还有个地方需要修改一下,就是main组件的build.gradle文件,我们把module的模式改成了application,main组件就不能引入application,引入的话就会报错,所以当是debug调试模式时,这里就不引入该组件,以免报错。所以在集成组件前,要先判断是什么模式,如下图:
4、接下来还得修改 AndroidManifest.xml,当把一个module设置为application时,AndroidManifest.xml需要包含一个app所需要的属性,例如app的icon、theme、launch Activity这些属性设置,而当module为library时,这些属性就都不需要用到,所以当我们处于不同模式时,AndroidManifest.xml文件的配置也得不同。方法如下:
(1)、Android目录模式切换到Project目录模式
(2)、 在各个组件的src文件夹中新创建一个debug目录,再把我们用于debug调试的AndroidManifest.xml文件放进去
(3)、调试用的AndroidManifest.xml文件可以直接复制manifests目录里的,然后添加application的基本信息,如下:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android=“http://schemas.android.com/apk/res/android”
package=“net.zy13.module.modulea”>
<application
android:allowBackup=“true”
android:icon=“@mipmap/ic_launcher”
android:label=“@string/app_name”
android:roundIcon=“@mipmap/ic_launcher_round”
android:supportsRtl=“true”
android:theme=“@style/AppTheme”>
以上内容会有很多错误提示,其实提示的无非就是资源找不到,既然前面我们已经创建了公共的common组件,那么我们只需要把main组件中相应的资源移动到common组件中就可以了。
5、接下来在各个组件的build.gradle文件中,指定不同模式下使用的AndroidManifest.xml文件,在android {}里添加如下代码:
sourceSets {
main {
if (isDebug) {
manifest.srcFile ‘src/debug/AndroidManifest.xml’
}else{
manifest.srcFile ‘src/main/AndroidManifest.xml’
//集成开发模式下排除debug文件夹中的所有Java文件
java {
exclude ‘debug/**’
}
}
}
}
6、以上设置完成,并且sync project(同步项目)之后,各个组件会是这样的目录结构:
7、在各个组件里创建一个用于调试启动的MainActivity活动,然后把fragment加载到activity_main.xml里,所对应的activity_main.xml布局文件如下:
<?xml version="1.0" encoding="utf-8"?><fragment xmlns:android=“http://schemas.android.com/apk/res/android”
android:name=“net.zy13.module.modulea.fragment.ModuleAFragment”
android:layout_width=“match_parent”
android:layout_height=“match_parent”>
8、添加MainActivity活动后,我们还需要手动设置该活动为入口,打开src/debug/目录下的AndroidManifest.xml文件,修改MainActivity的配置如下:
<activity
android:name=“.MainActivity”>
9、以上步骤完成之后,组件就可以单独作为一个app来开发测试了,android studio的运行app里面,同时多了几个可运行的项目,如下图:
八、统一项目版本号
各个组件的build.gradle文件中,有很多版本号。为了避免每次修改都得同时修改多份build.gradle文件,也避免不同的组件使用的版本不一样,导致冲突,所以我们可以把这些版本号统一管理起来,方法如下:
1、在项目根目录下的build.gradle文件中,定义版本号常量
ext {
/**
-
组件调试模式
-
isDebug = true 是组件模块,说明是单独的App
-
isDebug = false是集成模式,说明是依赖Lib
-
每次更改“isDebug”的值后,需要点击 “Sync Project” 按钮
*/
isDebug = true
//版本号
android = [
compileSdkVersion: 30,
buildToolsVersion: “30.0.0”,
minSdkVersion : 15,
targetSdkVersion : 30,
versionCode : 1,
versionName : “1.0”
]
}
2、然后在各个组件的build.gradle文件中,做这样的修改:
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
}
}
九、使用路由实现组件之间的通信
通过前面的学习,我们已经知道组件可以拆分了,但是当他们集成到main组件中时,还是需要一定的通信,例如业务A需要调用到业务B的一个页面,甚至进行传参。但是在“组件化”模式下,业务A和业务B是完全分开的,在业务A的认知里,根本就不存在业务B,也就没办法直接调用。当然,如果要业务A与业务B可以直接通信,互相引入就可以,但是这样的话,项目耦合性太高,架构也混乱,会把“组件化”的所有优点都一一撇掉,所以我们应该用另外一种方式来处理。
这里需要引入一个概念:路由”,就如我们实际访问网络一样,我们电脑发送的请求都经过路由器转发,在“组件化”中,我们也可以设置这么一个中转站,来统一处理不同组件之间的调用关系。关于路由的用法,后面会补充,这里先用最简单的方法,来实现组件之间的调用(页面跳转),代码如下:
try {
Class c= Class.forName(“net.zy13.module.modulea.MainActivity”);
Intent intent = new Intent(context,c);
startActivity(intent);
} catch (ClassNotFoundException e) {
Log.e(“组件”,“组件未集成,无法跳转”);
}
以上代码是通过完整的类名来进行跳转,在debug模式下,调用其他组件时,找不到对应组件,不会直接报错,只是提示“未集成,无法跳转”。我们可以把这个方法写成一个工具类,放在common组件中,方法如下:
1、在common组件里创建一个工具类PageUtils.java,代码如下:
import android.content.Context;
import android.content.Intent;
import android.util.Log;
/**
-
@author 安阳 QQ:15577969
-
@version 1.0
-
@team 美奇软件开发工作室
-
@date 2020/11/12 11:55
*/
public class PageUtils {
/**
-
页面跳转
-
@param context
-
@param className
*/
public static void jump(Context context, String className){
try {
Class c = Class.forName(className);
Intent intent = new Intent(context,c);
context.startActivity(intent);
} catch (ClassNotFoundException e) {
Log.e(“组件”,“未集成,无法跳转”);
}
}
/**
-
页面跳转,可以传参,参数放在intent中,所以需要传入一个intent
-
@param context
-
@param className
-
@param intent
*/
public static void jump(Context context,String className,Intent intent){
try {
Class c = Class.forName(className);
intent.setClass(context,c);
context.startActivity(intent);
} catch (ClassNotFoundException e) {
Log.e(“组件”,“未集成,无法跳转”);
}
}
}
2、在需要跳转的地方,直接用以下方法调用:
尾声
一转眼时间真的过的飞快。我们各奔东西,也各自踏上了自己的旅途,但是即使多年不见,也因为这份情谊我们依旧如从前那般“亲密”。不忘初心方得始终。加油吧,程序员们,在我看来35岁,40岁从来不是危机,只要永远不要忘记自己为何踏上征程!
为了让更多在学习中或者最近要准备面试的朋友们看到这篇文章,希望你们能多多评论,点赞+转发!
再次感谢所有给我提供过题目的朋友们,感谢一路有你!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
d jump(Context context,String className,Intent intent){
try {
Class c = Class.forName(className);
intent.setClass(context,c);
context.startActivity(intent);
} catch (ClassNotFoundException e) {
Log.e(“组件”,“未集成,无法跳转”);
}
}
}
2、在需要跳转的地方,直接用以下方法调用:
尾声
一转眼时间真的过的飞快。我们各奔东西,也各自踏上了自己的旅途,但是即使多年不见,也因为这份情谊我们依旧如从前那般“亲密”。不忘初心方得始终。加油吧,程序员们,在我看来35岁,40岁从来不是危机,只要永远不要忘记自己为何踏上征程!
为了让更多在学习中或者最近要准备面试的朋友们看到这篇文章,希望你们能多多评论,点赞+转发!
再次感谢所有给我提供过题目的朋友们,感谢一路有你!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!