我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android快速搭建MVVM框架

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android快速搭建MVVM框架

架构

上面是从一个开源项目中了解到的框架结构,以最简洁的方式搭建一个app的基础框架。

框架的几个特点是:

通过Jetpack的Navigation构建单Activity多Fragment结构,我们知道Activity是属于比较重的组件,而Fragment是比较轻量化的,因此这种结构对界面性能方面有很大影响 通过koin这个依赖注入框架来管理ViewModel等实例的生命周期,早期的SSH框架也是因为Spring这个依赖注入特性而更加出名 使用当前比较优秀的数据请求框架来负责各种类型数据的处理 麻雀虽小,五脏俱全,任何一个app都离不开这些基础的架构,而上面的框架搭建起来很简洁,后期维护也很清晰

 

具体剖析一、Navigation

简介:

Navigation是Jetpack四大组件中的其中一个,目前也比较稳定了

我们都知道fragment有非常多的优势,它本身是一个VIew派生而来的控件,嵌套灵活,渲染所消耗的资源明显小于activity,数据的传递也更加方便,当然它的优点并不止这些。

但是在应用开发的过程中,开发者们也发现了不少这种做法带来的坑。例如需要维护复杂的fragment回退栈、使用不当的情况下经常出现fragment重叠、经常由于activity已经销毁导致使用上下文crash、等等等等的问题。

navigation就是为了解决这些问题而出现的,用于实现单activity多fragment形式的官方解决方案

使用样例:

1)先配置跳转信息,在res/navigation目录下新建一个navigation.xml,配置如下内容:


上面fragment和activity标签就是代表需要跳转的具体类,action标签代表一个具体的跳转信息,argument代表的是跳转到这个类时可以传递的参数定义

2)界面跳转,比如上面的TabFragment跳转到BrowserActivity时可以这样操作:

Navigation.findNavController(homeRecycleView).navigate(TabFragmentDirections.actionTabToBrowser().setUrl("http://www.baidu.com"))

而BrowserActivity里面只要两行代码就能获取到参数:

val args by navArgs()
val url = args.url

要使用上面的argument必须在gradle里面引入safeArgs相关依赖,如下:

1)App的build.gradle文件添加:

apply plugin: 'androidx.navigation.safeargs'

2)Project的build.gradle文件中添加:

dependencies {
        classpath 'com.android.tools.build:gradle:3.6.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.2.1"
    }

当然也可以不使用argument标签来进行参数传递,不过这个标签的好处就是对类型做了限定,所以也是safe argument的由来,个人感觉另一个好处就是每个界面传递的参数一目了然,不会漏掉或者传错

findNavController传入的参数可以是Activity或者View,最终逻辑都是寻找到NavHostFragment,然后获取它的mNavController,这样做得好处是我们只要传递给它一个view就能进行跳转了,源码如下:

private static NavController findViewNavController(@NonNull View view) {
        while (view != null) {
            NavController controller = getViewNavController(view);
            if (controller != null) {
                return controller;
            }
            ViewParent parent = view.getParent();
            view = parent instanceof View ? (View) parent : null;
        }
        return null;
}

从上面大概可以了解到使用Navigation进行fragment管理的好处不仅是对各种异常情况的处理,代码也会简洁很多,而且参数传递也多了一些特性

 

二、koin框架

简介:

Koin框架,适用于使用Kotlin开发 ,是一款轻量级的依赖注入框架,无代理,无代码生成,无反射。相对于dagger 而言更加适合Kotlin语言

使用样例:

1)app的build.gradle中引入依赖:

dependencies {
    // Koin for Android
    implementation 'org.koin:koin-android:2.0.1'
    // or Koin for Lifecycle scoping
    implementation 'org.koin:koin-androidx-scope:2.0.1'
    // or Koin for Android Architecture ViewModel
    implementation 'org.koin:koin-androidx-viewmodel:2.0.1'
}

2)初始化,在Application onCreate中注册组件:

override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@App)
            //注册组件
            modules(appModule)
        }
   }

3)module定义:

val viewModelModule = module {
    viewModel { LoginViewModel(get(),get()) }
   }
val repositoryModule = module {
   single { SquareRepository() }
   single { HomeRepository() }
   single { ServiceImpl1() }
   single(named(name = "test")) { ServiceImpl2() }
   single{ (view : View) -> Presenter(view) }
}
val appModule = listOf(viewModelModule, repositoryModule)

module定义的原理其实就是注册类的定义,这样在依赖注入的时候才能根据你要的类型来构建对应的实例

4)依赖注入:

val service : Service by inject() //默认注入的是 ServiceImpl1
val service : Service by inject(name = "test") //注入的是ServiceImpl2
val presenter : Presenter by inject { parametersOf(view) }
val loginViewModel:LoginViewModel by viewModel()

