安卓现代化开发系列——传世不朽ViewModel

简介: 安卓现代化开发系列——传世不朽ViewModel

由于安卓已经诞生快二十载,其最初的开发思想与现代的开发思想已经大相径庭,特别是Jetpack库诞生之后,项目中存在着新老思想混杂的情况,让许多的新手老手都措手不及,项目大步向屎山迈进。为了解决这个问题,开发者必须弄懂新旧两种开发模式,这就是《安卓现代化开发系列》诞生的意义,本系列并不会包含隐晦难懂的代码,一切的文字都是以理解本质为主,起到一个抛砖引玉的作用。

本章与「状态保存与SavedState」章有较强联系,建议阅读过后再浏览本章节。


1、ViewModel的来源——从状态保存谈起


1.1、SavedState并不是ViewModel的特性

在「状态保存与SavedStated」一章中,提到了ViewModelSavedState的关系,其中提到了SavedStateHandle的意义是用来解决ViewModel难以「访问组件入参」以及「保存状态」的两个难题。

但是我们回头看SavedStateHandle是如何被引入ViewModel的:

image.png

可以看见,SavedStateHandle通过构造函数传入ViewModel,也就是说ViewModel在默认情况下,是没有「访问组件入参」以及「保存状态」2个功能的,那么ViewModel的对于状态保存的意义在哪里呢?

答案是:ViewModel可以在「配置更改导致的Activity重建」后仍然保存自身的实例。

换句话说,开发者使用ViewModel之后,无需担心再花费精力去处理配置更新导致的组件销毁问题,因为ViewModel并不会受到影响。

那么这个「配置更改导致的Activity重建」后仍可以保存实例的机制又是如何实现的呢?

1.2、无人问津的onRetainNonConfigurationInstance()

Activity的源码中,存在着一个几乎没什么人用的Api——onRetainNonConfigurationInstance()

这个Api并没有在「状态保存与SavedStated」一章中被介绍的原因也是几乎没人使用它。

image.png

从名字可以看出,这个方法的用途是保存一些与配置无关的实例,读过「状态保存与SavedStated」的读者肯定会联想到Activity中保存实例的方法:onSaveInstanceState(Bundle),两者的区别如下:

onSaveInstanceState(Bundle) onRetainNonConfigurationInstance()
调用时机 组件onStop()前/后被调用 配置发生改变时被调用
支持类型 只支持基础类型和Parcelable/Serializable类型 支持任意类型
大小限制 受Binder限制,数据不能超过1MB 大小不受限制
实现原理 先通过Binder反序列化,再存储于内存中 直接存于内存中

回到本节的标题,为什么onRetainNonConfigurationInstance()无论是支持的类型还是大小都遥遥领先于onSaveInstanceState(Bundle),但它却几乎不受程序员的待见呢?

答案就是它的调用时机过于局限了,这也和这个API的设计初衷有关系,因为它只能用于处理「配置更新导致的Activity销毁」的这种场景,因此它并不是每次都进入onStop()(高版本在之前,低版本在之后)都被调用。

对于配置发生改变时要保存的状态,onSaveInstanceState(Bundle)也能做,即使有类型和大小的限制,程序员们也习惯于统一往onSaveInstanceState(Bundle)中实现所有的状态保存逻辑,因为这能降低维护的复杂性。

以上造就了onRetainNonConfigurationInstance()几乎无人使用的窘境。

1.3、丢掉or进一步扩展,这是一个问题

上一节提到,onRetainNonConfigurationInstance()遭遇了程序员的冷落,但是这能证明这个Api是无用的吗?

如果你对Binder的机制有一点了解的话,可以知道的是,为了实现跨进程,所有通过Binder传输的对象都要反复的序列化和反序列化,这就导致了性能上的劣势,当然还存在大小的限制。

如果配置更新导致了组件的销毁,页面中其实存在着有许多不需要跟随配置更改而改变的字段,例如已经加载好的bitmap。对于这类内存巨大的字段用Binder存起来也不合适,这就给onRetainNonConfigurationInstance()这个Api生存的空间,开发者可以通过这个Api缓存一些较大的对象来避免因配置更改后反复加载的缺点。

但是又回到了上一节的问题,这个Api实在不好用,我们应该直接抛弃它吗?答案是否定的。

