ANR实战案例3 - 应用在部分低端机ANR优化案例

news2024/11/28 9:36:51

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用


文章目录

  • 系列文章目录
  • 前言
  • 一、Blocked状态
    • 1.案例一
    • 2.案例二
    • 3.案例三
  • 二、高低端机区分
    • 1.WebView预加载
  • 三、磁盘IO耗时
    • 1.外置存储卡路径复用
    • 2.SD卡路径复用
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、Blocked状态

1.案例一

该案例为应用内主子线程锁抢占,由于应用内主线程下载View展示与子线程中下载缓存刷新锁抢占,部分低端机上系统资源不足,从而产生的ANR。
在这里插入图片描述

查看trace文件中主线程堆栈可知,主线程中实例对象被237线程所持有,继而查看237线程堆栈如下:
在这里插入图片描述
通过堆栈中transact调用可知,系统资源不足。分析系统相关类调用,可以发现主线程和thread-237同时调用了ContextImp中方法。
主线程中getTheme调用
在这里插入图片描述
线程237中ensureExternalDirsExistOrFilter调用:
在这里插入图片描述
ensureExternalDirsExistOrFilter调用:
在这里插入图片描述
咨询了框架组的同事,得到如下回复

1.调用getPhoneStorageState()和getExternalStorageState()等接口时,
此接口存在I0耗时且容易卡在PKMS\MountService binder返回
2.手机硬件老化时,IO耗时变长,调用此类接口有很大的几率会被卡住
3.插入损坏的SD卡时,调用此类接口有很大的几率会被卡住

由上可知ContextImp中ensureExternalDirsExistOrFilter和getTheme都锁住了同一个对象mSync,追踪原来是mSync这把锁锁住主线程,因此本案例的最终解决方案是,减少子线程237中系统类的交互,继而减少耗时等待。通过FileCache工具类初始化一个变量存储FileUtils.getStoragePath文件路径,减少doRefreshVideoCache时交互的频率,减小卡住的机率,详细可参考:

	private static List<String> mSdcardPaths = null;
	/**
     * SD卡路径获取
     * @return SdcardPath列表
     */
    public static List<String> getSdcardPaths(){
        if (mSdcardPaths == null){
            mSdcardPaths = FileUtils.getStoragePath(XxxApplication.getApplication());
        }
        return mSdcardPaths;
    }

解决思路:采取空间换时间的原则。

2.案例二

该案例为应用启动阶段主线程与FacebookSDK锁抢占,比案例一相对复杂。
可借鉴思路:从问题源头逐一追踪阻塞主线程锁的持有链。
在这里插入图片描述
由堆栈可知,主线程与线程39之间产生了资源等待,继而查看thread-39堆栈:
在这里插入图片描述

由关键字held by可知,线程39与主线程资源等待的原因为,同时都在使用Shareprefrence,线程39通过getPreferencesDir方法获取sp路径时候产生了等待。同时可以看到,线程39与线程38产生了线程等待,继而查看thread-38堆栈:
在这里插入图片描述
这里可能会有个疑问,线程38中获取文件路径为什么会跟线程39中Sharepreference获取产生资源竞争呢?如果你熟悉sp就会马上反应过来,sp的本质也是一个xml文件。不熟悉也没关系,我们接着观察分析,会发现thread-38和thread-39最终都调用了ContextImp中方法,继而查看:
thread-38中ContextImpl.getExternalFilesDirs调用
在这里插入图片描述
内部ensureExternalDirsExistOrFilter调用:
在这里插入图片描述

ensureExternalDirsExistOrFilter方法分析可参考案例一。

