Android组件化原理详细介绍

目录
  • 什么是组件化?
  • 为什么使用组件化?
  • 一步步搭建组件化
    • 1.新建模块
    • 2.统一Gradle版本号
    • 3.创建基础库
    • 4.组件模式和集成模式转换
    • 5.AndroidManifest的切换
    • 6.*业务Application切换
  • 组件之间的跳转
    • 1.添加依赖
    • 2.初始化ARouter
    • 3.添加跳转
  • 组件之间的数据传递
    • 1.定义接口
    • 2.实现接口
  • 组件Application的动态切换
    • 1.定义抽象类 BaseApplication 继承 Application
    • 2.所有的组件的 Application 都继承 BaseApplication
    • 3.定义 AppConfig 类
    • 4.主模块application实现两个初始化方法
  • 主模块使用其他组件的 Fragment
    • 1.ARouter
    • 2.反射

什么是组件化?

一个大型APP版本一定会不断的迭代,APP里的功能也会随之增加,项目的业务也会变的越来越复杂,这样导致项目代码也变的越来越多,开发效率也会随之下降。并且单一工程下代码耦合严重,每修改一处代码后都要重新编译,非常耗时,单独修改的一个模块无法单独测试。

组件化架构的目的是让各个业务变得相对独立,各个组件在组件模式下可以独立开发调试,集成模式下又可以集成到“app壳工程”中,从而得到一个具有完整功能的APP。

组件化每一个组件都可以是一个APP可以单独修改调试,而不影响总项目。

为什么使用组件化?

编译速度: 可以但需测试单一模块,极大提高了开发速度
超级解耦: 极度降低了模块间的耦合,便于后期的维护和更新
功能重用: 某一块的功能在另外的组件化项目中使用只需要单独依赖这一模块即可
便于团队开发: 组件化架构是团队开发必然会选择的一种开发方式,它能有效的使团队更好的协作

一步步搭建组件化

这里以演示为例,只设置登录这一个功能组件

组件化开发要注意的几点问题 :

  • 要注意包名和资源文件命名冲突问题
  • Gradle中的版本号的统一管理
  • 组件在AppIicationLibrary之间如何做到随意切换
  • AndroidManifest. xml文件的区分
  • Library不能在Gradle文件中有applicationId

这里以演示为例,只设置登录和个人中心这两个功能组件

1.新建模块

并且在module里新建一个activity

到这里我们看到login和我们的app都在有一个绿点证明创建成功

个人中心member模块创建同理,并且每个模块目前都可以独立运行。

2.统一Gradle版本号

每一个模块都是一个application,所以每个模块都会有一个build.gradle,各个模块里面的配置不同,我们需要重新统一Gradle
在主模块创建config.gradle

config.gradle里去添加一些版本号

ext{
    android = [
            compileSdkVersion :30,
            buildToolsVersion: "30.0.2",
            applicationId :"activitytest.com.example.moduletest",
            minSdkVersion: 29,
            targetSdkVersion :30,
            versionCode :1,
            versionName :"1.0",
    ]
    androidxDeps = [
            "appcompat": 'androidx.appcompat:appcompat:1.1.0',
            "material": 'com.google.android.material:material:1.1.0',
            "constaraintlayout": 'androidx.constraintlayout:constraintlayout:1.1.3',
    ]
    commonDeps = [
            "arouter_api"          : 'com.alibaba:arouter-api:1.5.1',
            "glide"                : 'com.github.bumptech.glide:glide:4.11.0'

    ]
    annotationDeps = [
            "arouter_compiler" : 'com.alibaba:arouter-compiler:1.5.1'
    ]
    retrofitDeps = [
            "retrofit"  : 'com.squareup.retrofit2:retrofit:2.9.0',
            "converter" : 'com.squareup.retrofit2:converter-gson:2.9.0',
            "rxjava"    : 'io.reactivex.rxjava2:rxjava:2.2.20',
            "rxandroid" : 'io.reactivex.rxjava2:rxandroid:2.1.1',
            "adapter"   : 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
    ]
    androidxLibs = androidxDeps.values()
    commonLibs = commonDeps.values()
    annotationLibs = annotationDeps.values()
    retrofitLibs = retrofitDeps.values()
}

在主模块的build.gradle里添加

apply from: "config.gradle"

