Android组件化开发,从未如此简单

简介: 组件化方式的开发,有很多的文章去阐述,而本篇的特点,在于有实际的组件化实战代码,有开源的组件化Demo样例,重在浅显易懂,重在能够应用于实际业务,也重在简单。

组件化,在实际的业务开发中,越来越多的会使用这种方式,特别是业务逻辑复杂,功能模块较多的项目,越能凸显出组件化的优点,比如各个模块拆分,使其业务分明,比如耦合度低,组件之间相互独立,再比如编译运行速度大大降低,还有代码复用,减少代码冗余,责任明确,减少合并冲突等等,可谓是优点多多,正因为有足够多的优点,组件化开发,一直是目前项目开发中的所推崇的开发方式之一。


组件化方式的开发,有很多的文章去阐述,而本篇的特点,在于有实际的组件化实战代码,有开源的组件化Demo样例,重在浅显易懂,重在能够应用于实际业务,也重在简单。


本文会从以下四个模块进行阐述,各位老铁,准备好板凳,我们开始。


1、为什么要采取组件化

2、如何实现组件化

3、组件化实战

4、开源及Demo


温馨提示,开源相关Demo地址,请滑至文末。


一、为什么要采取组件化


为什么要采取组件化的方式,这个在文章的开头已经诉述了部分优点,当然了,在这里再进行比较详细的概述一下。


1、提高编译运行速度


当我们的项目随着版本的不断迭代,随之增加的功能会越来越多,业务也会变得越来越复杂,最终会导致代码量急剧上升,相关的三方sdk也会不断的涌入,以至于,更改一处,就要全量编译运行,有时候甚至会出现,改一行而等10分钟的情况,非常的耗时,大大降低了开发效率。


而采取了组件化的方式后,相关业务模块,进行单独抽取,使得每个业务模块可以独立当做App存在,和其他模块,互不关联影响,在编译运行时期,只考虑本模块即可,从而减少了代码的编译量,提高了编译运行速度,节约了开发时间。


2、业务拆解,完全解耦


单独的业务模块进行抽取成一个独立的组件,也就是相互不关联的Module,在各自的模块中书写相关的代码,做到,业务拆解,人员拆解,实现真正的解耦。


3、功能复用,节约开发时间


所谓的功能复用,不仅仅是同项目之间的复用,更是以后同样功能模块的复用,比如A项目中有一个直播模块,后面开发的B项目也有,完全可以移植过来复用,无非就是UI等简单逻辑的修改。


4、责任明确,分工明确


组件化的项目,各个业务单独成Module,在独自的Module中开发相关的业务需求,相对于糅合到一个模块中的项目来说,业务之间拆分更加明确,更加清晰,我负责哪个业务,就去哪个组件下去写,使得所负责的任务清晰明确,后续定位问题,也能够第一时间发现并修改。


二、如何实现组件化


晓得了组件化的优点之后,那么在实际的业务开发中,如何实现呢?首先做为组件化,必须相关业务拆分,单独成一个Module,并且可以单独的编译运行,这是最起码的一个前提,否则,就不能成为真正意义上的组件化,要实现组件化开发,需要约束且需要的考虑的因素,大概总结如下。


1、代码架构拆分,合理规划


一个项目从0到1的实施,少不了很多基础的依赖,比如网络,比如图片加载,比如一些第三方sdk等等,无论你的项目是否是组件化,这些潜在的前提,是必不可少的,可能不是组件化的项目,这些底层的使用会和业务相关的代码放到一起,但在组件化的项目,基础库和业务层还是要进行剥离的。


首先呢,基础的依赖是一层,这一层,包含了上层业务层所需要的必须实现,比如网络库,比如图片加载库,比如Dialog加载库,再比如一些常见的,工具类,数据操作等等,我们可以叫它为基础库,基础库的存在,除了提供必须的能力之外,更是对能力的封装,封装在于,给业务层提供方便的调用方式,更是为了可替换,也就是说,后续一旦升级或者更换的其他的,直接在基础库中更改即可,业务层无需操作,大大减少耦合度,比如图片加载,一开始我们使用的是Glide,后边要调整为coil,只需要在基础库中更改即可。