thread-39中ContextImpl.getPreferencesDir调用
在这里插入图片描述
由上可知,thread-38中ContextImpl.getExternalFilesDirs方法,以及thread-39中ContextImpl.getPreferencesDir方法,都锁住了同一个对象mSync。接下来再去分析具体业务堆栈,thread-38中详细调用:
在这里插入图片描述
由于保密原则,只能贴出大概的调用,后续的追踪为:通过此处可以定位到thread-38中,我们应用通过RxJava开启了一个子线程进行相关的初始化,而这个调用的时机是自定义的一个ContentProvider的onCreate阶段。而主线程中FacebookSDK的初始化,SDK内部也是通过一个Provider进行初始化,二者在同一时间段进行,故而造成了thread-38中Rx子线程抢占了资源,从而导致主线程中FacebookSDK的初始化阻塞。最终解决方式是,将二者的初始化时机进行排序,错开调度。当然,也可以将thread-38中的初始化时机放到应用Application的onCreate阶段,经测试也是ok的。原则就是将二者调度时机避开,避免资源抢夺。
总结,该问题相对复杂,解决的思路一是依赖trace.txt文件的分析经验,找出阻塞点,其次是业务代码的追踪和SDK内部逻辑的熟悉。

3.案例三

可借鉴思路:涉及三方库沟通,单一日志无法明确问题时,可同一问题的多个场景整理综合分析,得出最终结论。
在这里插入图片描述
由主线程中关键字held by后面信息可知,主线程被thread-27所阻塞,继而查看thread-27堆栈如下:
在这里插入图片描述
比较二者的调用可知,thread-27和主线程同时调用了ContextImp中方法,继而查看:
thread-27中ContextImpl.getExternalFilesDirs调用:
在这里插入图片描述
内部ensureExternalDirsExistOrFilter调用:
在这里插入图片描述
ensureExternalDirsExistOrFilter方法分析可参考案例一。

main-thread中ContextImpl.getTheme调用:
在这里插入图片描述
分析:thread-27中ContextImpl.getExternalFilesDirs与main-thread中ContextImpl.getTheme方法,竞争的是同一把锁mSync,主线程获取不到锁从而阻塞。由于thread-27中为应用接入的xx广告SDK,故而将同一类场景进行归纳进行了分析,
场景二:
在这里插入图片描述
在这里插入图片描述
场景二同样为xx广告与业务动画调用时ContextImpl.getTheme资源争夺,导致的主线程阻塞。
场景三:
在这里插入图片描述
在这里插入图片描述
场景三为底部播放栏设置Drable时,调用ContextImpl.getTheme资源争夺,导致的主线程阻塞。

由上述场景综合分析,基本可以确定此处是,xx广告SDK内部的问题,SDK内部代码追踪发现StorageUtils.getOfflineCacheDirectory调用较为频繁,最终与SDK厂商进行沟通反馈解决该问题。

二、高低端机区分

1.WebView预加载

main (native):tid=1 systid=1184 
#00 pc 0x65cb78 TrichromeLibrary.apk + 50409472
       at J.N.M1Y_XVCN(N.java)
       at org.chromium.content.browser.BrowserStartupControllerImpl.a(BrowserStartupControllerImpl.java:2)
       at org.chromium.content.browser.BrowserStartupControllerImpl.g(BrowserStartupControllerImpl.java:8)
       at y8.run(y8.java:23)
       at org.chromium.base.ThreadUtils.f(ThreadUtils.java:2)
       at mA0.h(mA0.java:41)
       at mA0.b(mA0.java:20)
       at mA0.j(mA0.java:2)
       at com.android.webview.chromium.WebViewChromiumFactoryProvider.g(WebViewChromiumFactoryProvider.java:2)
       at com.android.webview.chromium.WebViewChromium.init(WebViewChromium.java:14)
       at android.webkit.WebView.<init>(WebView.java:435)
       at android.webkit.WebView.<init>(WebView.java:355)
       at android.webkit.WebView.<init>(WebView.java:337)
       at android.webkit.WebView.<init>(WebView.java:324)
       at android.webkit.WebView.<init>(WebView.java:314)
       at com.xxx.ui.mall.view.BPWebView.<init>(BPWebView.java:100)
       at com.xxx.ui.main.MainActivity$4.queueIdle(MainActivity.java:595)
       at android.os.MessageQueue.next(MessageQueue.java:404)
       at android.os.Looper.loop(Looper.java:183)
       at android.app.ActivityThread.main(ActivityThread.java:7740)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:612)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:997)

日志分析定位到MainActivity$4.queueIdle,而该方法只是进行了自定义BPWebView的预加载,BPWebView只是简单继承了WebView,并无过多额外操作,最终通过区分高低端机,低端机不进行WebView预加载,解决该问题。

