乱码三千 – 分享实用IT技术

乱码三千 – 码出一个新世界


  • 首页

  • 归档

  • 搜索

关于Git查看提交修改的文件列表

发表于 2022-06-28

1.查看最后一次提交记录的修改文件信息

1
git show --raw

2.查看指定commit id对应修改文件列表

1
git show --raw commit_id
1
git show --raw 2f80f1c8bb2cb8e91d22ad38480b681c194f6518

3.查看所有提交记录的修改文件信息

1
git log --stat
1
git log --name-only

4.查看所有修改相关的commit ID和comment信息

1
git log --pretty=oneline

5.查询指定author的修改信息

1
git log --author=jack.li

6.查看指定author在指定时间修改信息

1
2
3
4
5
6
7
8
$ git log --pretty="%h - %s" --author='Junio C Hamano' --since="2008-10-01" \
--before="2008-11-01" --no-merges -- t/
5610e3b - Fix testcase failure when extended attributes are in use
acd3b9e - Enhance hold_lock_file_for_{update,append}() API
f563754 - demonstrate breakage of detached checkout with symbolic link HEAD
d1a43f2 - reset --hard/read-tree --reset -u: remove unmerged new paths
51a94af - Fix "checkout --track -b newbranch" on detached HEAD
b0ad11e - pull: allow "git pull origin $something:$current_branch" into an unborn branch

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