基础库,在经历的封装中,我是把每一个能力项,封装成了一个Module,比如,网络一个,图片加载一个等等,这样做的一个目的,是便于打aar包,也是便于业务层的调用。如下图,是我曾经的封装,看起来很多,但只向业务层暴露依赖使用方式,毕竟这都是基础能力,业务层是看不到的,当然,这个看大家的实际业务需求。



当基础库满足实际的开发需求之后,基础库就可以提供给业务层使用,可以以aar的方式,也可以以library的形式,我是比较推荐aar的方式,远程依赖,方便业务层来调用,后期更改,业务层只需要更改版本号即可,对于基础库,可以逐个提供,也可以聚合提供。


逐个提供,就是把每个能力项,一个一个的给到业务层,让业务层进行使用,比较好的一点是方便业务层选择性使用,需要哪个就用哪个,不会造成内存上的浪费。


聚合层提供,就是把所有的能力项糅合到一起,然后进行拓展,给到业务层,优点是只依赖一个,简单便捷。


具体的提供,主要还是看实际的业务需求,除了基础库之外,在实际的开发中,也就是业务层中,一般还存在一个中间层,中间层是基础库和业务层中间的纽带,有着承上启下的作用,一般公共的方法,属性,资源,数据等都可以放到中间层。


具体的层次划分,大家可以看下图,当然了,实际的业务需求,应以本公司的为准。


在层次划分中,对于开发者来说,最重要的是业务模块,这是直接和开发相关的,组件化的最直接表现就是这一层次,需要根据不同的业务,来拆分出不同的模块。


2、业务单独拆分为Module,可单独编译运行


上边已经阐述,组件化的最直接表现就是在业务模块,也就是根据项目的实际功能,拆分出符合的业务模块,比如社区,比如用户信息,比如商城等等,拆分之后,一定要能保持单独运行,本着合则依赖,拆则运行的态度,那么我们需要解决两个问题,一个是application和library之间的动态切换,另一个就是需要动态改变清单文件资源指向,毕竟单独运行的Module,必须有一个主入口的存在。


application和library之间的动态切换


我们都知道,一个项目在build.gradle中apply plugin只能存在一个application,也就是可运行的模块,一般是我们的主模块,是最终要产出apk包的,而对应的library就是对应的插件,一般以依赖的方式进行使用,在最终打包的时候进行依赖使用,而在单独开发的时候就需要单独运行。


如下举例,根据动态参数来动态进行配置。


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


同样的,针对applicationId,每个业务组件,如果需要进行区分,也是需要进行判断的。


if(is_Module.toBoolean()){
applicationId"com.abner.test" }


动态改变清单文件资源指向


关于主入口,一个项目肯定只有一个,当把组件改为单独运行的时候,在组件的内部,就不得不去创建一个属于当前组件的主入口,也就是,单独运行时使用当前组件,合并依赖运行时使用主Module里的。


<activityandroid:name=".MainActivity"><intent-filter><actionandroid:name="android.intent.action.MAIN"/><categoryandroid:name="android.intent.category.LAUNCHER"/></intent-filter></activity>


动态切换,也是根据动态参数来动态进行配置。


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


关于业务Application,当前组件创建,当前组件下的mainfest引入即可,通过以上的配置,只需要改变动态参数值就可以实现,单Module的运行。


3、各模块统一依赖,统一版本号


随着业务功能的不断增加,相关的组件也会随着增加,如果各个组件,都在自己的组件里进行依赖,依赖多了之后,就会出现重复依赖,版本号不一致的情况,针对这种问题,最直接的处理方式是,统一gradle文件,也就是所有的组件统一使用一个,除了一些特殊的依赖之外,所有的依赖也可以放在统一的依赖之中。