MainActivity$4.queueIdle

        Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
            @Override
            public boolean queueIdle() {
                Looper.myQueue().removeIdleHandler(this);
                long[] m = PhoneLevelUtil.getDeviceMemory();
                if (m[1] * 100 / m[0] > 15) {
                    //BPWebView wb = new BPWebView(MusicApplication.getInstance());
                    String gameCenterUrl = DynamicIconUtil.getUrl();
                    if (!TextUtils.isEmpty(gameCenterUrl)) {
                        BPWebView wbGame = new BPWebView(MusicApplication.getInstance());
                        wbGame.setOnPageFinished(() -> {
                            wbGame.freeMemory();
                        });
                        wbGame.loadUrl(gameCenterUrl);
                    }

                    String subHomeLink;
                    if (TextUtils.isEmpty(SubscribePageUtil.subHomeLinkByService)) {
                        subHomeLink = ApiUrl.H5_BASE_URL + SubscribePageUtil.SUBSCRIBE_PAGE_URL;
                    } else {
                        subHomeLink = ApiUrl.H5_BASE_URL + SubscribePageUtil.subHomeLinkByService;
                    }
                    BPWebView wbSub = new BPWebView(MusicApplication.getInstance());
                    wbSub.setOnPageFinished(() -> {
                        wbSub.freeMemory();
                    });
                    wbSub.loadUrl(subHomeLink);
                }
                return false;
            }
        });

修改:

if (!GlobalVariate.isHighDevice) {
            return;
        }//低端机预加载,系统WebView产生的ANR较多

三、磁盘IO耗时

1.外置存储卡路径复用

main (blocked):tid=1 systid=17151 | waiting to lock <0x05dfb3bd> (java.lang.Object) held by thread 46
       at android.app.ContextImpl.getExternalCacheDirs(ContextImpl.java:836)
       at android.content.ContextWrapper.getExternalCacheDirs(ContextWrapper.java:311)
       at androidx.core.content.ContextCompat$Api19Impl.getExternalCacheDirs(ContextCompat.java:840)
       at androidx.core.content.ContextCompat.getExternalCacheDirs(ContextCompat.java:459)
       at androidx.core.content.FileProvider.parsePathStrategy(FileProvider.java:696)
       at androidx.core.content.FileProvider.getPathStrategy(FileProvider.java:635)
       at androidx.core.content.FileProvider.attachInfo(FileProvider.java:416)
       at android.app.ActivityThread.installProvider(ActivityThread.java:7673)
       at android.app.ActivityThread.installContentProviders(ActivityThread.java:7209)
       at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7121)
       at android.app.ActivityThread.access$1600(ActivityThread.java:268)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2056)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:268)
       at android.app.ActivityThread.main(ActivityThread.java:8107)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:627)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:997)
	   
	   