在各模块中去引用这些版本号
引用格式如下,两种写法均可

compileSdkVersion rootProject.ext.android["compileSdkVersion"]
buildToolsVersion rootProject.ext.android.buildToolsVersion

引用前:

引用后:

并且使用同样的方法,我们还可以统一我们的依赖库在config.gradle里去添加我们要依赖的库,并在各个模块中去添加依赖

 implementation  rootProject.ext.dependencies.publicImplementation

也可以采用第二种写法

dependencies = [

            "appcompat"             : 'androidx.appcompat:appcompat:1.2.0',

            "material"               : 'com.google.android.material:material:1.2.1',
            "constraintLayout"       : 'androidx.constraintlayout:constraintlayout:2.0.4',//约束性布局

            //test
            "junit"                  : "junit:junit:4.13.1",
            "testExtJunit"           : 'androidx.test.ext:junit:1.1.2',//测试依赖,新建项目时会默认添加,一般不建议添加
            "espressoCore"           : 'androidx.test.espresso:espresso-core:3.3.0',//测试依赖,新建项目时会默认添加,一般不建议添加

    ]

添加依赖:

dependencies {

    implementation rootProject.ext.dependencies.appcompat
    implementation  rootProject.ext.dependencies["constraintLayout"]
    testImplementation rootProject.ext.dependencies["junit"]
    androidTestImplementation rootProject.ext.dependencies["testExtJunit"]
    androidTestImplementation rootProject.ext.dependencies["espressoCore"]

}

3.创建基础库

和新建module一样,这里需要新建一个library我们把它命名为Baselibs

同样需要统一版本号,由于这是一个library模块,所以它不需要applicationId

我们一样可以把它写进config.gradle

other:[path:':Baselibs']

在每个模块去调用

implementation  project(rootProject.ext.dependencies.other)

同理,当本地库为单独所用,我们可以直接调用,而不需要将其写入config.gradle,两种方法选择合适使用即可。

implementation project(':Baselibs')

但有时因为gradle版本问题,我们可能无法依赖到这些公共库,因为我们在config.gradle里是以数组形式定义的,这时我们可以同for-each循环的方法将其依次导入
config.gradle

dependencies = [
          ......
        other:[':Baselibs']
    ]

其他模块的build.gradle

dependencies {
......
    rootProject.ext.dependencies.other.each{
        implementation project(it)
    }

4.组件模式和集成模式转换

在主模块gradle.properties里添加布尔类型选项。

在各个模块的build.gradle里添加更改语句

if(is_Module.toBoolean()){
    apply plugin: 'com.android.application'
}else{
    apply plugin: 'com.android.library'
}

每个模块的applicationId也需要处理

if(is_Module.toBoolean()){
            applicationId "activitytest.com.example.login"
        }

当我们将is_module改为false时,再次运行编译器我们的模块都不能单独运行了

在app模块中添加判断依赖就可以在集成模式下将各模块添加到app主模块中

// 每加入一个新的模块,就需要在下面对应的添加一行
    if (is_Module.toBoolean())]) {
        implementation project(path:':login')
        implementation project(path:':member')
    }

5.AndroidManifest的切换

为了单独开发加载不同的AndroidManifest这里需要重新区分下。
在组件模块里的main文件里新建manifest文件夹

并且重写一个AndroidManifest.xml文件,集成模式下,业务组件的表单是绝对不能拥有自己的 Application 和 launch 的 Activity的,也不能声明APP名称、图标等属性,总之app壳工程有的属性,业务组件都不能有,在这个表单中只声明了应用的主题,而且这个主题还是跟app壳工程中的主题是一致的

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

    <application
        android:theme="@style/Theme.MoudleTest">
        <activity android:name=".LoginActivity">

        </activity>
    </application>

</manifest>

并且我们还要使其在不同的模式下加载不同的AndroidManifest只需在各模块的build.gradle里添加更改语句

sourceSets {
        main {
            if (is_Module.toBoolean()) {
                manifest.srcFile 'src/main/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/mainfest/AndroidManifest.xml'
            }
        }
    }

6.*业务Application切换

每个模块在运行时都会有自己的application,而在组件化开发过程中,我们的主模块只能有一个application,但在单独运行时又需要自己的application这里就需要配置一下。
在业务模块添加新文件夹命名module

