MVVM实现与原理分析

2023-05-16

1-MVVM简介
1.1-MVC & MVP & MVVM
MVP

MVVM与MVP结构类似,MVP也是通过Presenter将View与Model解耦。不过MVVM是基于观察者模式,viewModel不持有Activity/Fragment实例,数据更新驱动UI更新。
MVC

视图层用xml或者代码描述,控制层由Activity和Fragment实现。控制层太复杂,显示逻辑和其他逻辑在一起不便维护
MVVM

低耦合。View模块可独立于Model的变化。

复用性。一个ViewModel可以绑定到不同的View上,例如Activty与其子Fragment之间共享一个ViewModel,从而实现各View模块的数据共享和通信。

高内聚。ViewModel内聚业务逻辑和数据。View模块只含视觉相关。
1.2-MVVM结构

在这里插入图片描述
Model

 

Model层表示用户程序的数据和业务逻辑,这一层的的推荐的实现策略之一就是观测数据的变化并传递出去(供谁使用),使其从ViewModel或者其他观察者/消费者中完全解耦
ViewModel

ViewModel是和Model(数据层)进行交互,并且ViewMode可以被View观察.ViewModel可以选择性地为视图提供钩子以将事件传递给模型.该层的一个重要实现策略是将Model与View分离,即ViewModel不应该意识到与谁交互的视图
View

此模式中的视图角色是观察(或订阅)ViewModel,观察数据b变化,以便于获取数据去更新UI元素.
2-MVVM实现
在Android中实现MVVM模式,一般是通过DataBinding+ViewModel+LiveData。

DataBinding
是一个支持在xml中绑定数据的工具。通过在标签包裹xml布局,可以直接在xml中绑定数据或绑定事件。其实就是通过xml生成对应的DataBinding类,在里面完成了findview及数据绑定和事件绑定。
ViewModel
可感知订阅者LifeCycle,用于管理数据及数据分发。通过ViewModel可以实现Activity及其子Fragment的数据共享和通信
LiveData
可观察的数据对象,即数据变化时会通知订阅者。ViewModel中管理多个LiveData供订阅者订阅,且数据变化时双向的即订阅者可以被动接收数据也可以主动发送(改变)数据
来看下用DataBinding+LiveData+ViewModel的简单实现吧:

1.在build.gradle开启dataBinding。这样编译的时候就会通过APT将xml文件自动生成对应的Binding类

android {
    dataBinding {
        enabled = true
    }
}


2.创建ViewModel

public class TestViewModel extends ViewModel {
    //可观察数据
    public MutableLiveData<String> userName = new MutableLiveData<>();
    public String getUserName() {
        return userName.getValue();
    }
    public void setUserName(String name) {
        //setValue只能在主线程执行
        //postValue可在子线程执行,通过主线程Handler切换到主线程执行setValue
        userName.postValue(name);
    }
}


3.创建布局xml及绑定数据

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <variable name="mViewModel" type="com.sxm.thirdframework.viewmodel.TestViewModel"/>
    </data>
    <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={mViewModel.userName}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>
</layout>


4.Activity中监听数据
 

public class MvvmTestActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //ActivityMvvmBinding类是根据xml文件名自动生成的Binding类
        ActivityMvvmBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm);
        binding.setMViewModel(new ViewModelProvider(this).get(TestViewModel.class));
        binding.setLifecycleOwner(this);//监听Activity的生命周期变化
        //监听LiveData数据变化
        binding.getMViewModel().userName.observe(this, testDo -> Log.d("MVVM_TEST", testDo));
    }
}

这里也可以不用DataBinding,在Activity中自己实现View的数据绑定。

3-源码分析
3.1-DataBinding
先从最简单的DataBindign说起。DataBinding原理和ButterKnife类似,都是通过APT实现代码自动生成

(1)数据类注册。在2章中的xml文件经过编译后标签包裹的数据部分会注册到BR类,BR类类似R文件,注册后方便通过id索引对应的数据。看下xml中注册的viewModel在BR文件内容

<data>
    <variable name="mViewModel" type="com.sxm.thirdframework.viewmodel.TestViewModel"/>
</data>
public class BR {
  public static final int _all = 0;

  public static final int mViewModel = 1;
}


(2)Binding类生成。而实现了标签的布局文件activity_mvvm.xml会在编译时生成对应的Binding类,类名为布局名去掉下滑线的驼峰式,这里就是ActivityMvvmBinding。ActivityMvvmBinding只是持有了设置了id的View的引用及绑定数据类的引用。具体实现类是ActivityMvvmBindingImpl,也是自动生成的。ActivityMvvmBindingImpl.executeBindings

