Paging3 (一)  入门

前言:

官方分页工具,  确实香.   但数据源不开放, 无法随意增删改操作;  只能借助 Room;  但列表数据不一定都要用 Room吧;

如果偏查询的分页数据用 Paging3 ;  其他一概用 老Adapter;  这倒也算个方案. [苦笑]

目录:

  1. 简单使用  -  数据源,Viewmodel,Adapter 等
  2. LoadResult  -  Error, Page.  Error 用法等
  3. PagingConfig
  4. 监听列表加载状态
  5. LoadStateAdapter  -  loading, 加载失败, 没有更多等
  6. Map  -  数据预处理

官方 Pagings 优势: 

  • 分页数据的内存中缓存。该功能可确保您的应用在处理分页数据时高效利用系统资源。
  • 内置的请求重复信息删除功能,可确保您的应用高效利用网络带宽和系统资源。
  • 可配置的 RecyclerView 适配器,会在用户滚动到已加载数据的末尾时自动请求数据。
  • 对 Kotlin 协程和 Flow 以及 LiveData 和 RxJava 的一流支持。
  • 内置对错误处理功能的支持,包括刷新和重试功能。

导包:

dependencies {
val paging_version = "3.0.0" //唯一必导包
implementation("androidx.paging:paging-runtime:$paging_version") // 测试用
testImplementation("androidx.paging:paging-common:$paging_version") // optional - RxJava2 support
implementation("androidx.paging:paging-rxjava2:$paging_version") // optional - RxJava3 support
implementation("androidx.paging:paging-rxjava3:$paging_version") // 适配 Guava 库 - 高效java扩展库
implementation("androidx.paging:paging-guava:$paging_version") // 适配 Jetpack Compose - 代码构建View; 干掉 layout
implementation("androidx.paging:paging-compose:1.0.0-alpha09")
}

1. 简单使用:

1.1 数据源  PagingSource 

自定义数据源, 继承 PagingSource

它有两个泛型参数,  1. 页码key,  没有特殊需求的话一般就是 Int 类型;  2.集合实体类型

重写两个方法:  1.load()  加载数据的方法;   2.getRefreshKey  初始加载的页码;  暂且返回 1 或 null

LoadResult.Page 后面再讲;

class DynamicDataSource: PagingSource<Int, DynamicTwo>() {

    //模拟最大页码
private var maxPage = 2 //模拟数据
private fun fetchItems(startPosition: Int, pageSize: Int): MutableList<DynamicTwo> {
Log.d("ppppppppppppppppppppp", "startPosition=${startPosition};;;pageSize=${pageSize}")
val list: MutableList<DynamicTwo> = ArrayList()
for (i in startPosition until startPosition + pageSize) {
val concert = DynamicTwo()
concert.title = "我是标题${i}"
concert.newsInfo = "我是内容${i}"
concert.nickName = "小王${i}"
list.add(concert)
}
return list
} override fun getRefreshKey(state: PagingState<Int, DynamicTwo>): Int? = null override suspend fun load(params: LoadParams<Int>): LoadResult<Int, DynamicTwo> {
val nextPageNumber = params.key ?: 1
val size = params.loadSize
Log.d("ppppppppppppppppppppp", "nextPageNumber=${nextPageNumber};;;size=${size}")
val response = fetchItems((nextPageNumber-1) * size, size) return LoadResult.Page(
data = response,
prevKey = null, // Only paging forward. 只向后加载就给 null
//nextKey 下一页页码; 尾页给 null; 否则当前页码加1
nextKey = if(nextPageNumber >= maxPage) null else (nextPageNumber + 1)
)
}
}

1.2 ViewModel

代码比较简单.  内容我们一会再讲

class DynamicPagingModel(application: Application) : AndroidViewModel(application) {
val flow = Pager(
//配置
PagingConfig(pageSize = 10, prefetchDistance = 2,initialLoadSize = 10)
) {
//我们自定义的数据源
DynamicDataSource()
}.flow
.cachedIn(viewModelScope)
}

