Android lifecyle 源码解剖 - gdutxiaoxu的博客(微信公众号 stormjun94)
版权声明:本文为博主原创文章,遵循CC 4.0
by-sa版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/gdutxiaoxu/article/details/86660766
说在前面
本次推出 Android Architecture Components 系列文章,目前写好了四篇,主要是关于 lifecycle,livedata
的使用和源码分析,其余的 Navigation, Paging library,Room,WorkMannager
等春节结束之后会更新,欢迎关注我的公众号,有更新的话会第一时间会在公众号上面通知。
github sample 地址:
ArchiteComponentsSample
Android 技术人,一位不羁的码农。
前言
前两篇博客,我们已经讲解了 lifecycle ,liveData, ViewModel 的使用,这一篇博客,让我们一起来看一下 lifecycle
的原理。
从自定义的 lifecycle 说起
首先我们先来复习一下,如果要自定义 lifecycle,我们要这样做。
public class CustomLifecycleActivity extends FragmentActivity implements LifecycleOwner {
private LifecycleRegistry mLifecycleRegistry;
private static final String TAG = "CustomLifecycleActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_lifecycle);
mLifecycleRegistry = new LifecycleRegistry(this);
mLifecycleRegistry.markState(Lifecycle.State.CREATED);
getLifecycle().addObserver(new GenericLifecycleObserver() {
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
Log.d(TAG, "onStateChanged: event = " + event);
}
});
}
@Override
protected void onStart() {
super.onStart();
mLifecycleRegistry.markState(Lifecycle.State.STARTED);
}
@Override
protected void onResume() {
super.onResume();
mLifecycleRegistry.markState(Lifecycle.State.RESUMED);
}
@Override
protected void onDestroy() {
super.onDestroy();
mLifecycleRegistry.markState(Lifecycle.State.DESTROYED);
}
@NonNull
@Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
}
- 第一步:先实现 LifecycleOwner 接口,并返回 mLifecycleRegistry
- 第二步:在 Activity 生命周期变化的时候,调用 mLifecycleRegistry.markState() 方法标记相应的状态
- 如果想添加 observer,调用 addObserver 方法添加观察者,这样会在 activity 生命周期变化的时候,回调 observer 的 onchange 方法。
我们先来看一下 getLifecycle() 方法, getLifecycle() 它返回的是一个 Lifecycle 的实例,sdk 中默认的实现类为
LifecycleRegistry。
接下来,我们一起来看一下它的 observer 方法。
public void addObserver(@NonNull LifecycleObserver observer) {
// 判断是否是 DESTROYED,如果是将初始状态置为 DESTROYED,否则为 INITIALIZED
State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
// ObserverWithState 包装
ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
// 将 observer 作为key,在缓存的 mObserverMap 中查找是否存在
ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
// 存在,直接返回回去,证明该 observer 已经添加过了。否则,证明还没有添加过该 observer
if (previous != null) {
return;
}
LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
if (lifecycleOwner == null) {
// it is null we should be destroyed. Fallback quickly
return;
}
// 这里 mAddingObserverCounter 为 0 ,mHandlingEvent 为 false
boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
State targetState = calculateTargetState(observer);
mAddingObserverCounter++;
while ((statefulObserver.mState.compareTo(targetState) < 0
&& mObserverMap.contains(observer))) {
pushParentState(statefulObserver.mState);
statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
popParentState();
// mState / subling may have been changed recalculate
targetState = calculateTargetState(observer);
}
if (!isReentrance) {
// we do sync only on the top level.
sync();
}
mAddingObserverCounter--;
}
在 addObserver 方法中,它主要干这几件事情
- 首先,先初始化状态, 判断当前 mState 是否是 DESTROYED,如果是将初始状态置为 DESTROYED,否则为 INITIALIZED,接着用 ObserverWithState 包装 observer 和 初始化状态 initialState
- 将 observer 作为 key,在缓存的 mObserverMap 中查找是否存在,如果存在,证明该 observer 已经添加过,直接返回回去,不必再进行处理。
- addObserver 方法中第 21 行 , isReentrance 一般情况下为 false,什么情况 为 true,暂时未想到,
接下来我们先来看 calculateTargetState 方法。
private State calculateTargetState(LifecycleObserver observer) {
// 取出 mObserverMap 的上一个 entry,previous
Entry<LifecycleObserver, ObserverWithState> previous = mObserverMap.ceil(observer);
// 如果不为空,获取它的状态
State siblingState = previous != null ? previous.getValue().mState : null;
// 判断 mParentStates 是否为 null,不为 null,去最后的一个状态,否则,为 null
State parentState = !mParentStates.isEmpty() ? mParentStates.get(mParentStates.size() - 1)
: null;
// 取最小的状态
return min(min(mState, siblingState), parentState);
}
- 首先,取出 mObserverMap 中上一个的 entry,该 LifecycleRegistry 实例如果是第一次调用 addObserver 实例的话,那么是 null,否则是上一个 observer 的 entry
- 根据 previous 是否为 null,设置 siblingState 的值
- 判断 mParentStates 是否为 null,不为 null,取 mParentStates 最后一次的状态
- 取 mState, siblingState 最小的状态 a,再取 a 与 parentState 的状态 b
public enum State {
DESTROYED,
INITIALIZED,
CREATED,
STARTED,
RESUMED;
public boolean isAtLeast(@NonNull State state) {
return compareTo(state) >= 0;
}
}
State 中,他们排序的顺序是 DESTROYED < INITIALIZED < CREATED < STARTED < RESUMED。
我们知道,我们在 activity 的 onCreate 方法中初始化 LifecycleRegistry,并标记它的状态为 CREATED。当我们第一次在
onCreate 方法调用 addObserver 的时候,在 calculateTargetState 方法中,若是首次调用 previous 为
null,则 siblingState,parentState 为 null, 而 mState 为 CREATED,所以最终的状态为 CREATED,即
State targetState = calculateTargetState(observer); 中 targetState 为 CREATED
// 取最小的状态
return min(min(mState, siblingState), parentState);
看完 calculateTargetState 方法,我们回过头再来看一下 addObserver 方法。
public void addObserver(@NonNull LifecycleObserver observer) {
// 省略若干行
// 这里 mAddingObserverCounter 为 0 ,mHandlingEvent 为 false
boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
State targetState = calculateTargetState(observer);
mAddingObserverCounter++;
while ((statefulObserver.mState.compareTo(targetState) < 0
&& mObserverMap.contains(observer))) {
pushParentState(statefulObserver.mState);
statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
popParentState();
// mState / subling may have been changed recalculate
targetState = calculateTargetState(observer);
}
if (!isReentrance) {
// we do sync only on the top level.
sync();
}
mAddingObserverCounter--;
}
这里 statefulObserver.mState 为 DESTROYED 或者 INITIALIZED,肯定比 CREATED 小。而
mObserverMap.contains(observer) 必定为 true,除非我们手动移除掉 mObserverMap。因而,会走进
while循环。
private void pushParentState(State state) {
mParentStates.add(state);
}
private ArrayList<State> mParentStates = new ArrayList<>();
pushParentState(statefulObserver.mState); 很简单,只是将 statefulObserver 的状态添加到
mParentStates 集合中。
继续往下走,接着会调用 statefulObserver.dispatchEvent(lifecycleOwner,
upEvent(statefulObserver.mState));
private static Event upEvent(State state) {
switch (state) {
case INITIALIZED:
case DESTROYED:
return ON_CREATE;
case CREATED:
return ON_START;
case STARTED:
return ON_RESUME;
case RESUMED:
throw new IllegalArgumentException();
}
throw new IllegalArgumentException("Unexpected state value " + state);
}
upEvent 方法也很简单,只是返回它的下一个 event。这里因为他们的 state为 INITIALIZED,所以它会返回 ON_CREATE。
void dispatchEvent(LifecycleOwner owner, Event event) {
State newState = getStateAfter(event);
mState = min(mState, newState);
mLifecycleObserver.onStateChanged(owner, event);
mState = newState;
}
static State getStateAfter(Event event) {
switch (event) {
case ON_CREATE:
case ON_STOP:
return CREATED;
case ON_START:
case ON_PAUSE:
return STARTED;
case ON_RESUME:
return RESUMED;
case ON_DESTROY:
return DESTROYED;
case ON_ANY:
break;
}
throw new IllegalArgumentException("Unexpected event value " + event);
}
这里 event 为 ON_CREATE,所以 newState 也为 CREATED。 mState = min(mState, newState);
mState newState,两者状态相同,所以 mState 也为 CREATED。接着回调 mLifecycleObserver 的
onStateChanged 方法。所以,这里,会收到我们的 onCreate 事件,与我们的预想相符。
但是我们并没有在 onStart,onResume, onPause , onStop 和 onDestroy 方法中调用
mLifecycleRegistry.handleLifecycleEvent 方法,它又是怎样促发 Observer 的 onStateChanged
方法的。这里先不揭晓,我们先来看一下 26.1.0 以后的 AppCompatActivity,待会你就明白了,会感叹 google 真的牛逼!
从 26.1.0 以后 AppCompatActivity 的设计说起
我们知道,在 26.1.0 以后,如果我们要使用 lifecycle,我们只需要调用以下的方法即可。
SupportActivity
getLifecycle().addObserver(new GenericLifecycleObserver() {
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
Log.d(TAG, "onStateChanged: event =" + event);
}
});
跟踪 getLifecycle() 方法,它会跳转到 SupportActivity 的 getLifecycle 方法 中。
public class SupportActivity extends Activity implements LifecycleOwner, Component {
private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ReportFragment.injectIfNeededIn(this);
}
@Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
}
在 SupportActivity 中,它默认为我们初始化 mLifecycleRegistry,作为一个成员变量。接着,他在
onCreate 方法中调用了 ReportFragment.injectIfNeededIn(this); 方法。
public class ReportFragment extends Fragment {
private static final String REPORT_FRAGMENT_TAG = "android.arch.lifecycle"
+ ".LifecycleDispatcher.report_fragment_tag";
public static void injectIfNeededIn(Activity activity) {
// ProcessLifecycleOwner should always correctly work and some activities may not extend
// FragmentActivity from support lib, so we use framework fragments for activities
android.app.FragmentManager manager = activity.getFragmentManager();
if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
// Hopefully, we are the first to make a transaction.
manager.executePendingTransactions();
}
}
在 injectIfNeededIn 方法中,它会判断我们是否已经添加 ReportFragment,没有的话,添加进去。
public class ReportFragment extends Fragment {
private static final String REPORT_FRAGMENT_TAG = "android.arch.lifecycle"
+ ".LifecycleDispatcher.report_fragment_tag";
private ActivityInitializationListener mProcessListener;
private void dispatchCreate(ActivityInitializationListener listener) {
if (listener != null) {
listener.onCreate();
}
}
private void dispatchStart(ActivityInitializationListener listener) {
if (listener != null) {
listener.onStart();
}
}
private void dispatchResume(ActivityInitializationListener listener) {
if (listener != null) {
listener.onResume();
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
dispatchCreate(mProcessListener);
dispatch(Lifecycle.Event.ON_CREATE);
}
@Override
public void onStart() {
super.onStart();
dispatchStart(mProcessListener);
dispatch(Lifecycle.Event.ON_START);
}
@Override
public void onResume() {
super.onResume();
dispatchResume(mProcessListener);
dispatch(Lifecycle.Event.ON_RESUME);
}
@Override
public void onPause() {
super.onPause();
dispatch(Lifecycle.Event.ON_PAUSE);
}
@Override
public void onStop() {
super.onStop();
dispatch(Lifecycle.Event.ON_STOP);
}
@Override
public void onDestroy() {
super.onDestroy();
dispatch(Lifecycle.Event.ON_DESTROY);
// just want to be sure that we won't leak reference to an activity
mProcessListener = null;
}
}
然后,它在 onCreat ,onStart, onResume, onPause, onStop, onDestroy 方法中分别调用 dispatch
方法进行分发生命周期。
private void dispatch(Lifecycle.Event event) {
Activity activity = getActivity();
if (activity instanceof LifecycleRegistryOwner) {
((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);
return;
}
if (activity instanceof LifecycleOwner) {
Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
if (lifecycle instanceof LifecycleRegistry) {
((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
}
}
}
在 dispatch 方法中,会先判断 activity 是不是实现了 LifecycleRegistryOwner
,如果是,直接分发,不过不是,判断是否实现 LifecycleOwner,获取它的 lifecycle,调用它 的 handleLifecycleEvent
进行分发。
public class SupportActivity extends Activity implements LifecycleOwner, Component {
private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this
而很明显,高版本的 SupportActivity 实现了 LifecycleOwner 接口,并写
LifecycleOwner.getLifecycle() 是 LifecycleRegistry
普通的 Activity
对于 26.1.0 以后的版本,你会发现,对于普通的 Activity,如果你想要使用 lifecycle,你只需要实现
LifecycleOwner 接口即可。当生命周期变化的时候,它也可以回调 Observer 的 onStateChanged 方法。
回到我们前面的问题:
我们并没有在 onStart,onResume, onPause , onStop 和 onDestroy 方法中调用
mLifecycleRegistry.handleLifecycleEvent 方法,它又是怎样促发 onStateChanged 方法的
我们猜想它也是通过 ReportFragment 实现的。但是在 Activity 的 onCreate 方法中,我们并没有发现它有添加
ReportFragment,我们在 As 全局搜一下,看哪些地方使用到 ReportFragment。如下图
从图中可以看到,有几个地方使用到他。我们先来看一下 LifecycleDispatcher
class LifecycleDispatcher {
private static final String REPORT_FRAGMENT_TAG = "android.arch.lifecycle"
+ ".LifecycleDispatcher.report_fragment_tag";
private static AtomicBoolean sInitialized = new AtomicBoolean(false);
static void init(Context context) {
if (sInitialized.getAndSet(true)) {
return;
}
// 在 init 方法中,监听全局 activity 的创建,从而来添加 fragment
((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(new DispatcherActivityCallback());
}
@SuppressWarnings("WeakerAccess")
@VisibleForTesting
static class DispatcherActivityCallback extends EmptyActivityLifecycleCallbacks {
private final FragmentCallback mFragmentCallback;
DispatcherActivityCallback() {
mFragmentCallback = new FragmentCallback();
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (activity instanceof FragmentActivity) {
((FragmentActivity) activity).getSupportFragmentManager()
.registerFragmentLifecycleCallbacks(mFragmentCallback, true);
}
ReportFragment.injectIfNeededIn(activity);
}
@Override
public void onActivityStopped(Activity activity) {
if (activity instanceof FragmentActivity) {
markState((FragmentActivity) activity, CREATED);
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
if (activity instanceof FragmentActivity) {
markState((FragmentActivity) activity, CREATED);
}
}
}
// 省略若干代码
}
可以看到,它 在 init 方法中,通过 context.getApplicationContext()
.registerActivityLifecycleCallbacks 监听全局 activity 的创建,在 activity oncreate
的时候,调用 ReportFragment.injectIfNeededIn(activity) ,从而来添加 fragment,进而分发相应的事件。
那 LifecycleDispatcher 的 init 方法又是在哪里调用的呢? 我们全局搜索一下
public class ProcessLifecycleOwnerInitializer extends ContentProvider {
@Override
public boolean onCreate() {
LifecycleDispatcher.init(getContext());
ProcessLifecycleOwner.init(getContext());
return true;
}
可以看到它是在 ProcessLifecycleOwnerInitializer 的 onCreate 方法中调用的。而
ProcessLifecycleOwnerInitializer 是一个 ContentProvider。我们知道 ContentProvider 一般是在
AndroidManifest 中生命的。
果然,在 extensions-1.1.1.aar 中,我们惊喜地发现,它在 Manifest 里面注册了。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.arch.lifecycle.extensions" >
<uses-sdk android:minSdkVersion="14" />
<application>
<provider
android:name="android.arch.lifecycle.ProcessLifecycleOwnerInitializer"
android:authorities="${applicationId}.lifecycle-trojan"
android:exported="false"
android:multiprocess="true" />
</application>
</manifest>
而 ContentProvider 的 onCreate 方法优先于 Application 的 onCreate 执行,所以在 Application
之前我们就调用了 ProcessLifecycleOwnerInitializer init 方法,监听了 Activity 的创建,当 Actiivty
创建的时候,会尝试为 Activity 添加 ReportFragment。而 ReportFragment 会在 Activity
生命周期变化的时候帮助我们分发生命周期。
ContentProvider 的 onCreate 方法优先于 Application 的 onCreate 执行,可以查看这一篇博客
Android系统中的Application和四大组件一些方法的启动顺序和一些坑
总结
ok,我们来梳理一下。
对于 26.1.0 以后的 SupportActivity
它在 Activity onCreate 的时候添加了 ReportFragment,这个 ReportFragment 相当于一个代理,它在
onActivityCreated 的时候 dispatch(Lifecycle.Event.ON_CREATE) 进行分发生命周期,onStart,
onResume, onPause, onStop, onDestroy 的时候也是如此。而 在 dispatch 中 它调用了
LifecycleRegistry handleLifecycleEvent 的方法。而 LifecycleRegistry
方法中经过一系列处理,它又调用了 observer 的 onStateChange 方法,去通知相应的 observer。
对于普通的 Activity
它利用了 ContentProvide 的特征,它是在 Application onCreate 之前初始化的,他在
ProcessLifecycleOwnerInitializer oncreate 的时候监听 Activity 的创建,在 Activity
创建的时候,判断是否已经添加过 ReportFragment,没有的话,添加进去。 _ 这是一个很巧妙的设计,隐式初始化了 lifecycle。_
用流程图表示如下:
该图片引用自 Android
架构组件(一)——Lifecycle
Lifecycle 设计借鉴
- 利用 ProcessLifecycleOwnerInitializer contentProvider 来隐式加载
想一下,如果 ProcessLifecycleOwnerInitializer 不利用 contentProvider 来隐式加载的话,对于 普通的
Activity,旧版本等,如果想使用 lifecycle,那必须在基类中,手动调用
ReportFragment.injectIfNeededIn(activity) 的方法。
- 利用 fragment 来分发生命周期
利用 fragment 来分发生命周期有两个优点
- 将逻辑从 Activity 中剥离出来,减少耦合,方便复用
- 可以做到在 Activity onCreate 之后才回调 observer 的 CREATED Event 事件。如果是通过 Application registerActivityLifecycleCallbacks 方法来分发生命周期的话,因为 ActivityLifecycleCallbacks 的 onActivityCreated 是在 Activity oncreate 之前调用的。
推荐阅读:
java 代理模式详解
观察者设计模式 Vs
事件委托(java)
Android Fragment 的妙用 - 优雅地申请权限和处理
onActivityResult
Android lifecyle 源码解剖 - gdutxiaoxu的博客(微信公众号 stormjun94)的更多相关文章
- activiti源码笔记对标的博客
http://blog.csdn.net/bluejoe2000/article/category/2812511/2 白乔专栏 http://blog.csdn.net/bluejoe2000/ar ...
- C#/ASP.NET MVC微信公众号接口开发之从零开发(四) 微信自定义菜单(附源码)
C#/ASP.NET MVC微信接口开发文章目录: 1.C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台 2.C#/ASP.NET MVC微信公众号接口开发之从零开发( ...
- VopSdk一个高逼格微信公众号开发SDK(源码下载)
看之前回复很多说明大家很有热情&文章被误删掉了,不想让有的朋友错失这个高逼格的东西,现在重新发布,这次就直接放出源码,文章最末下载地址. 看之前回复很多说明大家很有热情&文章被误删掉了 ...
- Appium Android Bootstrap源码分析之启动运行
通过前面的两篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>和<Appium Android Bootstrap源码分析之命令解析 ...
- Appium Android Bootstrap源码分析之命令解析执行
通过上一篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>我们知道了Appium从pc端发送过来的命令如果是控件相关的话,最终目标控件在b ...
- Appium Android Bootstrap源码分析之控件AndroidElement
通过上一篇文章<Appium Android Bootstrap源码分析之简介>我们对bootstrap的定义以及其在appium和uiautomator处于一个什么样的位置有了一个初步的 ...
- Appium Android Bootstrap源码分析之简介
在上一个系列中我们分析了UiAutomator的核心源码,对UiAutomator是怎么运行的原理有了根本的了解.今天我们会开始另外一个在安卓平台上基于UiAutomator的新起之秀--Appium ...
- 我的Android进阶之旅------>经典的大牛博客推荐(排名不分先后)!!
本文来自:http://blog.csdn.net/ouyang_peng/article/details/11358405 今天看到一篇文章,收藏了很多大牛的博客,在这里分享一下 谦虚的天下 柳志超 ...
- 使用小书匠及markdown here编辑博客和微信公众号
1. 使用小书匠连接Evernote并发布笔记到博客园 1.1 小书匠初探 我平时的信息收集的主要方法是采用Pocket+Evernote. 简单来说: 如果访问到非常有用,而且是必须要立刻记录的内容 ...
随机推荐
- 实现一个简单的散列表(HashMap)
下面参考java.util.HashMap<K, V>,写了一个简单的散列表,只实现了其中的put和get方法,使用链接法"碰撞冲突".代码最后,自定义了一个Peopl ...
- 这可能是最容易入门的socket教程了
前言: 如今,网络编程已然成为了一个后端开发工程师需要具备的核心技能之一.因此,该博客力求提供最简单.通俗的描述方式,来描绘网络编程中常见的知识点,同时附带代码示例,后期会加上具体的抓包分析,实际项目 ...
- iOS apns推送
前言:推送分为本地推送以及远程推送. 两者的区别为本地推送一般为定时推送.定期推送或者位置推送.而远程推送更为多样化,能满足较高的要求.当然远程推送需要服务器端开发,开发流程较复杂. 1.本地推送只需 ...
- MongoDB下载+安装+运行
一. 官网下载安装 MongoDB 提供了 OSX 平台上 64 位的安装包,你可以在官网下载安装包. 下载地址:MongoDB官网-Community Server 选择适合自己平台的版本, 下载对 ...
- CodeForces-Round235D
链接:http://codeforces.com/contest/401/problem/D 题意:给出一个数字num和m,问通过重新排列num中的各位数字中有多少个数(mod m)=0,直接枚举 ...
- Vue AES+MD5加密 后台解密
前端VUE vue项目中安装crypto-js npm install crypto-js --save-dev CryptoJS (crypto.js) 为 JavaScript 提供了各种各样的加 ...
- leetcode-164、最大间距
export default (arr) => { // 如果数组长度小于2返回0 if (arr.length < 2) { return 0 } // 排序 arr.sort() // ...
- Wireshark数据包分析入门
Wireshark数据包分析(一)——使用入门 Wireshark简介: Wireshark是一款最流行和强大的开源数据包抓包与分析工具,没有之一.在SecTools安全社区里颇受欢迎,曾一度超越 ...
- webpack实践(一)- 先入个门
一.前言 webpack是个啥呢?看官网的这段描述. webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler) 在我以前做纯html.css. ...
- 【RabbitMQ】显示耗时处理进度
[RabbitMQ]显示耗时处理进度 通过网页提交一个耗时的请求,然后启动处理线程,请求返回.处理线程每完成一部分就给前台推送完成的数量,前端显示进度. 依赖jar <?xml version= ...