@Override
protected void executeBindings() {
    long dirtyFlags = 0;
    synchronized(this) {
        dirtyFlags = mDirtyFlags;
        mDirtyFlags = 0;
    }
    java.lang.String mViewModelUserName = null;
    com.sxm.thirdframework.viewmodel.TestViewModel mViewModel = mMViewModel;

    if ((dirtyFlags & 0x3L) != 0) {
            if (mViewModel != null) {
                // read mViewModel.userName
                mViewModelUserName = mViewModel.getUserName();
            }
    }
    // batch finished
    if ((dirtyFlags & 0x3L) != 0) {
        //viewModel数据变化时通知EditText更新UI
        androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, mViewModelUserName);
    }
    if ((dirtyFlags & 0x2L) != 0) {
        //监听EditText文本变更,并反向通知viewModel更新数据
        //通过设置监听InverseBindingListener,来实现逆向数据更新
        androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.mboundView1, (androidx.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.OnTextChanged)null, (androidx.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, mboundView1androidTextAttrChanged);
    }
}


(3)ID注册了怎么通过BR中的ID找到对应的数据呢?DataBinderMapper类也是在编译时自动生成的,具体实现类DataBinderMapperImpl。就是维护一个映射关系,可以通过layout文件的id来映射对应的Binding类。在Activity中的代码:

//调用activity的setContentView设置ContentView
//通过DataBinderMapperImpl根据layout的id获取对应的ActivityMvvmBinding对象,
ActivityMvvmBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm);
//绑定ViewModel对象
binding.setMViewModel(new ViewModelProvider(this).get(TestViewModel.class));
//绑定自身LifeCyle
binding.setLifecycleOwner(this);
//监听LiveData数据变化
binding.getMViewModel().userName.observe(this, testDo -> Log.d("MVVM_TEST", testDo));


3.2-ViewModel
ViewModel和LiveData都是JetPack中Lifecycle里的工具,较DataBinding出现晚一些。且ViewModel+LiveData可以不使用DataBinding实现MVVM模式,需要手动实现DataBinding完成的工作,这样的好处是业务逻辑不用在xml中实现,可读性及可维护性强。ViewModel具有以下特性:

1.ViewModel可以实现Activity与子Fragment之间的数据共享及通信,且不直接持有Activity/Fragment引用
2.ViewModel的生命周期和绑定Activity不同,Activity在onDestroy之前ViewModel都是活跃状态。所以Activity异常恢复重建时ViewModel中数据任然还在
3.ViewModel通过观察者的LifeCycle感知其生命周期,避免内存泄漏。
先看看ViewModel的创建过程:
new ViewModelProvider(this).get(TestViewModel.class)
第一步是创建ViewModelProvider,看下ViewModelProvider的构造方法

public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
    this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
            ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
            : NewInstanceFactory.getInstance());
}


就是初始化了两个变量,一个是Factory,一个是ViewModelStore。Factory即ViewModel的工厂类,默认NewInstanceFactory。ViewModelStore维护了一个key是activity,value是viewModel的HashMap。构造方法中传入的this为Fragment/Activity。而Fragment及FragmentActivity
都实现了ViewModelStoreOwner接口。ViewModelStoreOwner接口只有一个方法ViewModelStore getViewModelStore()。

初始化了ViewModelProvider后get(TestViewModel.class)则通过类名反射构造一个TestViewModel实例,并存入ViewModelStore

到这里就能知道ViewModel的特性1了。由于ViewModelStore存储的是activity-viewModel。所以Activity及其子Fragment都实现了ViewModelStoreOwner接口,getViewModelStore()时都是从ViewModelStore获取的ViewModel实例,相同的Activity对应同一个实例。所以能实现数据共享和通信。

再来看看特性2是怎么实现的。

存储。当设备Configuration改变或其他原因导致Activity生命周期重建时,在onStop之后onDestory之前会调用ComponentActivity.onRetainNonConfigurationInstance将当前activity的viewModelStore封装成NonConfigurationInstances存储。
恢复。在Activity重建时onCreate方法通过getLastCustomNonConfigurationInstance获取NonConfigurationInstances对象从中拿到viewModelStore进而恢复View的状态。
销毁。那viewModel在什么时候销毁呢。看看ComponentActivity的构造方法就知道了。

