20230907
MoMo Lv5

Bug

√ release包出现反序列化失败崩溃

崩溃日志

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
42
43
44
45
46
47
48
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = d7.b)
at android.os.Parcel.writeSerializable(Parcel.java:1843)
at android.os.Parcel.writeValue(Parcel.java:1790)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:932)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1588)
at android.os.Bundle.writeToParcel(Bundle.java:1253)
at android.os.Parcel.writeBundle(Parcel.java:1001)
at android.content.Intent.writeToParcel(Intent.java:10858)
at android.app.IActivityTaskManager$Stub$Proxy.startActivity(IActivityTaskManager.java:3880)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1738)
at android.app.Activity.startActivityForResult(Activity.java:5412)
at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:2)
at android.app.Activity.startActivityForResult(Activity.java:5337)
at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:1)
at android.app.Activity.startActivity(Activity.java:5819)
at android.app.Activity.startActivity(Activity.java:5787)
at c4.y.a(BaseNavigation.kt:8)
at c4.b$a.l(BaseActivity.kt:3)
at c4.b.O(Unknown Source:2)
at c4.w$b.l(BaseFragment.kt:2)
at c4.a.d(R8$$SyntheticClass:5)
at d4.a.d(R8$$SyntheticClass:8)
at androidx.lifecycle.LiveData.b(LiveData.java:6)
at androidx.lifecycle.LiveData.c(LiveData.java:8)
at androidx.lifecycle.x.l(MutableLiveData.java:4)
at d4.b.l(SingleLiveEvent.kt:2)
at androidx.lifecycle.LiveData$a.run(LiveData.java:5)
at android.os.Handler.handleCallback(Handler.java:900)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:219)
at android.app.ActivityThread.main(ActivityThread.java:8673)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1109)
Caused by: java.io.NotSerializableException: com.google.gson.internal.s$a
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1240)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)
at android.os.Parcel.writeSerializable(Parcel.java:1838)
... 32 more

根据mapping.txt文件解出错误类是Clip

出现在从收藏页面分享给app内好友,从首页分享不会出错,问题在于release包的代码混淆导致了解析出错,暂不清楚具体原因

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package com.danale.edge.usersdk.type.clip

import com.danale.edge.foundation.privacy.Fuzzy
import com.danale.edge.usersdk.type.LabelBean
import com.google.gson.annotations.SerializedName

/**
* UserSDK生成的Clip,用于表示一段多媒体资源
* @param timeMillisGenerated 该[Clip]被生成的时间
* @param type 该[Clip]的类型,有两种类型,分别是"本机原始数据(本机生成的)Clip"(type = 1)和"非本机原始数据(非本机生成的)Clip"(type = 2)
* @param clip_id
*/
open class Clip(
@SerializedName("timestamp_ms") val timeMillisGenerated: Long,
@SerializedName("play_mode") val playMode: Int,
@SerializedName("type") val type: Int,
@SerializedName("clip_id") var clipID: String,
@SerializedName("ori_data") var originalDataList: List<OriginalData>? = null,
@SerializedName("non_ori_data_paths") var nonOriginalData: Map<String, String>? = null,
@SerializedName("custom_data") var custom_data: String? = null,
@SerializedName("label_item") var labelInfoList: List<LabelBean>? = null,
@SerializedName("favorite") var favorite: Int
) : java.io.Serializable, Comparable<Any?> {


companion object {
const val CLIP_TYPE_ORIGINAL_DATA = 1 // 原始数据Clip
const val CLIP_TYPE_NON_ORIGINAL_DATA = 2 // 非原始数据Clip

const val CLIP_PLAY_MODE_LOCAL = 0 // 本地播放
const val CLIP_PLAY_MODE_REMOTE_DEVICE = 1 // 设备端播放,需要拉取

fun getStartTimeMillis(clip: Clip) = clip.originalDataList?.get(0)?.startTimeMillis ?: 0
fun getEndTimeMillis(clip: Clip) = clip.originalDataList?.get(0)?.endTimeMillis ?: 0

fun getOriDataListString(clip: Clip): String? {
return clip.originalDataList?.joinToString(separator = "-") {
"${it.deviceID}-${it.startTimeMillis}-${it.endTimeMillis}"
}
}

}

var isFromSelf: Boolean = false

override fun equals(other: Any?): Boolean {
if (other !is Clip) return false
if (timeMillisGenerated != other.timeMillisGenerated) return false
if (type != other.type) return false
if (clipID != other.clipID) return false
if (originalDataList != other.originalDataList) return false
if (nonOriginalData != other.nonOriginalData) return false
if (custom_data != other.custom_data) return false
return labelInfoList == other.labelInfoList
}

override fun toString(): String {
return "timeMillisGenerated=$timeMillisGenerated)," +
"type=$type)," +
"clipID=${Fuzzy.interval(clipID)}, " +
"originalDataList=$originalDataList, " +
"nonOriginalData=$nonOriginalData, " +
"custom_data=$custom_data, " +
"labelInfoList=$labelInfoList, " +
"favorite=$favorite"
}

override fun hashCode(): Int {
var result = timeMillisGenerated?.hashCode() ?: 0
result = 31 * result + (type?.hashCode() ?: 0)
result = 31 * result + (clipID?.hashCode() ?: 0)
result = 31 * result + (originalDataList?.hashCode() ?: 0)
result = 31 * result + (nonOriginalData?.hashCode() ?: 0)
result = 31 * result + (custom_data?.hashCode() ?: 0)
result = 31 * result + labelInfoList.hashCode()
return result
}

override fun compareTo(other: Any?): Int {
if (other !is Clip) return 1
return (clipID ?: "").compareTo(other.clipID ?: "")
}

data class OriginalData(
@SerializedName("device_id") var deviceID: String,
@SerializedName("start_time_ms") var startTimeMillis: Long,
@SerializedName("end_time_ms") var endTimeMillis: Long,
) : java.io.Serializable

class NonOriginalData {
companion object {
const val KEY_MP4_PATH = "mp4_path"
const val KEY_FIRST_FRAME_IMAGE_PATH = "first_frame_image_path"
}
}

}

