LiveData详细分析

article/2025/11/11 5:51:11

目录介绍

  • 01.LiveData是什么东西
  • 02.使用LiveData的优势
  • 03.使用LiveData的步骤
  • 04.简单使用LiveData
  • 05.observe()和observerForever()
  • 06.LiveData原理介绍
  • 07.observe订阅源码分析
  • 08.setValue发送源码分析
  • 09.observeForever源码
  • 10.LiveData源码总结

00.使用LiveData实现bus事件总线

  • 利用LiveData实现事件总线,替代EventBus。充分利用了生命周期感知功能,可以在activities, fragments, 或者 services生命周期是活跃状态时更新这些组件。支持发送普通事件,也可以发送粘性事件;还可以发送延迟消息,以及轮训延迟消息等等。
  • https://github.com/yangchong211/YCLiveDataBus

01.LiveData是什么东西

  • 基于观察者模式
    • LiveData是一种持有可被观察数据的类。LiveData需要一个观察者对象,一般是Observer类的具体实现。当观察者的生命周期处于STARTED或RESUMED状态时,LiveData会通知观察者数据变化。
  • 感知生命周期
    • 和其他可被观察的类不同的是,LiveData是有生命周期感知能力的,这意味着它可以在activities, fragments, 或者 services生命周期是活跃状态时更新这些组件。那么什么是活跃状态呢?就是STARTED和RESUMED就是活跃状态,只有在这两个状态下LiveData是会通知数据变化的。
  • 自动解除数据订阅
    • 要想使用LiveData(或者这种有可被观察数据能力的类)就必须配合实现了LifecycleOwner的对象使用。在这种情况下,当对应的生命周期对象DESTORY时,才能移除观察者。这对Activity或者Fragment来说显得尤为重要,因为他们可以在生命周期结束的时候立刻解除对数据的订阅,从而避免内存泄漏等问题。

02.使用LiveData的优势

2.1 具有很明显的优点

  • UI和实时数据保持一致
    • 因为LiveData采用的是观察者模式,这样一来就可以在数据发生改变时获得通知,更新UI。
  • 不会发生内存泄露
    • 观察者被绑定到组件的生命周期上,当被绑定的组件销毁(onDestroy)时,观察者会立刻自动清理自身的数据。
  • 不会再产生由于Activity处于stop状态而引起的崩溃
    • 例如:当Activity处于后台状态时,是不会收到LiveData的任何事件的。
  • 不需要再解决生命周期带来的问题
    • LiveData可以感知被绑定的组件的生命周期,只有在活跃状态才会通知数据变化。
  • 实时数据刷新
    • 当组件处于活跃状态或者从不活跃状态到活跃状态时总是能收到最新的数据
  • 解决Configuration Change问题
    • 在屏幕发生旋转或者被回收再次启动,立刻就能收到最新的数据。
  • 数据共享
    • 如果对应的LiveData是单例的话,就能在app的组件间分享数据。这部分详细的信息可以参考继承LiveData

2.2 细节点补充

  • 组件和数据相关的内容能实时更新,组件在前台的时候能够实时收到数据改变的通知,当组件从后台到前台来时,LiveData能够将最新的数据通知组件,因此保证了组件中和数据相关的内容能够实时更新。
  • 如果横竖屏切换(configuration change)时,不需要额外的处理来保存数据,当屏幕方向变化时,组件会被recreate,然而系统并不能保证你的数据能够被恢复的。当我们采用LiveData保存数据时,因为数据和组件分离了。当组件被recreate,数据还是存在LiveData中,并不会被销毁。

03.使用LiveData的步骤

  • 创建一个持有某种数据类型的LiveData (通常是在ViewModel中)
  • 创建一个定义了onChange()方法的观察者。这个方法是控制LiveData中数据发生变化时,采取什么措施 (比如更新界面)。通常是在UI Controller (Activity/Fragment) 中创建这个观察者。
  • 通过 observe()方法连接观察者和LiveData。observe()方法需要携带一个LifecycleOwner类。这样就可以让观察者订阅LiveData中的数据,实现实时更新。

