Spade Lushen's blog

随手写点值得记录的东西,看心情更新

Tailscale客户端广播路由导致同一内网其他客户端无法正常使用内网的解决方法

Tailscale的路由广播功能(--advertise-routes)可以将客户端所在内网开放给同用户其他客户端使用。 但如果同一个内网下还有其他设备也在使用Tailscale,且接受了路由广播(--accept-routes),那么在连接上Tailscale之后就会无法与内网通信,甚至连路由器都ping不通。 目前这个问题官方还没有解决,但是有一些临时的解决方法。 Windows 在广播了路由的客户端上,将原本广播的子网范围扩大即可。比如原本广播的是192.168.1.0/24,则修改为192.168.0.0/23,这样就会优先走物理网卡访问内网了。 Linux Linux系统上,Tailscale会使用52号路由表,优先级高于主路由表,这就导致上面的方法不适用于Linux客户端,因为不管怎么改,都是先从Tailscale的路由表里走。 不过Linux可以通过对路由表做修改来解决。以广播192.168.1.0/24为例,有以下两种修改方法: 1. 从Tailscale的路由表中删除该子网路由: ip route delete table

ESXi Linux虚拟机控制台键盘输入不正常的解决办法

今天不知道为什么,在ESXi虚拟机中安装Linux系统时,控制台安装界面不响应我的键盘输入,图形安装界面却可以,不管用Web界面还是VMRC、VMWare都是这样,已经安装好的虚拟机狂敲按键能输入两个字符,按Ctrl+Alt+Fx可以切换tty。 搜了一晚上,终于搜到了解决方法,结果非常简单,就是把键盘的ScrollLock关掉就行了……

Gradle

解决使用logback 1.2.5以上版本时报错Error during SAX paser configuration

我的Ktor server项目要使用call logging功能,于是通过Gradle导入了最新版本(1.4.4)的logback: implementation("ch.qos.logback:logback-classic:1.4.4") 然而在启动时报了这样的错误: ch.qos.logback.core.joran.spi.JoranException: Error during SAX paser configuration. See https://logback.qos.ch/codes.html#saxParserConfiguration at ch.qos.logback.core.joran.event.

C#

C#使用HttpClient时正确导入客户端证书

项目需要使用.Net调用api,并且需要使用客户端证书,上网搜了一圈,按照搜到的方法导入: //配置HttpClient选项 var clientHandler = new HttpClientHandler(); //使用客户端证书 clientHandler.ClientCertificateOptions = ClientCertificateOption.Manual; clientHandler.ClientCertificates.Add(new X509Certificate2("client.crt.pem")); var client = new HttpClient(clientHandler); 但是印象中使用客户端证书需要有私钥,代码里并没有涉及私钥,调用也果然出错了,于是把添加的语句改成了: clientHandler.ClientCertificates.Add(X509Certificate2.CreateFromPemFile( "client.crt.pem", "client.key.pem" )); 结果还是报错:

MIUI使高刷模式不再动态降低刷新率

su pm disable com.miui.powerkeeper/com.miui.powerkeeper.statemachine.PowerStateMachineService 在终端或adb shell中执行以上命令即可。需要root权限。 在MIUI eu weekly 21.4.29版本上测试通过。 MIUI开启高刷模式后,打开特定的App时,还是会降低刷新率,并且不提供关闭这个特性的选项。 也许初衷是为了省电,但是降低刷新率还会明显感觉到触摸延迟增加,影响使用体验。同时,我的Redmi K30 5G的电池本就不能满足我一天中度使用,改变不了我随身携带移动电源的习惯,耗电量的增加对我来说完全无所谓。 最开始在网上找到了两种方法:第一种是修改两个系统props,测试无效;第二种是清除“电量和性能”这个App的数据,有效,但是每次重启后都要重新操作,每次清除还会重置我对App设置的杀后台策略,非常麻烦。 不过,

JetBrains

IntelliJ IDEA 2020.3 Gradle项目 调试/运行窗口还原回以前的样子

IDEA升级到2020.3之后,运行/调试窗口变成了这副奇葩模样: 完全不是以前熟悉的样子了,变得非常难用…… 搜了半天解决方法也没有搜到。 今天终于想到了原因,搜到了解决方法:https://stackoverflow.com/questions/56513939/how-can-i-disable-intellij-using-the-gradle-run-task-to-run-my-code 原来是藏在这个地方: 全部改成IntelliJ IDEA 熟悉的界面就回来了。

OpenWrt

小米路由器Pro(R3P)刷入OpenWrt