在1.1节中笔者提到的ViewModel了一个重要特性:「配置更新后不会销毁」,读者是否觉得它与onRetainNonConfigurationInstance()这个Api的特性非常相近呢?对的你没猜错,ViewModel就是基于这个Api来实现其跨越配置更改的特性的。

总结:onRetainNonConfigurationInstance()并不是没用了,而是谷歌基于这个Api实现了ViewModel,开发者只需要使用ViewModel便享受到了这个Api的便利。相对于难以使用的原生Api,ViewModel确实好用特别多。

注意一点的是:「不会因配置更新而销毁」并不是ViewModel的全部意义,这个只是它的一个非常重要的特性,ViewModel还有许多优秀的特性这点下面会聊到。

1.4、ViewModel如何跨越配置更新的鸿沟

经过前三节的铺垫,笔者想必已经明白了ViewModel是使用onRetainNonConfigurationInstance()来实现避免配置更新导致自身销毁的机制的,具体如何实现本节展开讲讲:

关于ComponentActivity,这个Activity的子类在「Lifecycle」与「状态保存」的章节中都频繁出现过,其基本是Jetpac核心功能的基础实现,因此下面的源码也是基于这个子类来讲解。

我们从ComponentActivity的源码出发,看看核心的代码:

image.png

核心代码就一个方法,就是上文提到的onReainNonConfigurationInstance(),而且谷歌还让其标记为final,即不可继承重写,这个方法只会强制返回一个类:NonConfigurationInstances,其中包括了一个Object类型的custom,一个ViewModelStore

标记为final并不意味着开发者不能实现该方法原本非常灵活的任意类型的返回值,而是谷歌将其放在了NonConfigurationInstances这个类的custom中,重写onRetainCustomNonConfigutaionInstance()即可,不过这并不是重点(因为这只是一种兼容老开发模式的手段),该类另外一个成员变量ViewModelStore才是本篇文章的核心。

上文提到,NonConfigurationInstances的核心是ViewModelStore,因此我们可以去掉custom之后单独围绕它来看,那么这个Api就会被简化成下面这样:

image.png

简化后的代码非常清晰,其实就是在保存ViewModelStore

也许你并不清楚什么ViewModelStoreViewModel的关系,这里你只需要明白一点即可:ViewModelStore是一个缓存ViewModel的容器,通过它就可以拿到ViewModel

在配置更新时保存ViewModelStore,并在组件重建之后重新拿到ViewModelStore,那么自然而然的就拿到了对应的ViewModel

我们看看ComponentActivity是如何拿到ViewModelStore的:

image.png

每次ComponentActivity要访问ViewModelStore的时候,都会主动调用ensureViewModelStore()这个方法,看看有没有往非配置实例中写入ViewModelStore,如果有则读出来,如果没有就新建一个。

以上就是:「ViewModel可以在配置更新后不会销毁」的秘密。


2、ViewModel是谁?


ViewModel的定义放在第二节是笔者有意为之,在第一节中,笔者为读者展示了ViewModel如何解决了开发者一个巨大的痛点,即「处理因配置更新导致的组件销毁从而导致的状态丢失」的问题,读者相比已经对ViewModel有了一个基础的认知,但如同上面提到过得一样,跨越配置更新的鸿沟并不是ViewModel的全部优点和特性,下面为你逐步掀开ViewModel的头盖骨面纱。

2.1、定义

ViewModel 类是一种业务逻辑或屏幕级状态容器。它用于将状态公开给界面,以及封装相关的业务逻辑。它的主要优点是,它可以缓存状态,并可在配置更改后持久保留相应状态。这意味着在 activity 之间导航时或进行配置更改后(例如旋转屏幕时),界面将无需重新提取数据。

上文中重点讲了ViewModel可以让在配置更改后保存自身的存在,从定义中我们可以看出,其实ViewModel更重要的一个身份是「状态容器」,换句话说ViewModel负责广播状态,而组件(ActivityFragment)则回归纯粹UI的本质,引入ViewModel之后的不仅是多了一个组件的区别,更多的改变则在于开发范式的转变(关于这个话题下文会讲)。

2.2、ViewModel简单使用

简单看一下引入ViewModel之后的Activity的变化(Fragment代码基本类似,不重复展示):

image.png

还记得ViewModel的两个特性吗,这里重温下:

  • 状态容器
  • 规避配置更新导致的丢失