04.简单使用LiveData

4.1 单独使用LiveData

  • 举一个最简单的案例代码:
    liveData = new MutableLiveData<>();
    liveData.observe(this, new Observer<String>() {@Overridepublic void onChanged(@Nullable final String newText) {// 更新数据tv3.setText(newText);}
    });
    liveData.setValue("小杨真的是一个逗比么");
    
  • 那么上面这一段代码大概是什么意思呢?
    • 首先创建一个 MutableLiveData(LiveData是抽象类)对象 ,通过 observe 方法可以订阅修改数据的通知,通过 postValue()或者 setValue() 方法发送事件更新数据,已经订阅的 Observer 能够得到数据更改的通知,就会回调 onChanged() 方法。

4.2 使用LiveData配合ViewModel

  • LiveData是一个数据的包装。具体的包装对象可以是任何数据,包括集合。它是一个抽象类,首先先创建一个类实现LiveData。代码如下所示:
    public class TextViewModel extends ViewModel {/*** LiveData是抽象类,MutableLiveData是具体实现类*/private MutableLiveData<String> mCurrentText;public MutableLiveData<String> getCurrentText() {if (mCurrentText == null) {mCurrentText = new MutableLiveData<>();}return mCurrentText;}}
    
  • 创建一个观察的对象,观察LiveData中的数据。目前在组件的onCreate()方法中开始观察数据,代码如下所示:
    • 思考下,可以在onResume()中调用么,个人觉得不太好。因为系统会多次调用onResume()方法。
    private void initLiveData() {// 创建一个持有某种数据类型的LiveData (通常是在ViewModel中)model = ViewModelProviders.of(this).get(TextViewModel.class);// 创建一个定义了onChange()方法的观察者// 开始订阅final Observer<String> nameObserver = new Observer<String>() {@Overridepublic void onChanged(@Nullable final String newText) {// 更新数据tvText.setText(newText);}};// 通过 observe()方法连接观察者和LiveData,注意:observe()方法需要携带一个LifecycleOwner类model.getCurrentText().observe(this, nameObserver);
    }
    
  • 然后去创建更新对象数据内容的对象。如何去更新那个文本中的数据呢?代码如下所示:
    • 想要在UI Controller中改变LiveData中的值呢?(比如点击某个Button设置文本内容的更改)。
    • LiveData并没有提供这样的功能,但是Architecture Component提供了MutableLiveData这样一个类,可以通过setValue(T)和postValue(T)方法来修改存储在LiveData中的数据。MutableLiveData是LiveData的一个子类,从名称上也能看出这个类的作用。
    • 调用setValue()方法就可以把LiveData中的值改为 “小杨真的是一个逗比么” 。同样,通过这种方法修改LiveData中的值同样会触发所有对这个数据感兴趣的类。那么setValue()和postValue()有什么不同呢?区别就是setValue()只能在主线程中调用,而postValue()可以在子线程中调用。
    findViewById(R.id.tv_click).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {count++;String text;switch (count%5){case 1:text = "小杨真的是一个逗比么";break;case 2:text = "逗比赶紧来star吧";break;case 3:text = "小杨想成为大神";break;case 4:text = "开始刷新数据啦";break;default:text = "变化成默认的数据";break;}model.getCurrentText().setValue(text);}
    });
    

05.observe()和observerForever()

  • 一般我们使用 LiveData 的 observe(),当数据更新后,LiveData 会通知它的所有活跃的观察者。
    • 与 RxJava 不同的,LiveData 只会通知活跃的观察者,例如 Activity 位于 Destroyed 状态时是不活跃的,因此不会收到通知。
  • 当然我们也可以使用 LiveData 的 observerForever() 方法进行订阅,区别是 observerForever() 不会受到 Activity 等组件的生命周期的影响,只要数据更新就会收到通知。