在里面建一个application文件

并且我们在build.gradle文件里配置module文件夹使其在单独运行时能够运行单独的application
在配置manifest的语句中添加java.srcDir 'src/main/module'

sourceSets {
        main {
            if (is_Module.toBoolean()) {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                java.srcDir 'src/main/module'
            } else {
                manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
            }
        }
    }

同时我们在basic基础层内新建application,用于加载一些数据的初始化

public class BaseApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("fff","baseapplication");
    }
}

在业务模块内module里重写该模块的application

public class LoginApplication extends BaseApplication {
    @Override
    public void onCreate() {
        super.onCreate();
    }
}

至此,组件化框架搭建结束

组件之间的跳转

这里采用阿里巴巴的开源库ARouter来实现跳转功能,我会在以后的文章单独拿出一篇来一步步去解读Arouter源码,让我们自己去搭建一个自己的路由

一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦

由 github 上 ARouter 的介绍可以知道,它可以实现组件间的路由功能。路由是指从一个接口上收到数据包,根据数据路由包的目的地址进行定向并转发到另一个接口的过程。这里可以体现出路由跳转的特点,非常适合组件化解耦。

要使用 ARouter 进行界面跳转,需要我们的组件对 Arouter 添加依赖,因为所有的组件都依赖了 Baselibs模块,所以我们在 Baselibs 模块中添加 ARouter 的依赖即可。其它组件共同依赖的库也最好都放到 Baselibs中统一依赖。

这里需要注意的是,arouter-compiler 的依赖需要所有使用到 ARouter 的模块和组件中都单独添加,不然无法在 apt 中生成索引文件,也就无法跳转成功。并且在每一个使用到 ARouter 的模块和组件的 build.gradle 文件中,其 android{} 中的 javaCompileOptions 中也需要添加特定配置。

1.添加依赖

Baselibs里的build.gradle添加依赖

dependencies {
    api 'com.alibaba:arouter-api:1.3.1'
    // arouter-compiler 的注解依赖需要所有使用 ARouter 的 module 都添加依赖
    annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'
}
// 所有使用到 ARouter 的组件和模块的 build.gradle
android {
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ moduleName : project.getName() ]
            }
        }
    }
}
dependencies {
    ...
    implementation project (':base')
    annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'
}

主模块需要对跳转模块进行依赖:

// 主项目的 build.gradle 需要添加对 login 组件和 share 组件的依赖
dependencies {
    // ... 其他
    implementation project(':login')
    implementation project(':share')
}

2.初始化ARouter

添加了对 ARouter 的依赖后,还需要在项目的 Application 中将 ARouter 初始化,我们这里将 ARouter 的初始化工作放到主模块Application 的 onCreate()方法中,在应用启动的同时将 ARouter 初始化。

public class MainApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        // 初始化 ARouter
        if (isDebug()) {
            // 这两行必须写在init之前,否则这些配置在init过程中将无效
            // 打印日志
            ARouter.openLog();
            // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
            ARouter.openDebug();
        }

        // 初始化 ARouter
        ARouter.init(this);

    }
    private boolean isDebug() {
        return BuildConfig.DEBUG;
    }
}

3.添加跳转

这里我们在首页添加登录分享两个跳转页面。

login.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ARouter.getInstance().build("/login/login").navigation();
    }
});
share.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ARouter.getInstance().build("/share/share").navigation();
    }
});

然后,需要在登录和分享组件中分别添加 LoginActivityShareActivity ,然后分别为两个 Activity 添加注解 Route,其中path 是跳转的路径,这里的路径需要注意的是至少需要有两级,/xx/xx