抽取封装之后,便于所有的依赖管理,也便于统一版本号依赖。如下所示,抽取之后,每个组件下的build.gradle文件里,直接使用统一的文件即可,在代码上也是非常的清晰简单。


app下



Module下



4、考虑问题一,组件之间页面跳转


由于组件之间互不依赖,一个突出的问题就是,页面之间如何跳转,比如我A模块要跳转到B模块中的一个页面,我该如何跳转呢?


这个市场上已经有很多成熟的三方了,比如ARouter ,ActivityRouterDeepLinkDispatch等等,当然用的比较多的就是ARouter了,已经有了成熟的,我们直接使用即可,没有必要再重复的造轮子。


通过ARouter 可以很方便的实现组件之间的通信,更重要的是方便了H5和原生交互之间的跳转。


官方文档如下

https://github.com/alibaba/ARouter


”一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦“,github 上 ARouter 的介绍可以知道,它可以实现组件间的路由功能,路由是指从一个接口上收到数据包,根据数据路由包的目的地址进行定向并转发到另一个接口的过程,这里可以体现出路由跳转的特点,非常适合组件化解耦。


使用起来也是非常的简单


 

ARouter.getInstance().build("/test/test").navigation()


具体使用,看大家可以根据查看官网,而关于实际的业务使用,我们放到下面的第3项组件化实战中。


5、考虑问题二,组件之间数据传递


数据之间的传递,如果是页面单纯的传递数据,直接可以使用ARouter提供的传递方式,比如:


ARouter.getInstance().build("/test/test")
            .withLong("key1", 666L)
            .withString("key3", "888")
            .withObject("key4", newTest("Jack", "Rose"))
            .navigation();


如果不是页面之间的传递,那么就需要我们自己定义接口或者通过中间层common来实现了,当然了需要进行逻辑处理。


6、考虑问题三,组件之间Fragment使用


A组件要想使用B组件里的Fragment,我们可以通过反射的方式进行获取,既然有了ARouter,我们直接可以使用ARouter提供的方式,非常的简单。


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


7、考虑问题四,组件之间功能互相调用


A组件想要触发B组件里一个功能,一个事件,那么如何处理呢?这个可以利用ARouter的IProvider,具体可以看下面的实战中代码。


8、考虑问题五,组件之间资源命名


组件多了,资源命名难免会出现重复,比如类名,比如layout名字,比如string名字,再比如其他的资源名等等,在实际的开发中,一旦名字重复,有可能造成资源冲突等问题,为了避免这样的问题出现,一般我们在组件化开发的时候,以组件的名字做为前缀,可以进行避免。

团队协作开发,我们可以进行资源约束,给开发者进行相关提示。


android {
 ....
resourcePrefix"module_xxx" ....
}


三、组件化实战


经过第2项中的阐述,相信对组件化已经有了一个初步的认识,那么接下来我们就一步一步进入实战当中,需要注意的是,在实际的业务中,基础库肯定是先有的,毕竟是我们开发项目的能力项,因为做为一个Demo,不可能在进行对基础库做一个封装,那样就太耗时了,所以啊,老铁们,我们直接进行业务层的书写,毕竟,组件化,一般就是针对于业务层而言。


1、创建项目


我这里首先创建一个项目,毕竟不是实际业务,目前就先创建四个模块,app是主模块,common是中间层,account和code是组件,也就是实际的业务模块,如下图。



具体的组件架构流程图如下,common做为中间层,角色承担十分重要,比如公用的方法,属性,数据,资源等,都可以放置这层,便于业务模块直接的中转跳转或数据获取。



2、统一依赖,统一版本号


根项目下新建一个gradle文件,如下图



gradle文件主要是做为组件之间的统一使用,比如版本号,比如依赖等等,有了这样的一个公共配置,所有的组件都可以进行统一管理,当然,除了部分无法实现之外。


全部代码如下,相关注释很是齐全。