06.LiveData原理介绍

6.1 简单的原理介绍

  • LiveData可对数据进行观测, 并具有生命周期感知能力, 这就意味着当liveData只会在生命周期处于活跃(inActive)的状态下才会去执行观测动作, 而他的能力赋予不能脱离LifeCycle的范围。
  • 需要注意的是,LiveData内维护的mVersion表示的是发送信息的版本,每次发送一次信息, 它都会+1, 而ObserverWrapper内维护的mLastVersion为订阅触发的版本号, 当订阅动作生效的时候, 它的版本号会和发送信息的版本号同步.他们初始值都为-1。

6.2 然后思考一些问题

  • a.liveData如何实现订阅者模式,如何处理发送事件?
  • b.如何做到感知生命周期的,怎么跟 LifecycleOwner 进行绑定的?
  • c.LiveData 只在 LifecycleOwner active 状态发送通知,是怎么处理的?
  • d.LiveData 会自动在 DESTROY 的状态下取消订阅,是怎么处理的?
  • e.生命周期变化后数据处理流程是怎么样的?
  • f.为什么观察者只能与一个LifecycleOwner绑定,而不是多个?

07.observe订阅源码分析

7.1 首先看看observe方法源码

  • 直接查看源代码,如下所示:
    • 当前绑定的组件(activity或者fragment)状态为DESTROYED的时候, 则会忽视当前的订阅请求,也就是忽略owner的注册;
    • 如果需要与生命周期绑定, 则需要传入LifecycleOwner对象, 将我们的LiveData数据观测者(Observer)包装注册到生命周期的观测者中, 就是源码中创建wrapper对象过程;
    • 需要注意的问题是,不能添加具有不同生命周期的相同观察者,否则就会抛出IllegalArgumentException异常,但是owner可以add多个Observer;
    • 最后添加一个LifecycleObserver,它将在LifecycleOwner更改状态时得到通知,并做出及时的对应更新活动。
    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {//当前绑定的组件(activity或者fragment)状态为DESTROYED的时候, 则会忽视当前的订阅请求if (owner.getLifecycle().getCurrentState() == DESTROYED) {// ignorereturn;}//创建生命周期感知的观察者包装类LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);//如果指定的键尚未与某个值关联,则将其与给定的值关联ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);//对应观察者只能与一个owner绑定if (existing != null && !existing.isAttachedTo(owner)) {throw new IllegalArgumentException("Cannot add the same observer"+ " with different lifecycles");}if (existing != null) {return;}//lifecycle注册//添加一个LifecycleObserver,它将在LifecycleOwner更改状态时得到通知owner.getLifecycle().addObserver(wrapper);
    }
    