说说移动设备的各种标识码(DeviceID, IMEI, UUID, UDID, OAID, IDFA, GAID

发表于 2022-04-25

名词解释

  • Device ID:设备ID。

  • IMEI:(International Mobile Equipment Identity)国际移动设备标识的缩写。是由15位数字组成的“电子串号”,它与每台手机一一对应,每个IMEI在世界上都是唯一的。

  • MEID:(Mobile Equipment Identifier) 移动设备识别码是CDMA手机的身份识别码,也是每台CDMA手机或通讯平板唯一的识别码。

  • IDFA:(Identifier For Advertising)iOS独有的广告标识符。

  • UDID:(Unique Device Identifier)唯一设备标识码。

  • UUID:(Universally Unique Identifier)通用唯一识别码。目前最广泛应用的UUID,是微软公司的全局唯一标识符GUID。其目的是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。

  • OAID:(Open Anonymous Device Identifier) 开放匿名设备标识符。

  • GAID:Google Advertising ID。

详细介绍

(1)Device ID

设备ID,具有唯一性。其中又包括IMEI 和 MEID/ESN。一般情况我们获取手机的DeviceId也就是手机的IMEI码。

(2)IMEI

GSM设备返回的,并且是写在主板上的,重装APP不会改变IMEI。

Android:Android 6.0以上系统需要用户授予read_phone_state权限(弹窗授权),如果用户拒绝就无法获得。但是Andorid Q(10.0)版本,将无法获取IMEI,出于用户隐私等一系列安全问题的考虑。将用其他匿名标识符代替,比如OAID。

IOS:iOS 5 之后被禁止。

IMEI是联通、移动手机的标识,MEID是电信手机的标识。

IMEI可以在在移动设备上查询到,一般来说一个双卡手机不止一个IMEI值,全网通双卡手机有两个IMEI和一个MEID。

(3)MEID

在手机键盘直接键入*#06#可获得。

MEID/ESN:CDMA设备返回的,一般不使用。

(4)mac地址

硬件标识符,包括WiFi mac地址和蓝牙mac地址。

Android:Android 6.0之后被禁止,若获取则会被判定为有害应用。

IOS:iOS 7 之后被禁止(同时禁止的还有OpenUDID)。

(5)UDID

用来标示设备的唯一性,由40个字符的字母和数字组成 。

Android:获取UUID后,写入.so文件生成UDID。即使APP重装,值也不变,除非root手机(普通用户做不到)。

IOS:iOS 6 之后被禁止获取系统原生的UDID,但可以通过uuid,写入到钥匙串中,从而获得自定义的UDID(非系统原生),即使用户重装APP,只要每次都取这个钥匙串返回,就是不变的。

OpenUDID:是一个替代 UDID 的第三发解决方案。缺点是如果你完全删除全部带有 OpenUDID SDK 包的 App(比如恢复系统等),那么 OpenUDID 会重新生成,而且和之前的值会不同,相当于新设备。

(6)UUID

APP重装后会改变。

(7)IDFA

IDFA 是苹果 iOS 6 开始新增的广告标识符,用于给开发者跟踪广告效果用的,可以简单理解为 iPhone 的设备临时身份证,说是临时身份证是因为它允许用户更换,IDFA 存储在用户 iOS 系统上,同一设备上的应用获取到的 IDFA 是相同的。iOS 用户可以通过(设置程序 -> 通用 -> 还原 -> 还原位置与隐私)更换 IDFA,iOS 10 系统开始提供禁止广告跟踪功能,用户勾选这个功能后,应用程序将无法读取到设备的 IDFA。(在统计唯一用户的时候,IDFA 的可变性会造成部分用户的重复统计。)

适用于对外:例如广告推广,换量等跨应用的用户追踪等。

总结:iOS 6 时面世,可以监控广告效果,同时保证用户设备不被APP追踪的折中方案。可能发生变化,如系统重置、在设置里还原广告标识符。用户可以在设置里打开“限制广告跟踪”。

(8)OAID

APP类广告效果追踪需要使用到用户的设备标识进行广告点击和转化效果的匹配,而安卓系统当前强依赖于IMEI的获取,上面提到Andorid Q(10.0)版本后,将无法获取IMEI。基于此背景,进行广告投放的效果追踪,需要能够替代及补充IMEI的设备标识。

目前OAID作为目前市场主流的归因补充标识,OAID是由中国信息通讯研究院号召,移动安全联盟推行的安卓设备标识,在无法获取设备号情况下可以使用该标识进行数据匹配,目前主流厂商均已在新版本系统中支持该标识的获取,具有权威性。OAID可以理解为是国内联合推出的广告标识。

与IDFA类似,对于OAID 用户也可以手动在设置中通过重置广告标识符更换OAID或者“限制广告跟踪”。

与OAID相关的设备标识体系中拥有四种设备标识:

(9)GAID

Google推出的广告标识,与IDFA类似。在装了google play service的安卓手机上,才可以获取到GAID。

广告id是用户特殊的,独特的,可重置的广告id,由Google Play Service 提供,它为用户更好的控制,为开发人员提供简单、标准的系统继续使用你的应用程序,它用于广告目的的匿名标示符和或者重置其标示符或者退出以利益为基础的Google Play的应用程序。

主要用于广告定向,并且和IDFA一样 对于GAID 用户也可以自己设置禁止广告跟踪。

整理,借鉴了多个地方的资料,有不准确的欢迎指出!

用户唯一标识

为什么要建设用户唯一标识

如何区分某个用户就是他这个用户,而不是另一个用户,在数据埋点中,是一个非常重要的事情。因为如果做不到用户的唯一识别,那凡是涉及到用户的数据都将是错的(比如用户量、新增用户数、活跃用户数等等)。所以建设用户唯一标识,尤为重要。

基本概念

设计埋点字段的时候,有两个字段是一定要包括的,即设备ID和用户ID。这两个字段应该纳入通用字段,每个埋点的事件都必须要集成收集。

(1)设备ID

使用相应的算法,生成一个设备ID,以唯一识别用户的终端设备。不同终端的设备ID,其生成算法规则不一样,以下列举不同终端的设备ID的生成规则:

AndroidApp

安卓系统历经多次升级,对权限控制越来越严格,唯一识别手机的方法也在发生变化。下面整理一下安卓系统适合做设备唯一标识符的几个标识符,以及其特性:

img

从表格中看出,IMEI是最适合做设备唯一标识的,奈何获取IMEI需要授予权限且Android 10以后不再开放IMEI的权限。综合起来,安卓系统中,应该按照IMEI ->OAID -> ANDROID_ID的顺序生成设备ID。即先获取IMEI号,获取不到IMEI时获取OAID,获取不到OAID时,再获取ANDROID_ID,然后使用相关算法生成设备ID。

IOS App

苹果系统,可用于识别唯一设备的标识不多,如下图。综合起来,苹果系统生成设备ID的标识符顺序应该是IDFA -> IDFV ->UDID,即先获取IDFA,获取不到在获取IDFV,获取不到IDFV时,再获取UDID,然后使用相关算法生成一个设备ID。

img

Web网站

Web网站,使用cookie_id作为设备ID,并存储在浏览器的cookie中。

微信小程序

通常做法使用openid作为设备ID,当然也可以自己生产一个ID,作为设备ID。如果用过openid作为设备ID,需要注意微信小程序的冷启动问题(获取 openid 是一个异步的操作,所以会导致数据上报的时候,可能还没获取到openid,这就是导致设备ID为空)。

2)用户ID

