安卓现代化开发系列——传世不朽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

相关文章
|
2天前
|
JavaScript 前端开发 Java
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
IT寒冬使APP开发门槛提升,安卓程序员需转型。选项包括:深化Android开发,跟进Google新技术如Kotlin、Jetpack、Flutter及Compose;研究Android底层框架,掌握AOSP;转型Java后端开发,学习Spring Boot等框架;拓展大前端技能,掌握JavaScript、Node.js、Vue.js及特定框架如微信小程序、HarmonyOS;或转向C/C++底层开发,通过音视频项目如FFmpeg积累经验。每条路径都有相应的书籍和技术栈推荐,助你顺利过渡。
13 3
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
|
1天前
|
搜索推荐 Android开发 iOS开发
探索安卓与iOS开发的差异性与互补性
【8月更文挑战第19天】在移动应用开发的广阔天地中,安卓与iOS两大平台各据一方,引领着行业的潮流。本文将深入探讨这两个平台在开发过程中的不同之处以及它们之间的互补关系,旨在为开发者提供一个全面的视角,帮助他们更好地把握市场动态,优化开发策略。通过分析各自的开发环境、编程语言、用户界面设计、性能考量及市场分布等方面,我们将揭示安卓与iOS开发的独特魅力和挑战,同时指出如何在这两者之间找到平衡点,实现跨平台的成功。
|
3天前
|
移动开发 开发工具 Android开发
探索安卓与iOS开发的差异:技术选择的影响
【8月更文挑战第17天】 在移动应用开发的广阔天地中,安卓和iOS两大平台各领风骚。本文通过比较这两个平台的编程语言、开发工具及市场策略,揭示了技术选择对开发者和产品成功的重要性。我们将从开发者的视角出发,深入探讨不同平台的技术特性及其对项目实施的具体影响,旨在为即将步入移动开发领域的新手提供一个清晰的指南,同时给予资深开发者新的思考角度。
|
4天前
|
vr&ar Android开发 iOS开发
探索安卓和iOS开发的未来趋势
在移动应用开发的广阔天地里,安卓和iOS两大平台如同双子星座般璀璨夺目。随着技术的不断进步,这两个平台的开发趋势也在悄然发生着变化。本文将带你一探究竟,看看未来安卓和iOS开发将会迎来哪些令人激动的新特性和挑战。让我们一起跟随技术的脚步,开启这场探索之旅吧!
|
5天前
|
移动开发 Java Android开发
安卓与iOS开发:异同探析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文旨在深入探讨这两个平台在开发环境、编程语言、用户界面设计、性能优化及市场分布等方面的异同,为开发者提供实用的比较视角和决策参考。通过对比分析,我们不仅能更清晰地认识到各平台的特性,还能洞察未来移动开发的可能趋势。
|
1天前
|
移动开发 Android开发 iOS开发
揭秘移动开发之谜:安卓与iOS之间的技术鸿沟有多深?探索两大平台的开发差异及其对应用性能和用户体验的惊人影响!
【8月更文挑战第19天】在移动应用开发领域,安卓与iOS占据主导地位。两者在技术架构、开发工具及市场分布上各有特色。本文通过案例对比分析,展示安卓使用Java/Kotlin与iOS采用Swift/Objective-C的语言差异;探讨iOS统一细腻设计与安卓自定义Material Design的UI区别;并讨论安卓广泛市场覆盖与iOS高用户价值对开发者策略的影响。理解这些差异有助于制定有效的开发计划。
|
1天前
|
安全 Android开发 iOS开发
探索安卓与iOS开发的差异:平台特性与用户体验的比较
【8月更文挑战第19天】 在移动应用开发的广阔天地中,安卓与iOS两大平台各领风骚。本文将深入探讨这两个平台在开发过程中的关键差异,从编程语言和工具到用户界面设计,再到市场分布和安全性考虑。我们将一窥究竟,是什么让安卓开发如此灵活多变,又是什么让iOS开发显得精致而统一。通过这篇比较分析,开发者可以更清晰地认识到各自平台的优势和挑战,从而做出更明智的开发决策。
7 0
|
4天前
|
Java Android开发 iOS开发
探索安卓与iOS开发的差异:从新手到专家的旅程
本文将带你走进移动应用开发的两大平台——安卓和iOS,揭示它们之间的主要差异。我们将从新手的视角出发,逐步深入到更复杂的技术层面,帮助你理解这两个平台的开发环境、编程语言和用户界面设计等方面的不同。无论你是刚入门的新手,还是有一定经验的开发者,这篇文章都将为你提供有价值的见解和建议。现在,让我们一起开始这段探索之旅吧!
|
4天前
|
搜索推荐 Android开发 iOS开发
探索安卓与iOS开发的差异之美
在数字时代的浪潮中,移动应用开发如同一场精心编排的交响乐,安卓和iOS这两大平台扮演着不同乐器的角色,各自以独特的方式奏响。本文将带领读者走进这场音乐盛宴,感受两大平台在开发过程中所展现的不同韵律,从设计理念到用户体验,从市场占有率到生态系统,我们将一探究竟,欣赏它们如何在竞争激烈的市场中和谐共存,共同推动技术的进步与创新。
12 0
|
5天前
|
开发工具 Android开发 iOS开发
探索安卓与iOS开发的差异:构建未来应用的关键考量
在数字时代,选择正确的开发平台是成功的一半。本文深入探讨了安卓与iOS两大移动操作系统的开发差异,并分析了各自对创新、用户体验和市场需求的响应。通过比较两者的设计哲学、开发工具、市场覆盖和用户参与度,我们揭示了每个平台的独特优势和潜在挑战,旨在为开发者提供决策时的洞见,帮助他们在竞争激烈的应用市场中做出明智的选择。