7.2 看看LifecycleBoundObserver源码

  • 然后看一下观察者类LifecycleBoundObserver的源代码
    • LifecycleBoundObserver对象, 它继承于ObserverWrapper, 并最终实现了GenericLifecycleObserver接口;
    • 在发生状态转换事件时,会调用onStateChanged方法,在这个方法中,如果是DESTROYED状态,则先要移除观察者,然后在取到生命周期状态变更事件
    class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {@NonNull final LifecycleOwner mOwner;LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) {super(observer);mOwner = owner;}@Overrideboolean shouldBeActive() {return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);}@Overridepublic void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {// 当接收到 DESTROYED 的事件会自动解除跟 owner 的绑定removeObserver(mObserver);return;}activeStateChanged(shouldBeActive());}@Overrideboolean isAttachedTo(LifecycleOwner owner) {return mOwner == owner;}@Overridevoid detachObserver() {mOwner.getLifecycle().removeObserver(this);}
    }//抽象类
    private abstract class ObserverWrapper {final Observer<T> mObserver;boolean mActive;int mLastVersion = START_VERSION;ObserverWrapper(Observer<T> observer) {mObserver = observer;}abstract boolean shouldBeActive();boolean isAttachedTo(LifecycleOwner owner) {return false;}void detachObserver() {}void activeStateChanged(boolean newActive) {if (newActive == mActive) {return;}// immediately set active state, so we'd never dispatch anything to inactive// ownermActive = newActive;boolean wasInactive = LiveData.this.mActiveCount == 0;LiveData.this.mActiveCount += mActive ? 1 : -1;if (wasInactive && mActive) {onActive();}if (LiveData.this.mActiveCount == 0 && !mActive) {onInactive();}if (mActive) {dispatchingValue(this);}}}//接口
    public interface GenericLifecycleObserver extends LifecycleObserver {/*** Called when a state transition event happens.** @param source The source of the event* @param event The event*/void onStateChanged(LifecycleOwner source, Lifecycle.Event event);
    }
    
  • 通过上面的代码可以发现什么?
    • GenericLifecycleObserver是一个接口,ObserverWrapper是一个抽象类,而LifecycleBoundObserver则是ObserverWrapper的子类,并且重写了其中几个方法;
    • 在LifecycleBoundObserver的shouldBeActive()方法,在 owner 处于至少是 STARTED 的状态下认为是 active 状态;
    • 而且它也实现了 GenericLifecycleObserver 接口,可以监听 lifecycle 回调。在 onStateChanged() 方法里处理了生命周期改变的事件,在这个方法中,当接收到 DESTROYED 的事件会自动解除跟 owner 的绑定;
    • 将下个流程交给了 activeStateChanged(),这里具体可以看抽象类ObserverWrapper中的activeStateChanged源码;
  • 看一下ObserverWrapper抽象类中activeStateChanged方法中,onActive和onInactive分别干什么呢?
    • 对于onActive方法,当活动观察者的数量从0变为1时调用;对于onInactive方法,当活动观察者的数量从1变为0时调用
    if (wasInactive && mActive) {onActive();
    }
    if (LiveData.this.mActiveCount == 0 && !mActive) {onInactive();
    }
    
  • 看一下ObserverWrapper抽象类中activeStateChanged方法中,dispatchingValue是干什么呢?
    • 这个方法在分析下面setValue源码时还会说到,具体看下面的介绍!

7.3 看看mObservers.putIfAbsent操作

  • 关于observe源码中这一行代码ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper)作用是什么呢?
    • mObservers.putIfAbsent(observer, wrapper)存入容器中,mObservers.putIfAbsent这个添加数据的方式比较少见。
    • 看了下面源代码可知,支持键值对存储,用链表实现,不是线程安全的。既然这里有存数据,那肯定有地方会取数据用到,这个后面会说到……
    //mObservers是一个集合
    private SafeIterableMap<Observer<T>, ObserverWrapper> mObservers =new SafeIterableMap<>();//在SafeIterableMap类中的putIfAbsent方法
    public V putIfAbsent(@NonNull K key, @NonNull V v) {Entry<K, V> entry = get(key);if (entry != null) {return entry.mValue;}put(key, v);return null;
    }protected Entry<K, V> put(@NonNull K key, @NonNull V v) {Entry<K, V> newEntry = new Entry<>(key, v);mSize++;if (mEnd == null) {mStart = newEntry;mEnd = mStart;return newEntry;}mEnd.mNext = newEntry;newEntry.mPrevious = mEnd;mEnd = newEntry;return newEntry;}
    

7.4 注册观察者流程

  • 那么注册观察者之后的触发流程是怎样的?
    • 调用 observe() 注册后,由于绑定了 owner,所以在 active 的情况下,使用LiveData中setValue发送数据,则 Observer 会立马接受到该数据修改的通知。
    • observe ——> onStateChanged ——> activeStateChanged ——> dispatchingValue ——> considerNotify ——> onChanged
    • 至于最终走到了onChanged方法,这个方法则是交给外部开发者处理接收消息事件的逻辑

08.setValue发送源码分析

8.1 setValue源码分析

  • LiveData 更新数据方式有两个,一个是 setValue() 另一个是 postValue(),这两个方法的区别是,postValue() 在内部会抛到主线程去执行更新数据,因此适合在子线程中使用;而 setValue() 则是直接更新数据。
    @MainThread
    protected void setValue(T value) {assertMainThread("setValue");// 这里的 mVersion,它本问题关键,每次更新数据都会自增,默认值是 -1。mVersion++;mData = value;dispatchingValue(null);
    }
    
  • 跟进下 dispatchingValue() 方法,注意,这里需要重点看considerNotify代码:
    private void dispatchingValue(@Nullable ObserverWrapper initiator) {// mDispatchingValue的判断主要是为了解决并发调用dispatchingValue的情况// 当对应数据的观察者在执行的过程中, 如有新的数据变更, 则不会再次通知到观察者。所以观察者内的执行不应进行耗时工作if (mDispatchingValue) {//给分发失败打个标记mDispatchInvalidated = true;return;}// 标记分发开始mDispatchingValue = true;do {mDispatchInvalidated = false;//这里需要注意:区分ObserverWrapper对象为空,和不为空的逻辑是不一样的if (initiator != null) {// 等下重点看这里的代码considerNotify(initiator);initiator = null;} else {//可以发现这里用到mObservers集合,使用迭代器遍历数据for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {// 等下重点看这里的代码considerNotify(iterator.next().getValue());if (mDispatchInvalidated) {break;}}}} while (mDispatchInvalidated);// 标记分发开始mDispatchingValue = false;
    }
    
  • 接下来看一下上面源码中initiator对象为空判断逻辑区别
    • dispatchingValue 这里分两种情况:ObserverWrapper不为null和ObserverWrapper为null
    • ObserverWrapper不为null 的情况。LifecycleBoundObserver.onStateChanged 方法里调用了 activeStateChanged ,而该方法调用dispatchingValue(this);传入了 this ,也就是 LifecycleBoundObserver ,这时候不为 null 。也就是说生命周期改变触发的流程就是这种情况,这种情况下,只会通知跟该 Owner 绑定的 Observer。
    • ObserverWrapper为null 的情况。经过分析发现在setValue方法中调用dispatchingValue(null)传递了空对象,这个时候的流程则会通知 active 的mObservers

8.2 看一下considerNotify()做什么

  • 然后看一下considerNotify() 方法做了什么,代码如下所示,这里有道词典翻译下注释
    • 如果ObserverWrapper的mLastVersion小于LiveData的mVersion,就会去回调mObserver的onChanged方法。
    • 每个新的订阅者,其version都是-1,LiveData一旦设置过其version是大于-1的(每次LiveData设置值都会使其version加1),这样就会导致LiveDataBus每注册一个新的订阅者,这个订阅者立刻会收到一个回调,即使这个设置的动作发生在订阅之前。
    private void considerNotify(ObserverWrapper observer) {if (!observer.mActive) {return;}// 检查最新的状态b4调度。也许它改变了状态,但我们还没有得到事件。// 我们还是先检查观察者。活动,以保持它作为活动的入口。// 因此,即使观察者移动到一个活动状态,如果我们没有收到那个事件,我们最好不要通知一个更可预测的通知顺序。if (!observer.shouldBeActive()) {observer.activeStateChanged(false);return;}//注意认真看下面的代码if (observer.mLastVersion >= mVersion) {return;}observer.mLastVersion = mVersion;//noinspection uncheckedobserver.mObserver.onChanged((T) mData);
    }
    
  • 思考一下dispatchingValue除了setValue会调用,其他还有地方调用么?
    • dispatchingValue除了在我们主动更新数据的时候会触发,
    • 当LifeCircleOwner的状态发生变化的时候,会调用LiveData.ObserverWrapper的activeStateChanged函数。
    • 在我们的观察者状态变更(inactive->active)的时候, 也会通知到, 这就导致了LiveData必然支持粘性事件。
    • 如果这个时候ObserverWrapper的状态是active,就会调用LiveData的dispatchingValue。
    • 它主要是处理分发通知逻辑,并且在分发通知前会判断 owner 的状态,再加上 LiveData 本身内部的版本管理,确保了只会发送最新的数据给 active 状态下的 Observer。
    • LiveData 对同时多次修改数据做了处理,如果同时多次修改,只会修改为最新的数据。
    private abstract class ObserverWrapper {final Observer<T> mObserver;boolean mActive;int mLastVersion = START_VERSION;//省略部分代码void activeStateChanged(boolean newActive) {if (newActive == mActive) {return;}// 当observer的状态从active->inactive, 或者inactive->active的时候走以下流程// ownermActive = newActive;boolean wasInactive = LiveData.this.mActiveCount == 0;LiveData.this.mActiveCount += mActive ? 1 : -1;if (wasInactive && mActive) {onActive();}if (LiveData.this.mActiveCount == 0 && !mActive) {onInactive();}//当observer是从inactive->active的时候,需要通知到观察者if (mActive) {dispatchingValue(this);}}
    }
    

8.3 发送消息事件流程

  • 那么发送消息事件之后的触发流程是怎样的?
    • setValue ——> dispatchingValue(null) ——> considerNotify(注意,这里是个for迭代器循环,表示通知所有观察者) ——> onChanged

09.observeForever源码

  • 这个方法是干什么用的呢?看一下源代码
    • 将给定的观察者添加到观察者列表中,意味着给定的观察者将接收所有事件,并且永远不会被自动删除,不管在什么状态下都能接收到数据的更改通知
    @MainThread
    public void observeForever(@NonNull Observer<T> observer) {// 创建一个AlwaysActiveObserver对象AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);if (existing != null && existing instanceof LiveData.LifecycleBoundObserver) {throw new IllegalArgumentException("Cannot add the same observer"+ " with different lifecycles");}if (existing != null) {return;}//刷新数据wrapper.activeStateChanged(true);
    }
    

10.LiveData源码总结

  • LiveData的观察者可以联动生命周期, 也可以不联动。在联动生命周期时,会自动在 DESTROYED 的状态下移除 Observer ,取消订阅,所以不用担心内存泄露;
  • LiveData的观察者只能与一个LifecycleOwner绑定, 否则会抛出异常。而一个 owner 可以绑定多个 Observer 实例;
  • LiveData 跟 LifecycleOwner 绑定,能感知生命周期变化,并且只会在 LifecycleOwner 处于 Active 状态(STARTED/RESUMED)下通知数据改变;如果数据改变发生在非 active 状态,数据会变化,但是不发送通知,等 owner 回到 active 的状态下,再发送通知;
  • 使用observeForever()方法,会注意AlwaysActiveObserver对象,意味着给定的观察者将接收所有事件,并且永远不会被自动删除,不管在什么状态下都能接收到数据的更改通知
  • LiveData 利用版本管理、绑定 Lifecycle 确保了只会发送最新的数据给 active 状态下的 Observer
  • image
  • image

参考博客

  • https://developer.android.com/reference/android/arch/lifecycle/LiveData
  • https://juejin.im/post/5dce5b16f265da0ba5279b11
  • https://mp.weixin.qq.com/s/glbd3mzU_cUPGAlJMlQ5Hg
  • https://github.com/googlesamples/android-architecture-components
  • https://mp.weixin.qq.com/s/yzR2LAor7dUmZDctG2g7MQ

开源LiveData事件总线:https://github.com/yangchong211/YCLiveDataBus


http://chatgpt.dhexx.cn/article/Os9YBYGx.shtml

相关文章

LiveData+Room

文章目录 LiveData1.LiveData的作用2.LiveData的特点3.LiveData与ViewModel(无参数)的结合4.LiveData与ViewModel(有参数)的结合5.map()和switchMap()5.0 map()和switchMap()的作用5.1map()5.11没带ViewModel版5.12带ViewModel版 5.2switchMap() Room1.Room的定义2.Room的三个组…

Android架构组件(二)——LiveData

Android架构组件&#xff08;二&#xff09;——LiveData 上一篇文章讲到了Android架构组件之一Lifecycle组件&#xff08;Android 架构组件&#xff08;一&#xff09;——Lifecycle-Aware Components&#xff0c;现在我们再来看看另一个成员LiveData。 定义 简单地说&#xf…

Android LiveData 使用详解

说在前面 本次推出 Android Architecture Components 系列文章&#xff0c;目前写好了四篇&#xff0c;主要是关于 lifecycle&#xff0c;livedata 的使用和源码分析&#xff0c;其余的 Navigation&#xff0c; Paging library&#xff0c;Room&#xff0c;WorkMannager 等春节…

由浅入深,详解 LiveData 的那些事

引言 关于LiveData,在2022尾声的今天&#xff0c;从事 Android 开发的小伙伴一定不会陌生。相应的&#xff0c;关于 LiveData 解析与使用的文章更是数不胜数&#xff0c;其中不乏优秀的创作者&#xff0c;在众多的文章以及前辈面前&#xff0c;本篇也不敢妄谈能写的多么深入,易…

LiveData的原理和使用

LiveData Livedata是什么&#xff1f;它的作用是什么&#xff1f;我们能用它来干什么&#xff1f; 首先&#xff0c;LiveData是一种可观察的数据存储类。这句话可以看成两个部分&#xff0c;一个是可观察的类&#xff0c;另一个是数据存储的类。 LiveData 是可以被观察的&am…

python电子书合集

Python电子书合集 小编最早接触的编程语言是C&#xff0c;决心走上编程之路的是JavaScript&#xff0c;后来入职靠的Java&#xff0c;后来发现还有一门短小精悍的语言叫——Python。 本来以为是一门新语言&#xff0c;结果才发现是自己out了&#xff0c;没想到这门语言比小编还…

《Python Cookbook》(中文第三版):电子书

《Python Cookbook&#xff08;第3版&#xff09;中文版》介绍了Python应用在各个领域中的一些使用技巧和方法&#xff0c;其主题涵盖了数据结构和算法&#xff0c;字符串和文本&#xff0c;数字、日期和时间&#xff0c;迭代器和生成器&#xff0c;文件和I/O&#xff0c;数据编…

python编程入门电子书-《Python编程 从入门到实践》高清电子书免费下载

今天给大家分享一本书 获取方式 公众号后台回复 Python基础 获取百度网盘下载链接 书籍简介 本书旨在让你成为优秀的程序员&#xff0c;具体地说&#xff0c;是优秀的Python程序员。通过阅读本书&#xff0c;你将迅速掌握编程概念&#xff0c;打下坚实的基础&#xff0c;并…

【小沐学Python】Python实现在线电子书制作(MkDocs + readthedocs + github + Markdown)

文章目录 1、简介2、安装3、创建新项目4、添加页面5、编辑导航页6、设置主题7、更改图标图标8、构建网站9、部署9.1 准备github项目9.2 注册登录Read the Docs9.3 导入github项目到 Read the Docs 10、Markdown语法10.1 横线10.2 标题10.3 段落10.4 文字高亮10.5 换行10.6 斜体…

超全的Python完全版电子书——从基础到爬虫、分析等高级应用,限时下载

python3.11即将于下半年发布&#xff0c;新的版本速度提升2倍&#xff0c;以弥补与其他编程语言在速度上的缺陷。可以预见Python语言在未来的应用范围会越来越广。 python学习方向建议&#xff1a; 如果你是本科及以下学历&#xff0c;建议你学习以下两个方向 1、爬虫。简单的…

【小沐学Python】Python实现在线电子书制作(Sphinx + readthedocs + github + Markdown)

文章目录 1、简介2、安装3、创建测试工程4、项目文件结构5、编译为本地文件6、编译为http服务7、更改样式主题8、支持markdown9、修改文档显示结构10、项目托管到github11、部署到ReadtheDocs结语 1、简介 Sphinx 是一个 文档生成器 &#xff0c;您也可以把它看成一种工具&…

python编程入门电子书-Python3零基础教材电子书合集

Python3零基础教材电子书合集&#xff0c;传送门&#xff1a;https://www.52pojie.cn/thread-676318-1-1.html 一、《Python编程从入门到实践》 链接&#xff1a;https://pan.baidu.com/s/1o9wJq0y 密码&#xff1a;12od 这书楼主现在也在看&#xff0c;讲的很细&#xff0c;…

学习Python必看的经典书籍(附电子书)

哈喽&#xff0c;我是牙儿 今天给大家推荐几本经典的Python书籍 一起来看看都有哪些吧~ 1 《Python学习手册&#xff08;第4版&#xff09;》 这本书全面、深入地介绍了 Python 语言&#xff0c;不管你是编程新手还是 Python 初学者&#xff0c;它将帮助你快速实现使用 Pyt…

超全的Python完全版电子书.pdf !从基础到爬虫、分析等高级应用,限时下载

python3.11即将于下半年发布&#xff0c;新的版本速度提升2倍&#xff0c;以弥补与其他编程语言在速度上的缺陷。可以预见Python语言在未来的应用范围会越来越广。 python学习方向建议&#xff1a; 如果你是本科及以下学历&#xff0c;建议你学习以下两个方向 1、爬虫。简单…

python入门经典电子书-推荐6本学习Python的免费电子书

便宜并不是没好货&#xff0c;这里的一些书籍已经被很多大学作为课本来使用&#xff0c;比如麻省理工的计算机科学与编程入门课程&#xff0c;加利福尼亚大学的编程思想课程都用到了下面的某(几)本书籍。 简明 简明 Python 教程是Swaroop C.H. 教授为Python初学者写的一本书。…

Python 开源电子书资源

转载自公众号&#xff1a;Mocun6 昨天给大伙儿送了书&#xff0c;留言区的篇幅占了整篇文章的一半&#xff0c;看来大家都想好&#xff08;把&#xff09;好&#xff08;我&#xff09;学&#xff08;掏&#xff09;习&#xff08;空&#xff09;。今天不送纸质书了&#xff0c…

100多本python书,免费电子版下载

推荐&#xff1a; 1、Coffee Break Python Slicing: 24 Workouts to Master Slicing in Python, Once and for All 切片&#xff08;Slicing&#xff09;是 Python 里非常有用的一个功能&#xff0c;属于 Python 开发人员最基本的技能之一。 如果你是初学者而且想了解 Slicin…

学习 Python 必看的书单(附电子书链接)

本文为你分享入门Python的必读书单。 学 Python 看什么书&#xff1f; 这是刚接触 Python 的朋友最疑惑的问题。 今天就结合自己入门时的学习历程和大家来聊一聊如何入门 Python&#xff0c;为了更有说服性一些&#xff0c;这里我把入门时看过的一些大佬推荐的书单进行了汇总…

python入门电子版-Python3零基础教材电子书合集

Python3零基础教材电子书合集,传送门:https://www.52pojie.cn/thread-676318-1-1.html 一、《Python编程从入门到实践》 链接:https://pan.baidu.com/s/1o9wJq0y 密码:12od 这书楼主现在也在看,讲的很细,建议大家零基础的从这书开始最好。个人觉得比《简明python教程》…

Python爬虫获取电子书资源实战

最近在学习Python&#xff0c;相对java来说python简单易学、语法简单&#xff0c;工具丰富&#xff0c;开箱即用&#xff0c;适用面广做全栈开发那是极好的&#xff0c;对于小型应用的开发&#xff0c;虽然运行效率慢点&#xff0c;但开发效率极高。大大提高了咱们的生产力。为…