图中一个显著的特征是原本应该位于Activity中的成员变量被移动到了ViewModel的内部,这体现了ViewModel作为状态容器的特性,这样做的好处就是让逻辑收拢在了ViewModel的内部,这让UI更加容易迁移和调试(因为降低了耦合度)。

「规避配置更新导致的丢失」这个特性在图中不好展示,但是读者可以参考上述的代码自己实现一个小demo,然后旋转屏幕,观察重建的Activity中的数据有没有发生丢失现象。

上文提到的「开发范式的转变」指的是开发模式逐渐过渡到MVVM或者其他开发思想中来,一种常见的特征就是状态均以LiveData或者StateFlow的形式出现。

2.3、SavedState的使用

关于SavedState的使用在「状态保存与SavedState」一章中有详细讲解如何使用,本章不再继续展开


3、ViewModel剖析


为了让读者对ViewModel的整个体系有大致的理解,这里先把ViewModel的几个关键组件列举出来,只需要留个印象即可,后续会串联起来。

3.1、核心组件

3.1.1、ViewModel

ViewModel无疑是该库中最核心的组件,但是其内部却极其简单,只有两个容器,主要的作用就是存放Tag和Closeable,这两者会在ViewModel被关闭的时候被清空。

image.png

那么ViewModel何时被关闭呢?答案是组件遇到「非配置更新导致的销毁」的时候。

简单看看ComponentActivity的源码可以看见,只有非配置更新导致的销毁,才会让ViewModelStore销毁。

image.png

综合来说,ViewModel的基类只提供一个销毁时的监听的功能,其创建、销毁由库中的其他组件实现,其业务实现则全部交给开发者。

3.1.2、ViewModelStore、ViewModelStoreOwner

ViewModelStore是存放ViewModel的仓库类,通过Key来区分不同的ViewModel实例。

ViewModelStoreOwner和生命周期篇讲过的LifecycleOwner基本类似的设计模式,本质只是一个提供实例的接口。

image.png

ViewModelStore遵循以下原则:

  • ViewModelStore需要配置更改后仍然得到保留,如果没法保留而被销毁了,那么保存的ViewModel实例也要一样,这里简单来说就是map内部的成员实例都不能丢。
  • 如果ViewModelStore的持有者不再需要它而且也不会重新创建它,则其所有者需要调用ViewModelStore的clear()方法通知它不再使用。

以上两条原则虽然对于绝大多数开发者来说并不会使用,因为大部分开发者都不会亲自开发ViewModelStore,但是谷歌亲自开发的Jetpack库中,均遵循这两条准则,因此开发者将其理解为ViewModelStore的「特性」即可。

3.1.3、ViewModelProvider、Factory

上文提到ViewModelStore只是一个存储ViewModel的容器,它并没有创建ViewModel的功能,而ViewModelProvider正好弥补了这个功能。

从下图中可以看出两点:

  • ViewModelProvider通过工厂类创建ViewModel
  • ViewModelProvider的核心代码是get(),其原理就是简单的有就取缓存,没有就用工厂类创建一个ViewModel并放置在ViewModel与缓存中

image.png

3.1.4、Factory

本小节由CreationExtras 来了,创建 ViewModel 的新方式 - 掘金 (juejin.cn) (opens new window)中精炼总结得来,可以读原文获取更加详细的信息。

上文提到 ViewModel 是使用工厂类来实例化的,因为ViewModelFragment需要在非开发者干预的情况下由系统创建,而工厂类就是定义了不同构造函数的创建方式。

image.png

可以看出存在着两套创建方式,一套是带 CreationExtras一套是不带的,导致这种原因是ViewModel在2.5.0 版本中新增了一种带 CreationExtras 的构建方式。

要讲清楚这一套新增的方式做了什么,我们首先把目光放回以前,看看以前的方式:

3.1.4.1、传统的Factory与其缺陷

假设开发者需要在ViewModel中增加非空构造函数,则需要实现对应的工厂类,假设有一个ViewModel的构造函数需要Application和一个仓储类作为入参,则需要实现一个这样的工厂类:

image.png

可以看出,该ViewModel的工厂类和其需要的参数基本要保持一致,这样才可以保证工厂类创建ViewModel时拥有所有的参数。