上面的依赖注入by inject是koin框架会根据注册类的定义构建一个实例,by viewModel()比较特殊,因为viewModel是和activity或者fragment的生命周期绑定的,所以这边注入也是注入到当前的fragment或者activity,可以看段代码:

fun  Koin.getViewModel(parameters: ViewModelParameters): T {
    val vmStore: ViewModelStore = parameters.owner.getViewModelStore(parameters)
    val viewModelProvider = rootScope.createViewModelProvider(vmStore, parameters)
    return viewModelProvider.getInstance(parameters)
}
fun  LifecycleOwner.getViewModelStore(
        parameters: ViewModelParameters
): ViewModelStore =
     when {
            parameters.from != null -> parameters.from.invoke().viewModelStore
            this is FragmentActivity -> this.viewModelStore
            this is Fragment -> this.viewModelStore
            else -> error("Can't getByClass ViewModel '${parameters.clazz}' on $this - Is not a FragmentActivity nor a Fragment neither a valid ViewModelStoreOwner")
     }

从上面可以看到创建的viewModel会绑定到当前的viewModelStore,这个也是真正做到依赖注入对创建对象的生命周期管理作用

相比dagger框架,koin框架不需要对注入对象手动调用注入,因为它创建的对象不是全局的,而是和当前对象绑定的,也就不需要等待注入参数准备好后再进行构建,特别如果注入对象里面还有注入对象,手动注入就会变得混乱

 

三、Retrofit2

简介:

Retrofit2简单的说就是一个网络请求的适配器,它将一个基本的Java接口通过动态代理的方式翻译成一个HTTP请求,并通过OkHttp去发送请求。此外它还具有强大的可扩展性,支持各种格式转换以及RxJava

使用样例:

1)创建interface 服务接口:

public interface IWeather {
     @GET("/v3/weather/now.json")
     Call weather(@Query("key")String key,@Query("location")String location);
     @FormUrlEncoded
     
     @POST("/article/query/{page}/json")
     WanResponse searchHot(@Path("page") int page, @Field("k") String key)
     @POST("users/new")
     Call createUser(@Body User user);
     //QueryMap可以实现将参数统一放到Map里面,减少参数定义
     @GET("/v3/weather/now.json")
     Call weather(@QueryMap Map key,@QueryMap Map location);
}

Retrofit2要求我们创建如上面所示的interface接口,而创建该接口的目的是,retrofit通过获取接口的@GET注解里面的值,与下面即将讲到的baseUrl拼接成一个请求网址,另外通过调用接口的方法,填充相应参数之类的

2)创建Retrofit:

Retrofit retrofit2 = new Retrofit.Builder()
        .baseUrl("https://api.thinkpage.cn")
        .addConverterFactory(GsonConverterFactory.create())
        .client(new OkHttpClient())
        .build();
IWeather iWeather = retrofit2.create(IWeather.class);

通过Retrofit.Builder()方法来创建一个Retrofit实例,baseUrl()是设置Url,这是必须的,addConverterFactory()该方法是设置解析器,即上面提到的GsonConverterFactory,最后通过build()完成创建

3)创建请求,设置请求参数,执行请求:

Call call = iWeather.weather("rot2enzrehaztkdk","beijing");
call.enqueue(new Callback() {
      @Override
      public void onResponse(Call call, Response response) {
         WeatherBean weatherBean = response.body();
         Log.d("cylog",weatherBean.results.get(0).now.temperature+"");
      }
      @Override
      public void onFailure(Call call, Throwable t) {
        Log.d("cylog", "Error" + t.toString());
      }
 });

通过调用IWeather的weather方法(我们在接口中定义的),把两个关键参数传递了进入,这两个参数均是使用@Query注解标记的,因此构成了url中的请求参数,而返回的call则是我们的请求。最后,调用call.enqueue方法,执行一个异步请求,如果成功了,则回调onResponse方法,否则回调onFailure方法。另外,这里补充一下:call.enqueue是一个异步方法,不在同一线程内,而call.execute是一个同步方法,在同一线程内

4) 上传文件

@Multipart
@PUT("user/photo") 
Call updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);

@Multipart表示能使用多个Part,而@Part注解则是对参数进行标记,RequestBody是一种类型,是okHttp3里面的一个类,既然请求参数是RequestBody类型的,那么我们要把请求体封装到RequestBody里面去,通过RequestBody.creat()方法进行创建,RequestBody创建有两个参数,第一个参数是MediaType,是媒体类型,第二个参数可为String、byte、file等,通过上述方法创建的RequestBody是一个请求体,将与其他的请求体一起发送到服务端,它们的key值是@Part("key")注解的值