fun Clip.setIsFromSelf(flag: Boolean) {
isFromSelf = flag
}

fun Clip.getIsFromSelf(): Boolean {
return isFromSelf
}

record

通过GSON进行序列化

2023.9.7

  • 69b6c8a70389bc0e6b1c013e6a5bd7d6667d9111

  • 15db304d83582b514a82dde17fc6665d4543ab27

1
2
3
4
5
val clipString = gson.toJson(clip)
pv.store(EXTRA_SEND_TO_CONTACT_CLIP, clipString)

val clipString = viewModel.pvStoreRepository.read(EXTRA_SEND_TO_CONTACT_CLIP) ?: ""
val clip = viewModel.gson.fromJson(clipString, Clip::class.java)

接收IOS大拿分享的视频出现内部错误

偶现

错误日志

1
2
3
4
5
6
7
8
9
2023-09-05 18:53:41.230 D/VideoFlowActivity: [main-1] handleShareEvent: accept=[true], eventId=[1276c0f077ef4756ad7a4073ab259134]
2023-09-05 18:53:41.230 I/UserSdkNative: [DefaultDispatcher-worker-1-7942] nativeShareHandleEvent, ref=0x2af6
2023-09-05 18:53:41.230 I/UserSdkNative: [Thread-9285-18158] [module/contact] HandleClipShareApply(1276c0f077ef4756ad7a4073ab259134, 1)
2023-09-05 18:53:41.233 E/UserSdkNative: [Thread-9285-18158] [module/contact] HandleClipShareApply(1276c0f077ef4756ad7a4073ab259134, 1) failed, hmi.HandleClipShareApply failed, syncClipNonOriData failed, zip.OpenReader(/storage/emulated/0/Android/data/com.danale.edge/files/user_local/volume/id_6ul5lak8wzfe/temp/hmi/clips/zip/601964a6e3d84514a554263d37f01abc) failed, zip: not a valid zip file
2023-09-05 18:53:41.233 D/UserSdkNative: [Thread-9285-18158] app_dip_fe_usr_sdk_general_json_callback, ctx 0x2af6
2023-09-05 18:53:41.234 W/VideoFlowActivity: [DefaultDispatcher-worker-1-7942] handleShareEvent error, c7.b: Api request failed with code:-2; biz:0(hmi.HandleClipShareApply failed, syncClipNonOriData failed, zip.OpenReader(/storage/emulated/0/Android/data/com.danale.edge/files/user_local/volume/id_6ul5lak8wzfe/temp/hmi/clips/zip/601964a6e3d84514a554263d37f01abc) failed, zip: not a valid zip file)
2023-09-05 18:53:41.234 E/VideoFlowActivity: [DefaultDispatcher-worker-1-7942] toastError exception
c7.b: Api request failed with code:-2; biz:0(hmi.HandleClipShareApply failed, syncClipNonOriData failed, zip.OpenReader(/storage/emulated/0/Android/data/com.danale.edge/files/user_local/volume/id_6ul5lak8wzfe/temp/hmi/clips/zip/601964a6e3d84514a554263d37f01abc) failed, zip: not a valid zip file)
at com.danale.edge.usersdk.jni.BaseJsonOperation$CallbackContext.nativeResponseCallback(BaseJsonOperation.kt:10)