用户ID,即用户在业务产品注册的用户账号。

收集到设备ID和用户ID后,就要想办法将设备ID和用户ID关联起来,也即用户唯一标识建模,详见下文。

用户唯一标识建设

设计一个字段,比如就叫distinct_id(设备ID命名为device_id,用户ID命名为user_id)这个字段用于识别唯一用户。凡是统计用户相关的数据时,都以distinct_id作为用户的唯一区别标识。下面,以具体案例进行阐述。

img

步骤说明:

  1. 小明在一部手机上启动了app。该手机的device_id为x1,此时生成一个dsitinct_id为d1;
  2. 小明在这个手机上用账号u1进行登录。此时device_id为x1,user_id为u1,dsitinct_id为d1;
  3. 小明继续在这手机上使用app。此时device_id为x1,user_id为u1,dsitinct_id为d1;
  4. 小明退出自己的账号,继续使用app。此时仍然device_id为x1,user_id为u1,dsitinct_id为d1;
  5. 小明把手机给了小花,小花用自己的账号u2登录app。此时u2去关联x1,因为x1已经与u1关联,故关联失败。所以重新生成一个distinct_id为d2来标识此用户(u2);
  6. 小花继续使用app。此时device_id为x1,user_id为u2,dsitinct_id为d2;
  7. 小明换了部新手机,使用app。此时device_id为一个新的x2,后台生成一个新的dsitinct_id为d3;
  8. 小明在新手机上,使用账号u1登录了app。此时u1去关联x2,因为x2之前没有与账号关联过,所以关联成功,但是u1已经有一个dsitinct_id为d1,所以此时的dsitinct_id仍然为d1;
  9. 小明继续在新手机上使用app。此时device_id为x2,user_id为u1,dsitinct_id为d1。

此时三个字段的映射关系为:

img

(1)后续修复

事件字段修复

小明换新手机后,在登录前,系统给分配的dsitinct_id为d3,不符合实际情况,故要将在新手机上登录前的dsitinct_id修复为d1。如下:

img

映射表修复

1)删除d3与x2的映射关系

2)将x2添加到d1的device_id_list字段

img

本文转载自: https://zhuanlan.zhihu.com/p/395387972

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

PC端快速下载视频网站的视频

发表于 2022-04-20

前言

有时候在逛B站或者看QQ音乐MV的时候 有把视频下载到本地的需求

网上有很多针对特定网站视频的解析工具, 比如针对B站的有:

  • B站视频在线解析

  • Downkyi下载姬

  • 唧唧下载器

对于我这种只是偶尔下载个视频的懒人而言, 为了下载一个视频单独安装一个软件, 实在是不太能接受 自然是优先选择在线解析的方案

但是 大部分在线解析网站都是针对某一个或者某几个主流的视频网站进行解析处理, 如果碰上冷门网站 那就有些尴尬了

