Jetpack系列 -- LiveData源码原理解析(解决黏性问题)

news2025/1/11 22:45:24

一、LiveData是什么?

注意:一般情况下,LiveData要配合ViewModel一起使用的,但是今天是单独使用LiveData,作为学习的话,我们可以只关注LiveData了。

LiveData是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。

二、各种LiveData各种系列使用

1.使用方式一:

MyLiveData.kt 

object MyLiveData {
    // 这里为info1的MutableLiveData 懒加载初始化(懒加载:用到时才加载)
    val info1 : MutableLiveData<String> by lazy { MutableLiveData() }
    init {
        info1.value = "default"
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val textView: TextView = findViewById(R.id.tv_textview)
        activity_main.xml
        // 1.观察者 眼睛 环节
        MyLiveData.info1.observe(this, {
            textView.text = it
        })
        // 2.触发数据 环节
        MyLiveData.info1.value = "default"
        thread {
            Thread.sleep(3000)
            MyLiveData.info1.postValue("三秒钟后,修改了哦")
        }
        thread {
            Thread.sleep(6000)
            MyLiveData.info1.postValue("六秒钟后,修改了哦")
        }
        // -------------- 下面是 触发修改数据 的写法
        // lambda 写法如下 observe:
        MyLiveData.info1.observe(this, {
        })
        // lambda 写法如下 observeForever:
        MyLiveData.info1.observeForever({
        })
        // 详细写法如下 observe:
        MyLiveData.info1.observe(this, object : Observer<String> {
            override fun onChanged(t: String?) {
            }
        })
        // 详细写法如下 observeForever:
        MyLiveData.info1.observeForever(object : Observer<String> {
            override fun onChanged(t: String?) {
            }
        })
    }
}

2.使用方式二:

MainActivity2.kt

class MainActivity2 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            startService(Intent(this, MyService::class.java))
            Toast.makeText(MainActivity2@this, "推送服务器启动成功",
                Toast.LENGTH_SHORT).show()
        }
        MyLiveData.data1.observe(this, {
            Log.d("server", "界面可见,说明用户在查看微信列表界面啦,更新消息列表UI界
                    面:${it}")
            Toast.makeText(this, "更新消息列表UI界面成功:${it}",
                Toast.LENGTH_SHORT).show()
        })
    }
}

MyLiveData.kt

object MyLiveData {
    // 这里为info1的MutableLiveData 懒加载初始化(懒加载:用到时才加载)
    val data1: MutableLiveData<String> by lazy { MutableLiveData() }
    // 注意:这里会奔溃 因为是在 thread { 首次实例化MyLiveData对象的,而下面确实setValue就会奔溃
    /*init {
        data1.value = "default"
    }*/
}

MyService.kt

class MyService : Service() {
    override fun onBind(intent: Intent): IBinder? = null
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int
    {
        thread {
            for (x in 1..100000) {
                Log.d("server", "服务器给推你推送消息啦(叮咚声响),消息内容是:${x}")
                MyLiveData.data1.postValue("服务器给推你推送消息啦,消息内容是:${x}")
                Thread.sleep(5000) // 2秒钟推一次
            }
        }
        return super.onStartCommand(intent, flags, startId)
    }
}

3.使用方式三(黏性):

MainActivity3.kt

class MainActivity3 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main3)
        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            MyLiveData.value1.value = "我就是我,不一样的烟火"
            startActivity(Intent(this, MainActivity4::class.java))
        }
    }
}

MainActivity4.kt

class MainActivity4 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main4)
        MyLiveData.value1.observe(this, {
            Toast.makeText(this, "观察者数据变化:$it", Toast.LENGTH_SHORT).show()
        })
    }
}

MyLiveData.kt

object MyLiveData {
    // 这里为info1的MutableLiveData 懒加载初始化(懒加载:用到时才加载)
    val value1 : MutableLiveData<String> by lazy { MutableLiveData() }
}

三、LiveData的源码原理解析

1. MutableLiveData 

继承了LiveData是一个可变的LiveData
是一个被观察者,是一个数据持有者
提供了 setValue 和 postValue方法,其中postValue可以在子线程调用
postValue方法,我们下面会具体分析 

public class MutableLiveData<T> extends LiveData<T> {

    /**
     * Creates a MutableLiveData initialized with the given {@code value}.
     *
     * @param value initial value
     */
    public MutableLiveData(T value) {
        super(value);
    }

