Glide生命周期监听原理以及简单应用利用空Fragment代理Activity

news2024/11/15 19:35:18

Glide关于生命周期监听的原理解析以及简单应用

在这里插入图片描述

文章目录

  • Glide关于生命周期监听的原理解析以及简单应用
    • 1.Glide生命周期监听原理
      • 1.1 从Glide初始化开始分析
      • 1.2 原理总结
    • 2.简单应用
      • 2.1 应用场景1-主题切换之昼夜模式变化监听
      • 2.2 应用场景2--SDK打开特定应用或Activity
    • 3.总结

相信大家都用过 Glide,Glide是一个快速高效的图片加载库,Glide提供了简单易用的API,可扩展的图片解码管道,以及自动的资源池技术。相信大家对它已经无比了解,我也不再赘述。详情大家可以看 Glide官网

本篇内容介绍的是Glide关于安全方面的内容——生命周期监听。

1.Glide生命周期监听原理

Glide作为一个图片加载工具,拥有非常强大的功能,其中一项能力就是对生命周期的监听。简单来说,在你的ImageView所在的fragment或者Activity被回收或主动退出后,Glide能够自动进行响应的处理,以防止内存泄露以及不必要的网络和性能的浪费。

1.1 从Glide初始化开始分析

话不多说,开始整篇,我们从Glide最常用的代码入手:

  	   Glide
  		  .with(this)
  		  .load(imageUrl)
          .placeholder(com.avatr.ivi.common.widget.R.drawable.ic_image_default)
          .error(com.avatr.ivi.common.widget.R.drawable.ic_image_default)
          .transform(transformation, new RoundedCorners(10))
          .into(image);

点进去看with方法


  public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }

  public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
  }

   public static RequestManager with(@NonNull FragmentActivity activity) {
    return getRetriever(activity).get(activity);
  }

  public static RequestManager with(@NonNull Fragment fragment) {
    return getRetriever(fragment.getContext()).get(fragment);
  }

  public static RequestManager with(@NonNull android.app.Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
  }

  public static RequestManager with(@NonNull View view) {
    return getRetriever(view.getContext()).get(view);
  }

可以看到with有多个重载方法,但不管哪个方法,调用逻辑都是一样的,首先看看getRetriever(context:Context)


  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // Context could be null for other reasons (ie the user passes in null), but in practice it will
    // only occur due to errors with the Fragment lifecycle.
    Preconditions.checkNotNull(
        context,
        "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
            + "returns null (which usually occurs when getActivity() is called before the Fragment "
            + "is attached or after the Fragment is destroyed).");
    return Glide.get(context).getRequestManagerRetriever();
  }

这个方法就是从Glide实例中获取RequestManagerRetriever,接着看Glide.get(context)


  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      GeneratedAppGlideModule annotationGeneratedModule =
          getAnnotationGeneratedGlideModules(context.getApplicationContext());
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context, annotationGeneratedModule);
        }
      }
    }

    return glide;
  }

这段代码很简单,double check 初始化并返回Glide实例;

接着往下看checkAndInitializeGlide经过基层调用,最终到了下面


   private static void initializeGlide(
      @NonNull Context context,
      @NonNull GlideBuilder builder,
      @Nullable GeneratedAppGlideModule annotationGeneratedModule) {
    Context applicationContext = context.getApplicationContext();
    List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
    

    //....省略部分代码.....


    Glide glide = builder.build(applicationContext);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      try {
        module.registerComponents(applicationContext, glide, glide.registry);
      } catch (AbstractMethodError e) {
        throw new IllegalStateException(
            "Attempting to register a Glide v3 module. If you see this, you or one of your"
                + " dependencies may be including Glide v3 even though you're using Glide v4."
                + " You'll need to find and remove (or update) the offending dependency."
                + " The v3 module name is: "
                + module.getClass().getName(),
            e);
      }
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
    }
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
  }