RxCachedThreadScheduler-4 (native):tid=46 systid=17212 
       at libcore.io.Linux.access(Linux.java)
       at libcore.io.ForwardingOs.access(ForwardingOs.java:72)
       at libcore.io.BlockGuardOs.access(BlockGuardOs.java:73)
       at libcore.io.ForwardingOs.access(ForwardingOs.java:72)
       at android.app.ActivityThread$AndroidOs.access(ActivityThread.java:7982)
       at java.io.UnixFileSystem.checkAccess(UnixFileSystem.java:281)
       at java.io.File.exists(File.java:815)
       at android.app.ContextImpl.ensureExternalDirsExistOrFilter(ContextImpl.java:3022)
       at android.app.ContextImpl.getExternalFilesDirs(ContextImpl.java:779)
       at android.app.ContextImpl.getExternalFilesDir(ContextImpl.java:768)
       at android.content.ContextWrapper.getExternalFilesDir(ContextWrapper.java:276)
       at com.xxx.storage.cache.ScopeStorageUtils.getBPRootDir(ScopeStorageUtils.java:121)
       at com.xxx.storage.cache.ScopeStorageUtils.getBPRootPath(ScopeStorageUtils.java:134)
       at com.xxx.storage.cache.ScopeStorageUtils.shouldUseScopeStorage(ScopeStorageUtils.java:49)
       at com.xxx.util.PhoneDeviceUtil.getMusicLocalDir(PhoneDeviceUtil.java:881)
       at com.xxx.util.PhoneDeviceUtil.getCacheDirPath(PhoneDeviceUtil.java:914)
       at com.xxx.biz.xxx.db.FilexxxerDBHelper.<init>(FilexxxerDBHelper.java:43)
       at com.xxx.biz.xxx.db.FilexxxerDB.initDBAndCheckUpdate(FilexxxerDB.java:49)
       at com.xxx.biz.xxx.db.FilexxxerDB.<init>(FilexxxerDB.java:44)
       at com.xxx.biz.xxx.db.FilexxxerDB.<init>(FilexxxerDB.java:38)
       at com.xxx.biz.xxx.db.FilexxxerDB$Holder.<clinit>(FilexxxerDB.java:60)
       at com.xxx.biz.xxx.db.FilexxxerDB.getInstance(FilexxxerDB.java:65)
       at com.xxx.storage.cache.PlaylistDB.getDB(PlaylistDB.java:57)
       at com.xxx.storage.cache.PlaylistDB.query(PlaylistDB.java:349)
       at com.xxx.storage.cache.HistoryPlaylistCache.queryDB(HistoryPlaylistCache.java:192)
       at com.xxx.storage.cache.HistoryPlaylistCache.<init>(HistoryPlaylistCache.java:39)
       at com.xxx.storage.cache.ItemCache.init(ItemCache.java:151)
       at com.xxx.common.base.XxApplicationInitor.init1(XxApplicationInitor.java:285)
       at com.xxx.common.base.XxApplicationInitor.access$500(XxApplicationInitor.java:115)
       at com.xxx.common.base.XxApplicationInitor$4.subscribe(XxApplicationInitor.java:177)
       at io.reactivex.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.java:40)
       at io.reactivex.Observable.subscribe(Observable.java:12284)
       at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
       at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:578)
       at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
       at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
       at java.util.concurrent.FutureTask.run(FutureTask.java:266)
       at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
       at java.lang.Thread.run(Thread.java:923)	

ScopeStorageUtils.getBPRootDir

    public static File getBPRootDir() {
        File externalFilesDir = MusicApplication.getInstance().getExternalFilesDir(null);
        if (externalFilesDir == null) {
            // Android4.4以下,externalFilesDir返回null,直接自己创建目录
            externalFilesDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
                    + File.separator + "Android"
                    + File.separator + "data"
                    + File.separator + MusicApplication.getInstance().getPackageName()
                    + File.separator + "files");
        }
        return externalFilesDir;
    }

由于手机硬件老化,主要耗时点为getExternalFilesDir,故解决方法为缓存getExternalFilesDir路径。

FileCache类缓存路径

private static File mExternalFilesDir = null;

/**
 * 外置的存储卡获取
 * @return 外置存储卡File
 */
public static File getExternalFilesDir(){
    if (mExternalFilesDir == null){
        mExternalFilesDir = XxApplication.getInstance().getExternalFilesDir("");
    }
    return mExternalFilesDir;
}

修改后ScopeStorageUtils.getBPRootDir调用:

    public static File getBPRootDir() {
        File externalFilesDir = FileCache.getExternalFilesDir();
        ...
        return externalFilesDir;
    }

2.SD卡路径复用

main (blocked):tid=1 systid=545 | waiting to lock <0x08ff64d2> (java.lang.Object) held by thread 237
       at android.app.ContextImpl.getTheme(ContextImpl.java:387)
       at android.content.ContextWrapper.getTheme(ContextWrapper.java:139)
       at android.content.Context.getColor(Context.java:704)
       at androidx.core.content.ContextCompat$Api23Impl.getColor(ContextCompat.java:889)
       at androidx.core.content.ContextCompat.getColor(ContextCompat.java:536)
       at com.xxx.ui.dialog.xxx.DownLoadHintView.setViewColor(DownLoadHintView.java:228)
       at com.xxx.ui.dialog.xxx.DownLoadHintView.loadData(DownLoadHintView.java:361)
       at com.xxx.ui.dialog.xxx.DownLoadHintView.access$100(DownLoadHintView.java:79)
       at com.xxx.ui.dialog.xxx.DownLoadHintView$ShowRunnable.run(DownLoadHintView.java:522)
       at android.os.Handler.handleCallback(Handler.java:938)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loop(Looper.java:223)
       at android.app.ActivityThread.main(ActivityThread.java:7815)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1094)
	   