public ComponentActivity() {
    Lifecycle lifecycle = getLifecycle();
    。。。//省略
    getLifecycle().addObserver(new LifecycleEventObserver() {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            if (event == Lifecycle.Event.ON_DESTROY) {
                if (!isChangingConfigurations()) {
                    getViewModelStore().clear();
                }
            }
        }
    });
    if (19 <= SDK_INT && SDK_INT <= 23) {
        getLifecycle().addObserver(new ImmLeaksCleaner(this));
    }
}


代码逻辑很清晰,就是通过监听Activity的Lifecycle,当进入Destroy前发送ON_DESTROY事件,调用getViewModelStore().clear()来销毁存储的viewModel。用一张图来概括:

在这里插入图片描述

 

3.3-Lifecycle
ViewModel的实现离不开Lifecycle,这里就来详细讲述下Lifecycle,看下怎么通过Lifecycle感知Activity/Fragment的生命周期,这里以ComponentActivity为例

首先ComponentActivity及Fragment都实现了LifecycleOwner接口。LifecycleOwner接口提供Lifecycle getLifecycle()方法

@Override
public Lifecycle getLifecycle() {
    return mLifecycleRegistry;
}


mLifecycleRegistry是封装了当前Activity实例弱引用,继承自Lifecycle

public LifecycleRegistry(@NonNull LifecycleOwner provider) {
    mLifecycleOwner = new WeakReference<>(provider);
    mState = INITIALIZED;
}


做完这些准备工作后,在ComponentActivity的onCreate方法中调用了
ReportFragment.injectIfNeededIn(this)。生命周期感知的核心就在这行代码了。来看源码:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mSavedStateRegistryController.performRestore(savedInstanceState);
    ReportFragment.injectIfNeededIn(this);
    if (mContentLayoutId != 0) {
        setContentView(mContentLayoutId);
    }
}

ReportFragment.injectIfNeededIn

public static void injectIfNeededIn(Activity activity) {
    if (Build.VERSION.SDK_INT >= 29) {
        // @1.如果是android10及以上,直接通过LifecycleCallbacks监听Activity的生命周期变化
        activity.registerActivityLifecycleCallbacks(
                new LifecycleCallbacks());
    }
    // @2.在Android10以下,则是通过向Activity添加一个ReportFragment
    // 通过ReportFragment来感知宿主Activity的生命周期变化
    // 这样做的原则也是遵循对修改关闭对拓展开放,在不修改Activity源码的基础上就能感知其生命周期变化
    android.app.FragmentManager manager = activity.getFragmentManager();
    if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
        manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
        manager.executePendingTransactions();
    }
}


@1 Android 10以上直接通过registerActivityLifecycleCallbacks注册监听Activity的生命周期回调。注册监听后Activity的生命周期变化都会感知,例如Activity.onPause–>Activity.dispatchActivityPaused–>ActivityLifecycleCallbacks.onActivityPaused

@2 Android 10以下,则是通过向Activity添加一个ReportFragment。Activity生命周期变化时会联动ReportFragment生命周期变化。例如Activity.performPause–>FragmentController.dispatchPause–>FragmentManager.dispatchPause–>Fragment.performPause–>ReportFragment.onPause–>ReportFragment.dispatch。监听者就能收到ON_PAUSE事件

@Override
public void onPause() {
    super.onPause();
    dispatch(Lifecycle.Event.ON_PAUSE);
}


所以ViewModel的第3个特性,就是通过观察者的Lifecycle来判断是否处于活跃状态,只有活跃状态的观察者才会收到数据变更消息,非活跃状态的观察者将会从订阅列表移除。

3.4-LiveData
数据与View的双向通信则是通过LiveData来实现的。来看看是怎么订阅数据LiveData的:

binding.getMViewModel().userName.observe(this, testDo -> Log.d("MVVM_TEST", testDo));
1
调用LiveData的observe方法传入了LifecycleOwner 即Activity/Fragment,第二个参数是一个Observer的匿名类对象。observe方法只能在主线程,所以数据变化时的回调也是在主线程。

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");//断言主线程
    //观察者处于非活跃状态不处理
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    //将LifecycleOwner及Observer匿名类对象封装到LifecycleBoundObserver
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    //以Oberver-LifecycleBoundObserver形式存入mObservers
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    owner.getLifecycle().addObserver(wrapper);
}