但是我们回顾这种方式有什么缺陷呢?其实缺陷还是挺大的,如下:

  • ViewModel需要的参数可能较多,而且不同的ViewModel也大相径庭,让工厂类丢失了工厂的作用,开发者几乎要为不同的ViewModel都创建一个工厂类。
  • 工厂类持有状态,不利于复用。

正因为上述的问题,谷歌提出了一种新的类似于Bundle的解决方式,就是上文提到的 CreationExtras

3.1.4.2、引入CreationExtras的巨变

我们重新看工厂类的另外一种方法,传入了一个CreationExtras的参数,这为新的构建方式提供了基础。

image.png

CreationExtras就像Activity跳跃时的Intent传参一样,是通过key-value的方式传递的,因此比较灵活,看看使用了新版之后该如何构建ViewModel

image.png

这个时候,Factory不再需要构造函数和任何成员变量了,变成了彻底的「无状态」,更利于复用。

可以看到的是,图中定义了一个key,是传输开发者定义的仓储类的,但是Application使用了一个预定义的Key,这个是谷歌开发者提前定义好的,其他还有几个预定义的key,如下图:

CreationExtras.Key Descriptions
ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY ViewModelProvider 可以基于 key 区分多个 VM 实例,VIEW_MODEL_KEY 用来提供当前 VM 的这个 key
ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY 提供当前 Application context
SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY 提供创建 createSavedStateHandle 所需的 SavedStateRegistryOwner
SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY createSavedStateHandle 所需的 ViewModelStoreOwner
SavedStateHandleSupport.DEFAULT_ARGS_KEY createSavedStateHandle 所需的 Bundle

上图展示的是工厂类如何使用该变量,但是变量是哪里传入的呢,读者还记得工厂类是哪里被使用吗?它是被ViewModelProvider使用的,那么CreateExtras自然是ViewModelProvider所提供的,我们重新看看下图:

image.png

可以看到,ViewModelProvider在构建ViewModel的时候,会将默认参数传给工厂类,这样工厂类就可以在构建ViewModel的时候,通过对应的key拿到对应的value,这样就可以满足不同ViewModel的构建需要了。

3.1.5、小总结

下面以一张图总结各个组件之间的关系:

image.png

3.2、ViewModel回首掏

3.2.1、从使用流程回首ViewModel

上文简单讲解了ViewModel几个核心组件的功能,下文将从ViewModel的使用流程去将几个组件所处的位置理清楚。

需要注意的是,由于需要讲解组件的使用,因此不会使用委托的方式创建ViewModel,同时以Activity为讲解组件。

下面看看ComponentActivity中使用ViewModel的典型案例:

image.png

当生命周期进入onCreate()的时候,通过ViewModelProvider来新建一个MyViewModel,留意到工厂类传的值是defaultViewModelProviderFactory

这个参数来自于一个接口:HasDefaultViewModelProviderFactory

image.png

这个接口是什么意思呢,这个接口是给ViewModelStoreOwner实现的,相当于某个StoreOwner存在着默认的创建ViewModel的工厂类。

上文中提到,ViewModelProvider需要从Owner手里获取工厂类来了解如何构建ViewModel,对于单个Owner来说,绝大多数情况创建ViewModel的方式都是相同的(大多数情况都是无参或者带SavedStateHandle),因此拥有一个「默认工厂」是极大的便利。

开发者可以在ComponentActivity中使用defaultViewModelProviderFactory的原因恰恰是它也实现了该接口:

image.png

可以看出,这是一个创建「参数带有SavedState」的ViewModel的工厂类,同时SavedState默认带有Activity的getIntent().getExtras(),而在Fragment中则是getArugments()(图中没体现,读者可以去Fragment的源码中查看)。

因此开发者在ComponentActivity中创建ViewModelProvider的时候使用的默认工厂其实就是SavedStateViewModelFactory

如果你从来没有手动传过这个默认工厂也没关系,ViweModelProvider 会自动从ViewModelStoreOwner中拿,哪怕 ViewModelStoreOwner没有默认工厂也会使用一个最简单的实例化工厂,但这个工厂只能用于简单的无参实例化, 不过鉴于开发者普遍使用 ComponentActivity这个问题应该不存在。

3.2.1、重谈ViewModel的by委托

开发者目前比较喜欢的创建方式是使用by委托来创建ViewModel,如下:

image.png

这种方式背后做了什么呢?我们看看viewModels的定义:

image.png