RxCachedThreadScheduler-49 (native):tid=237 systid=13518 
#00 pc 0x9ae90 libc.so (__ioctl + 8)
#01 pc 0x6932f libc.so (ioctl + 26)
#02 pc 0x39a13 libbinder.so (android::IPCThreadState::talkWithDriver(bool) + 238)
#03 pc 0x3a655 libbinder.so (android::IPCThreadState::waitForResponse(android::Parcel*, int*) + 32)
#04 pc 0x3a42b libbinder.so (android::IPCThreadState::transact(int, unsigned int, android::Parcel const&, android::Parcel*, unsigned int) + 122)
#05 pc 0x35267 libbinder.so (android::BpBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int) + 98)
#06 pc 0xc73ff libandroid_runtime.so (android_os_BinderProxy_transact(_JNIEnv*, _jobject*, int, _jobject*, _jobject*, int) + 82)
       at android.os.BinderProxy.transactNative(BinderProxy.java)
       at android.os.BinderProxy.transact(BinderProxy.java:540)
       at android.os.storage.IStorageManager$Stub$Proxy.mkdirs(IStorageManager.java:2695)
       at android.os.storage.StorageManager.mkdirs(StorageManager.java:1405)
       at android.app.ContextImpl.ensureExternalDirsExistOrFilter(ContextImpl.java:2895)
       at android.app.ContextImpl.getExternalFilesDirs(ContextImpl.java:771)
       at android.content.ContextWrapper.getExternalFilesDirs(ContextWrapper.java:278)
       at com.xxx.util.FileUtils.getScopeStoragePath(FileUtils.java:107)
       at com.xxx.util.FileUtils.getStoragePath(FileUtils.java:96)
       at com.xxx.biz.download.utils.LocalMediaCache.doRefreshVideoCache(LocalMediaCache.java:1065)
       at com.xxx.biz.download.utils.LocalMediaCache.doScanLocalVideo(LocalMediaCache.java:1155)
       at com.xxx.biz.download.utils.LocalMediaScaner$1.onNext(LocalMediaScaner.java:40)
       at com.xxx.biz.download.utils.LocalMediaScaner$1.onNext(LocalMediaScaner.java:28)
       at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:201)
       at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:255)
       at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
       at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
       at java.util.concurrent.FutureTask.run(FutureTask.java:266)
       at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
       at java.lang.Thread.run(Thread.java:923)

FileUtils.getScopeStoragePath

List<String> getScopeStoragePath(Context mContext, boolean... isRemoveAble) {
    List<String> allSdcardPath = new ArrayList<>();
    try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            File[] externalFilesDirs = mContext.getExternalFilesDirs("");
            ...
        } else {
            File externalFilesDir = mContext.getExternalFilesDir("");
            ...
        }
    } catch (Exception e) {
        Log.e("FileUtils", "getScopeStoragePath: ", e);
    }
    return allSdcardPath;
}

由于手机硬件老化,主要耗时点为getExternalFilesDirs,故解决方法为缓存getExternalFilesDirs路径。

FileCache类缓存路径

private static List<String> mSdcardPaths = null;

/**
 * SD卡路径获取
 * @return SdcardPath列表
 */
public static List<String> getSdcardPaths(){
    if (mSdcardPaths == null){
        mSdcardPaths = XxApplication.getApplication().getExternalFilesDirs("");
    }
    return mSdcardPaths;
}

修改后FileUtils.getScopeStoragePath

List<String> getScopeStoragePath(Context mContext, boolean... isRemoveAble) {
    List<String> allSdcardPath = new ArrayList<>();
    try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            File[] externalFilesDirs = FileCache.getSdcardPaths();
            ...
        } else {
            File externalFilesDir = FileCache.getExternalFilesDir();
            ...
        }
    } catch (Exception e) {
        Log.e("FileUtils", "getScopeStoragePath: ", e);
    }
    return allSdcardPath;
}

