Android事件分发机制源码解析大牛最佳总结

开头

在大厂,写得一手好文档是一个非常吃香的技能。这可不只是一个锦上添花的东西,而是很多工程师晋升,打造自己话语权的武器。 我这两年在组内的深刻体会就是,大部分厉害的高级工程师(不包括那些纯混日子靠资历晋升的人),写文档的能力一点也不含糊,很能抓住上级和项目的G点。

可能有人会觉得,我技术牛逼就行了,为啥还要提高写文档的能力,有这功夫我还不如多看看源码分析?这是一些初级或者刚入门的工程师的普遍的困惑。这是因为大部分刚刚入行的朋友有一个很深的误区,就是他们以为做软件工程是一个和计算机打交道的工作,其实不然。软件工程不只是和代码打交道,更重要的是和人打交道,是一份社会性质很强的工作。在大部分公司里面,尤其是大厂,牵涉到的人,组,都是非常非常多的。在小厂,人与人之间交流意见和设计可以口口相传,心领神会,但是一旦人开始多了,就只能靠文档了。除非你可以厉害到一个人把所有代码撸完,不然还是最好老老实实的夯实自己写文档的能力。

如果你有写技术博客的习惯,那么恭喜你,相信你已经对如何抓住文档受众的技巧有所了解了。这对你在大厂生存有很大的帮助。如果没有也不要伤心,这篇文章就是为你精心设计的。

在这篇文章里,我会大致的把一份安卓的项目设计文档的骨架,和一些我工作中实际遇到的正反例都列出来,方便大家以后在工作中实践。

安卓组件化开发的意义

当项目比较大需要多人协同开发的时候,组件化开发可以使大家分模块开发,并且不会互相影响。并且可以单独调试,不受其他模块制约。

项目结构解析

创建一个主应用,两个子应用(既可以做应用,也可以作为主应用的依赖),一个base库实现主应用和子应用的通讯。创建后项目结构如下

统一项目编译版本

  • gradle.properties文件设置统一版本
	# 统一编译版本等信息
	compile_Sdk_Version=28
	min_Sdk_Version=15
	target_Sdk_Version=28

  • 修改app、loginmodule、memodule、mudulelibrary的编译版本,以app为例
	apply plugin: 'com.android.application'

	android {
	    compileSdkVersion compile_Sdk_Version.toInteger()
	    defaultConfig {
	        applicationId "com.syw.modulesdemo"
	        minSdkVersion min_Sdk_Version.toInteger()
	        targetSdkVersion target_Sdk_Version.toInteger()
	        versionCode 1
	        versionName "1.0"
	        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
	    }
	    buildTypes {
	        release {
	            minifyEnabled false
	            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
	        }
	    }
	}

设置application和library切换开关

  • gradle.properties文件设置切换开关
	# application和library切换开关
	isLoginApplication=true
	isMeApplication=true

  • 主应用动态添加依赖
	//不要忘记toBoolean()
    if (!isLoginApplication.toBoolean()) {
        implementation project(':loginmodule')
    }
    if (!isMeApplication.toBoolean()) {
        implementation project(':memodule')
    }
    implementation project(':modulelibrary')

  • 子应用切换application和library、动态设置applicationId、动态设置AndroidManifest.xml、添加基础依赖。以login模块为例
	//1.动态设置application或library
	if (isLoginApplication.toBoolean()){
	    apply plugin: 'com.android.application'
	}else{
	    apply plugin: 'com.android.library'
	}

	android {
	    compileSdkVersion compile_Sdk_Version.toInteger()

	    defaultConfig {
	        //2.library不存在applicationId
	        if (isLoginApplication.toBoolean()){
	            applicationId "com.syw.loginmodule"
	        }
	        minSdkVersion min_Sdk_Version.toInteger()
	        targetSdkVersion target_Sdk_Version.toInteger()
	        versionCode 1
	        versionName "1.0"
	        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
	    }

	    buildTypes {
	        release {
	            minifyEnabled false
	            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
	        }
	    }

	//    3.library状态下AndroidManifest.xml没有application信息,不设置启动activity
	    sourceSets{
	        main{
	            if (isLoginApplication.toBoolean()){
	                manifest.srcFile 'src/main/AndroidManifest.xml'
	            }else{
	                manifest.srcFile 'src/main/manifests/AndroidManifest.xml'
	            }
	        }
	    }
	}

	dependencies {
	    implementation fileTree(include: ['*.jar'], dir: 'libs')
	    implementation 'com.android.support:appcompat-v7:28.0.0'
	    implementation 'com.android.support.constraint:constraint-layout:2.0.2'
	    testImplementation 'junit:junit:4.12'
	    androidTestImplementation 'com.android.support.test:runner:1.0.2'
	    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
	    implementation project(':modulelibrary')
	}

依赖库的manifest.xml

	<?xml version="1.0" encoding="utf-8"?>
	<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	    package="com.syw.loginmodule">

	    <application>
	        <activity android:name=".LoginActivity">
	        </activity>
	    </application>

	</manifest>

基础库设置

基础库设置接口,由子项目实现

以login模块为例,编写ILoginService

	public interface ILoginService {
	    void launch(Context context);

	    Fragment getFragment(FragmentManager fragmentManager, int containerId, Bundle bundle);
	}