project.ext {
//是否允许module单独调试isModuleDebug=falsemoduleName=""//单独调试module名//基础信息配置compileSdkVersion=30buildToolsVersion="30.0.2"minSdkVersion=21targetSdkVersion=30applicationId="com.abner.assembly"versionCode=1versionName="1.0.0"//设置app配置setAppDefaultConfig= {
extension ->
//指定为applicationextension.applyplugin:'com.android.application'extension.description"app"//公共的apply 主要是用于三方库extension.applyplugin:'kotlin-android'extension.applyplugin:'kotlin-parcelize'extension.applyplugin:'kotlin-kapt'appImplementation="app"//设置项目的androidsetAppAndroidConfigextension.android//设置项目的三方库依赖setDependenciesextension.dependencies    }
//设置application 公共的android配置setAppAndroidConfig= {
extension ->
extension.compileSdkVersionproject.ext.compileSdkVersionextension.buildToolsVersionproject.ext.buildToolsVersionextension.defaultConfig {
applicationIdproject.ext.applicationIdminSdkVersionproject.ext.minSdkVersiontargetSdkVersionproject.ext.targetSdkVersionversionCodeproject.ext.versionCodeversionNameproject.ext.versionNameextension.flavorDimensions"versionCode"testInstrumentationRunner"androidx.test.runner.AndroidJUnitRunner"javaCompileOptions {
annotationProcessorOptions {
arguments= [AROUTER_MODULE_NAME:project.getName()]
                    }
                }
            }
extension.compileOptions {
sourceCompatibilityJavaVersion.VERSION_1_8targetCompatibilityJavaVersion.VERSION_1_8            }
extension.kotlinOptions {
jvmTarget='1.8'            }
extension.buildFeatures.dataBinding=true    }
//动态改变,用于单模块调试setAppOrLibDefaultConfig= {
extension ->
if (project.ext.isModuleDebug&&project.ext.moduleName==project.name) {
extension.applyplugin:'com.android.application'extension.description"app"            } else {
extension.applyplugin:'com.android.library'extension.description"lib"            }
extension.applyplugin:'kotlin-android'extension.applyplugin:'kotlin-parcelize'extension.applyplugin:'kotlin-kapt'appImplementation=project.name//设置通用Android配置setAppOrLibAndroidConfigextension.android//设置通用依赖配置setDependenciesextension.dependencies    }
//设置通用的 android配置(可作为project单独调试)setAppOrLibAndroidConfig= {
extension ->
extension.compileSdkVersionproject.ext.compileSdkVersionextension.buildToolsVersionproject.ext.buildToolsVersionextension.defaultConfig {
minSdkVersionproject.ext.minSdkVersiontargetSdkVersionproject.ext.targetSdkVersionversionCodeproject.ext.versionCodeversionNameproject.ext.versionNameextension.flavorDimensions"versionCode"testInstrumentationRunner"androidx.test.runner.AndroidJUnitRunner"//ARouter 编译生成路由javaCompileOptions {
annotationProcessorOptions {
arguments= [AROUTER_MODULE_NAME:project.getName()]
                    }
                }
            }
//使用的jdk版本extension.compileOptions {
sourceCompatibilityJavaVersion.VERSION_1_8targetCompatibilityJavaVersion.VERSION_1_8            }
extension.kotlinOptions {
jvmTarget='1.8'            }
//动态改变清单文件资源指向extension.sourceSets {
main {
if (project.ext.isModuleDebug&&project.ext.moduleName==project.name) {
manifest.srcFile'src/main/manifest/AndroidManifest.xml'                    } else {
manifest.srcFile'src/main/AndroidManifest.xml'                    }
                }
            }
    }