今天给大家介绍一个非常简单实用, 并且对所有视频网站都通用的一个方法, 只需要一个浏览器就能快速获取视频源地址

原料准备

  • 浏览器 (本文以谷歌浏览器为例)
  • 电脑

开始抓取

  1. 第一步打开浏览器 将视频地址填入地址栏

  2. 按F12进入开发者选项 选择移动设备浏览模式 对Media进行过滤 然后F5刷新页面

    image-20220424112429767

  3. 拷贝视频文件路径 直接浏览器下载或者第三方软件下载

    image-20220424112609595

博主对B站和QQ音乐进行了测试, 均能获取到原视频地址, 如果没有出现地址, 建议多刷新几次试试, 一般都没有问题

很多在线视频解析的原理也就是这么一回事

总结

上面这种方法 相信有很多人都知道, 但如果你不是做技术的, 可能就不太清楚了, 希望本文能帮到大家

另外 如果你有批量下载视频的需求, 该方法就有些费劲了, 这种情况建议大家使用软件进行解析下载

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Android腾讯IM快速集成流程和注意事项

发表于 2022-04-01

前言

最近项目集成了及时通信业务, 采用的是腾讯IM

首先是SDK的快速集成, 步骤如下:

第一步 安装依赖包

1
implementation 'com.tencent.imsdk:imsdk-plus:5.6.1200'

第二步 初始化SDK (需要用到appid)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
V2TIMManager.getInstance()
.initSDK(this, "这里填写appid", V2TIMSDKConfig().apply {
logLevel = V2TIMSDKConfig.V2TIM_LOG_INFO


}, object : V2TIMSDKListener() {
override fun onConnecting() {
Log.i("KIM", "正在连接到腾讯云服务器")
}

override fun onConnectSuccess() {
Log.i("KIM", "已经成功连接到腾讯云服务器")

}

override fun onConnectFailed(code: Int, error: String?) {
Log.i("KIM", "连接腾讯云服务器失败 $code $error")
}
})

第三步 登录IM

1
2
3
4
5
6
7
8
9
10
11
12
V2TIMManager.getInstance().login(
"这里填当前用户id",
"这里填登录密钥 从后台获取",object : V2TIMCallback {
override fun onSuccess() {
Log.i(TAG, "登录成功")

}

override fun onError(code: Int, desc: String?) {
Log.i(TAG, "登录失败 $code $desc")
}
})

第四步 加入群聊房间 (如果是单聊可以跳过该步骤)

1
2
3
4
5
6
7
8
9
10
V2TIMManager.getInstance().joinGroup("这里填房间号 从后台获取", "", object : V2TIMCallback {
override fun onSuccess() {
Log.i(TAG, "加入群组成功")

}

override fun onError(code: Int, desc: String?) {
Log.i(TAG, "加入群组失败 $code $desc")
}
})

第五步 接收消息

如果业务不涉及音视频传输:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
V2TIMManager.getInstance().addSimpleMsgListener(object :
V2TIMSimpleMsgListener() {

// 对应 sendGroupCustomMessage
override fun onRecvGroupCustomMessage(
msgID: String?,
groupID: String?,
sender: V2TIMGroupMemberInfo?,
customData: ByteArray?
) {
super.onRecvGroupCustomMessage(msgID, groupID, sender, customData)
Log.i(TAG, "接收群聊自定义消息")
}

// 对应 sendGroupTextMessage
override fun onRecvGroupTextMessage(
msgID: String?,
groupID: String?,
sender: V2TIMGroupMemberInfo?,
body: String?
) {

Log.i(TAG, "接收群聊文本消息 $body")

}
override fun onRecvC2CTextMessage(msgID: String?, sender: V2TIMUserInfo?, text: String?) {
super.onRecvC2CTextMessage(msgID, sender, text)
Log.i(TAG, "接收单聊文本消息 $text")
}

override fun onRecvC2CCustomMessage(
msgID: String?,
sender: V2TIMUserInfo?,
customData: ByteArray?
) {
super.onRecvC2CCustomMessage(msgID, sender, customData)
Log.i(TAG, "接收单聊自定义消息 ")
}


})