1.3 前台使用: 

初始化 Adapter 及 RecycleView

mViewModel?.flow?.collectLatest  绑定监听,  然后通过 submitData() 刷新列表;

mAdapter = SimplePagingAdapter(R.layout.item_dynamic_img_two, null)

mDataBind.rvRecycle.let {
it.layoutManager = LinearLayoutManager(mActivity)
it.adapter = mAdapter
} //Activity 用 lifecycleScope
//Fragments 用 viewLifecycleOwner.lifecycleScope
viewLifecycleOwner.lifecycleScope.launchWhenCreated {
mViewModel?.flow?.collectLatest {
mAdapter.submitData(it)
}
}

1.4 Adapter

必须继承  paging 的 PagingDataAdapter

DiffCallback() 或 handler  NewViewHolder 不了解的可以看我的 ListAdapter 封装系列

open class SimplePagingAdapter(
private val layout: Int,
protected val handler: BaseHandler? = null
) :
PagingDataAdapter<DynamicTwo, RecyclerView.ViewHolder>(DiffCallback()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return NewViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context), layout, parent, false
), handler
)
} override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if(holder is NewViewHolder){
holder.bind(getItem(position))
}
}
}

over  简单的分页模拟数据已完成;  

2. LoadResult

它是一个密封类;   它表示加载操作的结果;

2.1 LoadResult.Error

表示加载失败;  需提供 Throwable 对象.

public data class Error<Key : Any, Value : Any>(
val throwable: Throwable
) : LoadResult<Key, Value>()

可用于:

  • 异常时返回,  HTTP, IO, 数据解析等异常;
  • 服务器错误码响应
  • 没有更多数据

2.1 LoadResult.Page

表示加载成功;

参数:

data 数据集合;

prevKey 前页页码 key;   //向下一页加载 给null

nextKey 后页页码 key;    //向上一页加载 给null

