卡顿分析与布局优化
卡顿分析与布局优化
大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能。Android系统每隔大概16.6ms发出VSYNC信 号,触发对UI进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需要的60fps,为了能够实现60fps, 这意味着程序的大多数操作都必须在16ms内完成。
我们通常都会提到60fps与16ms,可是知道为何会是以程序是否达到60fps来作为App性能的衡量标准吗?这 是因为人眼与大脑之间的协作无法感知超过60fps的画面更新。
12fps大概类似手动快速翻动书籍的帧率,这明显是可以感知到不够顺滑的。24fps使得人眼感知的是连续线 性的运动,这其实是归功于运动模糊的效果。24fps是电影胶圈通常使用的帧率,因为这个帧率已经足够支撑 大部分电影画面需要表达的内容,同时能够最大的减少费用支出。但是低于30fps是无法顺畅表现绚丽的画面 内容的,此时就需要用到60fps来达到想要的效果,当然超过60fps是没有必要的。
开发app的性能目标就是保持60fps,这意味着每一帧你只有16ms=1000/60的时间来处理所有的任务。
如果某个操作花费时间是24ms,系统在得到VSYNC信号的时候就无法进行正常渲染,这样就发生了丢帧现象。那 么用户在32ms内看到的会是同一帧画面。
有很多原因可以导致丢帧, 一般主线程过多的UI绘制、大量的IO操作或是大量的计算操作占用CPU,都会导致App 界面卡顿。
一般主线程过多的UI绘制、大量的IO操作或是大量的计算操作占用CPU,导致App界面卡顿。 建议阅读:Android图形显示系统
卡顿分析
Systrace
Systrace 是Android平台提供的一款工具,用于记录短期内的设备活动。该工具会生成一份报告,其中汇总了Android 内核中的数据,例如 CPU 调度程序、磁盘活动和应用线程。Systrace主要用来分析绘制性能方面的问 题。在发生卡顿时,通过这份报告可以知道当前整个系统所处的状态,从而帮助开发者更直观的分析系统瓶颈,改进性能。
TraceView可以看出代码在运行时的一些具体信息,方法调用时长,次数,时间比率,了解代码运行过程的 效率问题,从而针对性改善代码。所以对于可能导致卡顿的耗时方法也可以通过TraceView检测。
要使用Systrace,需要先安装 Python2.7。安装完成后配置环境变量path,
随后在命令行输入: python --version进行验证
Systrace具体使用可以查看:https://www.jianshu.com/p/e73768e66b8d
执行systrace可以选择配置自己感兴趣的category,常用的有:
其实Systrace对于应用开发者来说,能看的并不多。主要用于看是否丢帧,与丢帧时系统以及我们应用大致的一个 状态。
python systrace.py -t 5 -o F:\Vance\optimizer\lsn2_jank\a.html gfx
input view am dalvik sched wm disk res -a com.enjoy.example
我们在抓取systrace文件的时候,切记不要抓取太长时间,也不要太多不同操作。
打开抓取的html文件,可以看到我们应用存在非常严重的掉帧,不借助工具直接用肉眼看应用UI是看不出来的。如 果只是单独存在一个红色或者黄色的都是没关系的。关键是连续的红/黄色或者两帧间隔非常大那就需要我们去仔 细观察。按*“W”* 放大视图,在UIThread(主线程)上面有一条很细的线,表示线程状态。
Systrace 会用不同的颜色来标识不同的线程状态, 在每个方法上面都会有对应的线程状态来标识目前线程所处的状态。通过查看线程状态我们可以知道目前的瓶颈是什么, 是 CPU 执行慢还是因为 Binder 调用, 又或是进行 IO 操作, 又或是拿不到 CPU 时间片。
线程状态主要有下面几个:
绿色:表示正在运行;
-
是否频率不够?(CPU处理速度)
-
是否跑在了小核上?(不可控,但实际上很多手机都会有游戏模式,如果我们应用是手游,那系统会优 先把手游中的任务放到大核上跑。)
蓝色:表示可以运行,但是CPU在执行其他线程;
- 是否后台有太多的任务在跑?Runnable 状态的线程状态持续时间越长,则表示 cpu 的调度越忙,没有及时处理到这个任务
- 没有及时处理是因为频率太低
紫色:表示休眠,一般表示IO;
官方介绍为:
橙色:不可中断的休眠
线程在遇到 I/O 操作时被阻止或正在等待磁盘操作完成。
紫色:可中断的休眠
线程在遇到另一项内核操作(通常是内存管理)时被阻止。
但是实际从Android 9模拟器中拉取数据,遇到IO显示紫色,没有橙色状态显示。
白色:表示休眠,可能是因为线程在互斥锁上被阻塞 ,如Binder堵塞/Sleep/Wait等
Trace API
其实对于APP开发而言,使用systrace的帮助并不算非常大,大部分内容用于设备真机优化之类的系统开发人员观 察。systrace无法帮助应用开发者定位到准确的错误代码位置,我们需要凭借很多零碎的知识点与经验来猜测问题 原因。如果我们有了大概怀疑的具体的代码块或者有想了解的代码块执行时系统的状态,还可以结合 Trace API 打标签。
Android 提供了Trace API能够帮助我们记录收集自己应用中的一些信息 :Trace.beginSection() 与
Trace.endSection()
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TraceCompat.beginSection("vance_launcher"); //Trace.beginSection()
setContentView(R.layout.activity_main);
TraceCompat.endSection(); //Trace.endSection()
}
}
Wall Duration: 执行耗时
CPU Durtation: 占用CPU耗时
如果掌握了更精准的问题在哪个阶段,可以结合TraceView查看更详细的方法情况。
App层面监控卡顿
systrace可以让我们了解应用所处的状态,了解应用因为什么原因导致的。若需要准确分析卡顿发生在什么函数, 资源占用情况如何,目前业界两种主流有效的app监控方式如下:
1、 利用UI线程的Looper打印的日志匹配;
2、 使用Choreographer.FrameCallback
Looper日志检测卡顿
Android主线程更新UI。如果界面1秒钟刷新少于60次,即FPS小于60,用户就会产生卡顿感觉。简单来说, Android使用消息机制进行UI更新,UI线程有个Looper,在其loop方法中会不断取出message,调用其绑定的Handler在UI线程执行。如果在handler的dispatchMesaage方法里有耗时操作,就会发生卡顿。
public static void loop() {
//......
for (;;) {
//......
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
//......
}
}
只要检测 msg.target.dispatchMessage(msg) 的执行时间,就能检测到部分UI线程是否有耗时的操作。注意到这行
执行代码的前后,有两个logging.println函数,如果设置了logging,会分别打印出>>>>> Dispatching to和
<<<<< Finished to 这样的日志,这样我们就可以通过两次log的时间差值,来计算dispatchMessage的执行时
间,从而设置阈值判断是否发生了卡顿。
public final class Looper {
private Printer mLogging;
public void setMessageLogging(@Nullable Printer printer) {
mLogging = printer;
}
}
public interface Printer {
void println(String x);
}
Looper 提供了 setMessageLogging(@Nullable Printer printer) 方法,所以我们可以自己实现一个Printer,在
通过setMessageLogging()方法传入即可:
public class BlockCanary {
public static void install() {
LogMonitor logMonitor = new LogMonitor();
Looper.getMainLooper().setMessageLogging(logMonitor);
}
}
public class LogMonitor implements Printer {
private StackSampler mStackSampler;
private boolean mPrintingStarted = false;
private long mStartTimestamp;
// 卡顿阈值
private long mBlockThresholdMillis = 3000;
//采样频率
private long mSampleInterval = 1000;
private Handler mLogHandler;
public LogMonitor() {
mStackSampler = new StackSampler(mSampleInterval);
HandlerThread handlerThread = new HandlerThread("block-canary-io");
handlerThread.start();
mLogHandler = new Handler(handlerThread.getLooper());
}
@Override
public void println(String x) {
//从if到else会执行 dispatchMessage,如果执行耗时超过阈值,输出卡顿信息
if (!mPrintingStarted) {
//记录开始时间
mStartTimestamp = System.currentTimeMillis();
mPrintingStarted = true;
mStackSampler.startDump();
} else {
final long endTime = System.currentTimeMillis();
mPrintingStarted = false;
//出现卡顿
if (isBlock(endTime)) {
notifyBlockEvent(endTime);
}
mStackSampler.stopDump();
}
}
private void notifyBlockEvent(final long endTime) {
mLogHandler.post(new Runnable() {
@Override
public void run() {
//获得卡顿时 主线程堆栈
List<String> stacks = mStackSampler.getStacks(mStartTimestamp, endTime);
for (String stack : stacks) {
Log.e("block-canary", stack);
}
}
});
}
private boolean isBlock(long endTime) {
return endTime - mStartTimestamp > mBlockThresholdMillis;
}
}
public class StackSampler {
public static final String SEPARATOR = "\r\n";
public static final SimpleDateFormat TIME_FORMATTER =
new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
private Handler mHandler;
private Map<Long, String> mStackMap = new LinkedHashMap<>();
private int mMaxCount = 100;
private long mSampleInterval;
//是否需要采样
protected AtomicBoolean mShouldSample = new AtomicBoolean(false);
public StackSampler(long sampleInterval) {
mSampleInterval = sampleInterval;
HandlerThread handlerThread = new HandlerThread("block-canary-sampler");
handlerThread.start();
mHandler = new Handler(handlerThread.getLooper());
}
/**
* 开始采样 执行堆栈
*/
public void startDump() {
//避免重复开始
if (mShouldSample.get()) {
return;
}
mShouldSample.set(true);
mHandler.removeCallbacks(mRunnable);
mHandler.postDelayed(mRunnable, mSampleInterval);
}
public void stopDump() {
if (!mShouldSample.get()) {
return;
}
mShouldSample.set(false);
mHandler.removeCallbacks(mRunnable);
}
public List<String> getStacks(long startTime, long endTime) {
ArrayList<String> result = new ArrayList<>();
synchronized (mStackMap) {
for (Long entryTime : mStackMap.keySet()) {
if (startTime < entryTime && entryTime < endTime) {
result.add(TIME_FORMATTER.format(entryTime)
+ SEPARATOR
+ SEPARATOR
+ mStackMap.get(entryTime));
}
}
}
return result;
}
private Runnable mRunnable = new Runnable() {
@Override
public void run() {
StringBuilder sb = new StringBuilder();
StackTraceElement[] stackTrace = Looper.getMainLooper().getThread().getStackTrace();
for (StackTraceElement s : stackTrace) {
sb.append(s.toString()).append("\n");
}
synchronized (mStackMap) {
//最多保存100条堆栈信息
if (mStackMap.size() == mMaxCount) {
mStackMap.remove(mStackMap.keySet().iterator().next());
}
mStackMap.put(System.currentTimeMillis(), sb.toString());
}
if (mShouldSample.get()) {
mHandler.postDelayed(mRunnable, mSampleInterval);
}
}
};
}
其实这种方式也就是 BlockCanary 原理。
Choreographer.FrameCallback
Android系统每隔16ms发出VSYNC信号,来通知界面进行重绘、渲染,每一次同步的周期约为16.6ms,代表一帧
的刷新频率。通过Choreographer类设置它的FrameCallback函数,当每一帧被渲染时会触发回调
FrameCallback.doFrame (long frameTimeNanos) 函数。frameTimeNanos是底层VSYNC信号到达的时间戳 。
public class ChoreographerHelper {
public static void start() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
long lastFrameTimeNanos = 0;
@Override
public void doFrame(long frameTimeNanos) {
//上次回调时间
if (lastFrameTimeNanos == 0) {
lastFrameTimeNanos = frameTimeNanos;
Choreographer.getInstance().postFrameCallback(this);
return;
}
long diff = (frameTimeNanos - lastFrameTimeNanos) / 1_000_000;
if (diff > 16.6f) {
//掉帧数
int droppedCount = (int) (diff / 16.6);
}
lastFrameTimeNanos = frameTimeNanos;
Choreographer.getInstance().postFrameCallback(this);
}
});
}
}
}
通过 ChoreographerHelper 可以实时计算帧率和掉帧数,实时监测App页面的帧率数据,发现帧率过低,还可以自动保存现场堆栈信息。
Looper比较适合在发布前进行测试或者小范围灰度测试然后定位问题,ChoreographerHelper适合监控线上环境的 app 的掉帧情况来计算 app 在某些场景的流畅度然后有针对性的做性能优化。
布局优化
层级优化
measure、layout、draw这三个过程都包含自顶向下的View Tree遍历耗时,如果视图层级太深自然需要更多的时间来完成整个绘测过程,从而造成启动速度慢、卡顿等问题。而onDraw在频繁刷新时可能多次出发,因此onDraw更不能做耗时操作,同时需要注意内存抖动。对于布局性能的检测,依然可以使用systrace与traceview按 照绘制流程检查绘制耗时函数。
Layout Inspector
在较早的时代SDK中有一个hierarchy viewer 工具,但是早在 Android Studio 3.1 配套的SDK中(具体SDK版本不记得了)就已经被弃用。现在应在运行时改用 Layout Inspector来检查应用的视图层次结构。
然后选择需要查看的进程与Activity:
在左侧id为content之下的就是我们写在XML中的布局。可以明显看出,我们的布局中是一个 LinearLayout ,其中又包含两个 LinearLayout 。我们应该尽量减少其层级,可以使用 ConstraintLayout 约束布局使得布局尽量扁平化,移除非必需的UI组件。
使用merge标签
当我们有一些布局元素需要被多处使用时,这时候我们会考虑将其抽取成一个单独的布局文件。在需要使用的地方通过 include 加载。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:orientation="vertical">
<!-- include layout_merge布局 -->
<include layout="@layout/layout_merge" />
</LinearLayout>
<!-- layout_merge -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:background="#ffffff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试merge" />
</LinearLayout>
这时候我们的主布局文件是垂直的LinearLayout,include的 “layout_merge” 也是垂直的LinearLayout,这时候include的布局中使用的LinearLayout就没意义了,使用的话反而减慢你的UI表现。这时可以使用merge标签优化。
<!-- layout_merge -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:background="#ffffff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试merge" />
</merge>
修改为merge后,通过LayoutInspector能够发现,include的布局中TextView直接被加入到父布局中。
使用ViewStub 标签
当我们布局中存在一个View/ViewGroup,在某个特定时刻才需要他的展示时,可能会有同学把这个元素在xml中 定义为invisible或者gone,在需要显示时再设置为visible可见。比如在登陆时,如果密码错误在密码输入框上显示 提示。
invisible
view设置为invisible时,view在layout布局文件中会占用位置,但是view为不可见,该view还是会创建对
象,会被初始化,会占用资源。gone
view设置gone时,view在layout布局文件中不占用位置,但是该view还是会创建对象,会被初始化,会占 用资源。
如果view不一定会显示,此时可以使用 ViewStub 来包裹此View 以避免不需要显示view但是又需要加载view消耗资源。
viewstub是一个轻量级的view,它不可见,不用占用资源,只有设置viewstub为visible或者调用其inflater()方法 时,其对应的布局文件才会被初始化。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:orientation="vertical">
<ViewStub
android:id="@+id/viewStub"
android:layout_width="600dp"
android:layout_height="500dp"
android:inflatedId="@+id/textView"
android:layout="@layout/layout_viewstub" />
</LinearLayout>
<!-- layout_viewstub -->
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffffff"
android:text="测试viewStub" />
加载viewStub后,可以通过 inflatedId 找到layout_viewstub 中的根View。
过度渲染
过度绘制是指系统在渲染单个帧的过程中多次在屏幕上绘制某一个像素。例如,如果我们有若干界面卡片堆叠在一 起,每张卡片都会遮盖其下面一张卡片的部分内容。但是,系统仍然需要绘制堆叠中的卡片被遮盖的部分。
GPU 过度绘制检查
手机开发者选项中能够显示过度渲染检查功能,通过对界面进行彩色编码来帮我们识别过度绘制。开启步骤如下:
-
进入开发者选项 (Developer Options)。
-
找到调试 GPU 过度绘制(Debug GPU overdraw)。
-
在弹出的对话框中,选择显示过度绘制区域(Show overdraw areas)。
Android 将按如下方式为界面元素着色,以确定过度绘制的次数:
请注意,这些颜色是半透明的,因此您在屏幕上看到的确切颜色取决于界面内容。
有些过度绘制是不可避免的。在优化应用的界面时,应尝试达到大部分显示真彩色或仅有 1 次过度绘制(蓝色)的视觉效果。
解决过度绘制问题
可以采取以下几种策略来减少甚至消除过度绘制:
- 移除布局中不需要的背景。
- 默认情况下,布局没有背景,这表示布局本身不会直接渲染任何内容。但是,当布局具有背景时,其有可能会导致过度绘制。
- 移除不必要的背景可以快速提高渲染性能。不必要的背景可能永远不可见,因为它会被应用在该视图上绘制的任何其他内容完全覆盖。例如,当系统在父视图上绘制子视图时,可能会完全覆盖父视图的背景。
- 使视图层次结构扁平化。
- 可以通过优化视图层次结构来减少重叠界面对象的数量,从而提高性能。
- 降低透明度。
- 对于不透明的 view ,只需要渲染一次即可把它显示出来。但是如果这个 view 设置了 alpha 值,则至少需要渲染两次。这是因为使用了 alpha 的 view 需要先知道混合 view 的下一层元素是什么,然后再结合上层的 view 进行Blend混色处理。透明动画、淡入淡出和阴影等效果都涉及到某种透明度,这就会造成了过度绘制。可以通过减少要渲染的透明对象的数量,来改善这些情况下的过度绘制。例如,如需获得灰色文本,可以在 TextView 中绘制黑色文本,再为其设置半透明的透明度值。但是,简单地通过用灰色绘制文本也能获得同样的效果,而且能够大幅提升性能。
布局加载优化
异步加载
LayoutInflater加载xml布局的过程会在主线程使用IO读取XML布局文件进行XML解析,再根据解析结果利用反射创建布局中的View/ViewGroup对象。这个过程随着布局的复杂度上升,耗时自然也会随之增大。Android为我们提供了 Asynclayoutinflater 把耗时的加载操作在异步线程中完成,最后把加载结果再回调给主线程。
dependencies {
implementation "androidx.asynclayoutinflater:asynclayoutinflater:1.0.0"
}
new AsyncLayoutInflater(this)
.inflate(R.layout.activity_main, null, new AsyncLayoutInflater.OnInflateFinishedListener() {
@Override
public void onInflateFinished(@NonNull View view, int resid, @Nullable ViewGroup parent) {
setContentView(view);
//......
}
});
1、使用异步 inflate,那么需要这个 layout 的 parent 的 generateLayoutParams 函数是线程安全的;
2、所有构建的 View 中必须不能创建 Handler 或者是调用 Looper.myLooper;(因为是在异步线程中加载的,异
步线程默认没有调用 Looper.prepare );
3、AsyncLayoutInflater 不支持设置 LayoutInflater.Factory 或者 LayoutInflater.Factory2;
4、不支持加载包含 Fragment 的 layout
5、如果 AsyncLayoutInflater 失败,那么会自动回退到UI线程来加载布局;
掌阅X2C思路
https://github.com/iReaderAndroid/X2C/blob/master/README_CN.md
相关文章:
卡顿分析与布局优化
卡顿分析与布局优化 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能。Android系统每隔大概16.6ms发出VSYNC信 号,触发对UI进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需要的60fps,为了能够实现60fp…...
【Vivado HLS Bug】Ubuntu环境下Vivado HLS导出IP报错:HLS ERROR: [IMPL 213-28]
Export IP Invalid Argument / Revision Number Overflow Issue (Y2K22) (xilinx.com)一.问题描述: 在Ubuntu20.04环境中使用Vivado HLS导出IP时报错:HLS ERROR: [IMPL 213-28] 二.解决方法: 1.从如下链接中下载官方补丁Export IP Invalid…...
2022最新版-李宏毅机器学习深度学习课程-P14 批次(batch)与动量(momentum)
一、batch 回顾epoch、shuffle batch size大还是小?都有好处 大batchsize的好处 由于GPU有并行计算的能力,左边并不一定用时更长 反而是,batch size小的时候,要跑完一个epoch所用的update时间更长,所以时间方面的比较…...
谜题(Puzzle, ACM/ICPC World Finals 1993, UVa227)rust解法
有一个5*5的网格,其中恰好有一个格子是空的,其他格子各有一个字母。一共有4种指令:A, B, L, R,分别表示把空格上、下、左、右的相邻字母移到空格中。输入初始网格和指令序列(以数字0结束),输出指…...
acwing算法基础之数据结构--双链表
目录 1 知识点2 模板 1 知识点 一般的结构体写法为, struct BiListNode {int val;BiListNode *left;BiListNode *right; };但我们不用这个,而用数组模拟双链表,此时,用编号为0的结点表示头结点,用编号为1的结点表示尾…...
将中文名格式化输出为英文名
要求: 编写Java程序,输入样式为:Zhong wen ming的人名,以 Ming,Zhong.W 的形式打印出来。其中.W是中间单词的首字母;例如输入”Willian Jefferson Clinton“,输出形式为:Clinton,Willian.J public static …...
设计模式_迭代器模式
迭代器模式 介绍 设计模式定义案例迭代器模式行为型:关注对象与行为的分离 提供了一种统一的方式来访问多个不同的集合两个集合:使用了不同的数据存储方式 学生 和 警察 查询显示出集合的内容 ,使用相同的代码 问题堆积在哪里解决办法不同…...
【数据结构】:栈的实现
1 栈 1.1栈的概念及结构 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则 压栈…...
微前端一:技术选型
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。 微前端架构具备以下几个核心价值: 1、技术栈无关 主框架不限制接入应用的技术栈,微应用具备完全自主权 2、独立开发、独立部署 微应用仓库独立࿰…...
FPGA project : flash_continue_write
本实验学习了通过spi通信协议,驱动flash;完成连续写操作。 连续写: 本质上还是页编程指令,两种连续写的方式: 1,每次只写1byte的数据。 2,每次写满1页数据,计算剩余数据够不够写…...
论文阅读:Rethinking Range View Representation for LiDAR Segmentation
来源ICCV2023 0、摘要 LiDAR分割对于自动驾驶感知至关重要。最近的趋势有利于基于点或体素的方法,因为它们通常产生比传统的距离视图表示更好的性能。在这项工作中,我们揭示了建立强大的距离视图模型的几个关键因素。我们观察到,“多对一”…...
本地配置免费的https咋做?
大家好这里是tony4geek。 今天和公司的小伙伴对接项目,因为涉及到https的权限调用。所以在服务器本地localhost 要配置https用来测试 。现在把过程中遇到的问题记录下来。 • 因为是测试用所以生成https的证书用免费的就可以了。 openssl req -x509 -nodes -days …...
微信小程序框架---详细教程
🎬 艳艳耶✌️:个人主页 🔥 个人专栏 :《Spring与Mybatis集成整合》《Vue.js使用》 ⛺️ 越努力 ,越幸运。 目录 1.框架 1.1响应的数据绑定 1.2.页面管理 1.3.基础组件 1.4.丰富的 API 2.视图层 View 2.1.介绍 …...
【LeetCode刷题(数组and排序)】:存在重复元素
给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 示例 1: 输入:nums [1,2,3,1] 输出:true 示例 2: 输入:nums [1,2…...
半导体产业链解析:晶圆厂、无晶圆厂与代工厂的比较与作用
半导体产业一直是全球科技发展的关键驱动力,在半导体产业中,晶圆厂、无晶圆厂公司和代工厂是三个重要的参与者。它们在产业环节、生产方式、经营模式和市场竞争等方面存在一些显著差异。本文将探讨半导体晶圆厂、无晶圆厂公司和代工厂之间的区别…...
Apipost一键压测已支持导入CSV文件
最近更新中Apipost对UI页面进行了一些调整,另外一键压测功能支持参数化!本篇文章将详细介绍这些改动! API调试页面的细节改动 在请求区填入请求参数或脚本时会有相应的标识 如在Query中填入多个参数时上方会展示数量 在预、后执行脚本中写…...
RabbitMQ的5种模式——再探RabbitMQ的模式,简单、工作,发布订阅(广播),路由、主题 页面分析
前言 RabbitMQ作为一款常用的消息中间件,在微服务项目中得到大量应用,其本身是微服务中的重点和难点,有不少概念我自己的也是一知半解,本系列博客尝试结合实际应用场景阐述RabbitMQ的应用,分析其为什么使用࿰…...
初识华为云数据库GaussDB for openGauss
01 前言 GaussDB是华为自主创新研发的分布式关系型数据库。该产品具备企业级复杂事务混合负载能力,同时支持分布式事务,同城跨AZ部署,数据0丢失,支持1000的扩展能力,PB级海量存储。同时拥有云上高可用,高可…...
深圳寄包裹到德国
深圳,作为全球最发达的城市之一,以其高效的物流服务在全球范围内享有盛名。如果你正在寻找一种方式将包裹从深圳寄送到德国,那么本文将为你提供详细的步骤和建议。 第一步:了解国际邮寄的基本信息 首先,你需要了解包裹…...
系统架构师备考倒计时22天(每日知识点)Redis篇
Redis篇 1.Redis与Memcache能力对比 工作MemCacheRedis数据类型简单 key/value 结构丰富的数据结构持久性不支持支持分布式存储客户端哈希分片/一致性哈希多种方式,主从、Sentinel、Cluster 等多线程支持支持支持(Redis5.0及以前版本不支持)内存管理私有内存池/内…...
现有库存(on-hand inventory),库存水平(inventory level),库存位置(inventory position)
库存管理中,这几个名词特别容易混,干脆写一篇博客总结下。 现有库存(on-hand inventory),是指持有的真实库存量 库存水平(inventory level),现有库存减去延迟交付的订单 inventory level on-hand inventory − backorder quant…...
智慧空开让用电更安全、管理更智能——电脑APP远程控制开合闸
安科瑞 崔丽洁 01 什么是低压断路器?低压断路器的定义是:能够接通、承载及分断正常电路条件下的电流,也能在规定的非正常电路条件(过载、短路、特别是短路)下接通、承载一定时间和分断电流的开关电器。 断路器的分类&…...
PyTorch 中张量运算广播
TLDR 右对齐,空补一,从左往右依维运算 [m] [x, y] [m x, m y] 正文 以如下 a b 两个 tensor 计算为例 a torch.tensor([[1],[2],[3], ]) b torch.tensor([[[1, 2, 3],],[[4, 5, 6],],[[7, 8, 9],], ]) # a.shape (3, 1) # b.shape (3, 1, 3)首先…...
Blender:使用立方体制作动漫头像
好久没水文章 排名都掉到1w外了 ~_~ 学习一下blender,看能不能学习一点曲面变形的思路 一些快捷键 ctrl 空格:区域最大化,就是全屏 ctrl alt 空格:也是区域最大化 shift b:框选区域然后最大化显示该范围 shift 空…...
【ppt技巧】ppt里的图片如何提取出来?
之前分享过如何将PPT文件导出成图片,今天继续分享PPT技巧,如何提取出PPT文件里面的图片。 首先,我们将PPT文件的后缀名,修改为rar,将文件改为压缩包文件 然后我们将压缩包文件进行解压 最好是以文件夹的形式解压出来…...
Python学习基础笔记七十三——调试程序
为什么要调试? 我们发现程序运行的结果和我们预期的不符。 程序运行的错误,我们通常叫做bug。 有两种类型的bug:语句错误和逻辑错误。 所谓语句错误,就是执行代码的时候,解释器就可以直接发现的代码错误,…...
BOSHIDA DC电源模块关于电容器的电解液位置
BOSHIDA DC电源模块关于电容器的电解液位置 DC电源模块中的电容器扮演着一个非常重要的角色,它们能够对电路提供稳定的电源电压,同时也可以作为电路中的滤波器,去除电路中的噪声和纹波。在DC电源模块中使用的电容器通常是电解型电容器&#…...
如何实现 Es 全文检索、高亮文本略缩处理(封装工具接口极致解耦)
如何实现 Es 全文检索、高亮文本略缩处理 前言技术选型JAVA 常用语法说明全文检索开发高亮开发Es Map 转对象使用核心代码 Trans 接口(支持父类属性的复杂映射)Trans 接口可优化的点高亮全局配置类如下真实项目落地效果为什么不用 numOfFragments、fragm…...
C++多线程编程(第四章 案例1,C++11和C++17 多核并行计算样例)
目录 4.1手动实现多核base16编码4.1.1 实现base16编码4.1.2无多线程代码4.1.3 C 11多线程代码4.1.4 C 17多线程并发4.1.5 所有测试代码汇总 4.1手动实现多核base16编码 4.1.1 实现base16编码 二进制转换为字符串 一个字节8位,拆分为两个4位字节(最大值…...
获取远程仓库的信息和远程分支的信息
前记: git svn sourcetree gitee github gitlab gitblit gitbucket gitolite gogs 版本控制 | 仓库管理 ---- 系列工程笔记. Platform:Windows 10 Git version:git version 2.32.0.windows.1 Function:获取远程仓库的信息和远…...
关于做暧暧的网站/网络营销的八大职能
1.virtaulbox 配置NatNetwork File->Preference->network->add new nat network 2.virtaulbox 虚拟机配置网络选择 3.启动虚拟机,查看Ip (设置为动态Ip /etc/sysconfig/network-scripts) 查看是否可以ping通外网: 4.设置 port forwarding 主机可…...
临海企业网站建设公司/广告投放运营主要做什么
前言 现实就是,99%的职业,年龄增大后都没前途。没前途是绝大多数普通人的宿命,有前途的人也用不着等到老。有没有前途已然不重要,无论做哪一行,健康的心态永远是首位。 从我个人的角度写写30多岁码工的感受ÿ…...
网站备案主体是/福州seo代理商
derby模块抽象 我已经发布了许多有关Derby的博客: 同一主机上的多个Derby网络服务器 Apache Derby数据库用户和权限与Maven和内存中Derby数据库的集成测试 这本不打算是一个系列。 但是多年来,我越来越多地使用Derby。 最近,我开始将Derby用作…...
网站建设总体需求分析/google搜索引擎入口
版权声明 本文是zhyfly兄贴在LinuxSir.Org 的一个帖子而整理出来的,如果您对版权有疑问,请在本帖后面跟帖。谢谢;本文的HTML版本由北南南北整理;修改了整篇文档的全角及说明文字中的单词中每个字母空格的问题;为标题加…...
政府网站建设 报价/营销策划品牌策划
1 安装 参见WSO2 ESB 4.8.0 文档Installing the Product部分。 1.1 安装要求 1.1.1 系统要求 操作系统 l Microsoft Windows l Linux (Ubuntu, Fedora, Gentoo, SUSE, and Debian, and so on) l Oracle Solaris and l Mac OS X 内存 l 最小2 GB l 堆内存最小512MB&#…...
下载类网站开发条件/好搜搜索引擎
我的个人总结 1、您 2021 年在CSDN有哪些收获 ① 2021年在CSDN写了442篇文章 完整的大数据系列更文、大数据必背面试题30天更文和要掌握的Java知识,详情可看: https://manor.blog.csdn.net/article/details/120084427 ②回答了194个问题,…...