可以看出,这个委托还有2个参数,分别是传入CreationExtrasFactory,读者还记得它们是做什么的吗?如果你忘了建议回去看看第三节。

继续从代码中可以读出,如果开发者不传Factory,那么就用使用ComponentActivity的默认工厂类,这个上面也提到了,实际上就是SavedStateViewModelFactory

相同的是,如果开发者不传CreationExtras,那么就会使用使用defaultViewModelCreationExtras

ViewModelLazy就不进一步研究了,代码非常简单,读者自行研究即可,本质上就是套了一层kotlin的lazy委托来实现懒加载。

终上所述,在ComponentActivity中使用by viewModels创建ViewModel基本等价于下面的代码:

image.png

当然,使用by委托还可以享受懒加载的优势,这样就不用监听生命周期,建议一律使用谷歌官方封装的委托。


4、项目实践中看ViewModel


4.1、构建ViewModel的几种情况

4.1.1、构造函数为空的ViewModel

对于这种ViewModel使用来说最为简单,直接使用委托即可

4.1.2、需要访问SavedStateHandle的ViewModel

对于这种ViewModel使用也非常简单,直接使用委托即可,因为ComponentActivityFragment等常见场景都已经适配了SavedStateHandle

4.1.3、需要更多参数的ViewModel

4.1.3.1、自定义工厂类+自定义CreationExtras

对于这种场景,开发者需要在委托中传入自定义的工厂类,例如某个ViewModel需要一个SavedStateHandle和一个仓储类作为入参,开发者可以实现如下代码:

代码虽然多了很多,但是实际上只多做了几件事:

  • 自定义了一个key,在构建CreationExtra的时候,使用了该key传入用户自定义的仓储类。需要注意的是,由于CreationExtras不可变的设计,开发者需要用MutableCreationExtras套住原来的Extras来传递新值。
  • 构建工厂类的时候,通过用户自定义的key获取到了仓储类。需要注意的是获取SavedStateHandle并不是简单的使用key来获取,而是使用了谷歌官方封装的扩展方法,因为SavedStateHandle的构建相对麻烦,还需要访问SavedStateRegistry(状态保存一章有提到)。

image.png

4.1.3.2、使用Hilt依赖注入框架实现参数注入

上述提到的工厂类仍然非常麻烦,虽然开发者只需要设计一个工厂类,但是对于不同的ViewModel仍需要实现不同的CreateExtras取值方式,有没有一种更解耦的方式呢,答案是有的,就是依赖注入。这里推荐使用Jetpack的Hilt。

关于Hilt的具体使用读者可以去安卓开发者文档参考具体使用说明,文章链接如下:

使用 Hilt 实现依赖项注入(opens new window)

将 Hilt 和其他 Jetpack 库一起使用)(opens new window)

4.2、跨组件通信

在安卓中跨组件通信,特别是Activity与它治下的Fragment之间的消息通信以及这些Fragment之间相互通信一直是一个老大难的问题,因为这涉及了组件引用、生命周期等需要注意的因素:

  • 如果某个Fragment需要父组件通信,直接获取父组件引用会提高耦合度。
  • 如果多个Fragment需要共享同一个变量,变量应该缓存在哪里呢?如果放在父组件中依然会存在耦合度过高的问题(因为依然要访问父组件的真实引用)。

image.png

引入了ViewModel的开发阶段之后,组件的实际逻辑已经迁移到ViewModel之中,因此直接让组件通过成员变量去沟通信息也不合适(这样也有配置更新导致的状态丢失问题)。

那获取其他组件的ViewModel不就行了吗?

答案是对的,既然逻辑已经迁移到ViewModel中了,同时ViewModel也有降低耦合性(避免直接拿组件的实例)、避免配置更新导致销毁等好处,那直接获取组件的ViewModel来实现信息的沟通是非常方便的。

那么问题就来到了「如何拿到对应组件的ViewModel」?

还记得开发者是如何在Activity或者Fragment中拿到ViewModel的吗,如果你已经忘记了可以重新阅读一下上文,开发者是通过ViewModelStore来获取ViewModel的,换句话说,「只要拿到ViewModelStore,就可以通过ViewModel的类来找到对应的ViewModel」。