Retrofit2的好处就是对各种请求的封装,这样代码写起来就简洁很多,还有一个特性是比较符合HTTP2.0多路复用,多路复用正是同一个域名下的请求可以共用一个连接,这与Retrofit2的定义刚好不谋而合

 

四、WorkManager

简介:

WorkManager 在工作的触发器 满足时, 运行可推迟的后台工作。WorkManager会根据设备API的情况,自动选用JobScheduler, 或是AlarmManager来实现后台任务,WorkManager里面的任务在应用退出之后还可以继续执行,这个技术适用于在应用退出之后任务还需要继续执行的需求,对于在应用退出的之后任务也需要终止的需求,可以选择ThreadPool、AsyncTask

使用样例:

1)使用状态机

val request1 = OneTimeWorkRequestBuilder().build()
val request2 = OneTimeWorkRequestBuilder().build()
val request3 = OneTimeWorkRequestBuilder().build()
WorkManager.getInstance().beginWith(request1)
        .then(request2)
        .then(request3)
        .enqueue()

2)设置约束条件:     

val myConstraints = Constraints.Builder()
        .setRequiresDeviceIdle(true)//指定{@link WorkRequest}运行时设备是否为空闲
        .setRequiresCharging(true)//指定要运行的{@link WorkRequest}是否应该插入设备
        .setRequiredNetworkType(NetworkType.NOT_ROAMING)
        .setRequiresBatteryNotLow(true)//指定设备电池是否不应低于临界阈值
        .setRequiresCharging(true)//网络状态
        .setRequiresDeviceIdle(true)//指定{@link WorkRequest}运行时设备是否为空闲
        .setRequiresStorageNotLow(true)//指定设备可用存储是否不应低于临界阈值
        .addContentUriTrigger(myUri,false)//指定内容{@link android.net.Uri}时是否应该运行{@link WorkRequest}更新
        .build()
val request = PeriodicWorkRequestBuilder(24,TimeUnit.SECONDS)
        .setConstraints(myConstraints)//注意看这里!!!
        .build()

3)加入队列后监听任务状态:  

val liveData: LiveData =WorkManager.getInstance().getStatusById(request.id)
    public final class WorkStatus {   
      private @NonNull UUID mId;  
      private @NonNull State mState;   
      private @NonNull Data mOutputData;   
      private @NonNull Set mTags;   
      public WorkStatus(
            @NonNull UUID id,
            @NonNull State state,
            @NonNull Data outputData,
            @NonNull List tags) {
        mId = id;
        mState = state;
        mOutputData = outputData;
        mTags = new HashSet(tags);
    }
    public enum State {
      ENQUEUED,//已加入队列
      RUNNING,//运行中
      SUCCEEDED,//已成功
      FAILED,//已失败
      BLOCKED,//已刮起
      CANCELLED;//已取消
      public boolean isFinished() {
        return (this == SUCCEEDED || this == FAILED || this == CANCELLED);
      }
   }

4)combine 操作符-组合

现在我们有个复杂的需求:共有A、B、C、D、E这五个任务,要求 AB 串行,CD 串行,但两个串之间要并发,并且最后要把两个串的结果汇总到E,代码如下:  

   val chuan1 = WorkManager.getInstance()
    .beginWith(A)
    .then(B)
   val chuan2 = WorkManager.getInstance()
   .beginWith(C)
   .then(D)
   WorkContinuation
    .combine(chuan1, chuan2)
    .then(E)
    .enqueue()

使用WorkManager的好处就是对android各种API的策略做了适配,特别目前android对后台执行任务的限制越来越厉害,app需要做很多处理来适配各个版本,不仅代码逻辑复杂,效果也不能做到非常好。不过目前WorkManager还处于试验阶段,可以等它稳定后再引入

 

五、kotlin suspendCoroutine

kotlin的一大特色就是协程,其中一个作用就是将异步回调写成同步方式,这里就用到了suspendCoroutine,它可以挂起当前协程而不阻塞线程,这样就能等待异步回调返回前挂起当前协程,比如想获取camera实例,正常是监听camera打开的回调来获取,这样写逻辑就比较复杂,但是用suspend fun可以实现没有线程阻塞的执行暂停(suspend只能在协程里面调用,注册回调后就结束,只是挂起当前协程,不会阻塞线程,影响其他协程运行),直到调用resume方法返回结果,这样就能等待camera实例返回再继续执行,代码如下:   

 private suspend fun openCamera(
            manager: CameraManager,
            cameraId: String,
            handler: Handler? = null
    ): CameraDevice = suspendCancellableCoroutine { cont ->
        manager.openCamera(cameraId, object : CameraDevice.StateCallback() {
            override fun onOpened(device: CameraDevice) = cont.resume(device)
            override fun onDisconnected(device: CameraDevice) {
                Log.w(TAG, "Camera $cameraId has been disconnected")
                requireActivity().finish()
            }
            override fun onError(device: CameraDevice, error: Int) {
                val msg = when(error) {
                    ERROR_CAMERA_DEVICE -> "Fatal (device)"
                    ERROR_CAMERA_DISABLED -> "Device policy"
                    ERROR_CAMERA_IN_USE -> "Camera in use"
                    ERROR_CAMERA_SERVICE -> "Fatal (service)"
                    ERROR_MAX_CAMERAS_IN_USE -> "Maximum cameras in use"
                    else -> "Unknown"
                }
                val exc = RuntimeException("Camera $cameraId error: ($error) $msg")
                Log.e(TAG, exc.message, exc)
                if (cont.isActive) cont.resumeWithException(exc)
            }
        }, handler)
    }