订阅其实就是将观察者及观察者回调接口匿名类加入到一个观察者列表mObservers。当调用LiveData.setValue时通知观察者,setValue只能在主线程。postValue是在子线程执行,通过主线程Handler切换到主线程调用setValue。这里主要分析setValue方法:

@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");//断言主线程
    mVersion++;//数据变更次数
    mData = value;//更新数据
    dispatchingValue(null);//@3.通知订阅者
}


@3.通知订阅者

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            considerNotify(initiator);
            initiator = null;
        } else {
            //核心方法,遍历观察者列表mObservers
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                //@4.依次执行传入的回调接口匿名类的onChanged
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}


//@4.依次执行传入的回调接口匿名类的onChanged

private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    //以最新的数据修改为准
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    //执行回调接口方法
    observer.mObserver.onChanged((T) mData);
}


最终在观察者的onChanged回调中收到了修改后的数据,可以看得出来如果两次setValue相同的数据,回调会执行两次。如果需要过滤重复数据,需要在onChanged回调中自行处理。
 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MVVM实现与原理分析 的相关文章

  • Android 网络切换 发送多次广播问题

    最近发现做项目监听网络切换广播 xff0c 根据网络条件切换一些设置 测试发现每次3G WIFI 或者WIFI到3G xff0c 网络切换的广播都会发出多次 比如3G gt WIFI 会发送三个广播 1 连接wifi 2 关闭手机网络 3
  • 在draw.io中创建容器形状

    draw io中的任何形状都可以变成容器 包含其他多个形状的形状 在绘图区域上移动容器时 xff0c 位于其中的形状将随容器移动 容器可用于指示流程图中的步骤或子过程组 xff0c 数据集合 xff0c 树形图或任何其他具有层次结构的图中的
  • H264基础及RTP分包解包

    一 H 264基础概念 SODB 数据比特串 xff0d xff0d xff1e 最原始的编码数据 RBSP 原始字节序列载荷 xff0d xff0d xff1e 在SODB的后面填加了结尾比特 xff08 RBSP trailing bi
  • Mac App Store中的开发预览版软件更新如何关闭(如Safari 12.0 seed)

    这段时间莫名其妙App Store中出现Safari 12 0 seed开发预览版更新 xff0c 这个是针对开发者提供的 xff0c 作为普通用户 xff0c 不希望接触到这种bug不断的产品 xff0c 那么 xff0c 该如何关闭呢
  • 在VMware-player中安装 VMware Tools的步骤

    1 用root身份登录ubuntu后 xff0c player 管理 安装VMware Tools 3 把上述文件复制到 home 用户为名的文件夹中 例如本人把上述文件复制到 home tgl tglFile中了 4 xff09 首先我进
  • ubuntu18.04编译问题

    1 没有安装curl fantasy 64 fantasy All Series my dev android AOSP prebuilts sdk tools jack admin start server prebuilts sdk t
  • VMware 扩展Ubuntu虚拟机的磁盘空间

    1 准备工作 使用 df h 指令查看一下磁盘空间的使用情况 可以看到 xff0c 现在挂载的磁盘空间为40G xff0c 用了16G xff0c 还剩22G 下面开始扩展磁盘空间 2 编辑虚拟机设置 打开虚拟机 xff0c 找到待扩展的虚
  • 解决Android Studio 无法通过gradle 下载https://dl.google.com/android/repository/addons_list-3.xml 解决办法

    安卓gradle的时候 xff0c 会弹出来这样报错 Task prepareKotlinBuildScriptModel UP TO DATE IOException https dl google com android reposit
  • libGL error: MESA-LOADER: failed to open vmwgfx (search paths /usr/lib/x86_64-linux-gnu/dri)

    使用emulator use system libs 命令 Using the libstdc 43 43 so 6 that is available in your system instead of the one bundled w
  • Java内部类详解

    一 内部类基础 在Java中 xff0c 可以将一个类定义在另一个类里面或者一个方法里面 xff0c 这样的类称为内部类 广泛意义上的内部类一般来说包括这四种 xff1a 成员内部类 局部内部类 匿名内部类和静态内部类 下面就先来了解一下这
  • 使用Ubuntu18.04编译android8.1

    使用虚拟机中的Ubuntu18 04编译android8 1 1 软硬件要求 1 1 硬件 16G的内存 xff1b 200G的存储盘 1 2 软件 Ubuntu18 04 这样的软硬件要求并非必须 xff0c 但是经过测试这样的配置刚好能
  • Android 8 细分版本 分支 以及支持的设备

    Android 8 细分版本 分支 以及支持的设备 细分版本分支版本支持的设备OPM8 181005 003android 8 1 0 r48OreoPixel COPM7 181005 003android 8 1 0 r47OreoNe
  • Android 打印堆栈日志的几种方法

    在Android调试过程中经常会出现程序出现的结果不是我们预期的结果 xff0c 那就需要加Log打印调试 xff0c 看调用过程是否正确 xff0c 此时就需要打印程序的调用栈 xff0c 特别是Android代码相当庞大 xff0c 打
  • 在 Ubuntu 18.04 上安装 SmartGit

    在开始安装之前 xff0c 很有趣 确保我们系统中的所有软件包都是最新的 为此 xff0c 在终端 Ctl 43 Alt 43 T 中 xff0c 我们只需编写 xff1a 1 sudo apt update sudo apt upgrad
  • 复工第一事:干掉 Notepad++

    点击上方 芋道源码 xff0c 选择 设为星标 管她前浪 xff0c 还是后浪 xff1f 能浪的浪 xff0c 才是好浪 xff01 每天 10 33 更新文章 xff0c 每天掉亿点点头发 源码精品专栏 原创 Java 2021 超神之
  • Ubuntu18.04编译Android8.0系统源码

    首先 需要一个台式电脑 xff0c 有个i7处理器 xff0c 有一个1T的机械 43 500G的固态 xff0c 如果条件允许改一个服务器也可以 我这里是一个台式电脑 在台式电脑上安装一个虚拟机 xff0c 基本是使用的VMware xf
  • m, mm以及mmm编译命令以及make snod的使用

    1 xff09 编译指定Package Android源码目录下的build envsetup sh文件 xff0c 描述编译的命令 croot 切到Android源码树的根目录 当你深入Android源码树的子目录 xff0c 想回到根目
  • 复杂条件逻辑的梳理

    为什么会感觉有些需求无从下手 在产品需求梳理或者业务逻辑调研阶段 xff0c 有时会遇到产品需求无从下手的问题 xff0c 分析下来 xff0c 一般情况如下 xff1a 需求边界不明确 xff0c 输入和输出的界定不清晰 xff0c 无法
  • Android源码刷机步骤

    打开OEM开关 xff1a 先点击设置 关于手机 版本号七次 开发者选项 打开OEM解锁 xff08 这步必须可以上网 xff0c 否则打不开 xff09 进入bootloader页面 使用方法1必须安装adb platform tools
  • Android Studio导入和调试Android8.0源码

    生成IDE相关文件 idegen专门为IDE环境调试源码而设计的工具 xff0c 依次执行如下命令 xff1a source build envsetup sh mmm development tools idegen developmen