@Route(path = "/login/login")
public class Login extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

    }
}
@Route(path = "/share/share")
public class Share extends AppCompatActivity {

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

这样就可以实现跳转了。

组件之间的数据传递

由于主项目与组件,组件与组件之间都是不可以直接使用类的相互引用来进行数据传递的,那么在开发过程中如果有组件间的数据传递时应该如何解决呢,这里我们可以采用 [接口 + 实现] 的方式来解决。

Baselibs基础库里定义组件可以对外提供访问自身数据的抽象方法的 Service。并且提供了一个 ServiceFactory,每个组件中都要提供一个类实现自己对应的 Service 中的抽象方法。在组件加载后,需要创建一个实现类的对象,然后将实现了 Service 的类的对象添加到ServiceFactory 中。这样在不同组件交互时就可以通过 ServiceFactory 获取想要调用的组件的接口实现,然后调用其中的特定方法就可以实现组件间的数据传递与方法调用。

当然,ServiceFactory 中也会提供所有的 Service 的空实现,在组件单独调试或部分集成调试时避免出现由于实现类对象为空引起的空指针异常。

下面我们就按照这个方法来解决组件间数据传递与方法的相互调用这个问题,这里我们通过分享组件 中调用 登录组件 中的方法来获取登录状态是否登录这个场景来演示。

1.定义接口

其中 service文件夹中定义接口,LoginService 接口中定义了 Login 组件向外提供的数据传递的接口方法,EmptyService 中是 service 中定义的接口的空实现,ServiceFactory 接收组件中实现的接口对象的注册以及向外提供特定组件的接口实现。

LoginService

public interface LoginService {
    /**
     * 是否已经登录
     * @return
     */
    boolean isLogin();
    /**
     * 获取登录用户的 Password
     * @return
     */
    String getPassword();
}

EmptyService

public class EmptyService implements LoginService {
    @Override
    public boolean isLogin() {
        return false;
    }

    @Override
    public String getPassword() {
        return null;
    }
}

ServiceFactory

public class ServiceFactory {
    private LoginService loginService;
    private ServiceFactory(){
 /**
     * 禁止外部创建 ServiceFactory 对象
     */
    private ServiceFactory() {
    }
    /**
     * 通过静态内部类方式实现 ServiceFactory 的单例
     */
    public static ServiceFactory getInstance() {
        return Inner.serviceFactory;
    }
    private static class Inner {
        private static ServiceFactory serviceFactory = new ServiceFactory();
    }
 /**
     * 接收 Login 组件实现的 Service 实例
     */
    public void setLoginService(LoginService loginService){
        this.loginService = loginService;
    }
       /**
     * 返回 Login 组件的 Service 实例
     */
    public LoginService getLoginService(){
        if(loginService == null){
            return new EmptyService();
        }else{
            return loginService;
        }
    }
}

2.实现接口

在login模块

public class AccountService implements LoginService {

    private boolean login;
    private String password;

    public AccountService(boolean login, String password) {
        this.login = login;
        this.password = password;
    }

    @Override
    public boolean isLogin() {
        return login;
    }

    @Override
    public String getPassword() {
        return password;
    }
}

这里新建一个Util类用来存储登录数据

public class LoginUtil {
    static boolean isLogin = false;
    static String password = null;
}

实现一下登录操作

login = (Button)findViewById(R.id.login);
        login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                LoginUtil.isLogin = true;
                LoginUtil.password = "admin";
                ServiceFactory.getInstance().setLoginService(new AccountService(LoginUtil.isLogin,LoginUtil.password));
            }
        });

在login模块的application里定义ServiceFactory类

public class LoginApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        ServiceFactory.getInstance().setLoginService(new AccountService(LoginUtil.isLogin,LoginUtil.password));
    }
}

在分享模块获取登录信息

share = (Button)findViewById(R.id.share);
        share.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(ServiceFactory.getInstance().getLoginService().isLogin()){
                    Toast.makeText(ShareActivity.this,"分享成功!",Toast.LENGTH_SHORT).show();
                }else{
                    Toast.makeText(ShareActivity.this,"分享失败,请先登录!",Toast.LENGTH_SHORT).show();
                }
            }
        });

一个项目时只能有一个 Application 的,Login 作为组件时,主模块的 Application 类会初始化,而 Login 组件中的 Applicaiton 不会初始化。确实是存在这个问题的,我们这里先将 Service 的注册放到其活动里,稍后我们会解决 Login 作为组件时 Appliaciton 不会初始化的问题。

组件Application的动态切换

在主模块中有 Application 等情况下,组件在集中调试时其 Applicaiton 不会初始化的问题。而我们组件的 Service 在 ServiceFactory 的注册又必须放到组件初始化的地方。

为了解决这个问题可以将组件的 Service 类强引用到主 Module 的 Application 中进行初始化,这就必须要求主模块可以直接访问组件中的类。而我们又不想在开发过程中主模块能访问组件中的类,这里可以通过反射来实现组件 Application 的初始化。