public data class Page<Key : Any, Value : Any> constructor(
/**
* Loaded data
*/
val data: List<Value>,
/**
* [Key] for previous page if more data can be loaded in that direction, `null`
* otherwise.
*/
val prevKey: Key?,
/**
* [Key] for next page if more data can be loaded in that direction, `null` otherwise.
*/
val nextKey: Key?,
/**
* Optional count of items before the loaded data.
*/
@IntRange(from = COUNT_UNDEFINED.toLong())
val itemsBefore: Int = COUNT_UNDEFINED,
/**
* Optional count of items after the loaded data.
*/
@IntRange(from = COUNT_UNDEFINED.toLong())
val itemsAfter: Int = COUNT_UNDEFINED
) : LoadResult<Key, Value>() {

3.PagingConfig

分页配置

参数:

pageSize:  每页容量

prefetchDistance:  当RecycleView 滑动到底部时, 会自动加载下一页.   如果能提前预加载, 可以省去部分等待加载的时间.

        prefetchDistance 就是距离底部提前加载的距离.  默认 = pageSize;   = 0 时将不会加载更多

enablePlaceholders:  允许使用占位符.  想了解的点这里

initialLoadSize: 初始加载数量,  默认 = pageSize * 3

maxSize:   似乎意义没有那么简单.  还没看源码,不清楚;  不能 < pageSize + prefetchDistance * 2

jumpThreshold: 某阈值!  好吧我摊牌了, 我不知道. [奸笑]

4.监听加载状态:  

LoadState:  表示加载状态密封类;

LoadState.NotLoading:  加载完毕,  并且界面也已相应更新

LoadState.Error:  加载失败.

LoadState.Loading:  正在加载..

lifecycleScope.launch {
mAdapter.loadStateFlow.collectLatest { loadStates ->
when(loadStates.refresh){
is LoadState.Loading -> {
Log.d("pppppppppppppp", "加载中")
}
is LoadState.Error -> {
Log.d("pppppppppppppp", "加载失败")
}
is LoadState.NotLoading -> {
Log.d("pppppppppppppp", "完事了")
}
else -> {
Log.d("pppppppppppppp", "这是啥啊")
}
}
} //或者:
mAdapter.addLoadStateListener { ... }
}

5. 状态适配器  LoadStateAdapter

用于直接在显示的分页数据列表中呈现加载状态。 例如:  尾部显示 正在加载, 加载失败, 没有更多等;

5.1 自定义 MyLoadStateAdapter  继承 LoadStateAdapter

重写  onCreateViewHolder,  onBindViewHolder

retry:  如果加载失败, 想要重试,  则提供该高阶函数参数;  否则不需要它

class MyLoadStateAdapter(
/**
* 当下一页加载失败时, 继续尝试加载下一页;
*/
private val retry: () -> Unit
) : LoadStateAdapter<LoadStateViewHolder>() { override fun onCreateViewHolder(
parent: ViewGroup,
loadState: LoadState
) = LoadStateViewHolder(parent, retry) override fun onBindViewHolder(
holder: LoadStateViewHolder,
loadState: LoadState
) = holder.bind(loadState)
}

5.2 自定义 LoadStateViewHolder

功能: 

  • 加载中 显示 Loading;
  • 加载失败  显示 错误信息.    包括 http, IO 异常,  后台给的错误 msg 等;
  • 没有更多
class LoadStateViewHolder (
parent: ViewGroup,
retry: () -> Unit
) : RecyclerView.ViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.view_loading_more, parent, false)
) {
private val binding = ViewLoadingMoreBinding.bind(itemView) init {
//当点击重试按钮时, 调用 PagingDataAdapter 的 retry() 重新尝试加载
binding.btnLoadingRetry.setOnClickListener {
retry()
}
} fun bind(loadState: LoadState) {
// 当加载失败时.
if(loadState is LoadState.Error){
// 将没有更多封装成 NoMoreException; 此时显示没有更多 View
if(loadState.error is NoMoreException){
hideNoMoreUi(false) //显示 没有更多 View
hideErrUi(true) //隐藏 失败 View
}else{
hideNoMoreUi(true)
hideErrUi(false, loadState.error.message) //显示失败 View时, 填充错误 msg
}
}else{
hideNoMoreUi(true)
hideErrUi(true)
} //加载中..
binding.pbLoadingBar.visibility = if(loadState is LoadState.Loading){
View.VISIBLE
}else{
View.GONE
}
} /**
* 隐藏没有更多View;
*/
private fun hideNoMoreUi(hide: Boolean){
if(hide){
binding.tvLoadingHint.visibility = View.GONE
}else{
binding.tvLoadingHint.visibility = View.VISIBLE
}
} /**
* 隐藏 加载失败View;
*/
private fun hideErrUi(hide: Boolean, msg: String? = null){
if(hide){
binding.tvLoadingError.visibility = View.GONE
binding.btnLoadingRetry.visibility = View.GONE
}else{
binding.tvLoadingError.text = msg
binding.tvLoadingError.visibility = View.VISIBLE
binding.btnLoadingRetry.visibility = View.VISIBLE
}
}
}

顺便补一下  NoMoreException;  用法? 在下面 PagingSource 喽.

class NoMoreException: RuntimeException()

5.3 layout  view_loading_more.xml

包含:   TextView: 没有更多;    ProgressBar: 加载中;   TextView: 错误信息;   Button: 重试按钮

<layout>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingHorizontal="16dp"
android:layout_width="match_parent"
android:layout_height="54dp">
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="#e5e5e5"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/tv_loading_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#798080"
android:text="已经到底了"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<ProgressBar
android:id="@+id/pb_loading_bar"
android:layout_width="32dp"
android:layout_height="32dp"
android:visibility="gone"
android:indeterminateTint="#7671F8"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="@+id/tv_loading_error"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="@color/shape_red"
android:text="错误信息"
android:layout_marginEnd="8dp"
android:maxLines="2"
android:ellipsize="end"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/btn_loading_retry"
app:layout_constraintStart_toStartOf="parent"/>
<Button
android:id="@+id/btn_loading_retry"
android:layout_width="60dp"
android:layout_height="38dp"
android:textColor="@color/white"
android:text="重试"
android:visibility="gone"
android:background="@drawable/shape_blue_7671f8_r8"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