重点是builder.build(applicationContext)


  Glide build(@NonNull Context context) {
    if (sourceExecutor == null) {
      sourceExecutor = GlideExecutor.newSourceExecutor();
    }

    if (diskCacheExecutor == null) {
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }

    if (animationExecutor == null) {
      animationExecutor = GlideExecutor.newAnimationExecutor();
    }

    if (memorySizeCalculator == null) {
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
    }

    if (connectivityMonitorFactory == null) {
      connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
    }

    if (bitmapPool == null) {
      int size = memorySizeCalculator.getBitmapPoolSize();
      if (size > 0) {
        bitmapPool = new LruBitmapPool(size);
      } else {
        bitmapPool = new BitmapPoolAdapter();
      }
    }

    if (arrayPool == null) {
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }

    if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }

    if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }

    if (engine == null) {
      engine =
          new Engine(
              memoryCache,
              diskCacheFactory,
              diskCacheExecutor,
              sourceExecutor,
              GlideExecutor.newUnlimitedSourceExecutor(),
              animationExecutor,
              isActiveResourceRetentionAllowed);
    }

    if (defaultRequestListeners == null) {
      defaultRequestListeners = Collections.emptyList();
    } else {
      defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
    }

    GlideExperiments experiments = glideExperimentsBuilder.build();
    //这里,创建的RequestManagerRetriever
    RequestManagerRetriever requestManagerRetriever =
        new RequestManagerRetriever(requestManagerFactory, experiments);

    return new Glide(
        context,
        engine,
        memoryCache,
        bitmapPool,
        arrayPool,
        requestManagerRetriever,
        connectivityMonitorFactory,
        logLevel,
        defaultRequestOptionsFactory,
        defaultTransitionOptions,
        defaultRequestListeners,
        experiments);
  }