    /**
     * Creates a MutableLiveData with no value assigned to it.
     */
    public MutableLiveData() {
        super();
    }

    @Override
    public void postValue(T value) {
        super.postValue(value);
    }

    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

2.MutableLiveData的observe方法参数的this

此接口是宿主生命周期的代表

public interface LifecycleOwner{
	@NonNull Lifecycle getLifecycle();
}

3.MutableLiveData的observe方法参数的Observer

Observer是一个观察者
Observer中有一个回调方法,在 LiveData 数据改变时会回调此方法

public interface Observer<T> {
    /**
     * 当数据改变时调用。
     * @param t 新数据
     */
    void onChanged(T t);
}

4.源码分析

首先我们上面示例中的 LiveData.observe()方法开始。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val textView: TextView = findViewById(R.id.tv_textview)
        MyLiveData.info1.observe(this, {
            textView.text = it
        })
        thread {
            Thread.sleep(3000)
            MyLiveData.info1.postValue("三秒钟后,修改了哦")
        }
        thread {
            Thread.sleep(6000)
            MyLiveData.info1.postValue("六秒钟后,修改了哦")
        }
    }
}

我们点进observe方法中去它的源码

5.LiveData源码

1)在LiveData的observe方法中

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T>
observer) {
    assertMainThread("observe");
// 一:首先会通过LifecycleOwner获取Lifecycle对象然后获取Lifecycle 的State,如果是DESTROYED直接 return 了。忽略这次订阅
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
        return;
    }
// 二:把LifecycleOwner和Observer包装成LifecycleBoundObserver对象,至于为什么包装成这个对象,我们下面具体讲,而且这个是重点。
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner,
        observer);
// 三:把观察者存到 Map 中
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// 四:之前添加过LifecycleBoundObserver,并且LifecycleOwner不是同一个,就抛异常
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
// 五:通过Lifecycle和添加 LifecycleBoundObserver观察者,形成订阅关系
    owner.getLifecycle().addObserver(wrapper);
}

到现在,我们知道了LiveData的observe方法中会判断 Lifecycle 的生命周期,
会把LifecycleOwner和Observer包装成LifecycleBoundObserver对象,
然后 Lifecycle().addObserver(wrapper) 
Lifecycle 这个被观察者会在合适的时机 通知 观察者的回调方法。

2)什么时候通知,怎么通知的呢?这个具体流程是什么,继续看下面代码

thread {
    Thread.sleep(3000)
    MyLiveData.info1.postValue("三秒钟后,修改了哦")
}
thread {
    Thread.sleep(6000)
    MyLiveData.info1.postValue("六秒钟后,修改了哦")
}

在点击按钮的时候 LiveData会调用postValue ----> setValue方法,来更新最新的值,这时候我们
的观察者Observer就会收到回调,来更新 TextView。
所以接下来我们先看下 LiveData的setValue方法做了什么,LiveData还有一个postValue方法,我
们也一并分析一下。

6.LiveData的setValue与postValue

1)setValue

// LiveData.java
@MainThread
protected void setValue(T value){
    assertMainThread("setValue");
    mVersion++;
    mData=value;
    dispatchingValue(null); // 注意:这里的分发value值
}

调用了dispatchingValue方法,继续跟代码

void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) { // 如果传递进来的 非null,就进此if
                considerNotify(initiator);
                不管如何判断,都是调用了considerNotify() 方法
                        initiator = null;
            } else { // 如果传递进来的 null,就进此if
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>>
                     iterator =
                     mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    不管如何判断,都是调用了
                    considerNotify() 方法
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

不管如何判断,都是调用了considerNotify()方法

private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }

        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        // 最终调用了observer.mObserver.onChanged((T) mData)方法,
        // 这个observer.mObserver就是我们的 Observer接口,然后调用它的onChanged方法。
        observer.mObserver.onChanged((T) mData);
        // 恭喜恭喜 :到现在整个被观察者数据更新通知观察者这个流程就通了。
    }

2)postValue

子线程发送消息通知更新 UI,嗯?Handler 的味道

// LiveData.java
    protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if (!postTask) {
            return;
        }
        // 利用Handler切换到主线程去执行
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }

可以看到一行关键代码
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
点 postToMainThread 方法进去看下