总结

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

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

相关文章

Flink基础介绍-2 架构

Flink基础介绍-1 概述 二、Flink架构2.1 Flink的设计架构2.2 Flink的运行架构2.3 Flink的系统架构 二、Flink架构 2.1 Flink的设计架构 Flink是一个分层的架构系统&#xff0c;每一层所包含的组件都提供了特定的抽象&#xff0c;用来服务于上层组件&#xff0c;Flink的分层体…

搬家货运系统软件开发功能

生活中要用到搬家的场景很多&#xff0c;租房子、买房子、换房子都要搬家&#xff0c;不管是从时间还是专业性上来说&#xff0c;很多人都更愿意找专业的搬家公司来代为处理&#xff0c;于是市面上出现了各类各样的货运搬家软件&#xff0c;让有需求的用户可以直接在线预约搬家…

Java实现网上人才招聘系统【附源码】

网上人才招聘系统 1、概述 3 2、系统分析 4 2.1、问题定义 4 2.2、可行性研究 4 2.2.1、可行性需求分析 4 2.2.2、数据流分析 5 2.2.3、数据字典 6 2.2.4、程序流程图 6 2.2.4、开发进度计划 6 2.3、需求分析 7 2.3.1、功能需求分析 7 2.3.2、数据需求分析 10 2.3.3、性能需求…

Dozer拷贝DO到VO

DO&#xff1a; data object 查出来的数据 VO&#xff1a; value object 要展示的数据&#xff0c;返回给前端的数据 DO查出来的字段比较多&#xff0c;VO过滤一下返回给前端 工具类Dozer <dependency><groupId>com.github.dozermapper</groupId><arti…

Vue3 router路由跳转传参Json数据,隐藏参数Url不显示跳转内容,使用History 来写,setup语法糖的写法

前言&#xff1a;Vue3中 Router的自2022-8-22 日后不能使用 params传参 —— 可以这么写&#xff0c;但是接收的时候会是空值 —— query可以用但不能用隐藏Url地址内的值&#xff0c;所以我们用History来写 history敲不出来的可以看最下面 这个相当于history模式父级调到子集后…

Linux文本三剑客之~~~awk 详细讲解 与date粗略讲解

目录 一. awk工作原理&#xff1a;1.1命令格式:1.2 awk常见的内建变量(可直接用)如下所示: 二 实验示例2.1 按行输出文本2.2按字段输出文本2.3 通过管道符双引号调用shell命令 date命令 一. awk工作原理&#xff1a; 逐行读取文本&#xff0c;默认以空格或tab键为分隔符进行分…

软件测试月薪2万,需要技术达到什么水平?

最近跟朋友在一起聚会的时候&#xff0c;提了一个问题&#xff0c;说一个软件测试工程师如何能月薪达到二万&#xff0c;技术水平需要达到什么程度&#xff1f;人回答说这只能是大企业或者互联网企业工程师才能拿到。也许是的&#xff0c;小公司或者非互联网企业拿二万的不太可…

网络性能监控(NPM)工具

网络是每个企业的支柱。即使在小型或企业级企业中&#xff0c;网络中断期间的生产力损失也可能导致巨大的损失。网络监控解决方案可帮助您预测潜在的中断并主动解决网络问题。这有助于维护无拥塞网络&#xff0c;使您的业务保持正常运行。这可以在网络监控工具的帮助下实现。网…

spring boot 完整后端接口案例

第一章 会员管理项目父模块搭建 1.1 创建模块mengxuegu-member mengxuegu-member 作为所有工程的父工程&#xff0c;用于管理项目的所有依赖。 1.2 添加pom依赖&#xff0c;pom.xml 文件位于&#xff1a;会员管理系统/03-配套资料/pom文件/member-pom.xml <?xml version&…

自建minio实现doris的快速备份与恢复

一.概述 doris支持通过腾讯云bos、阿里云oss、hdfs实现备份与恢复&#xff0c;但是我们公司doris部署在线下机房&#xff0c;如采用oss/bos, 大数据备份与恢复比较慢&#xff0c;会占用一定的带宽&#xff0c;如采用hdfs&#xff0c;担心小文件太多影响现有的hadoop集群。为了保…