这里创建了Glide的一系列组件模块,然后调用构造函数传入。当然这里不是我们本章的终点,继续往下看RequestManagerRetriever.get()方法


  @NonNull
  public RequestManager get(@NonNull FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else {
      assertNotDestroyed(activity);
      frameWaiter.registerSelf(activity);
      FragmentManager fm = activity.getSupportFragmentManager();
      return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }

  @NonNull
  public RequestManager get(@NonNull Fragment fragment) {
    Preconditions.checkNotNull(
        fragment.getContext(),
        "You cannot start a load on a fragment before it is attached or after it is destroyed");
    if (Util.isOnBackgroundThread()) {
      return get(fragment.getContext().getApplicationContext());
    } else {
      // In some unusual cases, it's possible to have a Fragment not hosted by an activity. There's
      // not all that much we can do here. Most apps will be started with a standard activity. If
      // we manage not to register the first frame waiter for a while, the consequences are not
      // catastrophic, we'll just use some extra memory.
      if (fragment.getActivity() != null) {
        frameWaiter.registerSelf(fragment.getActivity());
      }
      FragmentManager fm = fragment.getChildFragmentManager();
      return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible());
    }
  }

  @SuppressWarnings("deprecation")
  @NonNull
  public RequestManager get(@NonNull Activity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else if (activity instanceof FragmentActivity) {
      return get((FragmentActivity) activity);
    } else {
      assertNotDestroyed(activity);
      frameWaiter.registerSelf(activity);
      android.app.FragmentManager fm = activity.getFragmentManager();
      return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }

  @SuppressWarnings("deprecation")
  @NonNull
  public RequestManager get(@NonNull View view) {

  	//...省略部分代码及注释
    if (activity instanceof FragmentActivity) {
      Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
      return fragment != null ? get(fragment) : get((FragmentActivity) activity);
    }

    // Standard Fragments.
    android.app.Fragment fragment = findFragment(view, activity);
    if (fragment == null) {
      return get(activity);
    }
    return get(fragment);
  }

get()方法有多个多态方法,但到现在为止,我们常用的其实只有两个get(@NonNull FragmentActivity activity)get(@NonNull Fragment fragment),我们以前者为重点继续分析


  @NonNull
  public RequestManager get(@NonNull FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else {
      assertNotDestroyed(activity);
      frameWaiter.registerSelf(activity);
      //终点是这里
      FragmentManager fm = activity.getSupportFragmentManager();
      return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }

继续看supportFragmentGet:


  private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
    SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      // This is a bit of hack, we're going to start the RequestManager, but not the
      // corresponding Lifecycle. It's safe to start the RequestManager, but starting the
      // Lifecycle might trigger memory leaks. See b/154405040
      if (isParentVisible) {
        requestManager.onStart();
      }
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

  private SupportRequestManagerFragment getSupportRequestManagerFragment(
      @NonNull final FragmentManager fm, @Nullable Fragment parentHint) {
    // If we have a pending Fragment, we need to continue to use the pending Fragment. Otherwise
    // there's a race where an old Fragment could be added and retrieved here before our logic to
    // add our pending Fragment notices. That can then result in both the pending Fragmeng and the
    // old Fragment having requests running for them, which is impossible to safely unwind.
    SupportRequestManagerFragment current = pendingSupportRequestManagerFragments.get(fm);
    if (current == null) {
      current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
      if (current == null) {
        current = new SupportRequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        pendingSupportRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

  • 首先,这段代码的意义很简单,构造一个SupportRequestManagerFragment类型的空的Fragment,注入当前Activity,并确保该Activity有且只有一个该类型Fragment(为什么只需要一个,以及如何保证只有一个,大家可以自行探索);
  • 然后,将GlideLifecycle实例注入到空Fragment;因为fragment会响应宿主activity的生命周期,这样就可以做到不侵入宿主(组件外部)activity的前提下监听其生命周期;进而在生命周期发生变化时,触发GlideLifecycle回调,控制GlideRequest工作状态。

1.2 原理总结

OK,Glide图片加载的任务如何关联生命周期的流程我们已经明白,这里再次总结一下:

  • 利用Fragment监听/代理Activity

fragment可以响应宿主activity的生命周期回调,并且会先回调fragment;
这样做有一个好处,当我们的代码作为SDK时,可以在不侵入使用者代码的情况下,达到对生命周期的监听

  • 空fragment只是宿主Activity的代理

fragment不仅可以响应activity的生命周期回调,而且可以响应其它例如onActivityResult,onConfigurationChangeonRequestPermissionsResult等等,同时,它还可以调用Activity的方法,例如startActivity,getResources()等其它方法

  • 何为空Fragment?

就是没有UI布局,不会显示出来的Fragment,它作为Activity的一部分,可以响应宿主Activity的所有生命周期以及其它的Activity回调;

2.简单应用

利用空Fragment代理Activity实际上有很多实用的应用场景,以下就简单举例两种

2.1 应用场景1-主题切换之昼夜模式变化监听



class ContextHelper private constructor() {
    private lateinit var mApp: Application
    private val mStack = Stack<Activity>()

    companion object {
        val INSTANCE: ContextHelper by lazy {
            ContextHelper()
        }
    }


    fun init(context: Context) {
        if (context is Activity) {
            mStack.add(context)
        }
        mApp = context.applicationContext as Application
        mApp.registerActivityLifecycleCallbacks(object : ActivityLifecycleAdapter() {
            override fun onActivityCreated(activity: Activity) {
                mStack.add(activity)
                notifyActivityChange(activity)
            }

            override fun onActivityDestroyed(activity: Activity) {
                super.onActivityDestroyed(activity)
                mStack.remove(activity)
                if (mStack.isNotEmpty()) {
                    notifyActivityChange(mStack.peek())
                } else {
                    notifyActivityChange(null)
                }
            }
        })
    }

    private fun notifyActivityChange(activity: Activity?) {
        for (each in mActivityChangeListeners) {
            each.onTopActivityChange(activity)
        }
    }

    private val mActivityChangeListeners = ArrayList<IOnTopActivityChangeListener>()

    fun addTopActivityChangeListener(listener: IOnTopActivityChangeListener) {
        mActivityChangeListeners.add(listener)
    }

    fun removeTopActivityChangeListener(listener: IOnTopActivityChangeListener) {
        mActivityChangeListeners.remove(listener)
    }

    /**
    * TopActivity 变化监听
    */
    interface IOnTopActivityChangeListener {
        fun onTopActivityChange(activity: Activity?)
    }

    public fun getTopActivity(): Activity? {
        try {
            return mStack.peek()
        } catch (e: Exception) {
            e.printStackTrace()
            return null
        }
    }

    public fun getApp(): Application {
        return mApp
    }



abstract class ActivityLifecycleAdapter : ActivityLifecycleCallbacks {
    override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
        onActivityCreated(activity)
    }

    override fun onActivityStarted(activity: Activity) {
    }

    override fun onActivityResumed(activity: Activity) {
    }

    override fun onActivityPaused(activity: Activity) {
    }

    override fun onActivityStopped(activity: Activity) {
    }

    override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
    }

    abstract fun onActivityCreated(activity: Activity)
    
}

基本道理大家都懂,就是在application注册ActivityLifecycleCallbacks,首先在Activity或者Application中初始化ContextHelper
这就获取到了TopActivity;

然后,在TopActivity中注入空Fragment


class AnySdkManager{


	fun init(context:Context){

		val topActivity = ContextHelper.INSTANCE.getTopActivity()
        topActivity?.let {
            addFragment(it)
        }
        ContextHelper.INSTANCE.addTopActivityChangeListener(object : IOnTopActivityChangeListener{
            override fun onTopActivityChange(activity: Activity?) {
                topActivity?.let {
		            addFragment(it)
		        }
            }

        })
	}

    private fun addFragment(activity: Activity) {
        val sfm = (activity as AppCompatActivity).supportFragmentManager
        var fragment = sfm.findFragmentByTag(TINDER_FRAGMENT)
        if (fragment == null) {
            fragment = ActivityProxyFragment()
            mFragment = fragment as ActivityProxyFragment
            sfm.beginTransaction().add(fragment, TINDER_FRAGMENT).commit()
            Log.i(TAG, "addFragment: ")
        }
    }
}

监听到Activity变化后,就可以注入我们的空Fragment

    public static class ConfigurationChangeFragment extends Fragment {
        private static final String TAG = "ConfigurationChangeFrag";
        private AssetManager mLastAssetManager;
        private int mLastUiMode;

        private IOnUiModeChangeListener mListener;

        public ConfigurationChangeFragment() {

        }

        public void setOnUiModeChangeListener(IOnUiModeChangeListener listener) {
            this.mListener = listener;
        }

        @Override
        public void onAttach(@NonNull Context context) {
            super.onAttach(context);
            mLastAssetManager = context.getAssets();
            mLastUiMode = context.getResources().getConfiguration().uiMode;
            CommonUtilsLog.i(TAG, "onAttach: assetManager:" + context.getAssets());
        }

        @Override
        public void onConfigurationChanged(@NonNull Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            CommonUtilsLog.i(TAG, "onConfigurationChanged: mLastUiMode:" + mLastUiMode + ",newUiMode:" + newConfig.uiMode + ",mLastAssetManager:" + mLastAssetManager + ",assetManager:" + getContext().getAssets());
            if (mLastUiMode != newConfig.uiMode) {
                mLastAssetManager = getContext().getAssets();
                mLastUiMode = newConfig.uiMode;
                if (mListener != null) {
                    mListener.onUiModeChange(newConfig);
                }
            }
        }

        public void release() {
            mListener = null;
        }
    }

    interface IOnUiModeChangeListener {
        void onUiModeChange(Configuration newConfig);
    }

这样就达到了利用空Fragment监听configurationChange的目的;注入空Fragment要注意,一个Activity不要重复注入,还有多个Activity也不要重复注入,否则会造成资源的浪费和回调重复等问题,这里只是简单示例,大家自行研究怎么解决这些问题;

2.2 应用场景2–SDK打开特定应用或Activity

监听/获取TopActivity和添加Fragment方法同上,但Fragment实现有所不同,如下:


class ActivityProxyFragment : Fragment() {
    private lateinit var launcher: ActivityResultLauncher<Intent>
    private lateinit var mOnActivityResult: IOnActivityResultCallback
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val contract = object : ActivityResultContract<Intent, ActivityResult>() {
            override fun createIntent(context: Context, input: Intent): Intent {
                Log.i(TAG, "createIntent: ")
                return input
            }

            override fun parseResult(resultCode: Int, intent: Intent?): ActivityResult {
                Log.i(TAG, "parseResult: ")
                return ActivityResult(resultCode, intent)
            }

        }
        launcher = registerForActivityResult(contract) {
            it.data
            Log.i(TAG, "onActivityResult: resultCode:$it.resultCode")
            mOnActivityResult.onActivityResult(it.resultCode, it.data)
        }
    }

    fun startActivityForResult(
        intent: Intent,
        callback: IOnActivityResultCallback
    ) {
        mOnActivityResult = callback
        launcher.launch(intent)
    }

    interface IOnActivityResultCallback {

        fun onActivityResult(resultCode: Int, data: Intent?)
    }

    companion object {
        private const val TAG = "ActivityProxyFragment"
    }
}

这样,在你的SDK代码中,就可以通过该ProxyFragment调用startActivityForResult

        mFragment.startActivityForResult(
            anyIntent,
             object : ActivityProxyFragment.IOnActivityResultCallback {
            override fun onActivityResult(resultCode: Int, data: Intent?) {

            	//do sth 
            	}
         })


3.总结

实际fragment的应用场景还有很多,我这里只是简单举两个例子,实际场景可以让ProxyFragment尽可能的实现多种Activity的功能,然后注入。

此次分享到此结束,如有问题,希望大家批评指正,非常感谢。

恭祝各位看官老爷身体健康,诸事顺利,好运连绵😘😘😘

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

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

相关文章

docker的部署及基本用法

目录​​​​​​​ 1 docker 介绍 1.1 什么是docker&#xff1f; 1.2 docker在企业中的应用场景 1.3 docker与虚拟化的对比 1.4 docker的优势 1.5 容器工作方式 2 部署docker 2.1 配置软件仓库 2.2 docker 安装 2.3 配置docker 镜像加速器 2.4 启动服务 2.5 激活内核网络选项…

ctfhub-web-SSRF通关攻略

一、内网访问 1.打开ctfhub给的环境地址 2.观察题目 发现让我们访问127.0.0.1下的flag.php 在地址栏后面有一个url参数 ?urlhttp://127.0.0.1/flag.php 提交即可 二、伪协议读取文件 1.打开ctfhub给的环境 2.观察题目 发现让我们读取flag.php文件 读取文件用到的协议是…

2024最值得购买的耳机?开放式耳机测评

在2024年&#xff0c;多款开放式耳机在市场上备受关注&#xff0c;它们各具特色&#xff0c;满足了不同消费者的需求。今天甜心根据当前市场情况和用户反馈&#xff0c;为大家推荐几款最值得购买的开放式耳机&#xff1a; 虹觅HOLME Fit2 虹觅HOLME Fit2是一款集颜值、舒适度、…

WireShark网络分析~环境搭建

一、虚拟网络设备搭建 &#xff08;一&#xff09;eNSP介绍 网络由网络设备和计算机构成&#xff0c;eNSP是模拟网络拓扑关系的软件。 &#xff08;二&#xff09;eNSP下载 华为官网&#xff1a;https://forum.huawei.com/enterprise/zh/thread/blog/580934378039689216 &am…

2k1000LA 调试4G

问题&#xff1a; 其实算不上 调试&#xff0c; 之前本来4G是好的&#xff0c;但是 我调试了触摸之后&#xff0c;发现4G用不了了。 其实主要是 pppd 这个命令找不到。 首先来看 为什么 找不到 pppd 这个命令。 再跟目录使用 find 命令&#xff0c;能够找到这个命令&#…

python可视化-密度图

1、加载数据 import pandas as pd import numpy as np from sklearn.datasets import load_iris import warnings# 禁用所有警告信息 warnings.filterwarnings(ignore)# 加载数据 iris load_iris() iris iris.keys() df pd.DataFrame(iris.data, columnsiris.feature_names)…

【JS】localeCompare实现中文排序

如何对两个中文进行字典顺序排序&#xff0c;如’本’拼音首字母’b’&#xff0c;‘初’拼音首字母’c’&#xff0c;所以’本’<‘初’。 JS默认根据编码顺序排序 使用localeCompare即可&#xff0c;如 ‘本’ < ‘初’ 则返回负数 使用方法 referenceStr.localeComp…

HR招聘面试人才测评工具,mbti职业性格测试

MBTI职业性格测试是国际最为流行的职业人格评估工具&#xff0c;作为一种对个性的判断和分析&#xff0c;是一个理论模型&#xff0c;从纷繁复杂的个性特征中&#xff0c;归纳提炼出4个关键要素——动力、信息收集、决策方式、生活方式&#xff0c;进行分析判断&#xff0c;从而…

万邑通信息科技笔试题库:北森测评言语数字图形真题答题要求及真题分享

万邑通&#xff08;上海&#xff09;信息科技股份有限公司是一家提供跨境电商整体供应链解决方案的企业。我们专注于为全球客户提供跨境售后物流服务&#xff0c;通过供应链管理与互联网技术相结合&#xff0c;有效降低库存成本&#xff0c;提升库存周转率和资金回报率。我们的…

【应用开发】解决正点原子I.MX6ull应用编程zlib移植问题

问题描述 在正点原子应用开发移植zlib库的时候&#xff0c;文档中有这样一段描述&#xff0c;先删除开发板中的zlib库&#xff0c;然后再拷贝zlib库 这就会导致在使用scp命令拷贝编译好的zlib库的时候报错没有zlib.so.1&#xff0c;如下图所示&#xff1a; 解决方法 千万不…

如何使用ssm实现计算机科学与技术学习网站的设计与开发

TOC ssm248计算机科学与技术学习网站的设计与开发jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&…

基于layui实现简单的万智牌生命计数器页面

对照手机App“旅法师营地”的万智牌生命计数器窗口&#xff08;如下图所示&#xff09;&#xff0c;使用layui、jQuery等实现简单的万智牌生命计数器页面。   主要实现的功能如下&#xff1a;   1&#xff09;点击左右两侧的-1、1、-5、5区域更新左右两侧生命值&#xff1…

简过网:公务员考试缺考有什么影响?会影响下一次报名吗?

每年报名公务员考试的人有很多&#xff0c;但是弃考的人也有不少&#xff0c;比如发现个临时突发情况参加不了才公&#xff0c;那么&#xff0c;公务员考试缺考有什么影响&#xff1f;会影响下一次报名吗&#xff1f; 答案是不会…… 如果在笔试阶段&#xff0c;如果考生选择缺…

基于Thymeleaf、bootstrap、layUI 混合前端应用

目录 1、项目结构 2、pom依赖导入 3、页面加载机制 4、前端案例 4.1、加载公共页面及静态文件 4.2、Bootstrap col-md栅栏 4.3、BootStrap Table表格加载 4.4、Layui select下拉项加载 4.5、Layui radio 单选项加载 4.6、Ajax Post请求 以下代码基于Spring boot Thy…

STM32外部中断事件控制器-EXTI

EXTI简介&#xff1a; EXTI 是 External Interrupt 的缩写&#xff0c;表示外部中断事件控制器。EXTI 可以监测指定 GPIO 口的电平信号变化&#xff0c;并在检测到指定条件时&#xff0c;向内核的中断控制器 NVIC 发出中断申请。NVIC 在裁决后&#xff0c;如果满足条件&#xf…

峟思振弦式钢筋计在恶劣环境下的工作性能分析

在土木工程领域&#xff0c;尤其是建筑、桥梁、隧道等复杂结构的监测中&#xff0c;振弦式钢筋计作为一种高精度、高稳定性的测量仪器&#xff0c;扮演着至关重要的角色。尤其在恶劣环境下&#xff0c;其卓越的工作性能更是备受关注。本文将从耐久性、实时监控、安全性评估及适…

Vue 3 + GSAP:解锁高性能动画的神奇组合

在一个偶然的场景使用了 GSAP&#xff08;GreenSock Animation Platform&#xff09;&#xff0c;感觉挺好玩的&#xff0c;在此浅浅记录一下。 GSAP 是一个功能强大的 JS 动画库&#xff0c;常用于创建高性能、流畅的动画。它不仅支持基本的动画&#xff0c;还提供了时间轴控…

SpringBoot日常:集成代码覆盖率测试工具JaCoCo

文章目录 简介开始集成1、pom添加依赖2、pom添加插件3、业务代码4、单元测试代码5、开始测试6、查看结果 如何排除不需要的路径&#xff1f;设置覆盖率目标并验证JACOCO的不足JACOCO改进版super-Jacoco代码覆盖率的知识扩展 简介 JaCoCo&#xff08;Java Code Coverage&#x…

一把手告诉你联盟营销白帽电商广告借力NewsBreak头条优势

揭秘美国NewsBreak头条新闻推广联盟营销白帽电商广告优势 在当今数字化浪潮中&#xff0c;电商行业竞争日益激烈&#xff0c;各大平台纷纷寻求创新的推广方式以吸引更多商家入驻并提升用户购物体验。NewsBreak&#xff0c;作为美国一款领先的新闻聚合平台&#xff0c;凭借其庞…

静态代理与动态代理的区别与选择

在当今软件开发领域&#xff0c;代理模式作为一种重要的设计模式&#xff0c;广泛应用于增强现有对象功能、控制访问权限以及实现远程调用等场景。本文旨在深入探讨静态代理与动态代理之间的核心区别&#xff0c;帮助开发者理解两者在实现机制、灵活性、性能表现及适用场景上的…