// ArchTaskExecutor.java
    privateTaskExecutor mDelegate;

    @Override
    public void postToMainThread(Runnable runnable) {
        mDelegate.postToMainThread(runnable);
    }

看到 mDelegate 是 TaskExecutor对象,现在目标是看下 mDelegate 的具体实例对象是谁

// ArchTaskExecutor.java
private ArchTaskExecutor(){
    mDefaultTaskExecutor=new DefaultTaskExecutor();
    mDelegate=mDefaultTaskExecutor; // 目前的重点是看下DefaultTaskExecutor是个啥
}

然后看它的postToMainThread方法

// DefaultTaskExecutor.java
    privatevolatileHandler mMainHandler;

    @Override
    public void postToMainThread(Runnable runnable) {
        if (mMainHandler == null) {
            synchronized (mLock) {
                if (mMainHandler == null) {
                    // 实例了一个 Handler 对象,注意构造参数 Looper.getMainLooper() 是主线的 Looper。
                    // 那么就可做到线程切换了。
                    mMainHandler = new Handler(Looper.getMainLooper()); // 注意:这里主
                    线程已经切换过来了
                }
            }
        }
        //noinspection ConstantConditions
        // 调用post 方法 注意:重点看runnable,这里面就是真正主线程的执行功能了
        mMainHandler.post(runnable); 
    }

下面看下这个 Runnable
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
这里面的方法参数是mPostValueRunnable是个 Runnable,我们看下代码

// LiveData.java
    private final Runnable mPostValueRunnable = new Runnable() {
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            //noinspection unchecked
            // 注意:postValue方法其实最终调用也是setValue方法,然后和setValue方法走的流程就是一样的了,
            // 这个上面已经分析过了
            setValue((T) newValue);
        }
    };

7.LifecycleBoundObserver

我们要详细看一下LifecycleBoundObserver类了,它包装了LifecycleOwner和Observer,这就是
接下来的重点内容了。

1)observe

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T>
            observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        // 用LifecycleBoundObserver对LifecycleOwner 和 Observer进行了包装
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner,
                observer);
        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);
    }

来看下LifecycleBoundObserver类,它是LiveData的内部类

class LifecycleBoundObserver extends ObserverWrapper implements
        GenericLifecycleObserver {
    @NonNull
    final LifecycleOwner mOwner;
    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T>
            observer) {
        super(observer);
        mOwner = owner;
    }
}

两个参数,一个 owner被成员变量mOwner存储,observer参数被ObserverWrapper的 mObserver存储。
LifecycleEventObserver是LifecycleObserver的子接口里面有一个onStateChanged方法,这个方法会在 Activity、Fragment 生命周期回调时调用(这个和Lifecycle有关)
ObserverWrapper 是Observer包装类

2)ObserverWrapper

活跃状态指的是 Activity、Fragment 等生命周期处于活跃状态

private abstract class ObserverWrapper {
    final Observer<? super T> mObserver;
    boolean mActive;
    int mLastVersion = START_VERSION;

    // 获取了我们的 Observer 对象,存储在 成员变量mObserver身上
    ObserverWrapper(Observer<? super 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
        // owner
        mActive = newActive;
        boolean wasInactive = LiveData.this.mActiveCount == 0;
        LiveData.this.mActiveCount += mActive ? 1 : -1;
        if (wasInactive && mActive) {
            // 可以继承 LiveData 来达到扩展 LiveData 的目标,并且是在活跃的状态调用
            onActive();
        }
        if (LiveData.this.mActiveCount == 0 && !mActive) {
            // 可以继承 LiveData 来达到扩展 LiveData 的目标,并且是在非活跃的状态调用
            onInactive();
        }
        if (mActive) {
            // 活跃状态,发送最新的值,来达到通知的作用,dispatchingValue(this) 方法咋这么眼熟,
            // 对之前在 LiveData 调用 setValue 方法时,最终也会调用到此方法。
            // 那ObserverWrapper类中的dispatchingValue这个方法是在activeStateChanged方法中调用,
            // 那activeStateChanged啥时候调用呢?
            // 我来看下ObserverWrapper的子类也就是最重要的那个类LifecycleBoundObserver,
            // 现在看它的完整代码(看下面代码 LifecycleBoundObserver完整代码)
            dispatchingValue(this);
        }
    }
}

LifecycleBoundObserver完整代码

