https://www.gravatar.com/avatar/7a0c24f697ea1587001c36d00039b60f?s=240&d=mp

Flutter、JetPack Compose和SwiftUI对比

从目前技术的发展来看,通过声明式API来构建UI将会成为主流。

框架 Flutter JetPack Compose SwiftUI
支持平台 Android、iOS、Web、Desktop Android iOS、MacOS、iPadOS
开发语言 dart kotlin swift
性能
优点 跨平台 原生,简化UI构建 原生,简化UI构建
缺点 性能相比原生稍差,跨多平台目前还不完善,UI嵌套深 不支持跨平台,Android的UI构建包 仅支持Apple自家的跨平台,iOS的UI构建包,缺乏竞争力

1. 如何选择?

这问题是个悖论,因为这三个东西完全不一样,面向的场景和未来也不一样。

flutter

跨平台,使用自己的skia引起渲染,支持移动端(android、iOS)、web、桌面应用程序。 所有的跨平台框架都绕不开目标平台,所以flutter想使用它来做跨平台开发,你得懂一些Android知识,iOS知识,h5知识。尤其在项目越来越复杂的情况下,和目标平台的耦合越来越深,导致后期还得进行相当一部分的原生开发。 但是跨平台是趋势,从iOS和Android出现的时候跨平台问题就开始探讨了,flutter用于快速出前期产品非常有效,学会它毫无疑问会提升自己的竞争力。

JetPack Compose

Android UI工具包,只能用于AndroidUI构建,未来发展如何不知道,开发方式类似flutter,可以大幅度简化原来Android UI的构建方式,短时间应该不会完全普及,有闲暇与兴趣的可以去学学。

SwiftUI

Apple未来的UI统一方案,能用于Apple全家桶的UI构建,以Apple的号召力,应该会大力推动它普及,iOS开发都应该去学。

2. 代码对比

在Android中使用DataBinding(Kotlin)

本文主要介绍DataBindingAndroid App中的使用方法。数据绑定是将“提供器”的数据源与“消费者”绑定并使其同步的一种通用技术。

1. Android应用程序使用数据绑定

1.1 介绍DataBinding

Android通过DataBinding提供了编写声明型布局的支持。这样可以最大程度简化布局逻辑相关联的代码。

数据绑定要求修改文件,外层需要包裹一个layout布局。主要通过@{}@={}语法把布局中的元素和表达式的引用写入到属性中。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="mainModel"
            type="me.ithome.jetpack.model.MainViewModel" />    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
        <TextView
            android:id="@+id/tv_userinfo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{mainModel.userData.toString()}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="56dp"
            android:onClick="@{(view) -> mainModel.getClick(view)}"
            android:text="@string/btn_getUserInfo"
            app:layout_constraintBottom_toTopOf="@+id/tv_userinfo"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

①用户变量,定义了能在这个布局里面使用的属性和类
②常规布局

DataBinding会基于layout创建一个Binding class,这个类包含了布局属性(定义的变量)到相关视图的所有绑定,并且会为布局中的数据元素生成setter,生成的类的名称是基于layout的名称(驼峰命名,加上Binding后缀)。比如布局名是activity_main.xml,生成的类就是ActivityMainBinding。你能通过这个类去inflate布局和数据模型,也可以通过DataBindingUtil类。

  1. DataBindingUtils加载布局
val mainBindingUtil = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
mainBindingUtil.lifecycleOwner = this
  1. inflate加载布局(此方法也能用于RecyclerView, ViewPager)
val mainBindingUtil = ActivityMainBinding.inflate(layoutInflater)
setContentView(mainBindingUtil.root)

上述两种方法大家二选一,一般在Activity中我们都用第一种。

1.2 如何启用DataBinding

想要在Android App工程中使用databinding,只需要在app/build.gradle文件中设置如下代码:

Android内存泄漏分析

概述