随机推荐

  • make snod注意事项-刷机后启动异常

    1 正确执行顺序 需要执行 source build envsetup sh lunch 2 单独编译 xff0c 刷机后运行异常 全编andorid后 xff0c 单独修改编译一个framwork模块 xff0c make snod会有如
  • adb remount 系统提示只读文件系统Read-only file system,解决用adb disable-verity

    在Android6 0 xff08 Android M xff09 userdebug版本上 eng版本不存在该问题 xff0c 发现使用adb remount 系统之后 xff0c 还是不能对system分区进行操作 xff0c 提示没有
  • 枚举 switchcase 标签必须为枚举常量的非限定名称

    enum switch case label must be the unqualified name of an enumeration constant 或 错误 枚举 switchcase 标签必须为枚举常量的非限定名称case Co
  • VMware为什么会越用占用的内存越大?该如何清理?

    现象描述 xff1a VMware用了一段时间后发现原来刚开始只占5G左右的内存 xff0c 慢慢的会占用几十个G xff0c 甚至更多 xff0c 磁盘空间占用越来越大 解决办法 xff1a 虚拟机内部执行cat dev zero gt
  • H264中的时间戳(DTS和PTS)

    xff08 1 xff09 Ffmpeg中的DTS 和 PTS H264里有两种时间戳 xff1a DTS xff08 Decoding Time Stamp xff09 和PTS xff08 Presentation Time Stamp
  • UEFI/Legacy两种启动模式下安装Win10/Ubuntu双系统

    文章目录 更多操作细节请移步到 UEFI Legacy两种启动模式下安装Win10 Ubuntu双系统 http www aigrantli com archives uefilegacy E4 B8 A4 E7 A7 8D E5 90 A
  • H264视频编码原理

    一 为什么要对视频编码 视频是由一帧帧的图像组成 xff0c 就像gif图片一样 一般视频为了不会让人感觉到卡顿 xff0c 一秒钟至少需要16帧画面 一般30帧 加入该视频是一个1280x720的分辨率 xff0c 那么不经过编码一秒钟传
  • H.264基础知识总结

    H264是视频编解码格式 xff1b 学习H264之前首先要搞明白一个问题 xff0c 视频为什么要编码 xff0c 编码传输不行吗 xff1f 视频就是一堆图片按时间顺序播放 xff0c 在编码标准出现之前 xff0c 不经过编码的原始码
  • linux文件分割(将大的日志文件分割成小的)

    linux文件分割 xff08 将大的日志文件分割成小的 xff09 linux下文件分割可以通过split命令来实现 xff0c 可以指定按行数分割和安大小分割两种模式 Linux下文件合并可以通过cat命令来实现 xff0c 非常简单
  • 华为AGC性能管理功能sdk集成

    集成SDK 1 xff09 在AGC网站的我的项目中选择需要启用性能管理的应用 xff0c 点击质量 gt 性能管理 xff0c 进入性能管理服务页面 xff0c 立即开通服务 2 xff09 添加AGC插件 xff0c 在Android
  • Android平台集成华为AGC性能管理服务问题处理指南

    最近尝试集成了华为AGC的性能管理服务 xff0c 集成过程中也遇到一些问题 本文就对我在集成性能管理服务的踩坑记录进行总结 xff0c 希望能帮到大家 问题一 xff1a 刚集成性能管理服务 xff0c 报错miss client id
  • Android ANR全解析&华为AGC性能管理解决ANR案例集

    1 ANR介绍 1 1 ANR是什么 ANR xff0c 全称为Application Not Responding xff0c 也就是应用程序无响应 如果 Android 应用的界面线程处于阻塞状态的时间过长 xff0c 就会触发 应用无
  • JAVA包装类

    什么是包装类 虽然 Java 语言是典型的面向对象编程语言 xff0c 但其中的八种基本数据类型并不支持面向对象编程 xff0c 基本类型的数据不具备 对象 的特性 不携带属性 没有方法可调用 沿用它们只是为了迎合人类根深蒂固的习惯 xff
  • Rxjava理论(一)

    大家都知道RxJava上手是非常难的一个框架 xff0c 为什么说是难呢 xff0c 因为它的功能非常强大 xff0c 各种操作符让人很难上手 xff0c 搭配使用带生命周期的框架有RxLife等 以至于后面出了很多类似Rxjava的框架
  • rxjava理论(二)

    doOnSubscribe的监听 在上一节我们介绍过subscribeOn是控制上游的observable在哪个线程执行 xff0c 关于怎么控制上游的observable可以看我上篇文章RxJava面经一 xff0c 拿去 xff0c 不
  • RxJava Hook(钩子)方法

    Hook技术又叫钩子函数 xff0c 在系统没有调用函数之前 xff0c 钩子就先捕获该消息 xff0c 得到控制权 这时候钩子程序既可以改变该程序的执行 xff0c 插入我们要执行的代码片段 xff0c 还可以强制结束消息的传递 RxJa
  • android底层之什么是Zram?

    ZRAM的理解 ZRAM xff08 压缩内存 xff09 的意思是说在内存中开辟一块区域压缩数据 就是说假设原来150MB的可用内存现在可以放下180MB的东西 本身不会提高内存容量和运行速度 只是让后台程序更少被系统砍掉罢了 xff0c
  • rxjava - compose()操作符

    1 问题背景 想要给多个流重复应用 34 一系列 34 相同的操作符 该怎么办 比如 我们使用Rx 43 Retrofit进行网络请求时 都有遇到这样场景 要在io线程中请求数据 在主线程订阅 更新UI 所以必须频繁使用下面这样的代码 su
  • RxJava2 背压

    1 背压 在RxJava中 xff0c 会遇到被观察者发送消息太快以至于它的操作符或者订阅者不能及时处理相关的消息 xff0c 这就是典型的背压 Back Pressure 场景 BackPressure经常被翻译为背压 xff0c 背压的
  • MVVM实现与原理分析

    1 MVVM简介 1 1 MVC amp MVP amp MVVM MVP MVVM与MVP结构类似 xff0c MVP也是通过Presenter将View与Model解耦 不过MVVM是基于观察者模式 xff0c viewModel不持有