//公用的三方库依赖,慎重引入,主要引入基础库依赖setDependencies= {
extension ->
extension.implementationfileTree(dir:'libs', include: ['*.jar'])
extension.implementation"org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"extension.implementation'androidx.core:core-ktx:1.3.1'extension.implementation'androidx.appcompat:appcompat:1.3.1'extension.implementation'com.google.android.material:material:1.4.0'extension.implementation'androidx.constraintlayout:constraintlayout:2.0.1'extension.testImplementation'junit:junit:4.+'extension.androidTestImplementation'androidx.test.ext:junit:1.1.2'extension.androidTestImplementation'androidx.test.espresso:espresso-core:3.3.0'extension.kapt'com.alibaba:arouter-compiler:1.5.2'if(appImplementation!="common"){
//common做为中间层,所有的Module都要依赖extension.implementationextension.project(path:':common')
            }
//针对每个Module单独进行依赖switch (appImplementation) {
case"app":
extension.implementationextension.project(path:':account')
extension.implementationextension.project(path:':code')
breakcase"account":
breakcase"common"://common组件是一个中间层,所有的组件都需要依赖此组件,公共的依赖便可放到这里extension.api'com.alibaba:arouter-api:1.5.2'//ARouter依赖break            }
    }
}


相关注释都有,相信大家一看便懂,无非就是抽取了公共的部分,相当于一个模板,大家完全可以拿走直接用,只需要改成自己的相关依赖即可,不过,针对这个文件,还是要做一个简单的概述。


概述一,如何单组件运行


只需要改动下面的两个参数即可,isModuleDebug参数,true就是开启单组件运行模式,moduleName就是你要运行的那个Module名字,之所以定义两个参数,是为了精准到位,组件之间清晰,方便部分组件之间依赖。


//是否允许module单独调试isModuleDebug=falsemoduleName=""//单独调试module名

 

概述二,统一管理,以后只维护这个文件即可


有了这个文件之后,其他的Module中的build.gradle里就可以根据需要进行简写,比如,app下:



比如其他组件下



这样,不管是依赖,还是版本号,以后统一的在一个文件里进行增加和改变,杜绝私自添加三方依赖,便于审阅。


概述三,单个组件如何进行依赖


通过appImplementation参数进行识别是哪一个Module,然后进行单独区分即可。



概述四,动态改变清单文件资源指向

文件中已经逻辑处理



3、单Module运行


在第2步中,对所有的组件进行了依赖和版本号管理,同样也加入了动态改变清单文件资源指向,那么一个单独的组件如何运行呢?我们以Demo中的account模块举例。


第1步,按照动态改变清单文件资源指向相关逻辑,新建mainfest目录,记住,必须和你定义的路径保持一致,如下:



第2步,在manifest文件下复制一份AndroidManifest.xml文件。



需要注意,manifest文件下的AndroidManifest.xml文件和组件下的AndroidManifest.xml文件是有区别的,区别就是,当你选择组件运行时,会编译manifest文件下的,当做依赖使用时,走组件下的,除此之外,当你选择组件运行时,manifest文件下的AndroidManifest.xml文件必须有主入口,如下:



而组件下,是不需要的。


AndroidManifest.xml文件创建完之后,如果需要有初始化配置的,大家可以自行创建Application,待相关组件名,样式引入后,更改统一管理gradle文件下的参数,如下:



再看模块目录,account已经和app保持一致,可以单独运行了。



4、组件之间进行跳转


新建的项目中,我把account和code做为业务组件,两个组件是互不关联的额,我们要实现的就是这两个组件之间的跳转,组件之间跳转使用的是阿里的ARouter,大家可以按照官网进行一步步依赖,我在第2步中已经依赖进去,最新的版本,如下所示:



依赖之后,按照官网对其初始化



在common中间层组件之中,我创建了两个文件,一个路由常量类,一个是路由工具类,大家直接可以看源码,这里就不贴代码了,就是对ARouter做了一个简单的封装而已。


页面跳转


从Account组件跳转到code组件。

给目标页面设置路由地址


@Route(path=CommonRouterConstant.CODE)
classCodeActivity : AppCompatActivity() {
overridefunonCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_code)
    }
}