编写接口管理类
public class ServiceFactory {
	// 单例模式
    private static final ServiceFactory instance=new ServiceFactory();
    public ServiceFactory(){}
    public static ServiceFactory getInstance() {
        return instance;
    }

    private ILoginService iLoginService;
    private IMeService iMeService;

    public ILoginService getLoginService() {
        return iLoginService;
    }

    public void setLoginService(ILoginService iLoginService) {
        this.iLoginService = iLoginService;
    }

    public IMeService getMeService() {
        return iMeService;
    }

    public void setMeService(IMeService iMeService) {
        this.iMeService = iMeService;
    }
}

子项目完成具体实现
public class LoginBridge implements ILoginService {
    @Override
    public void launch(Context context) {
        Intent intent = new Intent(context, LoginActivity.class);
        context.startActivity(intent);
    }

    @Override
    public Fragment getFragment(FragmentManager fragmentManager, int containerId, Bundle bundle) {
        LoginFragment loginFragment=new LoginFragment();
        loginFragment.setFragmentData(bundle);
        fragmentManager.beginTransaction().add(containerId,loginFragment).commit();
        return loginFragment;
    }
}

跳转方法的调用

跳转的具体实现已经完成,现在的问题是怎么调用,我们想要实现的是在MainActivity中通过如下代码调用

	ServiceFactory.getInstance().getLoginService().launch(MainActivity.this);

那么现在的问题是,什么时候将LoginService设置到ServiceFactory,刚开始的时候还在想,既然已经依赖了loginmodule,为啥不直接跳转。仔细想想,这就违背了组件化的本意了,我们应该减少子项目和主项目之间的耦合,通过中间库的方式。
为了调用getLoginService,我们首先需要setLoginService,将实现类设置到ServiceFactory。于是就有了下面的方法

在子项目中创建一个方法,在主项目初始化的时候,通过主项目调用,子项目将实现类设置给ServiceManager
  • 我们首先需要在基础库创建一个接口,通过主项目调用,实现全部子应用设置Service
	public interface Appcompat {
	    void initializa();
	}

  • 我们首先创建一个类,实现该接口,设置LoginService
	public class LoginApplication extends Application implements Appcompat {
	    @Override
	    public void initializa() {
	        ServiceManager.getInstance().setLoginService(new LoginBridge());
	    }
	}

  • 我们创建一个类,管理所有子项目设置Service的类。主项目通过反射的方法调用,实现全部子应用设置Service
	public class AppConfig {
	    public static final String[] COMPONENT={
	      "com.syw.loginmodule.LoginApplication",
	      "com.syw.memodule.MeApplication"
	    };
	}

  • 现在我们需要在主项目中调用方法了,首先在主项目初始化的时候,通过initializa方法调用,已经让所有子项目setXxxService。
	public class MainApplication extends Application {
	    @Override
	    public void onCreate() {
	        super.onCreate();
	        for (String component : AppConfig.COMPONENT) {
	            try {
	                Class<?> clazz=Class.forName(component);
	                Object obj = clazz.newInstance();
	                if (obj instanceof Appcompat){
	                    ((Appcompat) obj).initializa();
	                }
	            } catch (Exception e) {
	                e.printStackTrace();
	            }
	        }
	    }
	}

  • 在需要使用的地方,即可直接getXxxService,然后调用方法
	public class MainActivity extends AppCompatActivity {

	    @Override
	    protected void onCreate(Bundle savedInstanceState) {
	        super.onCreate(savedInstanceState);
	        setContentView(R.layout.activity_main);

	        findViewById(R.id.gologin).setOnClickListener(new View.OnClickListener() {
	            @Override
	            public void onClick(View v) {
	                ServiceManager.getInstance().getLoginService().launch(MainActivity.this);
	            }
	        });

	        findViewById(R.id.gome).setOnClickListener(new View.OnClickListener() {
	            @Override
	            public void onClick(View v) {
	                ServiceManager.getInstance().getMeService().launch(MainActivity.this);
	            }
	        });

	        findViewById(R.id.gofragment).setOnClickListener(new View.OnClickListener() {
	            @Override
	            public void onClick(View v) {
	                Bundle bundle=new Bundle();
	                bundle.putString("name","张三");
	                ServiceManager.getInstance().getLoginService().invokeLoginFragment(getSupportFragmentManager(),R.id.container,bundle);
	            }
	        });
	    }
	}

应该还可以参考阿里的aroute框架,目前先暂时记录到这里
demo下载链接https://download.csdn.net/download/ForMuHan/13067005

总结

学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

最后如何才能让我们在面试中对答如流呢?

答案当然是平时在工作或者学习中多提升自身实力的啦,那如何才能正确的学习,有方向的学习呢?有没有免费资料可以借鉴?为此我整理了一份Android学习资料路线:

这里是一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套BAT大厂面试资料专题包,在这里免费分享给大家,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家。需要的小伙伴们可以点击我的腾讯文档获取免费领取方式

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以去我的主页加一下技术群。来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

最后,祝愿即将跳槽和已经开始求职的大家都能找到一份好的工作!

给大家,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家。需要的小伙伴们可以点击我的腾讯文档获取免费领取方式

[外链图片转存中…(img-5sTYiEcF-1623418202247)]

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以去我的主页加一下技术群。来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

最后,祝愿即将跳槽和已经开始求职的大家都能找到一份好的工作!

这些只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢再关注一下~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值