1.定义抽象类 BaseApplication 继承 Application

Baselibs基础库模块

public abstract class BaseApplication extends Application {
    /**
     * Application 初始化
     */
    public abstract void initModuleApp(Application application);

    /**
     * 所有 Application 初始化后的自定义操作
     */
    public abstract void initModuleData(Application application);              //其他需要调用的方法
}

2.所有的组件的 Application 都继承 BaseApplication

这里我们以Login模块为例

public class LoginApplication extends BaseApplication{
    @Override
    public void onCreate() {
        super.onCreate();
        initModuleApp(this);
        initModuleData(this);
    }
    @Override
    public void initModuleApp(Application application) {
        ServiceFactory.getInstance().setLoginService(new AccountService(LoginUtil.isLogin,LoginUtil.password));
    }
    @Override
    public void initModuleData(Application application) {

    }
}

3.定义 AppConfig 类

Baselibs模块定义一个静态的 String 数组,我们将需要初始化的组件的 Application 的完整类名放入到这个数组中。

public class AppConfig {
    private static final String LoginApp = "com.example.login.LoginApplication";
    public static String[] moduleApps = {
            LoginApp
    };
}

4.主模块application实现两个初始化方法

// 主 Module 的 Applicaiton
public class MainApplication extends BaseApp {
    @Override
    public void onCreate() {
        super.onCreate();

        // 初始化组件 Application
        initModuleApp(this);

        // 其他操作

        // 所有 Application 初始化后的操作
        initModuleData(this);

    }