如果业务涉及音视频传输:

1
2
3
4
5
6
V2TIMManager.getMessageManager().addAdvancedMsgListener( object : V2TIMAdvancedMsgListener() {
override fun onRecvNewMessage(msg: V2TIMMessage?) {
Log.i(TAG, "收到消息类型${msg?.elemType} ")

}
})

第六步 发送消息

如果业务不涉及音视频传输:

  1. 单聊 (分为文本消息和自定义消息)

    • 文本消息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      V2TIMManager.getInstance().sendC2CTextMessage("这里填需要发送的文本",
      "这里填对方的登录id",
      V2TIM_PRIORITY_HIGH,
      object : V2TIMValueCallback<V2TIMMessage> {
      override fun onSuccess(t: V2TIMMessage?) {
      Log.i(TAG, "发送单聊文本消息成功")
      }

      override fun onError(code: Int, desc: String?) {
      Log.i(TAG, "发送单聊文本消息失败 $code $desc")
      }
      })
    • 自定义消息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      V2TIMManager.getInstance().sendC2CCustomMessage("这里填需要发送的自定义消息 格式为字节数组",
      "这里填对方的登录id",
      V2TIM_PRIORITY_HIGH,
      object : V2TIMValueCallback<V2TIMMessage> {
      override fun onSuccess(t: V2TIMMessage?) {
      Log.i(TAG, "发送单聊自定义消息成功")
      }

      override fun onError(code: Int, desc: String?) {
      Log.i(TAG, "发送单聊自定义消息失败 $code $desc")
      }
      })
  1. 群聊 (分为文本消息和自定义消息)

    • 文本消息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      V2TIMManager.getInstance().sendGroupTextMessage("这里填需要发送的文本",
      "这里填群聊房间id",
      V2TIM_PRIORITY_HIGH,
      object : V2TIMValueCallback<V2TIMMessage> {
      override fun onSuccess(t: V2TIMMessage?) {
      Log.i(TAG, "发送群文本消息成功")
      }

      override fun onError(code: Int, desc: String?) {
      Log.i(TAG, "发送群文本消息失败 $code $desc")
      }
      })
  • 自定义消息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    V2TIMManager.getInstance().sendGroupCustomMessage("这里填需要发送的自定义消息 格式为字节数组",
    "这里填群聊房间id",
    V2TIM_PRIORITY_HIGH,
    object : V2TIMValueCallback<V2TIMMessage> {
    override fun onSuccess(t: V2TIMMessage?) {
    Log.i(TAG, "发送群自定义消息成功")
    }

    override fun onError(code: Int, desc: String?) {
    Log.i(TAG, "发送群自定义消息失败 $code $desc")
    }
    })

如果业务涉及音视频传输:

该方法支持音视频图片 文本和自定义消息

1
V2TIMManager.getMessageManager().sendMessage()

注意事项

这里的坑主要在于消息的收发, 官方给的API比较多, 单聊和群聊串在一块, 看文档有时容易懵, 这里对收发消息的使用做了一个总结, 如下:

1
2
3
4
5
* IM群聊和单聊的消息收发走的是同一套  总共分为两种需求:
* 第一种: 不涉及音视频 只涉及字符串传输 那么监听消息使用addSimpleMsgListener 发消息使用sendC2C(Group)TextMessage和sendC2C(Group)CustomMessage
* 第二种: 涉及音视频等复杂消息传递 那么监听消息使用addAdvancedMsgListener 发消息使用V2TIMManager.getMessageManager().sendMessage()
*
* 注意: setGroupListener不是监听的群聊消息 而是对群组相关的事件监听 比如群创建通知 公告等等

附加

