cacheList = new ArrayList<>(size);
22 | while (size > 0) {
23 | TreeNode node = stack.pop();
24 | list.add(node.val);
25 | if (reverse) {
26 | if (node.right != null) cacheList.add(node.right);
27 | if (node.left != null) cacheList.add(node.left);
28 | } else {
29 | if (node.left != null) cacheList.add(node.left);
30 | if (node.right != null) cacheList.add(node.right);
31 | }
32 | size--;
33 | }
34 | stack.addAll(cacheList);
35 | reverse = !reverse;
36 | result.add(list);
37 | }
38 | return result;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/code/src/main/java/me/wcy/code/string/Longest_Substring_Without_Repeating_Characters.java:
--------------------------------------------------------------------------------
1 | package me.wcy.code.string;
2 |
3 | import java.util.HashSet;
4 | import java.util.Set;
5 |
6 | /**
7 | * https://leetcode.com/explore/interview/card/top-interview-questions-medium/103/array-and-strings/779/
8 | *
9 | * 基本思路是维护一个窗口,每次关注窗口中的字符串,在每次判断中,左窗口和右窗口选择其一向前移动。
10 | * 同样是维护一个HashSet,正常情况下移动右窗口,如果没有出现重复则继续移动右窗口,如果发现重复字符,
11 | * 则说明当前窗口中的串已经不满足要求,继续移动有窗口不可能得到更好的结果,此时移动左窗口,直到不再有重复字符为止,
12 | * 中间跳过的这些串中不会有更好的结果,因为他们不是重复就是更短。
13 | * 因为左窗口和右窗口都只向前,所以两个窗口都对每个元素访问不超过一遍,因此时间复杂度为O(2*n)=O(n),是线性算法。
14 | * 空间复杂度为HashSet的size,也是O(n)。
15 | *
16 | * Created by wcy on 2020/10/27.
17 | */
18 | public class Longest_Substring_Without_Repeating_Characters {
19 |
20 | public int lengthOfLongestSubstring(String s) {
21 | int max = Integer.MIN_VALUE;
22 | int subStart = 0;
23 | Set set = new HashSet<>();
24 | for (int i = 0; i < s.length(); i++) {
25 | // 当前字符
26 | char c = s.charAt(i);
27 | if (set.contains(c)) {
28 | max = Math.max(max, set.size());
29 | // 移除相同字符前面的字符
30 | while (s.charAt(subStart) != c) {
31 | set.remove(s.charAt(subStart));
32 | subStart++;
33 | }
34 | // 移除相同字符
35 | set.remove(s.charAt(subStart));
36 | subStart++;
37 | }
38 | set.add(c);
39 | }
40 |
41 | max = Math.max(max, set.size());
42 | return max;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/doc/resume/resume.md:
--------------------------------------------------------------------------------
1 | # 个人信息
2 |
3 | - 王晨彦/男/1993
4 | - 本科/西南科技大学/软件工程
5 | - 工作年限:6年
6 | - 手机:13136109681
7 | - Email:wangchenyan2015@gmail.com
8 | - 技术博客:https://juejin.im/user/2313028193754168/posts
9 | - Github:https://github.com/wangchenyan
10 | - 期望职位:Android 开发专家
11 |
12 | # 个人经历
13 |
14 | - 2021.05-至今 字节跳动资深 Android 开发工程师
15 | - 2018.10-2021.05 阿里巴巴资深 Android 开发工程师
16 | - 2016.07-2018.10 网易高级 Android 开发工程师
17 | - 2012.09-2016.06 西南科技大学 软件工程
18 |
19 | # 项目经验
20 |
21 | ## 字节教育在线教室(2021.05-至今)
22 |
23 | ### 工作职责
24 |
25 | 1. 负责在线教室解决方案层整体架构设计,提供开箱即用同时具备良好扩展能力的教室服务,降低业务接入成本,同时提供灵活定制的能力
26 | 2. 负责在线教室信令&状态机模块的重构和性能优化工作,通过良好的设计实现平台层和组件层的解耦;进行了深入的性能优化,减少信令包体积60%,解码速度提升2倍以上,GC次数下降28%
27 | 3. 负责台灯自习室性能优化,3个Top 10卡顿点优化了到1个,启动布局主线程耗时减少85%,GC 次数减少 48.9%
28 |
29 | ### 技术要点
30 |
31 | 架构设计、性能优化
32 |
33 | ## 口碑&饿了么(2018.10-2021.05)
34 |
35 | ### 工作职责
36 |
37 | 1. 负责口碑店铺动态化能力搭建,实现端内动态发布能力
38 | 2. 负责口碑店铺行业化框架封装,提供行业化店铺小程序独立发布能力
39 | 3. 负责饿了么合并下单和收银台日常迭代与性能优化
40 |
41 | ### 技术要点
42 |
43 | Native 动态化,小程序
44 |
45 | ## 网易考拉(2018.03-2018.10)
46 |
47 | ### 工作职责
48 |
49 | 1. 负责网易考拉 Android 种草社区模块设计与开发
50 | 2. 负责网易考拉 Android 多媒体库的开发与维护
51 | 3. 负责网易考拉 Android 权限优化、安装包瘦身、新版本适配等
52 |
53 | ### 技术要点
54 |
55 | 路由总线、性能优化
56 |
57 | ## 网易七鱼智能客服(2016.07-2018.03)
58 |
59 | ### 工作职责
60 |
61 | 1. 负责网易七鱼 Android SDK 的设计与开发
62 | 2. 负责网易七鱼客服 APP 的设计与开发,实现热修复能力
63 | 3. 通过内存映射优化七鱼 SDK 日志性能,写入速度提升 20 倍
64 |
65 | ### 技术要点
66 |
67 | 多进程、长连接、热修复
68 |
69 | # 专业技能
70 |
71 | * 熟练掌握 Java(Kotlin) 语言,熟悉 JVM
72 | * 熟练掌握多线程机制,线程间通信及线程安全
73 | * 熟悉 Gradle 语法,了解热修复和插件化
74 | * 熟悉掌握常用的数据结构知识和算法
75 | * 熟悉数据库,掌握 MySQL 的基本操作
76 | * 具备面向对象分析与设计能力,了解常用设计模式
77 | * 了解小程序、React Native、Weex等混合开发框架
78 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 |
4 | android {
5 | compileSdkVersion 30
6 |
7 | defaultConfig {
8 | applicationId "me.wcy.app"
9 | minSdkVersion 23
10 | targetSdkVersion 30
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 |
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 |
22 | viewBinding {
23 | enabled = true
24 | }
25 | }
26 |
27 | dependencies {
28 | implementation fileTree(dir: 'libs', include: ['*.jar'])
29 | implementation 'androidx.core:core-ktx:1.3.2'
30 | implementation 'androidx.appcompat:appcompat:1.2.0'
31 | implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
32 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
33 | implementation 'androidx.fragment:fragment-ktx:1.2.0-rc05'
34 | implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
35 | implementation 'androidx.asynclayoutinflater:asynclayoutinflater:1.0.0'
36 | implementation 'com.squareup.okhttp3:okhttp:4.9.0'
37 | implementation 'io.reactivex.rxjava2:rxjava:2.2.20'
38 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
39 | implementation 'com.github.mthli:RxCoroutineSchedulers:v2.0.3'
40 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'
41 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
42 | implementation 'com.facebook.fresco:fresco:2.5.0'
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/me/wcy/app/ViewBindingEx.kt:
--------------------------------------------------------------------------------
1 | package me.wcy.app
2 |
3 | import android.app.Activity
4 | import android.view.LayoutInflater
5 | import android.view.ViewGroup
6 | import androidx.annotation.MainThread
7 | import androidx.fragment.app.Fragment
8 | import androidx.viewbinding.ViewBinding
9 | import kotlin.reflect.KClass
10 |
11 | @MainThread
12 | inline fun Activity.viewBindings(): Lazy {
13 | return ViewBindingLazy(VB::class, { layoutInflater })
14 | }
15 |
16 | @MainThread
17 | inline fun Fragment.viewBindings(): Lazy {
18 | return ViewBindingLazy(VB::class, { layoutInflater })
19 | }
20 |
21 | class ViewBindingLazy(
22 | private val viewBindingClass: KClass,
23 | private var inflater: () -> LayoutInflater,
24 | private var viewGroup: ViewGroup? = null,
25 | private var attach: Boolean = false
26 | ) : Lazy {
27 | private var cached: VB? = null
28 |
29 | override val value: VB
30 | get() {
31 | val viewBinding = cached
32 | return if (viewBinding == null) {
33 | val method = viewBindingClass.java.getMethod(
34 | "inflate",
35 | LayoutInflater::class.java,
36 | ViewGroup::class.java,
37 | Boolean::class.java
38 | )
39 | (method.invoke(null, inflater.invoke(), viewGroup, attach) as VB).also {
40 | cached = it
41 | }
42 | } else {
43 | viewBinding
44 | }
45 | }
46 |
47 | override fun isInitialized() = cached != null
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/wcy/app/FlowDemoActivity.kt:
--------------------------------------------------------------------------------
1 | package me.wcy.app
2 |
3 | import android.os.Bundle
4 | import android.os.SystemClock
5 | import android.util.Log
6 | import androidx.activity.ComponentActivity
7 | import io.reactivex.Observable
8 | import io.reactivex.schedulers.Schedulers
9 | import kotlinx.coroutines.CoroutineScope
10 | import kotlinx.coroutines.Dispatchers
11 | import kotlinx.coroutines.SupervisorJob
12 | import kotlinx.coroutines.flow.*
13 | import kotlinx.coroutines.launch
14 |
15 | /**
16 | * Created by wangchenyan.top on 2021/9/6.
17 | */
18 | class FlowDemoActivity : ComponentActivity(),
19 | CoroutineScope by CoroutineScope(SupervisorJob() + Dispatchers.IO) {
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | super.onCreate(savedInstanceState)
23 |
24 | launch {
25 | test()
26 | }
27 | }
28 |
29 | private suspend fun test() {
30 | val list = listOf(1, 2, 3)
31 | val resList = mutableListOf()
32 | Log.e("WCY", "start")
33 | list.map { flowOf(it) }
34 | .merge()
35 | .map {
36 | SystemClock.sleep(3000)
37 | it.toString()
38 | }
39 | .flowOn(Dispatchers.IO)
40 | .collect {
41 | resList.add(it)
42 | }
43 | Log.e("WCY", "end")
44 | }
45 |
46 | private fun testRxjava() {
47 | Log.e("WCY", "rx start")
48 | val list = listOf(1, 2, 3)
49 | Observable.fromIterable(list)
50 | .observeOn(Schedulers.io())
51 | .map {
52 | SystemClock.sleep(3000)
53 | it.toString()
54 | }
55 | .toList()
56 | .subscribe { t ->
57 | Log.e("WCY", "rx end")
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/me/wcy/app/CoroutineActivity.kt:
--------------------------------------------------------------------------------
1 | package me.wcy.app
2 |
3 | import android.os.Bundle
4 | import android.util.Log
5 | import androidx.activity.ComponentActivity
6 | import androidx.lifecycle.lifecycleScope
7 | import kotlinx.coroutines.*
8 | import kotlinx.coroutines.sync.Mutex
9 | import kotlinx.coroutines.sync.withLock
10 | import me.wcy.app.databinding.ActivityCoroutineBinding
11 |
12 | /**
13 | * Created by wangchenyan.top on 2021/9/6.
14 | */
15 | class CoroutineActivity : ComponentActivity() {
16 | private val viewBinding by viewBindings()
17 | private var customScope: CoroutineScope? = null
18 | private val mutex = Mutex()
19 |
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 | setContentView(viewBinding.root)
23 |
24 | lifecycleScope.launch(Dispatchers.IO) {
25 | repeat(100) {
26 | launch {
27 | mutex.withLock {
28 | tick("lifecycleScope")
29 | delay(10000)
30 | }
31 | }
32 | delay(3000)
33 | }
34 | }
35 |
36 | viewBinding.btnStart.setOnClickListener {
37 | customScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
38 | customScope?.launch {
39 | repeat(100) {
40 | launch {
41 | mutex.withLock {
42 | tick("customScope")
43 | delay(10000)
44 | }
45 | }
46 | delay(3000)
47 | }
48 | }
49 | }
50 |
51 | viewBinding.btnCancel.setOnClickListener {
52 | customScope?.cancel()
53 | customScope = null
54 | }
55 | }
56 |
57 | private fun tick(tag: String) {
58 | Log.e("WCY", "$tag tick in ${Thread.currentThread().name}")
59 | }
60 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/wcy/app/asynclayout/AsyncLayoutHelper.kt:
--------------------------------------------------------------------------------
1 | package me.wcy.app.asynclayout
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import android.widget.FrameLayout
7 |
8 | /**
9 | * 组件布局异步 inflate 工具类,在 [androidx.fragment.app.Fragment.onCreateView] 中返回 [AsyncLayoutHelper.onCreateView],
10 | * 在 [androidx.fragment.app.Fragment.onViewCreated] 中调用 [AsyncLayoutHelper.waitViewCreated] 等待异步 inflate 完成,再更新 UI。
11 | *
12 | * 注意:除了上述两个方法,在其他生命周期方法中获取到的 View 可能为空,如果需要在其他生命周期操作 View,需要对 View 判空,
13 | * 或者通过 [AsyncLayoutHelper.isViewCreated] 判断。
14 | *
15 | * Created by wangchenyan.top on 2021/10/28.
16 | */
17 | class AsyncLayoutHelper {
18 | private lateinit var asyncLayoutInflater: AsyncLayoutInflaterEx
19 | private var onViewCreated: ((view: View) -> Unit)? = null
20 | private var isViewCreated = false
21 |
22 | fun onCreateView(
23 | inflater: LayoutInflater,
24 | container: ViewGroup?,
25 | layoutId: Int,
26 | placeholderId: Int = 0
27 | ): View? {
28 | container ?: return null
29 | val root = FrameLayout(container.context)
30 | if (placeholderId > 0) {
31 | inflater.inflate(placeholderId, root)
32 | }
33 |
34 | asyncLayoutInflater = AsyncLayoutInflaterEx(container.context)
35 | asyncLayoutInflater.inflate(
36 | layoutId,
37 | root,
38 | object : AsyncLayoutInflaterEx.OnInflateFinishedListener {
39 | override fun onInflateFinished(view: View, resid: Int, parent: ViewGroup?) {
40 | if (parent != null && parent.childCount > 0) {
41 | parent.removeAllViews()
42 | }
43 | parent?.addView(view)
44 | isViewCreated = true
45 | onViewCreated?.invoke(view)
46 | onViewCreated = null
47 | }
48 | })
49 |
50 | return root
51 | }
52 |
53 | fun waitViewCreated(onViewCreated: (view: View) -> Unit) {
54 | this.onViewCreated = onViewCreated
55 | }
56 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/wcy/app/RxJavaDemoActivity.kt:
--------------------------------------------------------------------------------
1 | package me.wcy.app
2 |
3 | import android.os.Bundle
4 | import android.os.SystemClock
5 | import android.util.Log
6 | import android.widget.Button
7 | import androidx.appcompat.app.AppCompatActivity
8 | import io.github.mthli.rxcoroutineschedulers.asScheduler
9 | import io.reactivex.Single
10 | import io.reactivex.disposables.Disposable
11 | import io.reactivex.subjects.PublishSubject
12 | import kotlinx.coroutines.Dispatchers
13 | import kotlinx.coroutines.asCoroutineDispatcher
14 | import java.util.concurrent.Executors
15 |
16 | /**
17 | * Created by wangchenyan.top on 2021/9/6.
18 | */
19 | class RxJavaDemoActivity : AppCompatActivity() {
20 | private var currentPosition = PublishSubject.create()
21 | private var disposable: Disposable? = null
22 | private var time = 0L
23 |
24 | private val playbackFsmDispatcher by lazy {
25 | Executors.newSingleThreadExecutor {
26 | Thread(it, "playback-msg-dispatch-thread")
27 | }.asCoroutineDispatcher()
28 | }
29 |
30 | override fun onCreate(savedInstanceState: Bundle?) {
31 | super.onCreate(savedInstanceState)
32 | setContentView(R.layout.activity_main)
33 |
34 | findViewById