Android内存泄漏,也就是我们常说的OOM(out of memory)OOM的后果大家都知道,严重影响APP的体验,轻则卡顿,重则闪退。而且内存泄漏在低端机上一般都会伴随着ANR,所以一定要重视它。

造成内存泄漏的常见因素有:

  • IO操作
  • Bitmap
  • Context
    • 单例持有引用
  • Service
    • BroadcastReceiver
    • ContentObserver
  • Handler
  • Thread

分析方法

分析内存泄漏的常用方法:

  • StrictMode, 在ApplicationonCreate中添加StrictMode代码块
  • Leakcanary,引用Leakcanary库(常规手段,仅供参与,不应完全依赖这个库)
  • monkey,模拟用户点击操作,检查是否出现泄漏(推荐,最有效的方法)
  • adb命令手动触发adb shell dumpsys meminfo packagename -d
  • Android Studio Memory Profiler,Android Studio自带的分析工具,已经非常好用了
  • MAT,终极神器,内存分析绕不开的话题(Android Studio搞不定就靠它了)

解决方案

  1. Context 如果大家收集过内存泄漏的崩溃日志,会发现大量错误都是由ContextHandler造成。 因为我们开发中会大量用到Context引用,而ContextApplicationActivityService之类的地方会有自己的实例,而ActivityService会因为生命周期的原因导致Context实例被回收,如果你继续使用其引用就会因为弱引用问题导致错误出现。 Context的引用也常见于单例模式。 所以建议大家尽可能使用ApplicationContext

  2. Bitmap Bitmap的问题一般是因为没有对使用的图片及时释放所致。 图片一般因为体积问题,会导致占用比较多的内存 解决方案:就是在ActivityonDestroy()中及时回收内存。

  3. IO操作 FileOutputStream()未关闭,IO流忘记关闭 解决方案:及时关闭IO流,避免泄露

  4. Handler泄漏 由于Activity已经关闭,Handler任务还未执行完成,其引用了Activity的实例导致内存泄露 解决方案:在Activity的onDestroy()方法回收Handler

  5. 异步线程泄露 一般发生在线程执行耗时操作时,如下载,此时Activity关闭后,由于其被异步线程引用,导致无法被正常回收,从而内存泄露 解决方案:把线程作为对象提取出来,在Activity的onDestroy()方法阻塞线程

  6. 静态成员变量 开发中会定义许多static变量,如果有大量的静态变量定义,并有引用资源,一定要在onDestroy或其他地方及时释放(把变量置空即可)

Android高频面试题汇总(一)

1.Leakcanary原理?

  1. 利用 application.registerActivityLifecycleCallbacks(lifecycleCallbacks) 来监听整个生命周期内的 Activity onDestoryed 事件
  2. 某个 Activity 被 destory 后,将它传给 RefWatcher 去做观测,确保其后续会被正常回收;
  3. RefWatcher 首先把 Activity 使用 KeyedWeakReference 引用起来,并使用一个 ReferenceQueue 来记录该 KeyedWeakReference 指向的对象是否已被回收;
  4. AndroidWatchExecutor 会在 5s 后,开始检查这个弱引用内的 Activity 是否被正常回收。判断条件是:若 Activity 被正常回收,那么引用它的 KeyedWeakReference 会被自动放入 ReferenceQueue 中。
  5. 判断方式是:先看 Activity 对应的 KeyedWeakReference 是否已经放入 ReferenceQueue 中;如果没有,则手动 GC:gcTrigger.runGc();;然后再一次判断 ReferenceQueue 是否已经含有对应的 KeyedWeakReference。若还未被回收,则认为可能发生内存泄漏

2.如何理解Java的多态?其中,重载和重写有什么区别?

多态是同一个行为具有多个不同表现形式或形态的能力,多态是同一个接口,使用不同的实例而执行不同操作,多态就是程序运行期间才确定,一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法。 多态存在的三个必要条件是:继承,重写,父类引用指向子类引用。 多态的三个实现方式是:重写,接口,抽象类和抽象方法。