class LifecycleBoundObserver extends ObserverWrapper implements
        GenericLifecycleObserver {
    @NonNull
    final LifecycleOwner mOwner;

    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T>
            observer) {
        super(observer);
        mOwner = owner;
    }

    @Override
    boolean shouldBeActive() {
        // 判断当前的 Lifecycle 的生命周期是否是活跃状态,会在回调观察则 Observer 的时候进行判断,
        // 只有在活跃状态,才会回调观察者Observer的onChanged方法。
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

    @Override
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        // onStateChanged每次 Activity、Fragment的生命周期回调的时候,都会走这个方法。
        // 获取Lifecycle对象然后获取Lifecycle 的State如果为DESTROYED则移除观察者,
        // 在 Activity、Fragment的生命周期走到 onDestroy 的时候,就会取消订阅,避免内存泄漏。
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        // 调用父类ObserverWrapper 的activeStateChanged方法,
        // 层层调用到观察者Observer的onChanged方法。(自己看下源码一目了然)
        activeStateChanged(shouldBeActive());
    }

    @Override
    boolean isAttachedTo(LifecycleOwner owner) {
        return mOwner == owner;
    }

    @Override
    void detachObserver() {
        // 移除观察者Observer,解除订阅关系。
        mOwner.getLifecycle().removeObserver(this);
    }
}

四、自定义JLiveDataBus解决黏性问题

JLiveDataBus.kt

object JLiveDataBus {

    private const val TAG = "JLiveDataBus"

    private val busMap: MutableMap<String, BusMutableLiveData<Any>> by lazy { HashMap<String, BusMutableLiveData<Any>>() }

    @Synchronized
    fun <T> with(key: String, type: Class<T>, isStick: Boolean = true): BusMutableLiveData<T> {
        Log.d(TAG, "with isStick $isStick")
        if (!busMap.containsKey(key)) {
            busMap[key] = BusMutableLiveData(isStick)
        } else {
            (busMap[key] as BusMutableLiveData<T>).isStick = isStick
        }
        return busMap[key] as BusMutableLiveData<T>
    }


    class BusMutableLiveData<T> private constructor() : MutableLiveData<T>() {

        // 启用粘性事件
        var isStick: Boolean = false

        // 次构造函数,必须调用主构造函数
        constructor(isStick: Boolean) : this() {
            Log.d(TAG, "constructor isStick $isStick")
            this.isStick = isStick
        }

        // 重写 增加hook
        override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
            super.observe(owner, observer)
            if (!isStick) {
                // 不启用粘性事件
                hook(observer = observer)
                Log.d(TAG, "不启动粘性事件")
            } else {
                Log.d(TAG, "启动粘性事件")
            }
        }

        private fun hook(observer: Observer<in T>) {
            // TODO 1.利用反射得到mLastVersion
            // 获取到LiveData类中的mObservers对象
            val liveDataClass = LiveData::class.java
            val mObserversField: Field = liveDataClass.getDeclaredField("mObservers")
            // 设置权限,私有修饰也可以访问
            mObserversField.isAccessible = true
            // 获取到mObservers这个成员变量的对象
            val mObservers: Any = mObserversField.get(this)
            // 获取到mObservers的class对象
            val mObserversClass: Class<*> = mObservers.javaClass
            // 获取到mObservers对象的get方法
            val get: Method = mObserversClass.getDeclaredMethod("get", Any::class.java)
            get.isAccessible = true
            // 执行get方法
            val invokeEntry: Any = get.invoke(mObservers, observer)
            // 获取到entry中的value
            var observerWraper: Any? = null
            if (invokeEntry != null && invokeEntry is Map.Entry<*, *>) {
                observerWraper = invokeEntry.value
            }
            if (observerWraper == null) {
                throw NullPointerException("observerWraper is null.")
            }
            // 获取到observerWraper的类对象
            val supperClass: Class<*> = observerWraper.javaClass.superclass!!
            val mLastVersion: Field = supperClass.getDeclaredField("mLastVersion")
            mLastVersion.isAccessible = true

            // TODO 2.得到mVersion
            val mVersion: Field = liveDataClass.getDeclaredField("mVersion")
            mVersion.isAccessible = true

            // TODO 3.mLastVersion = mVersion
            val mVersionValue: Any = mVersion.get(this)
            mLastVersion.set(observerWraper, mVersionValue)
        }
    }

}

BusActivity.kt

class BusActivity : AppCompatActivity() {