腾讯IM集成文档

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Android集成支付宝支付以及沙箱环境测试注意事项

发表于 2022-03-01

前言

现在很多App都涉及到支付, 毕竟开发APP目的是为了盈利吗, 通常我们会在App中同时集成支付宝和微信支付, 方便用户消费

集成相关

支付宝的集成相对简单, 前端无需添加APPID和密钥, 签名部分在在后端完成,只要确保应用签名和开发平台填入的保持一致就行, 不像微信不仅要配置APPID, 还要添加WXEntryActivity ,此外包名也必须匹配 少一个都不行

当然两者都需要在开发平台填入一个应用签名, 格式为MD5

沙箱环境

当我们SDK集成完毕, 后台接口也调好后, 开始测试支付, 那么问题来了, 支付可是得花钱的呀, 为了解决这个问题, 支付宝官方给我们提供了一个沙箱环境的支付宝, 方便开发测试

简而言之就是提供了一个测试用的支付宝账号, 里面有很多钱, 可以随便花, 哈哈, 不过仅限于测试

这个测试账号不能直接登录我们平常用的支付宝App, 需要下载一个沙箱用的支付宝App

这里提供了沙箱支付宝App下载地址

沙箱支付宝App下载

沙箱测试

安装完毕后登录沙箱账号, 看看你的应用能否调起支付

此时你会发现支付宝调不起来 ,如果返回错误码4000, 那么就是没有检测到沙箱支付宝App

此时 需要在支付Activity中开启沙箱环境, 代码如下:

1
2
3
4
5
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX)

}

如果依然无效, 那么大概率是后台签名出了问题, 后台网关需要改成沙箱环境地址

总结

支付宝集成归纳如下:

  • 前端无需配置APPID, 只需确保应用签名正确 包名不一样也能正常调起
  • 沙箱测试, 前端需要使用EnvUtils开启沙箱环境
  • 沙箱支付宝登录使用开发后台提供的买家账号登录, 用于支付

微信支付集成归纳如下:

  • 前端需要配置APPID和密钥 同时包名和应用签名需要正确

  • 还需要再创建一个WXEntryActivity, 并在清单文件中注册

    清单文件中可以考虑使用activity-alias 实现多合一注册, 示例如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
      <activity-alias
    android:name="${applicationId}.wxapi.WXPayEntryActivity"
    android:exported="true"
    android:targetActivity="WeiXinHandlerActivity"
    android:theme="@android:style/Theme.Translucent.NoTitleBar" />
    <activity-alias
    android:name="${applicationId}.wxapi.WXEntryActivity"
    android:exported="true"
    android:targetActivity="WeiXinHandlerActivity"
    android:theme="@android:style/Theme.Translucent.NoTitleBar" />

附加

支付宝支付客户端 DEMO&SDK下载

支付宝支付集成文档

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Studio报错解决 Duplicate class kotlinx.android.parcel.IgnoredOnParcel found in modules

发表于 2022-02-17

问题

Android开发过程中给项目添加依赖包时Studio偶尔出现以下问题:

1
Duplicate class kotlinx.android.parcel.IgnoredOnParcel found in modules

原因是依赖冲突

解决方案

排除org.jetbrains.kotlin包, 如下:

1
2
3
implementation ('xxxx:1.0.0'){
exclude group : "org.jetbrains.kotlin"
}

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Git回退部分文件到历史版本

发表于 2022-02-11

前言

有时候我们需要将部分代码或者部分图标恢复到历史某个版本, 此时我们可以采取以下方式

步骤如下

  1. 查看某个文件的历史版本记录

    1
    git log xxx.png

    image-20220211180311813

  2. 对某个文件进行版本回退

    1
    git reset 656fdsf65 xxx.png

    此时你会发现文件并未发生改变, 并提示unStage, 我们需要使用checkout将文件检出

  3. 检出文件内容

    1
    giit checkout xxx.png

    这时文件已经恢复

  4. 最后重新提交push即可

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