跳转目标路由


CommonRouterManger.get().navigationActivity(CommonRouterConstant.CODE)


页面带参数跳转


已经做了一个简单的封装,可通过下面的方式,可变参数,值是Any类型,大家可以参考源码。


CommonRouterManger.get().navigationActivityParams(
CommonRouterConstant.CODE,
"params"to"参数",
"params2"to100 )


接收参数呢,有两种形式,一种是intent来接收,一种是Router形式。


intent接收参数


valstringExtra=intent.getStringExtra("params")
valintExtra=intent.getIntExtra("params2", -1)


ARouter形式接收参数


@Autowired(name="params")
@JvmFieldvarmParams=""@Autowired(name="params2")
@JvmFieldvarmParams2=-1


ARouter形式接收参数,请一定要记得初始化,一般在父类中即可。


ARouter.getInstance().inject(this)


5、组件数据共享


数据共享的场景比较常见,比如用户信息组件,当用户登录后,需要保存用户的数据,那么其他相关组件怎么获取使用呢,这种时候,我们就可以让中间组件common,担负起应有的责任,因为common是所有业务组件都依赖的,用户信息组件存储数据后,在common组件里写一个工具类即可,这样,其他组件都可以通过common组件来获取数据了。


这种方式比较简单,大家可以看下源码,源码中简单举了一个例子,在common组件里定义了工具类,数据模型,提供了存储和获取数据的方法,然后account组件里模拟登录信息存储,在code组件里进行模拟获取。


当然了除了采用这种方式之外,也可以使用服务的方式进行获取,首先在common组件定义接口,定义其中自己想要获取的数据,如下:


interfaceAccountUserService : IProvider {
/*** AUTHOR:AbnerMing* INTRODUCE:获取用户信息*/fungetUser(): UserInfo?}


定义好接口之后,同样也分为两步,一步是在需要设置数据的组件里进行传递数据,一步是在需要数据的组件里获取数据,也就是Demo中的account组件实现其接口,进行传递数据,如下:


@Route(path=CommonRouterConstant.USER_INFO, name="AccountUserServiceIml")
classAccountUserServiceIml : AccountUserService {
overridefungetUser(): UserInfo? {
//获取用户信息后,转成需要的对象valuserJson=DataStoreUtils.getSyncData("user", "")
if (TextUtils.isEmpty(userJson)) {
returnnull        }
returnGson().fromJson(userJson, UserInfo::class.java)
    }
}


哪个组件需要用到数据,就可以如下操作:


valiml=CommonRouteServiceManager.provide(AccountUserService::class.java)
//模拟,通过服务进行获取用户相关信息iml?.getUser()?.let {
Toast.makeText(this, it.data?.nickName, Toast.LENGTH_SHORT).show()
    }

 

CommonRouteServiceManager是对ARouter获取服务做了一个简单的封装,大家可以看下源码。


6、组件之间调用功能


功能的调用其实还是用到了ARouter的一个获取服务的功能,和获取数据的思路基本一致,这里举一个简单的例子,在Account里有一个弹出Dialog的功能,但是呢,是在code组件里进行调用,就可以如下操作:


在原有的Service里增加方法:


interfaceAccountUserService : IProvider {
/*** AUTHOR:AbnerMing* INTRODUCE:测试弹出Dialog*/funshowDialog()
}


在account组件里进行实现方法


@Route(path=CommonRouterConstant.USER_INFO, name="AccountUserServiceIml")
classAccountUserServiceIml : AccountUserService {
/*** AUTHOR:AbnerMing* INTRODUCE:测试弹窗或其他功能*/overridefunshowDialog() {
AlertDialog.Builder(mContext)
            .setTitle("测试一个Dialog弹出框")
            .setMessage("简单测试以下")
            .setNegativeButton("取消") { _, _->            }
            .setPositiveButton("确定") { _, _->            }
            .show()
    }
privatevarmContext: Context?=nulloverridefuninit(context: Context?) {
mContext=context    }
}