    @Override
    public void initModuleApp(Application application) {
        for (String moduleApp : AppConfig.moduleApps) {
            try {
                Class clazz = Class.forName(moduleApp);
                BaseApp baseApp = (BaseApp) clazz.newInstance();
                baseApp.initModuleApp(this);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }
    @Override
    public void initModuleData(Application application) {
        for (String moduleApp : AppConfig.moduleApps) {
            try {
                Class clazz = Class.forName(moduleApp);
                BaseApp baseApp = (BaseApp) clazz.newInstance();
                baseApp.initModuleData(this);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }
}

到这里我们就通过反射,完成了组件 Application 的初始化操作,也实现了组件与化中的解耦需求。

主模块使用其他组件的 Fragment

我们在开发过程中经常使用 Fragment。一般情况下,我们都是直接通过访问具体 Fragment 类的方式实现 Fragment 的实例化,但是现在为了实现模块与组件间的解耦,在移除组件时不会由于引用的 Fragment 不存在而编译失败,我们就不能模块中直接访问组件的 Fragment 类。
这里介绍两种方法

1.ARouter

这里可以采用ARouter直接调用

fragment = (Fragment) ARouter.getInstance().build("/login/fragment").navigation();

2.反射

我们还是以Login模块为例,假如在该模块创建一个用户界面,命名为UserFragment
首先,在 Login组件中创建 UserFragment,然后在 LoginService 接口中添加newUserFragment方法返回一个Fragment,在Login组件中的 AccountServiceBaselibsLoginService 的空实现类中实现这个方法,然后在主模块中通过 ServiceFactory 获取 LoginService 的实现类对象,调用其 newUserFragment 即可获取到 UserFragment 的实例。

// Baselibs 模块的 LoginService
public interface LoginService {
//其他代码...
    Fragment newUserFragment(Activity activity, int containerId, FragmentManager manager, Bundle bundle, String tag);
}
// Login 组件中的 AccountService
public class AccountService implements LoginService {
    // 其他代码 ...

    @Override
    public Fragment newUserFragment(Activity activity, int containerId, FragmentManager manager, Bundle bundle, String tag) {
        FragmentTransaction transaction = manager.beginTransaction();
        // 创建 UserFragment 实例,并添加到 Activity 中
        Fragment userFragment = new UserFragment();
        transaction.add(containerId, userFragment, tag);
        transaction.commit();
        return userFragment;
    }
}
// 主模块的 FragmentActivity
public class FragmentActivity extends AppCompatActivity {

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

        // 通过组件提供的 Service 实现 Fragment 的实例化
        ServiceFactory.getInstance().getAccountService().newUserFragment(this, R.id.layout_fragment, getSupportFragmentManager(), null, "");
    }
}

到此这篇关于Android组件化原理详细介绍的文章就介绍到这了,更多相关Android组件化 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈android组件化之ARouter简单使用

    ARouter是阿里巴巴开源出来的一款android路由框架,github地址为 : https://github.com/alibaba/ARouter 至于ARouter的诸多好处我就不介绍了,这里主要讲解在项目组件化下,ARouter的一些简单使用 先贴上工程目录: 工程一共分为4个模块,基础组件app.基础服务(包涵路由服务)basecommonlibrary模块.业务模块libraryone.业务模块librarytwo; 在4个模块的gradle文件当中加入如下代码: android

  • Android组件化开发路由的设计实践

    调研了一下目前的路由框架,ARouter(阿里的),ActivityRouter都使用了apt技术 编译时注解,个人想法是一口吃不成胖子,先做个比较实用的. VpRouter路由框架主要应用于组件化开发中 设计目的 解耦 跨模块跳转 方便服务器配置schema,实现动态配置跳转目标 对外部提供远程访问的功能,实现跨应用调用响应 主要功能点 支持intent,http,schema三种跳转 路由表支持xml配置,可自定义,支持多路径 有拦截器 同时支持反射和隐式意图 支持结果回调 支持参数传递 链

  • Android开发组件化架构设计原理到实战

    目录 为什么需要组件化 组件化和模块化 模块化架构 组件化架构 组件化带来的优势 组件化需解决的问题 资源冲突解决 AndroidManifest 独立调试 单工程方案 多工程方案 页面跳转 Arouter 实现组件间方法调用 组件化的消息通信方式选择 广播 事件总线 Application生命周期分发 为什么需要组件化 小项目是不需要组件化的.当一个项目有数十个人开发,编译项目要花费10分钟,修改一个bug就可能会影响到其他业务,小小的改动就需要进行回归测试,如果是这种项目,那么我们需要进行组

  • 详解Android业务组件化之URL Schema使用

    前言: 最近公司业务发展迅速,单一的项目工程不再适合公司发展需要,所以开始推进公司APP业务组件化,很荣幸自己能够牵头做这件事,经过研究实现组件化的通信方案通过URL Schema,所以想着现在还是在预研阶段,很有必要先了解一下URL Schema,看看是如何使用的?其实在之前做Hybrid混合编程的时候就接触过URL Schema,总来的来说还不算陌生,今天就来回顾总结一下. 什么是 URL Schema?  android中的scheme是一种页面内跳转协议,是一种非常好的实现机制,通过定义

  • android module解耦组件化总体概述(推荐)

    原由 移动开发中,随着项目不断的跌代,需求越来越复杂后.项目工程也越来越庞大.那么此时的分module的开发,则是必然的选择了.在最终的组件化之路上,不妨把单一工程比如石器时代,那么接下来简单的拆分工程分多个moudle开来就是铜器时代. 铜器时代之简单分module 演进 由于从复杂的单工程拆分了多个module了,达到了代码及资源的初步的隔离,或需求模块的开发人员,开始专注于自己的需求模块module的开发了.但是随着部分需求有相关性,需要相互调用时.那么问题来了,在AXXX module中

  • Android组件化、插件化详细讲解

    目录 什么是组件化(通俗易懂) 反射的写法 反射的⽬的 关于DEX: 插件化原理:动态加载 问题⼀:未注册的组件(例如Activity)不能打开 问题⼆:资源⽂件⽆法加载 插件化有什么用? 什么是组件化(通俗易懂) 通俗易懂来讲就是,拆成多个module开发就是组件化. App的部分功能模块在打包时并不以传统⽅式打包进apk⽂件中,⽽是以另⼀种形式⼆次封装进apk内部,或者放在⽹络上适时下载,在需要的时候动态对这些功能模块进⾏加载,称之为插件化.这些单独⼆次封装的功能模块apk,就称作插件,初始

  • Android组件化原理详细介绍

    目录 什么是组件化? 为什么使用组件化? 一步步搭建组件化 1.新建模块 2.统一Gradle版本号 3.创建基础库 4.组件模式和集成模式转换 5.AndroidManifest的切换 6.*业务Application切换 组件之间的跳转 1.添加依赖 2.初始化ARouter 3.添加跳转 组件之间的数据传递 1.定义接口 2.实现接口 组件Application的动态切换 1.定义抽象类 BaseApplication 继承 Application 2.所有的组件的 Application

  • Android与H5互调详细介绍

    Android与H5互调详细介绍 微信,微博,微商,QQ空间,大量的软件使用内嵌了H5,这个时候就需要了解Android如何更H5交互的了:有些外包公司,为了节约成本,采用Android内嵌H5模式开发,便于在iOS上直接复用页面,最终解决成本. 为什么学android也要学h5? Android很多软件都有内嵌H5的,有什么用处.优势?节约成本,提高开发效率. 实现的原理是什么? 本质是:Java代码和JavaScript调用 案例一:Java与Js简单互调 首先,在Android代码中加载H

  • vue.js实例对象+组件树的详细介绍

    vue的实例对象 首先用js的new关键字实例化一个vue el: vue组件或对象装载在页面的位置,可通过id或class或标签名 template: 装载的内容.HTML代码/包含指令或者其他组件的HTML片段,template将是我们使用的模板 **data:** 数据通过data引入到组件中 在组件中的data要以函数的形式返回数据,当不同的界面用了同一个组件时,才不会以为一个组件的值发生改变而改变其他页面的内容. {{ }} 双括号语法里面放入数据的变量 组件注册语法糖 全局组件 A方

  • Android手机信号强度检测详细介绍

    最近到处在跑着找工作,难免在面试过程中遇到这样那样的问题,记得最清楚一次在面试过程中被问到,当手机处于弱网状态下,如何处理,如何监听网络信号强度变化.但是真是蒙了,回答的乱七八糟,思路一点都不明确.今天小编在这里带领大家了解下关于手机信号强度的相关几个概念. Android手机信号强度介绍 android定义了2种信号单位:dBm和asu.它们之间的关系是:dBm =-113+2asu,这是google给android手机定义的特有信号单位.例如,我的信号强度为-53dBm,则对应30asu,因

  • Android集成高德地图详细介绍

    最终效果是,本App展示地图,点击地图导航,调转三方实现导航. 1.迈出第一步,去创建自己的应用key https://lbs.amap.com/1.1创建完应用之后,点击页面的{添加},要求填写自己项目的信息 对于调试获取SHA1的方法是:1.打开cmd,2.在弹出的控制台窗口中输入 cd .android 定位到 .android 文件夹: 3.继续在控制台输入命令: 调试版本使用 debug.keystore,命令为:keytool -list -v -keystore debug.key

  • Android Crash与ANR详细介绍

    目录 Crash 空指针 角标越界 集合元素删除操作 异步操作后对界面元素的处理 Intent传递数据过大 在子线程中操作UI ANR Crash Crash是指程序闪退,导致APP不能正常使用.Crash产生的原因有很多,下面只是列举了一些常见原因. 空指针 空指针应该是项目中最容易产生crash的情况了,举个例子,我们获取某个对象的属性或方法时,这个对象为Null时,如何没有判空,则会出现空指针异常NullPointException,所以这就要求使用对象的时候进行非空判断,在这点,我觉得k

  • Vue父子组件通信全面详细介绍

    目录 1.Vue父子组件通信方式 2.不同文件间的通信方式 1 .父组件vue文件和子组件vue文件 2 .父组件jsx文件和子组件vue文件 3 .父组件vue文件和子组件jsx文件 4 .父组件jsx文件和子组件jsx文件 3.如何实现 1.Vue父子组件通信方式 父子组件通信方式一般为props和emit组合使用,那么在不同的文件中应该如何使用呢? |.vue文件和.jsx文件中有什么不同吗? 2.不同文件间的通信方式 1 .父组件vue文件和子组件vue文件 // 父组件 App.vue

  • Android中的WebView详细介绍

    Android中WebView的详细解释: 1. 概念: WebView(网络视图)能加载显示网页,可以将其视为一个浏览器.它使用了WebKit渲染引擎加载显示网页. 2. 使用方法: (1).实例化WebView组件: A.在Activity中实例化WebView组件.eg: 复制代码 代码如下: WebView webView = new WebView(this); B.调用WebView的loadUrl()方法,设置WevView要显示的网页.eg: 复制代码 代码如下: 互联网用:we

随机推荐