主要错误:

1
Api request failed with code:-2; biz:0(hmi.HandleClipShareApply failed, syncClipNonOriData failed, zip.OpenReader(/storage/emulated/0/Android/data/com.danale.edge/files/user_local/volume/id_6ul5lak8wzfe/temp/hmi/clips/zip/601964a6e3d84514a554263d37f01abc) failed, zip: not a valid zip file)

√远端同步过来的视频不能收藏

搜索页会出现

目前逻辑是远端的视频不能播放,所以在首页会过滤掉远端的视频只保留本地视频,即

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
/**
* 过滤首页远端视频资源,同时对远端视频发起拉取
*/
@SuppressLint("CheckResult")
private suspend fun filterPlayList(list: List<Clip>): List<Clip> {
val localList = list.filter { it.playMode == CLIP_PLAY_MODE_LOCAL }.toMutableList()
val remoteList = list.filter { it.playMode == CLIP_PLAY_MODE_REMOTE_DEVICE }.toMutableList()

// 当cursor未拉取到数据或拉取数量不足时会重复拉取,直到cursor为空或数量满足
while (localList.isEmpty() || localList.size < LOAD_VIDEO_MIN) {
if (nextCursor.isEmpty()) {
logger.e(TAG, "no newList")
break
}
val newList = getVideoList(nextCursor)
newList.filter { it.playMode == CLIP_PLAY_MODE_LOCAL }
.forEach { localList.add(it) }
newList.filter { it.playMode == CLIP_PLAY_MODE_REMOTE_DEVICE }
.forEach { remoteList.add(it) }
}
logger.d(TAG, "localList size=${localList.size}, remoteList size=${remoteList.size}")

// 远端拉取
getRemoteList(remoteList)

return localList
}

在搜索页中的视频做同样的操作,过滤掉远端视频

目前搜索页通过sdk分别获取人脸和tag的数据放到展示,存在问题是点进某个人脸或标签得到的视频是远端的导致没法播放,所以收藏也会失败

record

现在的代码中getFaceList是拉取sdk人脸库中的数据,也就是所有的人脸,包括别的摄像机拍摄出的人脸,属于远端数据,所以会出现有的视频无法观看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private fun loadPersonIdentities() {
viewModelScope.launch {
val newList = performOnIOWithDefault(emptyList()) {
// 获取所有人脸
val faceList = sdkFaceDatabase.blockingGetSdkFaceList()
// 获取本机所有标签
val labels = sdk.getAllLabel().await()
// 提取出标签中的 faceUserId 部分
val filteredLabels = labels.map { it.substringAfter(Label.IDENTITY_PREFIX.key) }
// 过滤出本机中的人脸
faceList.filter { filteredLabels.contains(it.faceUserId) }
}
searchPersonAdapter.replaceALl(newList)
_personCount.value = searchPersonAdapter.getFrontFaceList().size
}
}

首先拿到本机中的所有label,这个label是本机的数据

1
val labels = sdk.getAllLabel().await()

再根据Id进行筛选,根据label中的Label.IDENTITY_PREFIX.key部分取faceListlabels中的交集人脸列表得到newList,将newList作为最终数据添加到adapter

1
2
3
4
// 提取出人脸标签中的 faceUserId 部分
val filteredLabels = labels.map { it.substringAfter(Label.IDENTITY_PREFIX.key) }
// 过滤出本机中的人脸
val newList = faceList.filter { filteredLabels.contains(it.faceUserId) }

最终代码

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
private fun loadPersonIdentities() {
viewModelScope.launch {
// 获取所有人脸摘要
val faceSummaryList = performOnIOWithDefault(emptyList()) {
getAllFaceSummary()
}
// 获取所有人脸
val faceList = withContext(Dispatchers.IO) {
sdk.getFaceList(faceSummaryList)
}

/******************修改部分*****************/
val labels = sdk.getAllLabel().await()
// 提取出人脸标签中的 faceUserId 部分
val filteredLabels = labels.map { it.substringAfter(Label.IDENTITY_PREFIX.key) }
// 过滤出本机中的人脸
val newList = faceList.filter { filteredLabels.contains(it.faceUserId) }

searchPersonAdapter.faceList.clear()
searchPersonAdapter.faceList.addAll(newList)
/******************修改部分*****************/

// fixme: SuppressLint 后续换SortedList
searchPersonAdapter.notifyDataSetChanged()
_personCount.value = searchPersonAdapter.faceList.size
}
}

保存MP4视频出现文件不存在