公司办公室前段时间想换掉老旧的TP-LINK路由器,因为实在太难用。 选路由器这个任务交给了我,综合考虑之后我选择了小米路由器Pro,因为性价比不错,而且有官方的OpenWrt支持。 以下是刷入OpenWrt的过程。 准备工具 U盘一个(格式化成FAT32格式) 能访问路由器后台、访问SSH、使用U盘的电脑或手机一台 预先下载好OpenWrt固件(下载页面,找Firmware OpenWrt Install URL) 刷入开发版固件,获取SSH访问权限 获取到SSH权限之后,才能刷入非官方固件。 小米路由器获取SSH权限的方法是:先刷入开发版固件,用app绑定路由器后,访问开放平台,可以获取到root密码、下载ssh解锁工具。 接下来就按照这个步骤进行操作: 首先,打开MiWiFi下载页,点击ROM图标,找到小米路由器Pro 开发版的下载按钮,点击下载。 开发版最后一次更新时间是2017年8月25日,看样子也不会再更新了,所以直接放下载链接。 下载好固件后,直接去路由器后台更新固件,等待重启。 手机下载MiWiFi app,

Android

Android TextView 设置跑马灯效果

为TextView设置这些属性: ellipsize = TextUtils.TruncateAt.MARQUEE transformationMethod = SingleLineTransformationMethod.getInstance() setHorizontallyScrolling(true) 只有在TextView得到了焦点或处于selected状态时,才会开始滚动。 为方便使用,可以继承TextView并重写isFocused()方法,让TextView认为焦点一直有获取到: override fun isFocused() = true 默认情况下,TextView只滚动3次 。 可以修改marqueeRepeatLimit为-1,以实现无限滚动。当然,也可以指定滚动次数。 marqueeRepeatLimit = -1 TextView需要在绘制完成后,设置的文本才能滚动。 如果设置文本的操作是异步的,存在绘制完成前就设置了文本的可能,那就会出现一开始不滚动,关屏重开、弹出输入法等操作之后才开始滚动的情况。 这种情况则需要使用post来设置文本:

Android

Gradle 3.4.0以上版本自定义apk输出目录和输出文件名

因为需要实现自动化打包,所以需要自定义输出目录(绝对路径)和文件名。 但是搜索了很久,都没有找到有效的办法,很多都是旧版Gradle才能用的语法,新版Gradle编译不通过。 终于还是找到了可用的方法,在module层的build.gradle增加如下代码: android { applicationVariants.all { variant -> variant.outputs.all { output -> variant.packageApplicationProvider.get().outputDirectory = new File("path/to/output/dir") outputFileName = "output_file_name.apk" } } } 需要注意: 1.如果是多种build type或多种flavor同时打包,记得在输出文件夹路径或文件名中引用相关变量。 2.Android Studio调试时,

C#

C#实时判断Socket客户端的连接状态

C#的Socket.Connected属性,返回的是上次IO操作时的连接状态。如果之后连接被断开,访问这个属性依然会返回true,因此不能使用这个属性来判断实时连接状态。 C#还提供了一个Poll(int microSeconds, SelectMode mode)方法。如果mode参数定义为SelecMode.SelectRead时,这个方法在以下情况下会返回true: * 调用了Listen(即Socket处于监听模式)并且有挂起的连接 * 有数据可以读取 * 连接被关闭、重置或终止 因此,在客户端模式下,调用Socket.Poll(timeout, SelectMode.SelectRead),会出现以下结果: * 如果连接没有断开,并且没有数据等待接收(即Socket.Available == 0) ,返回false * 如果连接没有断开,并且有数据等待接收,返回true * 如果连接已断开,返回true 所以,进行以下判断:

Android

Bundle的数据大小限制

使用Intent去启动另一个 Activity,同时带上extra,包含一个Parcelable对象的数组。数组内的数据是不固定的,与业务有关。 开发时只测试了少量数据时的表现,一切正常,结果今天发现,数据量到达2500条左右时,无法启动目标Activity,并且还会发生异常。 于是改成了临时存储在一个静态map里,并将key作为extra,在目标Activity中取出并删除,避免了这个问题。

Android

NestedScrollView嵌套RecyclerView的问题

NestedScrollView嵌套RecyclerView,或者ScrollView嵌套RecyclerView并开启nestedScrolling,会导致RecyclerView创建所有item的ViewHolder,数量巨大时会非常卡。 如果需要解决,最好是只使用RecyclerView,通过viewType来创建不同的ViewHolder。 要注意的是,使用RecyclerView时,未显示的View会被回收,需要注意保存状态,比如输入框的文字;或者使用 RecyclerView.recycledViewPool.setMaxRecycledViews(viewType, 0) 来禁用指定viewType的回收。

Android

将App从后台切换回前台

使用极光推送的过程中, 发现每次点击通知的时候, 都会重启一遍App 猜测原因是极光推送识别到我的主Activity是BootActivity, 但是实际上在启动到MainActivity的时候, 它就已经finish了, 导致极光认为我程序已经关闭, 所以重启了一次 解决方法也很简单, 极光提供了通知点击的广播, BroadcastReceiver注册之后, 点击通知的事件就由开发者接管, 因此在onReceive里面添加如下代码, 就可以让app回到前台而不会重启: context.startActivity(Intent(context, MainActivity::class.java) .setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED or Intent.FLAG_ACTIVITY_NEW_TASK)) 注意代码中给Intent定义的Activity不是BootActivity 这样设置之后, 点击推送时, 如果app正在后台运行, 就跟从最近任务里打开的效果一样; 如果不在后台运行,