    private lateinit var binding: ActivityBusBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityBusBinding.inflate(layoutInflater)
        setContentView(binding.root)

        JLiveDataBus.with("test1", String::class.java, false).observe(this) {
            binding.busTv.text = it
        }

        thread {
            Thread.sleep(2000)
            JLiveDataBus.with("test1", String::class.java).postValue("新数据")
        }

    }
}

startActivity

JLiveDataBus.with("test1", String::class.java).value = "老数据"
        textView.setOnClickListener {
            startActivity(Intent(activity, BusActivity::class.java))
        }

由于我们在BusActivity使用的是不启用粘性,JLiveDataBus.with("test1", String::class.java, false).observe(this),所以当BusActivity启动时不会调用binding.busTv.text = "老数据",延迟2S后,正常接收到启动后的 新数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1017156.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python-保留小数位数的3种方法

在python实际运用中&#xff0c;需要对小数位数进行截取保留。 以下是&#xff0c;python保留小数点位数的3种方法。 方法一 方法&#xff1a;’%.nf’ % num n代表保留的小数位数&#xff0c;num表示需要截取的目标数 用法如下&#xff1a; old_num 1.23456 new_num %.2f…

[NLP] LLM---<训练中文LLama2(四)方式一>对LLama2进行SFT微调

指令精调 指令精调阶段的任务形式基本与Stanford Alpaca相同。训练方案也采用了LoRA进行高效精调&#xff0c;并进一步增加了可训练参数数量。在prompt设计上&#xff0c;精调以及预测时采用的都是原版Stanford Alpaca不带input的模版。对于包含input字段的数据&#xff0c;采…

竞赛 基于机器学习与大数据的糖尿病预测

文章目录 1 前言1 课题背景2 数据导入处理3 数据可视化分析4 特征选择4.1 通过相关性进行筛选4.2 多重共线性4.3 RFE&#xff08;递归特征消除法&#xff09;4.4 正则化 5 机器学习模型建立与评价5.1 评价方式的选择5.2 模型的建立与评价5.3 模型参数调优5.4 将调参过后的模型重…

yolov5在rk3588上加速

不采用fastdeploy等三方框架&#xff0c;使用rknn-lite2或者rknpu在rk3588上加速&#xff0c;测试加速的是rknn自带的yolov5模型。 备注&#xff1a; 1.测试视频&#xff1a;多人&#xff0c;帧:3000&#xff0c;时长:2min&#xff0c;分辨率:1920x1080&#xff0c;fps:25 2…

傅里叶变换应用 (02/2):频域和相位

一、说明 到目前为止&#xff0c;在我们的讨论中&#xff0c;我已经交替使用了“傅里叶变换”和“快速傅里叶变换&#xff08;FFT&#xff09;”。在这一点上&#xff0c;值得注意的是区别&#xff01;FFT 是“离散”傅里叶变换 &#xff08;DFT&#xff09; 的有效算法实现。“…

Remix+Cloudflare Pages+D1 快速上手

我们最近听到越来越多的关于Cloudflare的服务。 我对Clouflare D1特别感兴趣&#xff0c;所以我决定研究一下。 与这次我想使用的 Remix 一起&#xff0c;我想介绍 Remix Cloudflare Pages D1 的第一步。 我只是稍微地了解一下&#xff0c;但我所做的在下面的仓库中&#…

【深度学习】 Python 和 NumPy 系列教程(十二):NumPy详解:4、数组广播;5、排序操作

目录 一、前言 二、实验环境 三、NumPy 0、多维数组对象&#xff08;ndarray&#xff09; 多维数组的属性 1、创建数组 2、数组操作 3、数组数学 4、数组广播 5、排序操作 1. np.sort() 函数 2. np.argsort() 函数 3. ndarray.sort() 方法 4. 按列或行排序 5. n…

VHDL菜鸟入门到精通之激励文件编写

目录 一、概览 二、激励文件结构 三、样例 3.1 组合逻辑 3.2 时序逻辑 四、常用编写 4.1 时钟信号 4.2 延时 4.3 循环 4.4 进程 一、概览 二、激励文件结构 VHDL激励文件结构和设计文件较为类似&#xff0c;下面以3-8译码器的激励文件对结构进行说明。 激励文件主要…

git clone报错Failed to connect to github.com port 443 after 21055 ms:

git 设置代理端口号 git config --global http.proxy http://127.0.0.1:10085 和 git config --global https.proxy http://127.0.0.1:10085 然后就可以成功git clone hugging face的数据集了 如果是https://huggingface.co/datasets/shibing624/medical/tree/main 那么…

logstash通过kafka通道采集日志信息

1.修改文件/opt/app/elk/logstash-7.5.1/config.d/config1.conf&#xff0c;在input下添加kafka采集配置 #192.168.128.130:9103:kafka地址 #topics:主题 kafka {bootstrap_servers > ["192.168.128.130:9103"]group_id > "logstash"topics > [&…

Optuna学习博客

介绍 简单来说&#xff0c;OPtuna就是一个能够进行调整超参数的框架&#xff0c;它能够将自动调整超参数以及能够将超参数优化过程可视化&#xff0c;方便保存&#xff0c;分析。可拓展性较强。 使用方法 optuna的优化程序具体有三个组成部分。 objective&#xff08;目标函…

MySQL数据库管理及数据库基本操作

目录 1 MySQL数据库基本操作 1.1 SQL分类 1.2 SQL语言规范 1.3 数据库对象和命名 1.4 SQL语句分类 2 管理MySQL数据库 2.1 查看数据库结构 2.1.1 查看当前服务器中的数据库 2.1.2 查看数据库中包含的表 2.1.3 查看表的结构&#xff08;字段&#xff09; 2.2 数据类型…

【linux】进程创建,进程终止

进程创建&#xff0c;进程终止 1.进程创建1.1写时拷贝1.2fork常规用法1.3fork调用失败的原因 2.进程终止2.1退出码2.2进程退出场景2.3进程如何退出 1.进程创建 在前面创建子进程的时候就学过了fork函数&#xff0c;它能从已经存在进程中创建一个新进程&#xff0c;新进程为子进…

Python 图形化界面基础篇:打开和关闭新窗口

Python 图形化界面基础篇&#xff1a;打开和关闭新窗口 引言 Tkinter 库简介步骤1&#xff1a;导入 Tkinter 模块步骤2&#xff1a;创建 Tkinter 窗口步骤3&#xff1a;创建一个新窗口步骤4&#xff1a;关闭新窗口步骤5&#xff1a;启动 Tkinter 主事件循环 完整示例代码代码解…

C语言指针详解(4)———找工作必看指针笔试题汇总

指针对于编程工作的重要性 C语言指针在找工作中具有重要性。以下是几个原因&#xff1a; 1.高效的内存管理&#xff1a;C语言指针可以帮助程序员高效地管理内存&#xff0c;包括动态内存分配和释放&#xff0c;以及数据的访问和操作。这对于开发性能优化的应用程序非常重要&am…

7.代理模式

1.UML 2.代码 #include <iostream> using namespace std;class Subject{ public:virtual void Request() 0; };class RealSubject:public Subject { public:virtual void Request(){cout << "RealSubject" << endl;} }; class Proxy:public Subj…

VUE build:gulp打包:测试、正式环境

目录 项目结构 Gulp VUE使用Gulp Vue安装Gulp Vue定义Gulp.js package.json build文件夹 config文件夹 static-config文件夹 项目结构 Gulp Gulp是一个自动化构建工具&#xff0c;可以帮助前端开发者通过自动化任务来管理工作流程。Gulp使用Node.js的代码编写&#xff…

go初识iris框架(五) -MVC包的使用

在Iris框架中&#xff0c;封装了mvc包作为对mvc架构的支持&#xff0c;方便开发者遵循mvc的开发原则进行开发。 iis框架支持请求数据、模型、持久数据分层处理&#xff0c;并支持各层级模块代码绑定执行。 MVC即&#xff1a;model、view、controller三个部分&#xff0c;分别代…

【微信小程序】swiper的使用

1.swiper的基本使用 <jxz-header></jxz-header> <view class"banner"><swiperprevious-margin"30rpx"autoplayinterval"2000"indicator-dotsindicator-color"rgba(0,0,0,0.3)"indicator-active-color"#bda…

数字化管理平台建设实践

在勘察设计行业&#xff0c;各企业加速推进数字化转型。通过管理要素数字化&#xff0c;不断优化内部组织运营效率&#xff1b;通过生产手段数字化、技术产品数字化&#xff0c;提升服务质量&#xff0c;改善客户体验&#xff1b;通过数字化营销&#xff0c;精准对接市场需求&a…