crash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2023-09-05 22:33:13.945 I/FileUtil: [DefaultDispatcher-worker-8-1724] [S]saveVideo
2023-09-05 22:33:13.948 E/FileUtil: [DefaultDispatcher-worker-8-1724] [S]clip type is not original data
2023-09-05 22:33:14.077 E/BaseApplication: [DefaultDispatcher-worker-8-1724] uncaughtException, thread = Thread[DefaultDispatcher-worker-8,5,main]
java.io.FileNotFoundException:/storage/emulated/0/Android/data/com.danale.edge/files/user_local/volume/id_6xur6vx43a5c/temp/hmi/clips/nod/42adff7f955c4d56af4c48f5cf5e7ac1/mp4_path: open failed: ENOENT (No such file or directory)
at libcore.io.IoBridge.open(IoBridge.java:574)
at java.io.FileInputStream.<init>(FileInputStream.java:160)
at k6.b.c(FileUtil.kt:18)
at com.danale.edge.ui.flow.VideoFlowViewModel.V(VideoFlowViewModel.kt:17)
at u6.c$a.d(DownloadDialog.kt:4)
at qh.a.o(ContinuationImpl.kt:3)
at ei.q0.run(DispatchedTask.kt:18)
at ji.g.run(LimitedDispatcher.kt:2)
at li.j.run(Tasks.kt:1)
at li.a$a.run(CoroutineScheduler.kt:14)
Suppressed: ei.o0: [b2{Cancelling}@b8522be, Dispatchers.IO]
Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
at libcore.io.Linux.open(Native Method)
at libcore.io.ForwardingOs.open(ForwardingOs.java:563)
at libcore.io.BlockGuardOs.open(BlockGuardOs.java:274)
at libcore.io.ForwardingOs.open(ForwardingOs.java:563)
at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:8274)
at libcore.io.IoBridge.open(IoBridge.java:560)
... 9 more

(No such file or directory)

问题出现在:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (clip.type != Clip.CLIP_TYPE_ORIGINAL_DATA) {
/**
* FIXME: 目前不支持编辑已经是MP4的文件, 暂时全部返回原视频
* 收藏页 -> 添加拍摄者水印
* 非本人拍摄(app内分享) -> 添加拍摄者水印
* 已经有水印(识别视频) -> 直接保存原视频
* */
logger.e(TAG, "clip type is not original data")
val nonOriMP4Path = clip.nonOriginalData?.get(Clip.NonOriginalData.KEY_MP4_PATH)
return if (nonOriMP4Path != null) {
try {
File(nonOriMP4Path)
}catch (e: Exception){
null
}
} else {
null
}
}

File(nonOriMP4Path)中的路径不存在,可能原因是

​ 如果path存在缓存文件夹中,那么存在手动清除缓存导致文件夹不存在,(需要重新修改需求?)

​ 如果存在file文件夹中,前面的逻辑可能存在问题

√ 日志导出方式修改

改为双击应用版本整个item导出日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun showLog(view: View) {
val currentTime = System.currentTimeMillis()
val clickDelay = currentTime - lastClickTime

if (clickDelay < 500) {
clickCount++
} else {
clickCount = 1
}

lastClickTime = currentTime
if (clickCount >= 2) {
logVisible.set(true)
}
}

√ 我的页面,部分视频没有展示收藏缩略图

之前的获取方式会导致list不全

1
val list = sdk.getFavoriteList("").await().list

改成当指针不为空的时候持续调用接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
val list = performOnIOWithDefault(emptyList()) {

val resultClipsList = ArrayList<Clip>()
var currentCursor = ""

do {
val resp = sdk.getFavoriteList(currentCursor).await()

resp.nextCursor.run {
logger.d(TAG, "getFavoriteList, nextCursor = $this")
currentCursor = this
nextCursor = this
}
resp.list?.let { resultClipsList.addAll(it) }

} while (currentCursor.isNotEmpty())

resultClipsList
}

New

√ 收藏缩略图修改

存在拉伸,修改type为裁剪

android:scaleType="centerCrop"

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
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/group_favorite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:orientation="horizontal"
android:layout_marginEnd="24dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@+id/arrow">

<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/favorite_1"
android:layout_width="64dp"
android:layout_height="64dp"
android:scaleType="centerCrop"
android:layout_marginStart="4dp" />

<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/favorite_2"
android:layout_width="64dp"
android:layout_height="64dp"
android:scaleType="centerCrop"
android:layout_marginStart="4dp" />

<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/favorite_3"
android:layout_width="64dp"
android:layout_height="64dp"
android:scaleType="centerCrop"
android:layout_marginStart="4dp" />

</androidx.appcompat.widget.LinearLayoutCompat>
Powered by Hexo & Theme Keep
Unique Visitor Page View