5.4 PagingSource 需要根据情况  返回不同的  LoadResult

代码如下,  直接看注释就可以了;   

class DynamicDataSource: PagingSource<Int, DynamicTwo>() {

    private var maxPage = 1

    override fun getRefreshKey(state: PagingState<Int, DynamicTwo>): Int? = null

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, DynamicTwo> {
try {
val nextPageNumber = params.key ?: 1 //超过页码时, 返回没有更多状态 NoMoreException
if(nextPageNumber > maxPage){
return LoadResult.Error(NoMoreException())
} //这是 Retrofit 网络请求
val map = mapOf("page" to nextPageNumber, "pageSize" to params.loadSize)
val param = ApiManager.INSTANCE.getJsonBody(map)
val response = ApiManager.INSTANCE.mApi.getDynamicList(param) //后台 响应错误码时; 用 RuntimeException 返回错误信息
if(response.code != 200){
return LoadResult.Error(RuntimeException(response.msg))
} //解析响应数据
val jo = response.data
val list = jo?.getAsJsonArray("newsList")?.toString()?.toBeanList<DynamicTwo>() ?: mutableListOf()
maxPage = jo?.get("totalPage").toString().toInt() //返回正常数据
return LoadResult.Page(
data = list,
prevKey = null, // Only paging forward. 只向后加载就给null
// nextKey 下一页页码; 尾页给 null; 否则当前页码加1
nextKey = nextPageNumber + 1
)
} catch (e: IOException) {
// IOException for network failures.
return LoadResult.Error(e)
} catch (e: HttpException) {
// HttpException for any non-2xx HTTP status codes.
return LoadResult.Error(e)
} catch (e: Exception) {
// IOException for network failures.
return LoadResult.Error(e)
}
}
}

代码中 请求参数只给了 page 和 pageSize;  其他参数怎么给?  

  1. DynamicDataSource 的构造方法传入;
  2. 动态参数怎么办?  写回调, 从ViewModel 中组装请求数据
  3. 麻烦怎么办?  创建 BaseDataSource.  将相似代码封装.  请求参数通过高阶函数从ViewModel组装;

5.5 前台使用: 

首先正常初始化 Adapter,  RecycleView,  并调用  mViewModel?.flow?.collectLatest

其次  RecycleView 的 adaper 不要给 主数据Adapter;  而是给 withLoadStateFooter() 返回的 ConcatAdapter

val stateAdapter = mAdapter.withLoadStateFooter(MyLoadStateAdapter(mAdapter::retry))
mDataBind.rvRecycle.let {
it.layoutManager = LinearLayoutManager(mActivity)
// **** 这里不要给 mAdapter(主数据 Adapter); 而是给 stateAdapter ***
it.adapter = stateAdapter
}

PagingDataAdapter 的 withLoadStateFooter 方法会返回一个新的 ConcatAdapter 对象; 请将这个 ConcatAdapter 设置给 RecycleView
withLoadStateFooter 的参数 就是我们自定义的 MyLoadStateAdapter;  retry -> mAdapter.retry()

5.6 看一下  LoadStateAdapter 的源码;  

可以发现,  这是个单条目 Adapter.   

并且  只有当  LoadState.Loading, LoadState.Error 时才会出现;   当然也可以重写  displayLoadStateAsItem(), 让它所有状态都出现;

当 列表状态变化时,  会设置 loadState 参数;  动态增删改 Item;

abstract class LoadStateAdapter<VH : RecyclerView.ViewHolder> : RecyclerView.Adapter<VH>() {