关于BottomSheetDialogFragment使用过程中常见的一些问题

发表于 2022-02-10

问题汇总

1.默认白底去除

​ BottomSheetDialogFragment布局默认有个白色背景, 当你给布局加圆角时就会发现这个问题, 如果我们要将这个背景改为透明, 可采用以下做法:

1
2
3
4
5
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//去除默认的白底
(binding.root.parent as? View)?.setBackgroundColor(Color.TRANSPARENT)
}

2.下滑后弹窗未完全关闭 半透明蒙层还在

我们一般会配合setDimAmount()函数来这只弹窗的背景透明度, 比如:

1
2
3
4
override fun onStart() {
super.onStart()
dialog?.window?.setDimAmount(0.6f)
}

如果peekHeight属性值没有设置到位的话, 比如设置为0, 就容易出现下滑关闭弹窗后半透明背景依旧存在, 需要再点一次才会消失, 实际上是因为弹窗未完全关闭导致的, 解决方法是将布局的实际高度赋值给peekHeight, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val onGlobalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener {
val dialog = dialog as BottomSheetDialog
val bottomSheet =
dialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet) as FrameLayout?
val behavior = BottomSheetBehavior.from(bottomSheet!!)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.peekHeight = view.height
}
}

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

利用Android Studio快速给测试机截屏录屏

发表于 2022-01-12

前言

开发测试时, 可能会碰到需要给同事发送测试机上的应用截屏录屏, 如果你用的是自己的手机, 那么自然方便, 通过手机自带的截屏录屏功能配合微信QQ等完美搞定

但是, 如果测试机是公司的, 你只是临时一用, 同事要求你把测出的bug复现录个屏, 这时就有些麻烦了, 因为公司的测试机可能没有装微信等通讯软件, 即便装了也没有登录, 那图片视频就没法发送了, 想要解决有以下几种方法:

  • 给测试机装个微信然后登录或者安装局域网通讯应用
  • 将App装到自己手机上
  • 使用Android Studio的Device File Explorer, 将设备中的截图捞出来

以上三种方式, 无论哪一种似乎都有些麻烦

此时的你开始暴躁, 最后索性改用模拟器 哈哈

事实上 如果你是Andorid开发, 那么我们的Android Studio可以完美解决以上的问题, Studio不仅可以给手机截屏, 还能录屏!!!

是的 你没听错, Android Studio 就是这么滴强大 嘿嘿

准备条件

将手机调试模式连接至Android Studio

截屏

  1. 第一种方式 使用layout Inspector

    image-20220112171043000

    这是Studio自带的应用布局查看器, 可以快速查看当前应用打开界面

  2. 第二种方式 使用logcat工具 (推荐)

    image-20220112171505186

    点击截屏后:

    image-20220112171555143

    这个工具可以随意截取手机上任意一个页面, 不仅能对截取的图片进行旋转操作, 还可以添加手机边框, 去除阴影等等:

    image-20220112171922931

录屏

同样是logcat选项卡中的一个小工具:

image-20220112172036776

​ 点击后可以对录制的码率可宽高进行提前设置:

image-20220112172132571

最后导出的格式是Mp4, 配合Mp4在线转gif工具, 我们可以很方便地给文章配测试动态图, 下面是录屏效果图:

device-2022-01-12-173145

总结

对于Studio这两个功能, 我个人是非常满意的, 测试机截屏录屏分分钟搞定, 不用担心图片不方便传输了

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

python中播放音频的若干种方式

发表于 2022-01-11

前言

最近在做一个音乐自动化的工具, 涉及到背景音乐播放, 这里总结几种播放音频的方法, 如下

第一种 使用pygame模块

  1. 模块安装

    1
    pip install pygame
  2. 播放代码示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import pygame

    def playMusic(fileName):
    # 初始化
    pygame.mixer.init()
    # 加载音频文件
    pygame.mixer.music.load(fileName)
    # 设置音量 范围为0.0到1.0
    pygame.mixer.music.set_volume(0.5)
    # 开始播放
    pygame.mixer.music.play()