C# Forecast 预测数据方法,MathNet.Numerics学习分享

Forecast 数据预测 /// <summary>/// 数据预测/// </summary>/// <param name"xValues">数据序列</param>/// <param name"yValues">数据值</param>/// <param name"forecastPoint">第N个预测序列<…

腾讯面试经验,岗位是C++后端

分享一篇腾讯面经&#xff0c;岗位是C后端&#xff0c;考察的内容是C、Redis、网络。 c shared_ptr的原理 答&#xff1a;内部的共享数据和引用计数实现 补充&#xff1a; shared_ptr多个指针指向相同的对象。shared_ptr使用引用计数&#xff0c;每一个shared_ptr的拷贝都…

【瑞萨RA系列FSP库开发】RASC+Keil的环境搭建

文章目录 一、获取资源包二、安装 Keil 软件、RA 芯片包和 RASC三、RASC 集成到 Keil四、使用 RASC 生成 Keil 工程五、通过 Keil 打开 RASC 的 FSP 配置器界面六、配置和编译 Keil 工程七、使用调试器下载程序 本节将介绍如何在Keil上开发瑞萨RA MCU &#xff08;如需了解 e2 …

元宇宙虚拟展馆和VR的关系与区别

元宇宙是指一个数字化的虚拟世界&#xff0c;这个世界由虚拟现实、增强现实等多种技术构建而成。这个世界中&#xff0c;人们可以用虚拟身份在其中自由穿梭&#xff0c;享受到与现实世界不同的多样化和极致体验。虚拟现实&#xff08;VR&#xff09;则是一种技术手段&#xff0…

一文吃透3类CSS复合选择器【案例演示】

书写CSS样式表时&#xff0c;可以使用CSS基础选择器选中HTML元素。但是在实际网站开发中&#xff0c;一个网页可能包含成千上万的HTML元素&#xff0c;如果仅使用CSS基础选择器是远远不够的。为此&#xff0c;CSS提供了儿种复合选择器&#xff0c;实现了更强、更方便的选择功能…

云原生技术的个人总结

文章目录 云原生概念Kubernetes容器和微服务k8s 服务网格边缘计算云边端 云原生概念 重点在以下三个方面 应用容器化面向微服务架构应用支持容器的编排调度 介绍&#xff1a; 这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段&#xff0c;…

公网远程访问公司管家婆ERP进销存管理系统 - 无需公网IP

文章目录 1.管家婆服务2. 内网穿透2.1 安装cpolar内网穿透2.2 设置远程访问 3. 固定访问地址4. 配置固定公网访问地址 管家婆辉煌系列产品是中小企业进销存、财务管理一体化的典范软件&#xff0c;历经十余年市场的洗礼&#xff0c;深受广大中小企业的欢迎&#xff0c;在国内及…

jQuery为什么还在发布新版本?

是的&#xff0c;你没有看错&#xff0c;jQuery发布了3.7.0&#xff0c;这个曾经告诉大家write less do more的JS库&#xff0c;这个已经被很多人遗忘的前端必备&#xff0c;曾经有本书叫《锋利的jQuery》。但现在谁还在每天做着jQuery的工作&#xff0c;谁还熟练jQuery的写法&…

linux环境安装使用tomcat详解

01-安装Tomcat # 0.下载tomcat http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.46/bin/apache-tomcat-8.5.46.tar.gz # 1.通过工具上传到Linux系统中 # 2.解压缩到/usr目录中 [rootlocalhost ~]# tar -zxvf apache-tomcat-8.5.46.tar.gz -C /usr/ -C 用来指…

高创伺服电机飞车问题

高创伺服电机飞车问题 兜兜转转又回到了伺服电机上&#xff0c;此次遇到的伺服电机飞车问题困扰了我有好长一段时间。在飞车发生时&#xff0c;除了电机飞速转动使设备抖动带来的物理冲击&#xff0c;也有身边其他人你一言我一语的情绪冲击。希望这个办法可以解决你的飞车问题…