    var loadState: LoadState = LoadState.NotLoading(endOfPaginationReached = false)
set(loadState) {
if (field != loadState) {
val oldItem = displayLoadStateAsItem(field)
val newItem = displayLoadStateAsItem(loadState) if (oldItem && !newItem) {
notifyItemRemoved(0)
} else if (newItem && !oldItem) {
notifyItemInserted(0)
} else if (oldItem && newItem) {
notifyItemChanged(0)
}
field = loadState
}
} final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
return onCreateViewHolder(parent, loadState)
} final override fun onBindViewHolder(holder: VH, position: Int) {
onBindViewHolder(holder, loadState)
} final override fun getItemViewType(position: Int): Int = getStateViewType(loadState)

  //条目数量, final 不可重写;
final override fun getItemCount(): Int = if (displayLoadStateAsItem(loadState)) 1 else 0 abstract fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): VH abstract fun onBindViewHolder(holder: VH, loadState: LoadState) open fun getStateViewType(loadState: LoadState): Int = 0
  
  //只有当 Loading, Error 时, 才显示
open fun displayLoadStateAsItem(loadState: LoadState): Boolean {
return loadState is LoadState.Loading || loadState is LoadState.Error
}
}

5.7 LoadStateAdapter  改建头尾

如果我们把它强行改造成 Header footer: 

  1. 重写 displayLoadStateAsItem() 不管什么状态, 都返回true
  2. loadState 不能重写,  所以 notifyItemChanged(0) 必被调用;
  3. 暴力一点, 直接重写 notifyItemChanged() 让它什么都不做?   好吧  它也是 final, 不能重写
  4. 既然要调刷新, 那就调吧 [破涕为笑];  那怎么办 尽量少执行无用代码呗,   那就 onBindViewHolder() 啥也不干;
  5. 头尾由前端控制,  Adapter 只需要把这个 固定View显示就 ok 了
  6. 如果能阻止 notifyItemChanged(0) 那就更好了.  聪明的你有没有办法呢. [666]

最终 Adapter: 

class EndViewAdapter(val v: View) : LoadStateAdapter<EndHolder>() {

    override fun onCreateViewHolder(
parent: ViewGroup,
loadState: LoadState
) = EndHolder(v) override fun onBindViewHolder(holder: EndHolder, loadState: LoadState){
//啥也不干
} override fun displayLoadStateAsItem(loadState: LoadState) = true
} class EndHolder(itemView: View) : RecyclerView.ViewHolder(itemView)

好吧,  一运行, 崩了 [捂脸];   called attach on a child which is not detached

怎么办, 取消 RecycleView 的刷新闪烁动画:

(mDataBind.rvRecycle.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false;

整个 RecycleView 的条目刷新动画都没了,  这不是个事啊!  但博主已经没办法了 [捂脸]

没办法了怎么办? 不用 Header 了?   当然不是,  我们只是不用 LoadStateAdapter 做头尾了;  我们用 ConcatAdapter 做头尾;

就是在 withLoadState...  之后,  再自己组装  ConcatAdapter

6. MAP:  数据转换;  有的时候, 我们需要对响应数据 进行预先处理; 

例如: 根据条件,预先改变实体内容;

val flow: Flow<PagingData<DynamicTwo>> = Pager(
PagingConfig(pageSize = 10, prefetchDistance = 2,initialLoadSize = 10)
) {
DynamicDataSource()
}.flow
.cachedIn(viewModelScope)
.map {
it.map { entity ->
// 这里根据条件, 预先处理数据
if(entity.isLike == 1){
entity.nickName = "变变变, 我是百变小魔女"
}else{
entity.nickName = "呜哈哈哈"
}
entity
}
}

例如:  组合实体; 根据条件产生不同实体;

val flow: Flow<PagingData<GroupEntity>> = Pager(
PagingConfig(pageSize = 10, prefetchDistance = 2,initialLoadSize = 10)
) {
DynamicDataSource()
}.flow
.cachedIn(viewModelScope)
.map {
it.map { entity ->
// 这里根据条件, 预先处理数据
if(entity.isLike == 1){
GroupEntity.DynamicTwoItem(entity)
}else{
GroupEntity.DynamicItem(DynamicEntity())
}
}
} sealed class GroupEntity{
class DynamicTwoItem (val entity: DynamicTwo): GroupEntity()
class DynamicItem (val entity: DynamicEntity): GroupEntity()
}

又例如: 插入实体分隔符等

Over

回到顶部

孟老板 Paging3 (一) 入门的更多相关文章

  1. 孟老板 Paging3 (二) 结合Room

    BaseAdapter系列 ListAdapter系列 Paging3 (一) 入门 Paging3 (二) 结合 Room Paging3 (二)  结合Room Paging 数据源不开放, 无法 ...

  2. 孟老板 ListAdapter封装, 告别Adapter代码 (三)

    BaseAdapter系列 ListAdapter封装, 告别Adapter代码 (一) ListAdapter封装, 告别Adapter代码 (二) ListAdapter封装, 告别Adapter ...

  3. 孟老板 ListAdapter封装, 告别Adapter代码 (四)

    BaseAdapter系列 ListAdapter封装, 告别Adapter代码 (一) ListAdapter封装, 告别Adapter代码 (二) ListAdapter封装, 告别Adapter ...

  4. 孟老板 BaseAdapter封装(五) ListAdapter

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  5. 孟老板 BaseAdapter封装 (一) 简单封装

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  6. 孟老板 BaseAdapter封装 (二) Healer,footer

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  7. 孟老板 BaseAdapter封装 (三) 空数据占位图

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  8. 孟老板 BaseAdapter封装(四) PageHelper

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  9. 孟老板 ListAdapter封装, 告别Adapter代码 (上)

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

随机推荐

  1. 修改composer配置(以修改cache-files-maxsize为例)修改composer拉取包出现"Content-Length mismatch"的问题

    1.composer config -l -g查看composer配置信息 2.修改配置:composer config --global cache-files-maxsize 1024MiB

  2. 快速上手NumPy

      NumPy is the fundamental package for scientific computing in Python. NumPy是一个开源的Python科学计算库. 官网:ht ...

  3. 容器进阶:OCI与容器运行时

    Blog:博客园 个人 什么是容器运行时(Container Runtime) Kubernetes节点的底层由一个叫做容器运行时的软件进行支撑,它负责比如启停容器 这样的事情.最广为人知的容器运行时 ...

  4. Linux下的ARP攻击-断网

    1.软件工具安装 1. nmap --网络嗅探工具 2. dsniff ( arpspoof )    --ARP嗅探工具 3. net-tools ( ifconfig ) --网络工具 sudo ...

  5. 排坑&#183;ASCII码为160的空格(nbsp)

    阅文时长 | 2.83分钟 字数统计 | 1345.2字符 『排坑·ASCII码为160的空格(nbsp)』 编写人 | SCscHero 编写时间 | Wednesday, September 9, ...

  6. [Linux] 完全卸载mysql

    参考 https://www.jianshu.com/p/ef58fb333cd6

  7. inux操作系统测试工具

    inux操作系统测试工具 转载minions_222 最后发布于2017-08-02 11:42:13 阅读数 1481  收藏 展开 转自:http://blog.csdn.net/crisscha ...

  8. 解决Windows路径太长的目录以及文件名超长删除的问题

    因Windows文件夹有长度限制,在路径太深,长度达到600多个字符时,删除文件时出现报错"源文件名长度大于文件系统支持的长度.请尝试将其移动到具有较短路径名称的位置,或者在执行此操作前尝试 ...

  9. linux route命令的使用详解-(转自小C爱学习)

    route命令用于显示和操作IP路由表.要实现两个不同的子网之间的通信,需要一台连接两个网络的路由器,或者同时位于两个网络的网关来实现.在Linux系统中,设置路由通常是 为了解决以下问题:该Linu ...

  10. 彻底解决Could not transfer artifact org.apache.maven.plugins问题

    今天在学习maven框架的时候出现Could not transfer artifact org.apache.maven.plugins问题,后面根据很多博客综合总结,终于解决了,现在分享一下我的方 ...