优点: 支持wav和mp3格式

缺点: 声音是单声道的, 失真比较严重

第二种 使用pyaudio模块

  1. 模块安装

    1. 由于pyaudio的运行需要依赖portaudio, 因此需要先安装

      Mac平台直接运行以下指令即可

      1
      brew install portaudio

      其他平台可参考pyaudio官方说明文档 点击进入

    2. 安装pyaudio

      1
      pip install pyaudio
  2. 播放代码示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import pyaudio
    import wave

    def playMusic(fileName):

    chunk = 1024
    wf = wave.open(fileName, 'rb')
    p = pyaudio.PyAudio()
    stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
    channels=wf.getnchannels(),
    rate=wf.getframerate(),
    output=True)
    data = wf.readframes(chunk)
    while len(data) > 0:
    stream.write(data)
    data = wf.readframes(chunk)
    stream.stop_stream()
    stream.close()
    p.terminate()

优点: 支持音频录制, 回放为立体声, 支持Windows, Mac和Linux平台

缺点: 不能播放mp3格式音频, 模块安装相对费劲

第三种 使用simpleaudio

  1. 模块安装

    1
    pip install simpleaudio
  2. 播放代码

    1
    2
    3
    4
    5
    import simpleaudio as sa
    def playMusic3(fileName):
    wave_obj = sa.WaveObject.from_wave_file(fileName)
    play_obj = wave_obj.play()
    play_obj.wait_done()

优点: 声音为立体声 安装简单

缺点: 不能播放mp3格式音频

第四种 综合版

上面几种方式总是不太完美, 有没有一种既支持mp3和wav格式, 又能立体声播放, 同时使用起来又方便的方式, 答案是 没有 哈哈

不过 我们可以借助一个音频格式转换工具然后配合simpleaudio, 就能实现我们的完美需求, 这个工具就是pydub

pydub不仅可以对音频进行格式转换, 还可以对音频进行切片(比如我播放音乐的前10秒 ), 淡入淡出, 以及获取音频总时长

更多用法可以参见github :

GitHub

多格式音频播放实现
  1. 模块安装

    1
    pip install pydub
  2. 格式转换 比如mp3转wav

    1
    2
    3
    4
    5
    from pydub import AudioSegment

    def trans_mp3_to_wav(filepath):
    song = AudioSegment.from_mp3(filepath)
    song.export("now.wav", format="wav")
  3. 配合simpleaudio播放音频

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    from pydub import AudioSegment
    import simpleaudio as sa

    def trans_mp3_to_wav(filepath):
    song = AudioSegment.from_mp3(filepath)
    song.export("./now.wav", format="wav")

    def playMusic(fileName):
    wave_obj = sa.WaveObject.from_wave_file(fileName)
    play_obj = wave_obj.play()
    play_obj.wait_done()

    if __name__ == '__main__':
    trans_mp3_to_wav("bgm2.mp3")
    playMusic("now.wav")

注意事项: 由于pydub这个模块, 过分依赖ffmpeg, 因此我们在使用时如果报错:

FileNotFoundError: [Errno 2] No such file or directory: 'ffprobe'

那么需要先安装ffmpeg

各大平台安装ffmpeg方式:

  1. Mac平台

    1
    brew install ffmpeg
  2. Windows平台

    1. 从Windows binaries provided here下载并提取libav;
    2. 添加libav /bin 文件夹到你的环境变量(PATH)
  3. Linux平台

    1
    apt-get install ffmpeg

总结

除了以上几种之外还有一些其他的音频播放模块, 不过大多数都有各自的局限性, 结合自身需求进行选用即可

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

12…33

乱码三千

android程序员一枚,擅长java,kotlin,python,金融投资,欢迎交流~

327 日志
92 标签
RSS
© 2022 乱码三千
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%