因此开发者可以通过一下开发模式来实现Fragment之前的信息沟通:

  1. 把需要共享的信息以LiveData、StateFlow等不同形式(根据需要)声明在父组件的ViewModel中。
  2. Fragment在构建的时候通过父组件的ViewModelStore拿到父组件的ViewModel。
  3. Fragment通过修改父组件的ViewModel中的数据,通知订阅了该数据的其他Fragment,以达到Fragment之间通信的目的。

如下图所示:

image.png

这样避免了以下缺点:

  • 避免子组件直接获取父组件引用(引用的是ViewModel)导致耦合度过高。
  • 避免子组件相互引用(通过父组件ViewModel间接通讯)。

那么开发者如何使用代码来落实「Fragment获取ActivityViewModel」呢?谷歌已经封装好了一个委托方法,叫activityViewModels(),和viewModels()委托基本没什么区别:

image.png

读者可以看activityViewModels()的定义,其实并没有什么特别的,就是利用父ActivityViewModelStore来获取/创建一个的ViewModel,如果你理解了上文的内容,那么这个方法对你来说应该没有什么别的困难的。

通过这种方式,Activity下面的所有Fragment都可以拿到同一个ViewModelStore,因此也可以拿到同一个ViewModel

假如开发者想获取Fragment的父FragmentViewModel呢?

虽然谷歌没有提供直接的扩展方法(可能是觉得没什么用),但是可以模仿获取ActivityViewModel的方式,写一个获取父Fragment的版本:

image.png

思路是一样的,围绕着ViewModelStore理解即可,这是ViewModel的根源。


5、总结


ViewModel是安卓开发进入MVVM时代的产物,后MVVM时代不仅是开发模式与开发思想都发生了剧烈的变化,作为漩涡中心的ViewModel的位置的重要性不可小视,然而ViewModel的却是许多开发中眼中的「最熟悉的陌生人」,多数开发者只会使用或者做少许的定制,对ViewModel的实现机制并不了解,这限制了开发者的能力上限甚至写出了有问题的ViewModel代码。

本文从ViewModel能够跨越配置更新出发,讲解了ViewModel的核心组件的原理与关系,并提供了一些基础的定制化方案,但仍有许多细节和定制化内容有待读者挖掘。


更多系列好文:

安卓现代化开发系列——从生命周期到Lifecycle

安卓现代化开发系列——从状态保存到SavedState

相关文章
|
11天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
17天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
3天前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
18天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
21天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
【10月更文挑战第35天】在数字化时代,安卓应用的开发成为了一个热门话题。本文旨在通过浅显易懂的语言,带领初学者了解安卓开发的基础知识,同时为有一定经验的开发者提供进阶技巧。我们将一起探讨如何从零开始构建第一个安卓应用,并逐步深入到性能优化和高级功能的实现。无论你是编程新手还是希望提升技能的开发者,这篇文章都将为你提供有价值的指导和灵感。
|
18天前
|
存储 API 开发工具
探索安卓开发:从基础到进阶
【10月更文挑战第37天】在这篇文章中,我们将一起探索安卓开发的奥秘。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和建议。我们将从安卓开发的基础开始,逐步深入到更复杂的主题,如自定义组件、性能优化等。最后,我们将通过一个代码示例来展示如何实现一个简单的安卓应用。让我们一起开始吧!
|
19天前
|
存储 XML JSON
探索安卓开发:从新手到专家的旅程
【10月更文挑战第36天】在这篇文章中,我们将一起踏上一段激动人心的旅程,从零基础开始,逐步深入安卓开发的奥秘。无论你是编程新手,还是希望扩展技能的老手,这里都有适合你的知识宝藏等待发掘。通过实际的代码示例和深入浅出的解释,我们将解锁安卓开发的关键技能,让你能够构建自己的应用程序,甚至贡献于开源社区。准备好了吗?让我们开始吧!
26 2
|
20天前
|
Android开发
布谷语音软件开发:android端语音软件搭建开发教程
语音软件搭建android端语音软件开发教程!
|
25天前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!
|
24天前
|
移动开发 Java Android开发
探索Android与iOS开发的差异性与互联性
【10月更文挑战第32天】在移动开发的大潮中,Android和iOS两大平台各领风骚。本文将深入浅出地探讨这两个平台的开发差异,并通过实际代码示例,展示如何在各自平台上实现相似的功能。我们将从开发环境、编程语言、用户界面设计、性能优化等多个角度进行对比分析,旨在为开发者提供跨平台开发的实用指南。
38 0