区别点 重载 重写
参数列表 必须修改 不能修改
返回类型 可以修改 不能修改
异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常
访问 可以修改 一定不能做更严格的限制(可以降低限制)

3.谈一下JVM内存区域划分?哪部分是线程公有的,哪部分是私有的?

https://img.ibook8.club/mweb/16044872296914.jpg

快速理解二十三种设计模式(速记)

设计模式六大原则

  • 单一职责原则(Single Responsibility Principle)
  • 里氏替换原则(Liskov Substitution Principle)
  • 依赖倒置原则(Dependence Inversion Principle)
  • 接口隔离原则(Interface Segregation Principle)
  • 迪米特法则(最少知道原则)(Law of Demeter)
  • 合成复用原则(Composite Reuse Principle)

设计模式三大类

  1. 创建型模式(Creational Pattern):对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。 (5种)工厂模式、抽象工厂模式、单例模式、建造者模式、原型模式

  2. 结构型模式(Structural Pattern):关注于对象的组成以及对象之间的依赖关系,描述如何将类或者对象结合在一起形成更大的结构,就像搭积木,可以通过简单积木的组合形成复杂的、功能更为强大的结构。 (7种)适配器模式、装饰者模式、代理模式、外观模式、桥接模式、组合模式、享元模式

  3. 行为型模式(Behavioral Pattern):关注于对象的行为问题,是对在不同的对象之间划分责任和算法的抽象化;不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。 (11种)策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式

简单工厂模式

一个工厂根据不同的参数返回不同的产品

水果店理解为一个简单工厂,你要苹果,他给你苹果,你要梨子,他就给你梨子。

工厂模式

一个工厂创建一个具体的产品,一个产品对应一个工厂类。

宝马x1一个工厂类,x2一个工厂类,x3一个工厂类,奔驰A级一个工厂类,C级一个工厂类

抽象工厂模式

一个工厂创建一类产品,一类产品对应一个工厂。

比如汽车制造工厂是个抽象工厂,它既可以生产宝马X1,也可以生产X2,X3。罐头厂是一个抽象工厂,它可以生产橘子罐头,也可以生产黄桃罐头。

单例模式

这个没啥好说的,保证一个类仅有一个实例,并提供一个访问它的全局访问点。 需要记忆的是懒汉式饿汉式。 懒汉式:需要的时候才实例化,常用的例子就是非空检查和双重检查式的实现。 饿汉式:类加载的时候就先实例化。 类似这样的

private static Singleton instance = new Singleton();

饿汉式会线程安全些。 我们用的最主流的方式是静态内部类的方式。

public class SingleInstance{
  private SingleInstance(){}
 
  private static class SingleInstanceHolder{
     private static SingleInstance INSTANCE = new SingleInstance();
 }
 
  public static SingleInstance getInstance(){
    return SingleInstanceHolder.INSTANCE;
  }
}

一个省只会有一个省长。

Mac上安装yarn和nvm

Mac上安装yarn最方便的方式就是命令行输入:

brew install yarn

但是这样会把node也一并安装,作为开发者来说,保持多个node版本是必要的,所以我们一般都是通过nvm来管理node版本。

brew也提供过brew install yarn --without-node命令,但是现在已经失效。不必再试。

输入brew install,根据提示我们发现目前可用的指令是brew install yarn --ignore-dependencies

运行后会报错,提示你找不到node,那么我们是不是可以把nvmnode link 到brew去呢?

答案是可以的,步骤如下(node的版本号改为你实际使用的版本):

~ which node 
/usr/local/opt/nvm/versions/node/v14.14.0/bin/node
~ mkdir /usr/local/Cellar/node
~ ln -s /usr/local/opt/nvm/versions/node/v14.14.0 /usr/local/Cellar/node
~ brew link --overwrite node
~ brew doctor
~ brew install yarn --ignore-dependencies

安装这个顺序操作就OK了。