获取camera实例代码:    

private fun initializeCamera() = lifecycleScope.launch(Dispatchers.Main) {
        // Open the selected camera
        camera = openCamera(cameraManager, args.cameraId, cameraHandler)
        //use camera..
     }

suspend fun可以像上面直接返回结果,也可以使用use{result -> }来返回,前者是遇到异常直接抛出,没有处理就会崩溃,后者是try-catch形式,不会直接崩溃,适用于直接跳过异常情况

 

总结

经过上面对一些主要用到的框架和组件的介绍,我们基本可以了解到它们的主要作用,使用这些组合可以很快的搭建一个app的框架并且可以适配android各种版本的差异,并且后期维护也会更简单高效些



免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android快速搭建MVVM框架

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android快速搭建MVVM框架

架构上面是从一个开源项目中了解到的框架结构,以最简洁的方式搭建一个app的基础框架。框架的几个特点是:通过Jetpack的Navigation构建单Activity多Fragment结构,我们知道Activity是属于比较重的组件,而F
2022-06-06

android mvvm框架怎么搭建

要搭建Android MVVM框架,您可以按照以下步骤进行:创建Android项目:使用Android Studio创建一个新的Android项目。添加必要的依赖库:在项目的build.gradle文件中添加以下依赖库:// ViewMod
2023-10-22

用Django框架快速搭建博客

阅读文本大概需要 5 分钟。上次 2 篇文章给大家分享了 Django 搭建的基本知识,今天就把 Django 基本知识串起来,搭建一个简单的博客网站。项目环境语言:Python 3.6编辑器:Pycharm主要步骤创建项目创建 APP创建
2023-06-02

教你快速搭建一个springMVC框架

这篇文章将为大家详细讲解有关教你快速搭建一个springMVC框架,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。一、搭建步骤1、导入jar包、创建项目包结构2、在web.xml中配置前端控制
2023-05-31

Spring Boot快速搭建Spring框架教程

Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的
2023-05-31

怎么快速搭建一个SSM框架

怎么快速搭建一个SSM框架?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一、我用的是idea二、首先创建一个maven项目,结构如下:三、开始写配置文件pom文
2023-05-31

在windows下快速搭建web.py开发框架方法

用Python进行web开发的话有很多框架供选择,比如最出名的Django,tornado等,除了这些框架之外,有一个轻量级的框架使用起来也是非常方便和顺手,就是web.py。它由一名黑客所创建,但是不幸的是这位创建者于2013年自杀了。据
2022-06-04

详解Android的MVVM框架 - 数据绑定

本教程是跟着 Data Binding Guide 学习过程中得出的一些实践经验,同时修改了官方教程的一些错误,每一个知识点都有对应的源码,争取做到实践与理论相结合。Data Binding 解决了 Android UI 编程中的一个痛点,
2023-05-31

Android模块化框架怎么搭建

搭建Android模块化框架需要以下步骤:1. 设计模块化架构:确定项目的模块划分,每个模块的功能和职责等。2. 创建Android项目:使用Android Studio创建一个新的Android项目。3. 配置Gradle文件:在根目录下
2023-10-08

Android模块化框架如何搭建

搭建Android模块化框架可以按照以下步骤进行:1. 创建一个新的Android项目:使用Android Studio创建一个新的项目作为模块化框架的基础。2. 定义模块的结构:根据业务需求,定义模块的结构,包括模块间的依赖关系、通信方式
2023-10-11

快速搭建优秀网站:CSS网页布局框架设计指南

作为网页设计师,一个重要的任务就是设计一个优秀的CSS网页布局框架。这种框架可以使你的网站更加美观、易于使用,并且能够提高用户体验。在这篇文章中,我们将为你提供一些CSS网页布局框架设计指南,并结合具体的代码示例,帮助你快速搭建出一个优秀的
快速搭建优秀网站:CSS网页布局框架设计指南
2024-01-16

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录