在code组件里进行调用


valiml=CommonRouteServiceManager.provide(AccountUserService::class.java)
iml?.init(this)
iml?.showDialog()


四、开源及Demo


目前呢,源码中只是测试的Demo,简单的举例了组件化开发的相关流程及所需要,所注意的事项,大家可以做为参考,而实际的开发中,除了业务的不同,但所执行的流程基本是一致的,还有,关于ARouter的使用,其实官网中还有很多的使用方式,这个大家去官网看即可。


Github地址

https://github.com/AbnerMing888/AndroidAssembly

相关文章
|
5天前
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
21 5
|
3天前
|
设计模式 IDE Java
探索安卓开发:从新手到专家的旅程
【10月更文挑战第22天】 在数字时代的浪潮中,移动应用开发如同一座金矿,吸引着无数探险者。本文将作为你的指南针,指引你进入安卓开发的广阔天地。我们将一起揭开安卓平台的神秘面纱,从搭建开发环境到掌握核心概念,再到深入理解安卓架构。无论你是初涉编程的新手,还是渴望进阶的开发者,这段旅程都将为你带来宝贵的知识和经验的财富。让我们开始吧!
|
20天前
|
缓存 搜索推荐 Android开发
安卓开发中的自定义控件基础与进阶
【10月更文挑战第5天】在Android应用开发中,自定义控件是提升用户体验和界面个性化的重要手段。本文将通过浅显易懂的语言和实例,引导你了解自定义控件的基本概念、创建流程以及高级应用技巧,帮助你在开发过程中更好地掌握自定义控件的使用和优化。
27 10
|
12天前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
49 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
20天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异:从代码到用户体验
【10月更文挑战第5天】在移动应用开发的广阔天地中,安卓和iOS两大平台各占半壁江山。它们在技术架构、开发环境及用户体验上有着根本的不同。本文通过比较这两种平台的开发过程,揭示背后的设计理念和技术选择如何影响最终产品。我们将深入探讨各自平台的代码示例,理解开发者面临的挑战,以及这些差异如何塑造用户的日常体验。
|
3天前
|
搜索推荐 Android开发 UED
安卓开发中的自定义视图:打造个性化用户界面
【10月更文挑战第22天】在安卓应用的海洋中,如何让你的应用脱颖而出?一个独特且直观的用户界面(UI)至关重要。本文将引导你通过自定义视图来打造个性化的用户体验,从基础的视图绘制到触摸事件的处理,我们将一步步深入探讨。准备好了吗?让我们开始吧!
|
4天前
|
Android开发
我是一位Android工程师,用通义灵码的AS插件做开发工作助手,对比之前没有灵码,现在提效了60%
我是一位Android工程师,用通义灵码的AS插件做开发工作助手,对比之前没有灵码,现在提效了60%
15 0
|
17天前
|
消息中间件 存储 前端开发
资深Android开发的5个经典面试题
本文首发于公众号“AntDream”,欢迎关注。文章详细解答了五个常见的Android面试题,涵盖内存泄漏与溢出、Binder机制、MVC/MVP/MVVM架构、Handler机制及Context对象等内容,帮助读者深入了解Android开发的核心概念。
21 0
|
17天前
|
存储 安全 Android开发
探索Android开发之旅:从新手到专家的蜕变之路
【10月更文挑战第8天】在这篇文章中,我们将共同踏上一段激动人心的旅程,深入探索Android开发的奥秘。无论你是初涉编程世界的新手,还是渴望提升技能的开发者,这里都有你需要的知识与启示。通过简洁明了的语言和实际案例,我们将一起解锁Android开发的核心概念、掌握关键技能,并最终实现从新手到专家的华丽转变。
|
18天前
|
Android开发
Android开发显示头部Bar的需求解决方案--Android应用实战
Android开发显示头部Bar的需求解决方案--Android应用实战
16 0