├── .gitignore ├── LICENSE ├── README.md ├── README_cn.md ├── blockframework ├── build.gradle ├── consumer-rules.pro ├── gradle.properties ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── bytedance │ └── blockframework │ ├── contract │ ├── AbstractBlock.kt │ ├── AbstractBlockManager.kt │ ├── AbstractLifecycleBlock.kt │ ├── AbstractViewBlock.kt │ ├── BaseBlockLifeCycleAdapter.kt │ └── IBlockLifeCycleAdapter.kt │ ├── framework │ ├── async │ │ ├── AsyncBaseBlock.kt │ │ ├── AsyncUIBlock.kt │ │ └── IAsyncBlock.kt │ ├── base │ │ ├── BaseBlock.kt │ │ ├── BlockBuilder.kt │ │ ├── IUIBlock.kt │ │ ├── NoBindBlock.kt │ │ └── UIBlock.kt │ ├── config │ │ ├── BlockInit.kt │ │ └── IBlockInitConfig.kt │ ├── core │ │ ├── BlockCoreManager.kt │ │ ├── BlockGenerator.kt │ │ ├── BlockLifeCycleDispatcher.kt │ │ ├── BlockUnitHandler.kt │ │ ├── IBlockModel.kt │ │ └── message │ │ │ ├── BlockContractManager.kt │ │ │ └── TreeBlockMessageCenter.kt │ ├── event │ │ └── AllBlockViewCreatedEvent.kt │ ├── join │ │ ├── BlockContextImpl.kt │ │ ├── IBlockAbility.kt │ │ ├── IBlockDepend.kt │ │ ├── IBlockJoin.kt │ │ └── IBlockScene.kt │ ├── monitor │ │ ├── BlockLogger.kt │ │ └── BlockMonitor.kt │ ├── performance │ │ ├── HandlerProcessor.kt │ │ └── ThreadProcessor.kt │ ├── task │ │ ├── BlockLayoutInflater.kt │ │ ├── BlockViewBuildTask.kt │ │ ├── ITaskManager.kt │ │ ├── ScheduleTask.kt │ │ ├── Task.kt │ │ └── TaskManager.kt │ └── utils │ │ ├── BlockDsl.kt │ │ ├── BlockExt.kt │ │ └── LifecycleUtil.kt │ ├── interaction │ ├── BaseBlockMessageCenter.kt │ ├── CoreTreeLayerBlock.kt │ ├── EventManager.kt │ ├── IBlockMessageCenter.kt │ ├── State.kt │ ├── StateAndEventModel.kt │ ├── TreeConstrainBlockMessageCenter.kt │ └── TreeConstrainEventManager.kt │ └── utils │ └── Logger.kt ├── build.gradle ├── demo ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── bytedance │ │ └── demo │ │ ├── BlockScene.kt │ │ ├── DemoActivity.kt │ │ ├── DemoAdapter.kt │ │ ├── DemoHolder.kt │ │ ├── block │ │ ├── BottomInfoBlock.kt │ │ ├── DemoCardRootBlock.kt │ │ ├── MainContentBlock.kt │ │ └── RightInteractBlock.kt │ │ ├── data │ │ ├── DemoCardData.kt │ │ └── DemoModel.kt │ │ ├── depend │ │ └── IHolderBlockDepend.kt │ │ ├── event │ │ └── ChangFontThemeEvent.kt │ │ ├── service │ │ └── IMainContentBlockService.kt │ │ └── util │ │ ├── DemoTestUtil.kt │ │ └── FontType.kt │ └── res │ ├── drawable │ ├── avatar_img1.xml │ ├── avatar_img2.xml │ ├── avatar_img3.xml │ ├── avatar_img4.xml │ ├── avatar_img5.xml │ ├── avatar_img6.xml │ ├── ic_collect_icon.xml │ ├── ic_comment_icon.xml │ ├── ic_digg_icon.xml │ ├── ic_launcher.xml │ └── ic_more_icon.xml │ ├── layout │ ├── demo_activity_layout.xml │ ├── demo_bottom_info_block_layout.xml │ ├── demo_card_holder_layout.xml │ ├── demo_main_content_block_layout.xml │ └── demo_right_interact_block_layout.xml │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── misc ├── jingxuan.png ├── ott.png └── xigua.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | 17 | # Eclipse 18 | .classpath 19 | .project 20 | .settings 21 | eclipsebin 22 | 23 | # IntelliJ IDEA 24 | .idea 25 | *.ipl 26 | *.iws 27 | classes/ 28 | idea-classes/ 29 | coverage-error.log 30 | 31 | # Android 32 | gen 33 | bin 34 | project.properties 35 | out 36 | captures 37 | 38 | lint-results.xml 39 | 40 | /blockframework/build 41 | /demo/build 42 | /blockframework/lint-results.xml 43 | 44 | -------------------------------------------------------------------------------- /blockframework/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-android' 4 | id 'kotlin-android-extensions' 5 | id 'maven-publish' 6 | } 7 | 8 | android { 9 | compileSdkVersion 30 10 | defaultConfig { 11 | minSdkVersion 24 12 | targetSdkVersion 30 13 | } 14 | 15 | buildTypes { 16 | 17 | } 18 | 19 | lintOptions { 20 | // Turns off checks for the issue IDs you specify. 21 | disable 'GradleCompatible', 'RequiredSize'// 暂时禁用RequiredSize,有bug https://issuetracker.google.com/issues/37138580 22 | 23 | // Turns on checks for the issue IDs you specify. These checks are in 24 | // addition to the default lint checks. 25 | // enable 'RtlHardcoded','RtlCompat', 'RtlEnabled' 26 | 27 | // To enable checks for only a subset of issue IDs and ignore all others, 28 | // list the issue IDs with the 'check' property instead. This property overrides 29 | // any issue IDs you enable or disable using the properties above. 30 | // check 'NewApi', 'InlinedApi' 31 | 32 | // If set to true, turns off analysis progress reporting by lint. 33 | quiet true 34 | 35 | // if set to true (default), stops the build if errors are found. 36 | abortOnError false 37 | 38 | // if true, only report errors. 39 | ignoreWarnings true 40 | 41 | // no check 42 | checkReleaseBuilds false 43 | 44 | // if true, don't include source code lines in the error output 45 | noLines false 46 | 47 | // if true, show all locations for an error, do not truncate lists, etc. 48 | showAll true 49 | 50 | // if true, generate a text report of issues (false by default) 51 | textReport true 52 | 53 | // location to write the output; can be a file or 'stdout' 54 | textOutput 'stdout' 55 | 56 | // if true, generate an HTML report for use by for example Jenkins 57 | // htmlReport true 58 | // file to write report to (if not specified, defaults to lint-results.html) 59 | // htmlOutput file("lint-results.html") 60 | 61 | xmlReport true 62 | xmlOutput file("lint-results.xml") 63 | } 64 | } 65 | 66 | dependencies { 67 | 68 | implementation LIFECYCLE_EXTENSIONS 69 | implementation LIFECYCLE_COMMON 70 | } 71 | 72 | afterEvaluate { 73 | publishing { 74 | publications { 75 | release(MavenPublication) { 76 | from components.release 77 | groupId 'com.github.bytedance' 78 | artifactId 'block-framework' 79 | version = '1.0.0' 80 | } 81 | } 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /blockframework/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytedance/BlockFramework/d37e26ec8d28e95084ff5b7fb625d1a465fc43dd/blockframework/consumer-rules.pro -------------------------------------------------------------------------------- /blockframework/gradle.properties: -------------------------------------------------------------------------------- 1 | 2 | android.useAndroidX=true 3 | android.enableJetifier=true -------------------------------------------------------------------------------- /blockframework/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /blockframework/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/contract/AbstractBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.contract 17 | 18 | import android.content.Context 19 | import com.bytedance.blockframework.framework.monitor.BlockLogger 20 | import com.bytedance.blockframework.interaction.Event 21 | import com.bytedance.blockframework.interaction.IBlockMessageCenter 22 | import com.bytedance.blockframework.interaction.IObserver 23 | import com.bytedance.blockframework.interaction.StateAndEventModel 24 | 25 | /** 26 | * @Author Wei Zijie 27 | * @Date 2020/12/8 28 | * @Description 29 | */ 30 | abstract class AbstractBlock: StateAndEventModel(), IObserver { 31 | 32 | private val TAG = this.javaClass.simpleName 33 | private val DEBUG = BlockLogger.debug() 34 | 35 | open lateinit var context: Context 36 | 37 | abstract fun defineBlockService(): Class<*>? 38 | 39 | abstract fun isActive(): Boolean 40 | 41 | fun performInstall(_context: Context, messageCenter: IBlockMessageCenter) { 42 | context = _context 43 | this.blockBlockMessageCenter = messageCenter 44 | defineBlockService()?.let { 45 | this.blockBlockMessageCenter.registerService(it, BlockImplWrapper(this)) 46 | } 47 | } 48 | 49 | override fun onPrepared() { 50 | if (DEBUG) { 51 | BlockLogger.log(TAG, "onActive") 52 | } 53 | } 54 | 55 | fun performUnRegister() { 56 | clearState() 57 | clearEvent() 58 | clearService() 59 | onUnRegister() 60 | } 61 | 62 | override fun onEvent(event: Event): Boolean { 63 | return false 64 | } 65 | 66 | open fun getBlockService(klass: Class, activeIfNeed: Boolean = true): T? { 67 | return blockBlockMessageCenter.queryService(this, klass, activeIfNeed) 68 | } 69 | 70 | private fun clearState() { 71 | providers.forEach { 72 | blockBlockMessageCenter.unregisterStateProvider(it) 73 | } 74 | providers.clear() 75 | } 76 | 77 | private fun clearEvent() { 78 | observers.forEach { 79 | blockBlockMessageCenter.unregisterObserver(it) 80 | } 81 | observers.clear() 82 | } 83 | 84 | private fun clearService() { 85 | blockBlockMessageCenter.blockServiceMap.remove(defineBlockService()) 86 | } 87 | 88 | override fun onUnRegister() { 89 | if (DEBUG) { 90 | BlockLogger.log(TAG, "onInactive") 91 | } 92 | } 93 | } 94 | 95 | class BlockImplWrapper { 96 | @Volatile 97 | var impl: Any 98 | 99 | internal constructor(impl: Any) { 100 | this.impl = impl 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/contract/AbstractBlockManager.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.contract 17 | 18 | import android.content.Context 19 | import com.bytedance.blockframework.interaction.IBlockMessageCenter 20 | import com.bytedance.blockframework.interaction.StateAndEventModel 21 | 22 | /** 23 | * @Author Wei Zijie 24 | * @Date 2020/12/28 25 | * @Description 26 | */ 27 | abstract class AbstractBlockManager(var context: Context, override var blockBlockMessageCenter: IBlockMessageCenter) : StateAndEventModel() { 28 | 29 | var blockList: MutableList = mutableListOf() 30 | 31 | abstract fun onRegisterBlock(block: AbstractBlock) 32 | abstract fun onUnregisterBlock(block: AbstractBlock) 33 | 34 | override fun onPrepared() {} 35 | override fun onUnRegister() {} 36 | 37 | fun registerBlock(block: AbstractBlock) { 38 | if (blockList.contains(block)) { 39 | return 40 | } 41 | blockList.add(block) 42 | block.performInstall(context, blockBlockMessageCenter) 43 | onRegisterBlock(block) 44 | block.onPrepared() 45 | } 46 | 47 | open fun unregisterAllBlock() { 48 | val mIterator = blockList.iterator() 49 | while (mIterator.hasNext()) { 50 | val next = mIterator.next() 51 | next.performUnRegister() 52 | onUnregisterBlock(next) 53 | mIterator.remove() 54 | } 55 | } 56 | 57 | fun unregisterBlock(block: AbstractBlock) { 58 | blockList.remove(block) 59 | block.performUnRegister() 60 | onUnregisterBlock(block) 61 | } 62 | 63 | fun getBlockService(klass: Class): T? { 64 | return blockBlockMessageCenter.queryService(klass) 65 | } 66 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/contract/AbstractLifecycleBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.contract 17 | 18 | import androidx.lifecycle.Lifecycle 19 | import androidx.lifecycle.LifecycleOwner 20 | import androidx.lifecycle.LifecycleRegistry 21 | import com.bytedance.blockframework.framework.monitor.BlockLogger 22 | 23 | /** 24 | * @Author Wei Zijie 25 | * @Date 2020/12/8 26 | * @Description 27 | */ 28 | abstract class AbstractLifecycleBlock() : AbstractBlock(), LifecycleOwner { 29 | 30 | private val TAG = this.javaClass.simpleName 31 | private val DEBUG = BlockLogger.debug() 32 | 33 | private val mLifecycleRegistry = LifecycleRegistry(this) 34 | 35 | override fun getLifecycle(): Lifecycle { 36 | return mLifecycleRegistry 37 | } 38 | 39 | // 40 | fun performCreate() { 41 | onCreate() 42 | mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE) 43 | } 44 | 45 | open fun onCreate() { 46 | if (DEBUG) { 47 | BlockLogger.log(TAG, "onCreate") 48 | } 49 | } 50 | // 51 | 52 | // 53 | fun performStart() { 54 | onStart() 55 | mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START) 56 | } 57 | 58 | open fun onStart() { 59 | if (DEBUG) { 60 | BlockLogger.log(TAG, "onStart") 61 | } 62 | } 63 | // 64 | 65 | // 66 | fun performResume() { 67 | onResume() 68 | mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME) 69 | } 70 | 71 | open fun onResume() { 72 | if (DEBUG) { 73 | BlockLogger.log(TAG, "onResume") 74 | } 75 | } 76 | // 77 | 78 | // 79 | fun performPause() { 80 | mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE) 81 | onPause() 82 | } 83 | 84 | open fun onPause() { 85 | if (DEBUG) { 86 | BlockLogger.log(TAG, "onPause") 87 | } 88 | } 89 | // 90 | 91 | // 92 | fun performStop() { 93 | mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP) 94 | onStop() 95 | } 96 | 97 | open fun onStop() { 98 | if (DEBUG) { 99 | BlockLogger.log(TAG, "onStop") 100 | } 101 | } 102 | // 103 | 104 | // 105 | fun performDestroy() { 106 | mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY) 107 | onDestroy() 108 | } 109 | 110 | open fun onDestroy() { 111 | if (DEBUG) { 112 | BlockLogger.log(TAG, "onDestroy") 113 | } 114 | } 115 | // 116 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/contract/AbstractViewBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.contract 17 | 18 | import android.view.View 19 | import androidx.annotation.IdRes 20 | import com.bytedance.blockframework.interaction.BaseBlockMessageCenter 21 | 22 | /** 23 | * @Author Wei Zijie 24 | * @Date 2020/12/28 25 | * @Description 26 | */ 27 | abstract class AbstractViewBlock(private val mContentView: T) : AbstractBlock() { 28 | 29 | protected val contentView: T 30 | get() = mContentView 31 | 32 | protected fun findViewById(@IdRes id: Int): T { 33 | return mContentView.findViewById(id) 34 | } 35 | 36 | abstract fun getView(): View 37 | 38 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/contract/BaseBlockLifeCycleAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.contract 17 | 18 | import androidx.lifecycle.Lifecycle 19 | import androidx.lifecycle.LifecycleObserver 20 | import androidx.lifecycle.OnLifecycleEvent 21 | 22 | /** 23 | * @Author Wei Zijie 24 | * @Date 2021/1/24 25 | * @Description 26 | */ 27 | open class BaseBlockLifeCycleAdapter : IBlockLifeCycleAdapter, LifecycleObserver { 28 | 29 | override var blockList: MutableList = mutableListOf() 30 | 31 | @OnLifecycleEvent(value = Lifecycle.Event.ON_CREATE) 32 | override fun onCreate() { 33 | blockList.forEach { 34 | if (it.isActive()) { 35 | it.performCreate() 36 | } 37 | } 38 | } 39 | 40 | @OnLifecycleEvent(value = Lifecycle.Event.ON_START) 41 | override fun onStart() { 42 | blockList.forEach { 43 | if (it.isActive()) { 44 | it.performStart() 45 | } 46 | } 47 | } 48 | 49 | @OnLifecycleEvent(value = Lifecycle.Event.ON_RESUME) 50 | override fun onResume() { 51 | blockList.forEach { 52 | if (it.isActive()) { 53 | it.performResume() 54 | } 55 | } 56 | } 57 | 58 | @OnLifecycleEvent(value = Lifecycle.Event.ON_PAUSE) 59 | override fun onPause() { 60 | blockList.forEach { 61 | if (it.isActive()) { 62 | it.performPause() 63 | } 64 | } 65 | } 66 | 67 | @OnLifecycleEvent(value = Lifecycle.Event.ON_STOP) 68 | override fun onStop() { 69 | blockList.forEach { 70 | if (it.isActive()) { 71 | it.performStop() 72 | } 73 | } 74 | } 75 | 76 | @OnLifecycleEvent(value = Lifecycle.Event.ON_DESTROY) 77 | override fun onDestroy() { 78 | blockList.forEach { 79 | if (it.isActive()) { 80 | it.performDestroy() 81 | } 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/contract/IBlockLifeCycleAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.contract 17 | 18 | import androidx.lifecycle.LifecycleObserver 19 | 20 | /** 21 | * @Author Wei Zijie 22 | * @Date 2020/12/8 23 | * @Description 24 | */ 25 | interface IBlockLifeCycleAdapter : LifecycleObserver { 26 | 27 | var blockList: MutableList 28 | 29 | fun addLifecycleObserve(block: B) { 30 | blockList.add(block) 31 | } 32 | 33 | fun removeLifecycleObserve(block: B) { 34 | blockList.remove(block) 35 | } 36 | 37 | fun onCreate() { 38 | 39 | } 40 | 41 | fun onStart() { 42 | 43 | } 44 | 45 | fun onResume() { 46 | 47 | } 48 | 49 | fun onPause() { 50 | 51 | } 52 | 53 | fun onStop() { 54 | 55 | } 56 | 57 | fun onDestroy() { 58 | 59 | } 60 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/async/AsyncBaseBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.async 17 | 18 | import com.bytedance.blockframework.framework.base.BaseBlock 19 | import com.bytedance.blockframework.framework.config.BlockInit 20 | import com.bytedance.blockframework.framework.core.IBlockModel 21 | import com.bytedance.blockframework.framework.join.IBlockContext 22 | import com.bytedance.blockframework.framework.monitor.BlockMonitor 23 | import com.bytedance.blockframework.framework.monitor.TYPE_BLOCK_BIND 24 | import com.bytedance.blockframework.framework.monitor.currentTime 25 | import com.bytedance.blockframework.framework.performance.ThreadProcessor 26 | 27 | /** 28 | * 29 | * @Author: Created by zhoujunjie on 2023/8/9 30 | * @mail zhoujunjie.9743@bytedance.com 31 | **/ 32 | 33 | abstract class AsyncBaseBlock>(blockContext: IBlockContext) : 34 | BaseBlock(blockContext), IAsyncBind { 35 | 36 | override fun bindModel(model: MODEL?) { 37 | if (enableAsyncBind()) { 38 | val startSync = currentTime() 39 | recordPref(TYPE_BLOCK_BIND, "sync_bind_start") 40 | kotlin.runCatching { 41 | syncBind(model) 42 | }.onFailure { 43 | BlockInit.recordException(it) 44 | } 45 | recordPref(TYPE_BLOCK_BIND, "sync_bind_end", currentTime() - startSync) 46 | ThreadProcessor.work().post { 47 | val startAsync = currentTime() 48 | recordPref(TYPE_BLOCK_BIND, "async_bind_start") 49 | kotlin.runCatching { 50 | asyncBind(model) { 51 | ThreadProcessor.main().post { 52 | kotlin.runCatching { 53 | it.invoke() 54 | }.onFailure { 55 | BlockInit.recordException(it) 56 | } 57 | } 58 | } 59 | }.onFailure { 60 | BlockInit.recordException(it) 61 | } 62 | recordPref(TYPE_BLOCK_BIND, "async_bind_end", currentTime() - startAsync) 63 | } 64 | } else { 65 | val start = currentTime() 66 | recordPref(TYPE_BLOCK_BIND, "sync_bind_start") 67 | kotlin.runCatching { 68 | syncBind(model) 69 | asyncBind(model) { 70 | it.invoke() 71 | } 72 | }.onFailure { 73 | BlockInit.recordException(it) 74 | } 75 | recordPref(TYPE_BLOCK_BIND, "sync_bind_end", currentTime() - start) 76 | } 77 | } 78 | 79 | override fun syncBind(model: MODEL?) { 80 | 81 | } 82 | 83 | override fun asyncBind(model: MODEL?, syncInvoke: SyncInvoke) { 84 | 85 | } 86 | // 87 | 88 | private fun recordPref(type: String, message: String, cost: Long = 0) { 89 | BlockMonitor.record(blockContext.getScene().getName(), getBlockKey(), type, message, cost) 90 | } 91 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/async/AsyncUIBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.async 17 | 18 | import android.view.View 19 | import android.view.ViewGroup 20 | import androidx.annotation.IdRes 21 | import com.bytedance.blockframework.framework.base.IUIBlock 22 | import com.bytedance.blockframework.framework.base.UIBlockConfig 23 | import com.bytedance.blockframework.framework.core.IBlockModel 24 | import com.bytedance.blockframework.framework.join.IBlockContext 25 | 26 | /** 27 | * @Author: Created by zhoujunjie on 2023/8/9 28 | * @mail zhoujunjie.9743@bytedance.com 29 | **/ 30 | 31 | abstract class AsyncUIBlock>(blockContext: IBlockContext) : AsyncBaseBlock(blockContext), IUIBlock { 32 | 33 | override val uiConfig: UIBlockConfig = UIBlockConfig() 34 | 35 | override lateinit var containerView: View 36 | 37 | abstract fun layoutResource(): Int 38 | 39 | override fun onCreateView(parent: View?): View { 40 | if (layoutResource() == IUIBlock.USE_PARENT_LAYOUT) { 41 | return parent!! 42 | } 43 | return uiConfig.viewInflater.getView(layoutResource(), parent!!.context, parent as ViewGroup) 44 | } 45 | 46 | override fun onViewCreated(view: View) {} 47 | 48 | override fun getView(): View? { 49 | return if (this::containerView.isInitialized) { 50 | containerView 51 | } else { 52 | null 53 | } 54 | } 55 | 56 | fun findViewById(@IdRes id: Int): T? { 57 | return getView()?.findViewById(id) 58 | } 59 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/async/IAsyncBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.async 17 | 18 | import androidx.annotation.MainThread 19 | 20 | 21 | /** 22 | * @Author: Created by zhoujunjie on 2023/3/7 23 | * @mail zhoujunjie.9743@bytedance.com 24 | **/ 25 | 26 | typealias SyncInvoke = (() -> Unit) -> Unit 27 | 28 | interface IAsyncBind { 29 | 30 | fun enableAsyncBind(): Boolean 31 | 32 | @MainThread 33 | fun syncBind(model: MODEL?) 34 | 35 | /** 36 | * @param syncInvoke: Used to switch to the main thread 37 | */ 38 | fun asyncBind(model: MODEL?, syncInvoke: SyncInvoke) 39 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/base/BaseBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.base 17 | 18 | import com.bytedance.blockframework.contract.AbstractLifecycleBlock 19 | import com.bytedance.blockframework.framework.core.BlockGenerator 20 | import com.bytedance.blockframework.framework.core.BlockUnitHandler 21 | import com.bytedance.blockframework.framework.core.IBlockModel 22 | import com.bytedance.blockframework.framework.core.message.TreeBlockMessageCenter 23 | import com.bytedance.blockframework.framework.join.IBlockContext 24 | import com.bytedance.blockframework.framework.utils.blockHandler 25 | 26 | /** 27 | * 28 | * @Author: Created by zhoujunjie on 2023/8/9 29 | * @mail zhoujunjie.9743@bytedance.com 30 | **/ 31 | 32 | open class BaseBlock>(var blockContext: IBlockContext) : AbstractLifecycleBlock() { 33 | 34 | private val blockName by lazy { this::class.java.simpleName + "_" + this.hashCode() } 35 | 36 | internal var parent: BlockUnitHandler? = null 37 | internal var immediateBind = false 38 | 39 | open var lazyActive = false 40 | open var isBlockActivated = true 41 | open var activeTask: (() -> Unit)? = null 42 | 43 | open fun activeIfNeed() { 44 | if (!isActive()) { 45 | activeTask?.invoke() 46 | } 47 | } 48 | 49 | open fun getBlockKey(): String = blockName 50 | 51 | // 52 | @Deprecated(message = "please use onRegister()", replaceWith = ReplaceWith("onRegister()")) 53 | override fun onPrepared() { 54 | onRegister() 55 | } 56 | open fun onRegister() {} 57 | override fun onCreate() {} 58 | override fun onStart() {} 59 | override fun onResume() {} 60 | override fun onPause() {} 61 | override fun onStop() {} 62 | override fun onDestroy() {} 63 | override fun onUnRegister() {} 64 | // 65 | 66 | override fun defineBlockService(): Class<*>? = null 67 | override fun isActive(): Boolean = isBlockActivated 68 | 69 | open fun bindModel(model: MODEL?) {} 70 | 71 | open fun generateSubBlocks(generator: BlockGenerator) {} 72 | 73 | override fun getBlockService(klass: Class, activeIfNeed: Boolean): T? { 74 | if (blockBlockMessageCenter is TreeBlockMessageCenter) { 75 | var handler = blockHandler() 76 | var impl = handler.queryService(klass, activeIfNeed) 77 | while (impl == null) { 78 | handler = handler.attachBlock.parent ?: break 79 | impl = handler.queryService(klass, activeIfNeed) 80 | } 81 | return impl 82 | } 83 | return blockBlockMessageCenter.queryService(this, klass, activeIfNeed) 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/base/BlockBuilder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.base 17 | 18 | import android.view.ViewGroup 19 | import androidx.annotation.IdRes 20 | import com.bytedance.blockframework.framework.task.BlockInflater 21 | import com.bytedance.blockframework.framework.task.DefaultLayoutInflater 22 | 23 | /** 24 | * 25 | * @author: Created by zhoujunjie on 2023/8/9 26 | * @mail zhoujunjie.9743@bytedance.com 27 | **/ 28 | 29 | open class BaseBlockBuilder> { 30 | 31 | /** 32 | * Block Instance 33 | */ 34 | lateinit var instance: () -> Block 35 | 36 | /** 37 | * Conditions indicating whether a Block can be added, generally used to determine 38 | * when the same Block is reused in different scenarios 39 | * 40 | * For example:condition = { scene == InnerStream },means that only the Block 41 | * will be added in InnerStream scenarios, and not in other scenarios 42 | */ 43 | var condition: () -> Boolean = { true } 44 | 45 | /** 46 | * The placeholder ID corresponding to UIBlock, and the View to which 47 | * the Block is opposed will be added to the corresponding parentId in the parent Block 48 | */ 49 | @IdRes var parentId: Int = -1 50 | 51 | /** 52 | * In UIBlock, View is added to the layout parameters of the parent Block 53 | */ 54 | var layoutParams: ViewGroup.LayoutParams? = null 55 | 56 | /** 57 | * In UIBlock, whether View replaces the parentView occupying the pit, 58 | * please uses it in conjunction with [parentId]. 59 | * This is mainly used to reduce layout levels. 60 | */ 61 | var replaceParent = false 62 | 63 | /** 64 | * UIBlock custom layout inflate, using [DefaultLayoutInflater] by default 65 | */ 66 | var viewInflater: BlockInflater? = null 67 | 68 | /** 69 | * UIBlock must create a View on the main thread or not 70 | */ 71 | var createUIOnMainThread = false 72 | 73 | /** 74 | * Whether Block Bind immediately, the default is false, 75 | * which means that Bind will be triggered after the View tree is created. 76 | * please use in conjunction with [createUIOnMainThread] 77 | */ 78 | var immediateBind = false 79 | 80 | /** 81 | * Means Block delayed loading 82 | */ 83 | var lazyActive = false 84 | 85 | internal fun build(): Block = instance() 86 | } 87 | 88 | class BlockBuilder : BaseBlockBuilder>() 89 | -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/base/IUIBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.base 17 | 18 | import android.view.View 19 | import android.view.ViewGroup.LayoutParams 20 | import com.bytedance.blockframework.framework.task.BlockInflater 21 | import com.bytedance.blockframework.framework.task.DefaultLayoutInflater 22 | import kotlinx.android.extensions.LayoutContainer 23 | 24 | /** 25 | * 26 | * @Author: Created by zhoujunjie on 2023/8/27 27 | * @mail zhoujunjie.9743@bytedance.com 28 | **/ 29 | 30 | data class UIBlockConfig( 31 | var parentId: Int = -1, 32 | var layoutParams: LayoutParams? = null, 33 | var createUIOnMainThread: Boolean = false, 34 | var replaceParent: Boolean = false, 35 | var viewInflater: BlockInflater = DefaultLayoutInflater 36 | ) 37 | 38 | interface IUIBlock : LayoutContainer { 39 | 40 | companion object { 41 | const val USE_PARENT_LAYOUT = -1 42 | } 43 | 44 | val uiConfig: UIBlockConfig 45 | override var containerView: View 46 | fun onCreateView(parent: View?): View 47 | fun onViewCreated(view: View) 48 | fun getView(): View? 49 | fun customGenerateView(currentView: View, parent: View?): Boolean { 50 | return false 51 | } 52 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/base/NoBindBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.base 17 | 18 | import com.bytedance.blockframework.framework.join.IBlockContext 19 | 20 | /** 21 | * Description: Blocks that do not require Bind data 22 | * 23 | * @Author: Created by zhoujunjie on 2023/8/9 24 | * @mail zhoujunjie.9743@bytedance.com 25 | **/ 26 | 27 | open class NoBindBlock(blockContext: IBlockContext) : BaseBlock(blockContext) { 28 | final override fun bindModel(model: Nothing?) { 29 | super.bindModel(model) 30 | } 31 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/base/UIBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.base 17 | 18 | import android.view.View 19 | import android.view.ViewGroup 20 | import androidx.annotation.IdRes 21 | import com.bytedance.blockframework.framework.core.IBlockModel 22 | import com.bytedance.blockframework.framework.join.IBlockContext 23 | 24 | /** 25 | * 26 | * @author: Created by zhoujunjie on 2023/8/9 27 | * @mail zhoujunjie.9743@bytedance.com 28 | **/ 29 | 30 | abstract class UIBlock>(blockContext: IBlockContext) : BaseBlock(blockContext), IUIBlock { 31 | 32 | override val uiConfig: UIBlockConfig = UIBlockConfig() 33 | 34 | override lateinit var containerView: View 35 | 36 | override fun onCreateView(parent: View?): View { 37 | if (layoutResource() == -1) { 38 | return parent!! 39 | } 40 | return uiConfig.viewInflater.getView(layoutResource(), parent!!.context, parent as ViewGroup) 41 | } 42 | 43 | abstract fun layoutResource(): Int 44 | 45 | override fun onViewCreated(view: View) {} 46 | 47 | override fun getView(): View? { 48 | return if (this::containerView.isInitialized) { 49 | containerView 50 | } else { 51 | null 52 | } 53 | } 54 | 55 | fun findViewById(@IdRes id: Int): T? { 56 | return getView()?.findViewById(id) 57 | } 58 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/config/BlockInit.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.config 17 | 18 | import com.bytedance.blockframework.framework.monitor.BlockLogger 19 | 20 | /** 21 | * 22 | * @author: Created by zhoujunjie on 2023/8/9 23 | * @mail zhoujunjie.9743@bytedance.com 24 | **/ 25 | 26 | object BlockInit { 27 | 28 | private var config: IBlockInitConfig = IBlockInitConfig.DefaultInitConfig() 29 | 30 | fun useTreeMessageCenterEnable(): Boolean { 31 | return config.useTreeMessageCenterEnable() 32 | } 33 | 34 | fun allTaskMustRunOnMain(): Boolean { 35 | return config.allTaskMustRunOnMain() 36 | } 37 | 38 | fun recordEnable(): Boolean { 39 | return config.recordEnable() 40 | } 41 | 42 | fun logOptEnable(): Boolean { 43 | return config.logOptEnable() 44 | } 45 | 46 | fun enableDebugDependencyCheck(): Boolean { 47 | return config.enableDebugDependencyCheck() 48 | } 49 | 50 | fun init(config: IBlockInitConfig) { 51 | this.config = config 52 | BlockLogger.setLogger(config.getBlockLogger()) 53 | } 54 | 55 | fun recordException(e: Throwable?) { 56 | config.recordException(e) 57 | } 58 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/config/IBlockInitConfig.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.config 17 | 18 | import com.bytedance.blockframework.framework.monitor.BlockLogger 19 | import com.bytedance.blockframework.framework.monitor.DefaultBlockLogger 20 | 21 | /** 22 | * 23 | * @Author: Created by zhoujunjie on 2023/8/9 24 | * @mail zhoujunjie.9743@bytedance.com 25 | **/ 26 | 27 | interface IBlockInitConfig { 28 | 29 | fun recordEnable(): Boolean 30 | 31 | fun useTreeMessageCenterEnable(): Boolean 32 | 33 | fun getBlockLogger(): BlockLogger 34 | 35 | fun allTaskMustRunOnMain(): Boolean 36 | 37 | fun logOptEnable(): Boolean 38 | 39 | fun enableDebugDependencyCheck(): Boolean 40 | 41 | fun recordException(e: Throwable?) 42 | 43 | class DefaultInitConfig : IBlockInitConfig { 44 | 45 | override fun recordEnable(): Boolean { 46 | return false 47 | } 48 | 49 | override fun useTreeMessageCenterEnable(): Boolean { 50 | return true 51 | } 52 | 53 | override fun getBlockLogger(): BlockLogger = DefaultBlockLogger() 54 | 55 | override fun allTaskMustRunOnMain(): Boolean { 56 | return true 57 | } 58 | 59 | override fun logOptEnable(): Boolean { 60 | return false 61 | } 62 | 63 | override fun enableDebugDependencyCheck(): Boolean { 64 | return true 65 | } 66 | 67 | override fun recordException(e: Throwable?) { 68 | 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/core/BlockGenerator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.core 17 | 18 | import android.view.ViewGroup 19 | import com.bytedance.blockframework.framework.base.BlockBuilder 20 | import com.bytedance.blockframework.framework.base.IUIBlock 21 | import com.bytedance.blockframework.framework.core.message.BlockContractManager 22 | import com.bytedance.blockframework.framework.task.BlockViewBuildTask 23 | import com.bytedance.blockframework.framework.utils.BlockDsl 24 | import com.bytedance.blockframework.framework.utils.blockHandler 25 | 26 | /** 27 | * 28 | * @author: Created by zhoujunjie on 2023/8/7 29 | * @mail zhoujunjie.9743@bytedance.com 30 | **/ 31 | 32 | interface BlockGenerator { 33 | fun generate(action: SubBlockCreator.() -> Unit) 34 | } 35 | 36 | interface SubBlockCreator { 37 | @BlockDsl 38 | fun addBlock(builder: BlockBuilder.() -> Unit) 39 | } 40 | 41 | internal class BlockGeneratorImpl( 42 | private val supervisor: BlockUnitHandler?, 43 | private val contractManager: BlockContractManager, 44 | private val uiTasks: MutableList 45 | ) : BlockGenerator { 46 | override fun generate(action: SubBlockCreator.() -> Unit) { 47 | val adder = object : SubBlockCreator { 48 | override fun addBlock(builder: BlockBuilder.() -> Unit) { 49 | supervisor?.apply { 50 | val config = BlockBuilder().apply(builder) 51 | if (!config.condition()) { 52 | return@apply 53 | } 54 | val block = config.build() 55 | block.immediateBind = config.immediateBind 56 | if (block is IUIBlock) { 57 | block.uiConfig.parentId = config.parentId 58 | block.uiConfig.layoutParams = config.layoutParams 59 | block.uiConfig.replaceParent = config.replaceParent 60 | block.uiConfig.createUIOnMainThread = config.createUIOnMainThread 61 | // 子线程创建UI的Block不能进行immediateBind 62 | if (!config.createUIOnMainThread) { 63 | block.immediateBind = false 64 | } 65 | if (config.viewInflater != null) { 66 | block.uiConfig.viewInflater = config.viewInflater!! 67 | } else { 68 | block.uiConfig.viewInflater = block.blockContext.getLayoutInflater() 69 | } 70 | 71 | var parentView = getAttachView() 72 | if (config.parentId != -1) { 73 | parentView = parentView?.findViewById(config.parentId) 74 | } 75 | if (block.lazyActive) { 76 | block.activeTask = { 77 | val task = BlockViewBuildTask(block, parentView as? ViewGroup) 78 | task.run() 79 | val target = task.result.value 80 | block.blockHandler().apply { 81 | installView(target) 82 | activeView() 83 | } 84 | } 85 | } else { 86 | uiTasks.add(BlockViewBuildTask(block, parentView as? ViewGroup)) 87 | } 88 | } 89 | loadBlock(block) 90 | contractManager.registerBlock(block) 91 | block.generateSubBlocks(BlockGeneratorImpl(block.blockHandler(), contractManager, uiTasks)) 92 | } 93 | } 94 | } 95 | adder.action() 96 | } 97 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/core/BlockLifeCycleDispatcher.kt: -------------------------------------------------------------------------------- 1 | package com.bytedance.blockframework.framework.core 2 | 3 | import androidx.lifecycle.Lifecycle 4 | import androidx.lifecycle.LifecycleEventObserver 5 | import androidx.lifecycle.LifecycleOwner 6 | import com.bytedance.blockframework.framework.base.BaseBlock 7 | import com.bytedance.blockframework.framework.utils.LifecycleUtil.handleLifecycleState 8 | import com.bytedance.blockframework.framework.utils.blockHandler 9 | 10 | /** 11 | * description: 12 | * 13 | * @author Created by zhoujunjie on 2024/11/14 14 | * @mail zhoujunjie.9743@bytedance.com 15 | **/ 16 | 17 | class BlockLifeCycleDispatcher(private val parentBlock: BaseBlock<*, *>) : LifecycleEventObserver { 18 | 19 | private val TAG = "BlockLifeCycleDispatcher" 20 | 21 | override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { 22 | when (event) { 23 | Lifecycle.Event.ON_CREATE -> onCreate() 24 | Lifecycle.Event.ON_START -> onStart() 25 | Lifecycle.Event.ON_RESUME -> onResume() 26 | Lifecycle.Event.ON_PAUSE -> onPause() 27 | Lifecycle.Event.ON_STOP -> onStop() 28 | Lifecycle.Event.ON_DESTROY -> onDestroy() 29 | else -> Unit 30 | } 31 | } 32 | 33 | private fun onCreate() { 34 | parentBlock.blockHandler().getChildBlocks().forEach { 35 | handleLifecycleState(Lifecycle.State.CREATED, it) 36 | } 37 | } 38 | 39 | private fun onStart() { 40 | parentBlock.blockHandler().getChildBlocks().filter { !it.lazyActive }.forEach { 41 | handleLifecycleState(Lifecycle.State.STARTED, it) 42 | } 43 | } 44 | 45 | private fun onResume() { 46 | parentBlock.blockHandler().getChildBlocks().filter { !it.lazyActive }.forEach { 47 | handleLifecycleState(Lifecycle.State.RESUMED, it) 48 | } 49 | } 50 | 51 | private fun onPause() { 52 | parentBlock.blockHandler().getChildBlocks().filter { !it.lazyActive }.forEach { 53 | handleLifecycleState(Lifecycle.State.STARTED, it) 54 | } 55 | } 56 | 57 | private fun onStop() { 58 | parentBlock.blockHandler().getChildBlocks().filter { !it.lazyActive }.forEach { 59 | handleLifecycleState(Lifecycle.State.CREATED, it) 60 | } 61 | } 62 | 63 | private fun onDestroy() { 64 | parentBlock.blockHandler().getChildBlocks().filter { !it.lazyActive }.forEach { 65 | handleLifecycleState(Lifecycle.State.DESTROYED, it) 66 | } 67 | parentBlock.lifecycle.removeObserver(this) 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/core/BlockUnitHandler.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.core 17 | 18 | import android.content.Context 19 | import android.view.View 20 | import android.view.ViewGroup 21 | import com.bytedance.blockframework.contract.AbstractBlock 22 | import com.bytedance.blockframework.contract.BlockImplWrapper 23 | import com.bytedance.blockframework.framework.base.BaseBlock 24 | import com.bytedance.blockframework.framework.base.IUIBlock 25 | import com.bytedance.blockframework.framework.config.BlockInit 26 | import com.bytedance.blockframework.framework.monitor.BlockLogger 27 | import com.bytedance.blockframework.framework.utils.syncInvoke 28 | import com.bytedance.blockframework.framework.utils.uploadException 29 | import com.bytedance.blockframework.interaction.Event 30 | import com.bytedance.blockframework.interaction.IObserver 31 | 32 | /** 33 | * 34 | * @Author: Created by zhoujunjie on 2023/7/17 35 | * @mail zhoujunjie.9743@bytedance.com 36 | **/ 37 | 38 | class BlockUnitHandler internal constructor( 39 | val context: Context, 40 | val attachBlock: BaseBlock<*, *>, 41 | private val lifecycleHandler: BlockLifeCycleDispatcher 42 | ) { 43 | 44 | companion object { 45 | fun create(context: Context, block: BaseBlock<*, *>) = BlockUnitHandler(context, block, BlockLifeCycleDispatcher(block)) 46 | } 47 | 48 | private val childList: MutableList> = mutableListOf() 49 | private val serviceMap: MutableMap, BlockImplWrapper> = mutableMapOf() 50 | val eventToObserverMap: MutableMap, MutableList>> = mutableMapOf() 51 | 52 | fun registerObserver(observer: IObserver, eventClass: Class) { 53 | // update event to observer map 54 | val observers = eventToObserverMap[eventClass] ?: run { 55 | mutableListOf>().apply { 56 | eventToObserverMap[eventClass] = this 57 | } 58 | } 59 | if (!observers.contains(observer)) { 60 | observers.add(observer) 61 | } 62 | } 63 | 64 | fun notifyEvent(event: Event): Boolean { 65 | val observers = eventToObserverMap[event.javaClass] ?: return false 66 | for (observer in observers) { 67 | if (observer is AbstractBlock) { 68 | if (!observer.isActive()) { 69 | continue 70 | } 71 | } 72 | val intercept = observer.onEvent(event) 73 | if (intercept && event.canIntercept) { 74 | break 75 | } 76 | } 77 | return true 78 | } 79 | 80 | fun attachLifecycle() { 81 | val blockLifecycle = attachBlock.lifecycle 82 | syncInvoke { 83 | blockLifecycle.removeObserver(lifecycleHandler) 84 | blockLifecycle.addObserver(lifecycleHandler) 85 | } 86 | } 87 | 88 | fun loadBlock(subBlock: BaseBlock<*, *>) { 89 | subBlock.parent = this 90 | childList.add(subBlock) 91 | } 92 | 93 | fun installView(target: View) { 94 | val block = attachBlock as? IUIBlock ?: return 95 | val parentView = getAttachView() 96 | if (block.customGenerateView(target, parentView)) { 97 | block.containerView = target 98 | return 99 | } 100 | if (block.uiConfig.parentId == -1) { 101 | block.containerView = target 102 | return 103 | } 104 | 105 | val placeHolderView = parentView?.findViewById(block.uiConfig.parentId) 106 | if (placeHolderView == null) { 107 | if (!BlockInit.logOptEnable() || BlockLogger.debug()) { 108 | uploadException(RuntimeException("${(block as? BaseBlock<*, *>)?.getBlockKey() ?: ""} placeHolder is Null"), true) 109 | } 110 | return 111 | } 112 | if (block.uiConfig.replaceParent) { 113 | replaceSelfWithView(target, placeHolderView) 114 | } else { 115 | addViewInPlaceholder(target, placeHolderView, block.uiConfig.layoutParams) 116 | } 117 | block.containerView = target 118 | } 119 | 120 | private fun replaceSelfWithView(view: View, placeHolder: View) { 121 | val parent = placeHolder.parent as? ViewGroup ?: return 122 | val index = parent.indexOfChild(placeHolder) 123 | parent.removeViewInLayout(placeHolder) 124 | val layoutParams = placeHolder.layoutParams 125 | if (layoutParams != null) { 126 | parent.addView(view, index, layoutParams) 127 | } else { 128 | parent.addView(view, index) 129 | } 130 | } 131 | 132 | private fun addViewInPlaceholder(view: View, placeHolder: View, layoutParams: ViewGroup.LayoutParams? = null) { 133 | val containerView = placeHolder as? ViewGroup ?: return 134 | if (layoutParams != null) { 135 | containerView.addView(view, layoutParams) 136 | } else { 137 | containerView.addView(view) 138 | } 139 | } 140 | 141 | fun activeView() { 142 | val block = attachBlock as? IUIBlock ?: return 143 | block.getView()?.let { 144 | block.onViewCreated(it) 145 | (block as BaseBlock<*, *>).isBlockActivated = true 146 | } 147 | } 148 | 149 | fun getAttachView(): View? { 150 | var block: BaseBlock<*, *>? = attachBlock 151 | var attachView: View? = null 152 | var find = false 153 | while (!find) { 154 | attachView = (block as? IUIBlock)?.getView() 155 | block = block?.parent?.attachBlock 156 | find = attachView != null || block == null 157 | } 158 | return attachView 159 | } 160 | 161 | fun registerService(klass: Class, blockImplWrapper: BlockImplWrapper) { 162 | if (serviceMap.containsKey(klass)) { 163 | if (!BlockInit.logOptEnable() || BlockLogger.debug()) { 164 | uploadException(RuntimeException("registerService $klass already exists"), true) 165 | } 166 | } 167 | serviceMap[klass] = blockImplWrapper 168 | } 169 | 170 | fun queryService(klass: Class, activeIfNeed: Boolean = true): T? { 171 | return if (serviceMap.contains(klass)) { 172 | val target = serviceMap[klass]?.impl 173 | if(activeIfNeed) (target as? BaseBlock<*,*>)?.activeIfNeed() 174 | serviceMap[klass]?.impl as T? 175 | } else { 176 | if (!BlockInit.logOptEnable() || BlockLogger.debug()) { 177 | uploadException(RuntimeException("queryService $klass not find"), false) 178 | } 179 | null 180 | } 181 | } 182 | 183 | fun getChildBlocks(): List> = childList 184 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/core/IBlockModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.core 17 | 18 | /** 19 | * 20 | * @Author: Created by zhoujunjie on 2023/8/25 21 | * @mail zhoujunjie.9743@bytedance.com 22 | **/ 23 | 24 | interface IBlockModel { 25 | val data : T? 26 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/core/message/BlockContractManager.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.core.message 17 | 18 | import android.content.Context 19 | import com.bytedance.blockframework.contract.AbstractBlock 20 | import com.bytedance.blockframework.contract.AbstractBlockManager 21 | import com.bytedance.blockframework.interaction.BaseBlockMessageCenter 22 | import com.bytedance.blockframework.interaction.IBlockMessageCenter 23 | 24 | /** 25 | * 26 | * @Author: Created by zhoujunjie on 2023/8/4 27 | * @mail zhoujunjie.9743@bytedance.com 28 | **/ 29 | 30 | class BlockContractManager(context: Context, var messageCenter: IBlockMessageCenter = BaseBlockMessageCenter()) : AbstractBlockManager(context, messageCenter) { 31 | override fun onRegisterBlock(block: AbstractBlock) {} 32 | 33 | override fun onUnregisterBlock(block: AbstractBlock) {} 34 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/core/message/TreeBlockMessageCenter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.core.message 17 | 18 | import com.bytedance.blockframework.contract.BlockImplWrapper 19 | import com.bytedance.blockframework.framework.base.BaseBlock 20 | import com.bytedance.blockframework.interaction.BaseBlockMessageCenter 21 | 22 | /** 23 | * 24 | * @author Created by zhoujunjie on 2023/8/4 25 | * @mail zhoujunjie.9743@bytedance.com 26 | **/ 27 | 28 | open class TreeBlockMessageCenter : BaseBlockMessageCenter() { 29 | 30 | override fun registerService(klass: Class, blockImplWrapper: BlockImplWrapper) { 31 | var block = blockImplWrapper.impl as? BaseBlock<*,*> 32 | while (block != null) { 33 | block.parent?.registerService(klass, blockImplWrapper) 34 | block = block.parent?.attachBlock 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/event/AllBlockViewCreatedEvent.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.event 17 | 18 | import android.view.View 19 | import com.bytedance.blockframework.interaction.Event 20 | 21 | /** 22 | * 23 | * @Author: Created by zhoujunjie on 2023/8/28 24 | * @mail zhoujunjie.9743@bytedance.com 25 | **/ 26 | 27 | class AllBlockViewCreatedEvent(val rootView: View) : Event() -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/join/BlockContextImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.join 17 | 18 | import android.content.Context 19 | import android.view.View 20 | import androidx.lifecycle.Lifecycle 21 | import com.bytedance.blockframework.framework.base.BaseBlock 22 | import com.bytedance.blockframework.framework.config.BlockInit 23 | import com.bytedance.blockframework.framework.core.BlockCoreManager 24 | import com.bytedance.blockframework.framework.core.BlockUnitHandler 25 | import com.bytedance.blockframework.framework.core.IBlockModel 26 | import com.bytedance.blockframework.framework.monitor.BlockLogger 27 | import com.bytedance.blockframework.framework.task.BlockInflater 28 | import com.bytedance.blockframework.framework.task.DefaultInflaterProvider 29 | import com.bytedance.blockframework.framework.utils.uploadException 30 | import com.bytedance.blockframework.interaction.Event 31 | import com.bytedance.blockframework.interaction.IBlockMessageCenter 32 | 33 | /** 34 | * Block framework context, establishing internal and external connections within the Block framework 35 | * 36 | * @author: Created by zhoujunjie on 2023/8/9 37 | * @mail zhoujunjie.9743@bytedance.com 38 | **/ 39 | 40 | interface IBlockContext { 41 | fun getScene(): IBlockScene 42 | fun setMessageCenter(center: IBlockMessageCenter) 43 | fun getMessageCenter(): IBlockMessageCenter 44 | fun findBlockHandler(block: BaseBlock<*,*>): BlockUnitHandler 45 | fun setLayoutInflater(inflater: BlockInflater) 46 | fun getLayoutInflater(): BlockInflater 47 | fun registerDepend(clazz: Class, depend: T) 48 | fun findDepend(clazz: Class): T 49 | fun findDependOrNull(clazz: Class): T? 50 | fun getBlockService(clazz: Class) : T? 51 | fun notifyEvent(event: Event) 52 | fun addAfterBindTask(action: ()->Unit) 53 | } 54 | 55 | class BlockContextImpl(private val blockScene: IBlockScene) : IBlockContext { 56 | 57 | private val blockCore = BlockCoreManager() 58 | private var inflater: BlockInflater? = null 59 | private val dependMap : MutableMap, IBlockDepend> = mutableMapOf() 60 | 61 | internal fun initRootBlock(context: Context, rootBlock: BaseBlock<*,*>, messageCenter: IBlockMessageCenter, parent: View? = null) { 62 | blockCore.initScene(blockScene) 63 | blockCore.initRootBlock(context, rootBlock, messageCenter, parent) 64 | } 65 | 66 | override fun getScene(): IBlockScene { 67 | return blockScene 68 | } 69 | 70 | override fun registerDepend(clazz: Class, depend: T) { 71 | if (dependMap.containsKey(clazz)) { 72 | if (!BlockInit.logOptEnable() || BlockLogger.debug()) { 73 | uploadException(RuntimeException("registerService $clazz already exists"), true) 74 | } 75 | } 76 | dependMap[clazz] = depend 77 | } 78 | 79 | fun > bindBlockModel(model: M) { 80 | blockCore.bindBlockModel(model) 81 | } 82 | 83 | internal fun dispatchLifecycleState(state: Lifecycle.State) { 84 | blockCore.dispatchLifecycleState(state) 85 | } 86 | 87 | override fun findDepend(clazz: Class): T { 88 | return if (!dependMap.containsKey(clazz)) { 89 | throw Exception() 90 | } else { 91 | dependMap[clazz] as T 92 | } 93 | } 94 | 95 | override fun findDependOrNull(clazz: Class): T? { 96 | return dependMap[clazz] as? T 97 | } 98 | 99 | override fun getBlockService(clazz: Class): T? { 100 | return blockCore.getBlockService(clazz) 101 | } 102 | 103 | override fun notifyEvent(event: Event) { 104 | blockCore.notifyEvent(event) 105 | } 106 | 107 | override fun setMessageCenter(center: IBlockMessageCenter) { 108 | blockCore.setMessageCenter(center) 109 | } 110 | 111 | override fun getMessageCenter(): IBlockMessageCenter { 112 | return blockCore.getMessageCenter() 113 | } 114 | 115 | override fun findBlockHandler(block: BaseBlock<*,*>): BlockUnitHandler { 116 | return blockCore.findBlockHandler(block) 117 | } 118 | 119 | override fun setLayoutInflater(inflater: BlockInflater) { 120 | this.inflater = inflater 121 | } 122 | 123 | override fun getLayoutInflater(): BlockInflater { 124 | return inflater ?: DefaultInflaterProvider.getInflater() 125 | } 126 | 127 | override fun addAfterBindTask(action: ()->Unit) { 128 | return blockCore.addAfterBindTask(action) 129 | } 130 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/join/IBlockAbility.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.join 17 | 18 | /** 19 | * 20 | * @author: Created by zhoujunjie on 2024/1/2 21 | * @mail zhoujunjie.9743@bytedance.com 22 | **/ 23 | 24 | interface IBlockAbility -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/join/IBlockDepend.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.join 17 | 18 | /** 19 | * 20 | * @author: Created by zhoujunjie on 2023/8/9 21 | * @mail zhoujunjie.9743@bytedance.com 22 | **/ 23 | 24 | interface IBlockDepend -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/join/IBlockJoin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.join 17 | 18 | import android.content.Context 19 | import android.view.View 20 | import androidx.lifecycle.Lifecycle 21 | import com.bytedance.blockframework.framework.base.BaseBlock 22 | import com.bytedance.blockframework.framework.core.IBlockModel 23 | import com.bytedance.blockframework.framework.utils.getDefaultCenter 24 | import com.bytedance.blockframework.interaction.IBlockMessageCenter 25 | 26 | /** 27 | * 28 | * @Author: Created by zhoujunjie on 2023/8/9 29 | * @mail zhoujunjie.9743@bytedance.com 30 | **/ 31 | 32 | interface IBlockJoin { 33 | val blockContext: IBlockContext 34 | val blockScene: IBlockScene 35 | } 36 | 37 | fun IBlockJoin.initRootBlock(context: Context, rootBlock: BaseBlock<*, *>) { 38 | (blockContext as? BlockContextImpl)?.initRootBlock(context, rootBlock, getDefaultCenter(), null) 39 | } 40 | 41 | fun IBlockJoin.initRootBlock(context: Context, rootBlock: BaseBlock<*, *>, messageCenter: IBlockMessageCenter = getDefaultCenter(), parent: View? = null) { 42 | (blockContext as? BlockContextImpl)?.initRootBlock(context, rootBlock, messageCenter, parent) 43 | } 44 | 45 | inline fun > IBlockJoin.bindBlockModel(model: MODEL) { 46 | (blockContext as? BlockContextImpl)?.bindBlockModel(model) 47 | } 48 | 49 | fun IBlockJoin.dispatchBlockLifecycle(state: Lifecycle.State) { 50 | (blockContext as? BlockContextImpl)?.dispatchLifecycleState(state) 51 | } 52 | 53 | fun IBlockJoin.dispatchOnCreate() { 54 | dispatchBlockLifecycle(Lifecycle.State.CREATED) 55 | } 56 | 57 | fun IBlockJoin.dispatchOnStart() { 58 | dispatchBlockLifecycle(Lifecycle.State.STARTED) 59 | } 60 | 61 | fun IBlockJoin.dispatchOnResume() { 62 | dispatchBlockLifecycle(Lifecycle.State.RESUMED) 63 | } 64 | 65 | fun IBlockJoin.dispatchOnPause() { 66 | dispatchBlockLifecycle(Lifecycle.State.STARTED) 67 | } 68 | 69 | fun IBlockJoin.dispatchOnStop() { 70 | dispatchBlockLifecycle(Lifecycle.State.CREATED) 71 | } 72 | 73 | fun IBlockJoin.dispatchOnDestroy() { 74 | dispatchBlockLifecycle(Lifecycle.State.DESTROYED) 75 | } 76 | 77 | inline fun IBlockJoin.registerDepend(depend: T) { 78 | blockContext.registerDepend(T::class.java, depend) 79 | } 80 | 81 | inline fun IBlockJoin.blockService(): T? { 82 | return blockContext.getBlockService(T::class.java) 83 | } 84 | 85 | inline fun IBlockJoin.findDepend(): T { 86 | return blockContext.findDepend(T::class.java) 87 | } 88 | 89 | inline fun IBlockJoin.findDependOrNull(): T? { 90 | return blockContext.findDependOrNull(T::class.java) 91 | } 92 | 93 | fun IBlockJoin.getBlockContext(scene: IBlockScene): IBlockContext = BlockContextImpl(scene) 94 | 95 | -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/join/IBlockScene.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.join 17 | 18 | /** 19 | * 20 | * @Author: Created by zhoujunjie on 2023/8/9 21 | * @mail zhoujunjie.9743@bytedance.com 22 | **/ 23 | 24 | interface IBlockScene { 25 | fun getName(): String 26 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/monitor/BlockLogger.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.monitor 17 | 18 | import android.os.SystemClock 19 | import com.bytedance.blockframework.BuildConfig 20 | import com.bytedance.blockframework.utils.Logger 21 | 22 | /** 23 | * 24 | * @Author: Created by zhoujunjie on 2023/8/9 25 | * @mail zhoujunjie.9743@bytedance.com 26 | **/ 27 | 28 | interface BlockLogger { 29 | 30 | fun debug(): Boolean 31 | 32 | fun log(tag: String, info: String) 33 | 34 | fun report(tag: String, params: Map) 35 | 36 | companion object : BlockLogger { 37 | private var logger: BlockLogger = DefaultBlockLogger() 38 | fun setLogger(logger: BlockLogger) { 39 | this.logger = logger 40 | } 41 | 42 | override fun debug(): Boolean { 43 | return logger.debug() 44 | } 45 | 46 | override fun log(tag: String, info: String) { 47 | logger.log(tag, info) 48 | } 49 | 50 | override fun report(tag: String, params: Map) { 51 | logger.report(tag, params) 52 | } 53 | } 54 | } 55 | 56 | class DefaultBlockLogger : BlockLogger { 57 | 58 | override fun debug(): Boolean { 59 | return BuildConfig.DEBUG 60 | } 61 | 62 | override fun log(tag: String, info: String) { 63 | Logger.d(tag, info) 64 | } 65 | 66 | override fun report(tag: String, params: Map) { 67 | 68 | } 69 | } 70 | 71 | fun logger(tag: String, info: String) { 72 | BlockLogger.log(tag, info) 73 | } 74 | 75 | fun currentTime(): Long = SystemClock.elapsedRealtime() 76 | 77 | fun currentThread(): Thread = Thread.currentThread() -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/monitor/BlockMonitor.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.monitor 17 | 18 | import com.bytedance.blockframework.framework.config.BlockInit 19 | import com.bytedance.blockframework.framework.join.IBlockScene 20 | import java.util.Vector 21 | 22 | /** 23 | * 24 | * @author Created by zhoujunjie on 2024/3/7 25 | * @mail zhoujunjie.9743@bytedance.com 26 | **/ 27 | 28 | data class JobRecord( 29 | val scene: String = "", 30 | val key: String = "", 31 | val type: String = "", 32 | val message: String = "", 33 | val cost: Long = 0L 34 | ) { 35 | override fun toString(): String { 36 | if (cost <= 0) { 37 | return "$scene -- $key -- $type -- $message" 38 | } 39 | return "$scene -- $key -- $type -- $message -- cost: ${cost}ms" 40 | } 41 | } 42 | 43 | const val TYPE_BLOCK_TREE_INIT = "pref_init_block_tree" 44 | const val TYPE_BLOCK_TREE_CREATE = "pref_create_block_tree" 45 | const val TYPE_BLOCK_TREE_BIND = "pref_bind_block_tree" 46 | const val TYPE_BLOCK_VIEW_CREATE = "pref_create_block_view" 47 | const val TYPE_BLOCK_RUN_TASK = "pref_create_run_task" 48 | const val TYPE_BLOCK_BIND = "pref_bind_block" 49 | 50 | object BlockMonitor { 51 | 52 | private val recordMap = HashMap>() 53 | 54 | fun record(scene: String, key: String, type: String, message: String = "", cost: Long = 0L) { 55 | if (!BlockInit.recordEnable()) return 56 | if (cost <= 0) { 57 | logger(scene, "$scene -- $key -- $type -- $message") 58 | } else { 59 | logger(scene, "$scene -- $key -- $type -- $message -- cost: ${cost}ms") 60 | } 61 | } 62 | 63 | fun doctor(scene: IBlockScene) { 64 | recordMap[scene.getName()]?.let { 65 | it.forEach { record -> 66 | logger(record.key, record.toString()) 67 | } 68 | } ?: logger(scene.getName(), "There no BlockTree in this scene!") 69 | } 70 | 71 | fun report() { 72 | recordMap.forEach { record -> 73 | val treeInitTime = record.value.first { it.type == TYPE_BLOCK_TREE_INIT }?.cost ?: 0L 74 | val treeBuildViewTime = record.value.first { it.type == TYPE_BLOCK_TREE_CREATE }?.cost 75 | ?: 0L 76 | val treeBindTime = record.value.first { it.type == TYPE_BLOCK_TREE_BIND }?.cost ?: 0L 77 | BlockLogger.report(record.key, hashMapOf( 78 | "block_tree_init_cost" to treeInitTime.toString(), 79 | "block_tree_create_view_cost" to treeBuildViewTime.toString(), 80 | "block_tree_bind_cost" to treeBindTime.toString() 81 | )) 82 | } 83 | } 84 | 85 | } 86 | 87 | -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/performance/HandlerProcessor.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.performance 17 | 18 | import android.os.Handler 19 | 20 | 21 | /** 22 | * 23 | * @Author: Created by zhoujunjie on 2022/12/20 24 | * @mail zhoujunjie.9743@bytedance.com 25 | **/ 26 | class HandlerProcessor(private val handler: Handler, private val sync: Boolean) { 27 | 28 | fun post(runnable: () -> Unit) { 29 | if (sync) { 30 | runnable() 31 | } else { 32 | handler.post(runnable) 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/performance/ThreadProcessor.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.performance 17 | 18 | import android.os.Handler 19 | import android.os.HandlerThread 20 | import android.os.Looper 21 | 22 | object ThreadProcessor { 23 | 24 | const val BLOCK_WORK_THREAD_LABEL = "Block_WorkThread" 25 | 26 | @Volatile private lateinit var workHandler: Handler 27 | private val mainHandler = Handler(Looper.getMainLooper()) 28 | 29 | fun work(): Handler { 30 | if (!::workHandler.isInitialized) { 31 | synchronized(this) { 32 | if (!::workHandler.isInitialized) { 33 | val handlerThread = HandlerThread(BLOCK_WORK_THREAD_LABEL) 34 | handlerThread.start() 35 | workHandler = Handler(handlerThread.looper) 36 | } 37 | } 38 | } 39 | return workHandler 40 | } 41 | 42 | fun main(): Handler { 43 | return mainHandler 44 | } 45 | 46 | fun setWork(handler: Handler) { 47 | workHandler = handler 48 | } 49 | 50 | fun post(runnable: () -> Unit) { 51 | work().post(runnable) 52 | } 53 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/task/BlockLayoutInflater.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.task 17 | 18 | import android.content.Context 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | 23 | /** 24 | * 25 | * @Author: Created by zhoujunjie on 2023/7/18 26 | * @mail zhoujunjie.9743@bytedance.com 27 | **/ 28 | 29 | interface BlockInflaterProvider { 30 | fun getInflater(): BlockInflater 31 | } 32 | 33 | interface BlockInflater { 34 | fun getView(id: Int, context: Context, parent: ViewGroup): View 35 | } 36 | 37 | object DefaultInflaterProvider : BlockInflaterProvider { 38 | override fun getInflater(): BlockInflater { 39 | return DefaultLayoutInflater 40 | } 41 | } 42 | 43 | object DefaultLayoutInflater : BlockInflater { 44 | override fun getView(id: Int, context: Context, parent: ViewGroup): View { 45 | return LayoutInflater.from(context).inflate(id, parent, false) 46 | } 47 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/task/BlockViewBuildTask.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.task 17 | 18 | import android.view.View 19 | import android.view.ViewGroup 20 | import com.bytedance.blockframework.framework.base.BaseBlock 21 | import com.bytedance.blockframework.framework.base.IUIBlock 22 | import com.bytedance.blockframework.framework.monitor.BlockMonitor 23 | import com.bytedance.blockframework.framework.monitor.TYPE_BLOCK_VIEW_CREATE 24 | import com.bytedance.blockframework.framework.monitor.currentThread 25 | import com.bytedance.blockframework.framework.monitor.currentTime 26 | import java.util.concurrent.atomic.AtomicInteger 27 | 28 | /** 29 | * 30 | * @Author: Created by zhoujunjie on 2023/7/18 31 | * @mail zhoujunjie.9743@bytedance.com 32 | **/ 33 | 34 | class BlockViewBuildTask( 35 | val uiBlock: IUIBlock, 36 | private val parent: ViewGroup?, 37 | override val result: TaskResult = TaskResult() 38 | ) : Task { 39 | 40 | companion object { 41 | var sAtomViewTypeCreator = AtomicInteger(100) 42 | } 43 | 44 | override val taskPriority: Int = sAtomViewTypeCreator.decrementAndGet() 45 | 46 | override val mustMainThread: Boolean = uiBlock.uiConfig.createUIOnMainThread 47 | 48 | override fun run() { 49 | if (parent != null) { 50 | val startCreate = currentTime() 51 | BlockMonitor.record(getBlockScene(), (uiBlock as BaseBlock<*, *>).getBlockKey(), TYPE_BLOCK_VIEW_CREATE, "[${currentThread().name}] view_create_start") 52 | result.value = uiBlock.onCreateView(parent) 53 | BlockMonitor.record(getBlockScene(), (uiBlock as BaseBlock<*, *>).getBlockKey(), TYPE_BLOCK_VIEW_CREATE, "[${currentThread().name}] view_create_end", currentTime() - startCreate) 54 | } 55 | } 56 | 57 | fun getBlockScene(): String { 58 | return (uiBlock as? BaseBlock<*, *>)?.blockContext?.getScene()?.getName() ?: "" 59 | } 60 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/task/ITaskManager.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.task 17 | 18 | /** 19 | * 20 | * @author Created by zhoujunjie on 2024/8/7 21 | * @mail zhoujunjie.9743@bytedance.com 22 | **/ 23 | 24 | typealias TasksFinished = (List) -> Unit 25 | 26 | interface ITaskManager { 27 | fun addTask(tasks: List) 28 | fun handleTasks(mainFinished: TasksFinished? = null, subFinished: TasksFinished? = null, allFinished: TasksFinished? = null) 29 | fun clear() 30 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/task/ScheduleTask.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.task 17 | 18 | /** 19 | * 20 | * @Author: Created by zhoujunjie on 2023/7/18 21 | * @mail zhoujunjie.9743@bytedance.com 22 | **/ 23 | 24 | enum class TaskPriority{ 25 | Low, High, Boot 26 | } 27 | 28 | interface ScheduleTask { 29 | val mustMainThread: Boolean 30 | val taskPriority: Int 31 | fun run() 32 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/task/Task.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.task 17 | 18 | /** 19 | * 20 | * @Author: Created by zhoujunjie on 2023/7/18 21 | * @mail zhoujunjie.9743@bytedance.com 22 | **/ 23 | 24 | interface Task : ScheduleTask { 25 | override val mustMainThread: Boolean 26 | override val taskPriority: Int 27 | val result: TaskResult 28 | override fun run() 29 | } 30 | 31 | class TaskResult { 32 | lateinit var value: T 33 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/task/TaskManager.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.task 17 | 18 | import androidx.annotation.MainThread 19 | import androidx.annotation.WorkerThread 20 | import com.bytedance.blockframework.framework.config.BlockInit 21 | import com.bytedance.blockframework.framework.join.IBlockScene 22 | import com.bytedance.blockframework.framework.monitor.BlockLogger 23 | import com.bytedance.blockframework.framework.monitor.BlockMonitor 24 | import com.bytedance.blockframework.framework.monitor.TYPE_BLOCK_RUN_TASK 25 | import com.bytedance.blockframework.framework.monitor.currentTime 26 | import com.bytedance.blockframework.framework.monitor.logger 27 | import com.bytedance.blockframework.framework.performance.ThreadProcessor 28 | import com.bytedance.blockframework.framework.performance.HandlerProcessor 29 | import com.bytedance.blockframework.framework.utils.uploadException 30 | import java.util.PriorityQueue 31 | 32 | class TaskManager(private val scene: IBlockScene, private val allTaskMustRunOnMain: Boolean): ITaskManager { 33 | 34 | private val TAG = "TaskManager" 35 | 36 | private val managerName by lazy { this::class.java.simpleName + "_" + this.hashCode() } 37 | 38 | private var main: HandlerProcessor = HandlerProcessor(ThreadProcessor.main(), false) 39 | private var work: HandlerProcessor = HandlerProcessor(ThreadProcessor.work(), false) 40 | 41 | private val mainThreadTasks = PriorityQueue(20, 42 | Comparator { o1, o2 -> o2.taskPriority.compareTo(o1.taskPriority) }) 43 | private val subThreadTasks = PriorityQueue(20, 44 | Comparator { o1, o2 -> o2.taskPriority.compareTo(o1.taskPriority) }) 45 | 46 | private var allFinished: TasksFinished? = null 47 | 48 | private var isMainTasksFinish = false 49 | private var isSubTasksFinish = false 50 | 51 | private var allTasks = listOf() 52 | 53 | @Synchronized 54 | override fun addTask(tasks: List) { 55 | allTasks = tasks 56 | tasks.forEach { 57 | if (allTaskMustRunOnMain || it.mustMainThread) { 58 | mainThreadTasks.add(it) 59 | } else { 60 | subThreadTasks.add(it) 61 | } 62 | } 63 | isMainTasksFinish = mainThreadTasks.isEmpty() 64 | isSubTasksFinish = subThreadTasks.isEmpty() 65 | } 66 | 67 | @MainThread 68 | override fun handleTasks(mainFinished: TasksFinished?, subFinished: TasksFinished?, allFinished: TasksFinished?) { 69 | this.allFinished = allFinished 70 | val start = currentTime() 71 | BlockMonitor.record(scene.getName(), managerName, TYPE_BLOCK_RUN_TASK, "start_handle_tasks") 72 | if (!subThreadTasks.isEmpty()) { 73 | work.post { 74 | processTaskInSubThread(subFinished, mainFinished, start) 75 | } 76 | } 77 | if (!mainThreadTasks.isEmpty()) { 78 | processMainThreadTasks(mainFinished, start) 79 | } 80 | } 81 | 82 | @MainThread 83 | private fun processMainThreadTasks(mainFinished: TasksFinished?, start: Long) { 84 | val resultTasks = mutableListOf() 85 | val startRun = currentTime() 86 | BlockMonitor.record(scene.getName(), managerName, TYPE_BLOCK_RUN_TASK, "start_handle_main_tasks") 87 | do { 88 | val targetTask: ScheduleTask? = mainThreadTasks.poll() 89 | targetTask?.let { 90 | it.run() 91 | resultTasks.add(it) 92 | } 93 | if (mainThreadTasks.isEmpty()) { 94 | BlockMonitor.record(scene.getName(), managerName, TYPE_BLOCK_RUN_TASK, "mainTasks_end", currentTime() - startRun) 95 | mainFinished?.invoke(resultTasks) 96 | isMainTasksFinish = true 97 | if (isSubTasksFinish) { 98 | allFinished?.invoke(allTasks) 99 | BlockMonitor.record(scene.getName(), managerName, TYPE_BLOCK_RUN_TASK, "allTask_main_end", currentTime() - start) 100 | } 101 | } 102 | } while (!mainThreadTasks.isEmpty()) 103 | } 104 | 105 | @WorkerThread 106 | private fun processTaskInSubThread(subFinished: TasksFinished?, mainFinished: TasksFinished?, start: Long) { 107 | val resultTasks = mutableListOf() 108 | val failTasks = mutableListOf() 109 | val startRun = currentTime() 110 | BlockMonitor.record(scene.getName(), managerName, TYPE_BLOCK_RUN_TASK, "start_handle_sub_tasks") 111 | do { 112 | val targetTask: ScheduleTask? = subThreadTasks.poll() 113 | runCatching { 114 | targetTask?.let { 115 | it.run() 116 | resultTasks.add(it) 117 | } 118 | }.onFailure { 119 | targetTask?.let { it1 -> 120 | failTasks.add(it1) 121 | } 122 | handleException(targetTask, it) 123 | } 124 | if (subThreadTasks.isEmpty()) { 125 | if (failTasks.size > 0) { 126 | main.post { 127 | mainThreadTasks.addAll(failTasks) 128 | isMainTasksFinish = false 129 | processMainThreadTasks(mainFinished, -1) 130 | } 131 | } 132 | BlockMonitor.record(scene.getName(), managerName, TYPE_BLOCK_RUN_TASK, "subTasks_end", currentTime() - startRun) 133 | main.post { 134 | subFinished?.invoke(resultTasks) 135 | isSubTasksFinish = true 136 | if (isMainTasksFinish) { 137 | BlockMonitor.record(scene.getName(), managerName, TYPE_BLOCK_RUN_TASK, "allTask_sub_end", currentTime() - start) 138 | allFinished?.invoke(allTasks) 139 | } 140 | } 141 | } 142 | } while (!subThreadTasks.isEmpty()) 143 | } 144 | 145 | override fun clear() { 146 | mainThreadTasks.clear() 147 | subThreadTasks.clear() 148 | } 149 | 150 | private fun handleException(task: ScheduleTask?, t: Throwable) { 151 | if (task is BlockViewBuildTask) { 152 | if (!BlockInit.logOptEnable() || BlockLogger.debug()) { 153 | logger(TAG, "${task.getBlockScene()} build UI Exception in ${Thread.currentThread().name}") 154 | } 155 | uploadException(t, true) 156 | BlockInit.recordException(t) 157 | } 158 | } 159 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/utils/BlockDsl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.utils 17 | 18 | @DslMarker 19 | annotation class BlockDsl 20 | -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/utils/BlockExt.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.framework.utils 17 | 18 | import android.os.Looper 19 | import com.bytedance.blockframework.framework.base.BaseBlock 20 | import com.bytedance.blockframework.framework.config.BlockInit 21 | import com.bytedance.blockframework.framework.core.BlockUnitHandler 22 | import com.bytedance.blockframework.framework.core.message.TreeBlockMessageCenter 23 | import com.bytedance.blockframework.framework.join.IBlockDepend 24 | import com.bytedance.blockframework.framework.monitor.BlockLogger 25 | import com.bytedance.blockframework.framework.performance.ThreadProcessor 26 | import com.bytedance.blockframework.interaction.BaseBlockMessageCenter 27 | import kotlin.properties.ReadOnlyProperty 28 | import kotlin.reflect.KProperty 29 | 30 | /** 31 | * 32 | * @author Created by zhoujunjie on 2023/8/9 33 | * @mail zhoujunjie.9743@bytedance.com 34 | **/ 35 | 36 | fun getDefaultCenter() = if (BlockInit.useTreeMessageCenterEnable()) TreeBlockMessageCenter() else BaseBlockMessageCenter() 37 | 38 | fun BaseBlock<*, *>.blockHandler(): BlockUnitHandler { 39 | return blockContext.findBlockHandler(this) 40 | } 41 | 42 | inline fun BaseBlock<*, *>.blockService(): ReadOnlyProperty, T?> { 43 | return object : ReadOnlyProperty, T?> { 44 | private var cacheV: T? = null 45 | 46 | override fun getValue(thisRef: BaseBlock<*, *>, property: KProperty<*>): T? { 47 | if (cacheV == null) { 48 | cacheV = getBlockService(T::class.java) 49 | } 50 | return cacheV 51 | } 52 | } 53 | } 54 | 55 | inline fun BaseBlock<*, *>.findDepend(): ReadOnlyProperty, T> { 56 | return object : ReadOnlyProperty, T> { 57 | private lateinit var cacheV: T 58 | 59 | override fun getValue(thisRef: BaseBlock<*, *>, property: KProperty<*>): T { 60 | if (!::cacheV.isInitialized) { 61 | cacheV = blockContext.findDepend(T::class.java) 62 | } 63 | return cacheV 64 | } 65 | } 66 | } 67 | 68 | fun BaseBlock<*, *>.traverseBlock(action: (block: BaseBlock<*, *>) -> Unit) { 69 | action(this) 70 | this.blockHandler().getChildBlocks().forEach { 71 | it.traverseBlock(action) 72 | } 73 | } 74 | 75 | fun BaseBlock<*, *>.traverseSubBlock(action: (block: BaseBlock<*, *>) -> Unit) { 76 | this.blockHandler().getChildBlocks().forEach { 77 | it.traverseBlock(action) 78 | } 79 | } 80 | 81 | fun BaseBlock<*, *>.afterBind(action: () -> Unit) { 82 | blockContext.addAfterBindTask(action) 83 | } 84 | 85 | fun syncInvoke(action: () -> Unit) { 86 | if (Looper.getMainLooper() == Looper.myLooper()) { 87 | action() 88 | } else { 89 | ThreadProcessor.main().post(action) 90 | } 91 | } 92 | 93 | fun uploadException(exception: Throwable, needThrow: Boolean) { 94 | if (!BlockLogger.debug()) { 95 | return 96 | } 97 | 98 | if (needThrow) { 99 | throw exception 100 | } 101 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/framework/utils/LifecycleUtil.kt: -------------------------------------------------------------------------------- 1 | package com.bytedance.blockframework.framework.utils 2 | 3 | import androidx.lifecycle.Lifecycle 4 | import com.bytedance.blockframework.contract.AbstractLifecycleBlock 5 | import com.bytedance.blockframework.framework.base.BaseBlock 6 | import com.bytedance.blockframework.framework.monitor.logger 7 | 8 | /** 9 | * description: 10 | * 11 | * @author Created by zhoujunjie on 2024/11/15 12 | * @mail zhoujunjie.9743@bytedance.com 13 | **/ 14 | 15 | object LifecycleUtil { 16 | 17 | fun handleLifecycleState(state: Lifecycle.State, block: BaseBlock<*, *>) { 18 | logger("LifecycleUtil", "$state --- ${block.getBlockKey()}---${block.lifecycle.currentState}") 19 | when (state) { 20 | Lifecycle.State.CREATED -> { 21 | if (block.lifecycle.currentState < Lifecycle.State.CREATED) { 22 | block.performCreate() 23 | } else { 24 | if (block.lifecycle.currentState > Lifecycle.State.STARTED) { 25 | block.performPause() 26 | } 27 | if (block.lifecycle.currentState > Lifecycle.State.CREATED) { 28 | block.performStop() 29 | } 30 | } 31 | } 32 | 33 | Lifecycle.State.STARTED -> { 34 | if (block.lifecycle.currentState < Lifecycle.State.STARTED) { 35 | if ((block as AbstractLifecycleBlock).lifecycle.currentState < Lifecycle.State.CREATED) { 36 | block.performCreate() 37 | } 38 | block.performStart() 39 | } else if ((block as AbstractLifecycleBlock).lifecycle.currentState > Lifecycle.State.STARTED) { 40 | block.performPause() 41 | } 42 | } 43 | 44 | Lifecycle.State.RESUMED -> { 45 | if (block.lifecycle.currentState < Lifecycle.State.RESUMED) { 46 | if (block.lifecycle.currentState < Lifecycle.State.CREATED) { 47 | block.performCreate() 48 | } 49 | if (block.lifecycle.currentState < Lifecycle.State.STARTED) { 50 | block.performStart() 51 | } 52 | block.performResume() 53 | } 54 | } 55 | 56 | Lifecycle.State.DESTROYED -> { 57 | if (block.lifecycle.currentState >= Lifecycle.State.RESUMED) { 58 | block.performPause() 59 | } 60 | if (block.lifecycle.currentState >= Lifecycle.State.STARTED) { 61 | block.performStop() 62 | } 63 | if (block.lifecycle.currentState >= Lifecycle.State.CREATED) { 64 | block.performDestroy() 65 | } 66 | } 67 | 68 | else -> Unit 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/interaction/BaseBlockMessageCenter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.interaction 17 | 18 | import com.bytedance.blockframework.contract.AbstractBlock 19 | import com.bytedance.blockframework.contract.BlockImplWrapper 20 | import com.bytedance.blockframework.framework.base.BaseBlock 21 | import com.bytedance.blockframework.framework.config.BlockInit 22 | import com.bytedance.blockframework.framework.monitor.BlockLogger 23 | import com.bytedance.blockframework.framework.utils.uploadException 24 | 25 | /** 26 | * @Author Wei Zijie 27 | * @Date 2020/12/28 28 | * @Description 29 | */ 30 | open class BaseBlockMessageCenter : IBlockMessageCenter { 31 | 32 | val TAG = "BaseBlockMessageCenter" 33 | 34 | // 35 | override var stateProviderMap: MutableMap, IStatusProvider> = mutableMapOf() 36 | override fun registerStateProvider(provider: IStatusProvider) { 37 | val stateClass = provider.stateClass 38 | if (!stateProviderMap.contains(stateClass)) { 39 | stateProviderMap[stateClass] = provider 40 | } else { 41 | if (!BlockInit.logOptEnable() || BlockLogger.debug()) { 42 | uploadException(RuntimeException("registerStateProvider $provider for state $stateClass already exists"), true) 43 | } 44 | } 45 | } 46 | 47 | override fun unregisterStateProvider(provider: IStatusProvider) { 48 | val stateClass = provider.stateClass 49 | if (stateProviderMap.contains(stateClass)) { 50 | stateProviderMap.remove(stateClass) 51 | } 52 | } 53 | 54 | override fun queryState(stateClass: Class): T? { 55 | return if (stateProviderMap.contains(stateClass)) { 56 | stateProviderMap[stateClass]?.state as T? 57 | } else { 58 | if (!BlockInit.logOptEnable() || BlockLogger.debug()) { 59 | uploadException(RuntimeException("queryState $stateClass not find"), true) 60 | } 61 | null 62 | } 63 | } 64 | // 65 | 66 | // 67 | open protected var eventManager: EventManager = EventManager() 68 | override fun subscribeEvent(observer: IObserver, eventClass: Class) { 69 | eventManager.registerObserver( 70 | observer = observer as IObserver, 71 | eventClass = eventClass as Class 72 | ) 73 | } 74 | 75 | override fun unregisterObserver(observer: IObserver) { 76 | eventManager.unregisterObserver(observer) 77 | } 78 | 79 | override fun notifyEvent(block: StateAndEventModel, event: T): Boolean { 80 | return eventManager.notifyEvent(block, event) 81 | } 82 | 83 | @Deprecated("ignore") 84 | override fun notifyEvent(event: T): Boolean { 85 | return eventManager.notifyEvent(event = event) 86 | } 87 | 88 | override fun notifyEventForResult(event: T): Boolean { 89 | return eventManager.notifyEventForResult(event) 90 | } 91 | 92 | override fun notifyEventForResult(block: StateAndEventModel, event: T): Boolean { 93 | return notifyEventForResult(event) 94 | } 95 | // 96 | 97 | override var blockServiceMap: MutableMap, BlockImplWrapper> = mutableMapOf() 98 | override fun registerService(klass: Class, blockImplWrapper: BlockImplWrapper) { 99 | if (blockServiceMap.containsKey(klass)) { 100 | if (!BlockInit.logOptEnable() || BlockLogger.debug()) { 101 | uploadException(RuntimeException("registerService $klass already exists"), true) 102 | } 103 | } 104 | blockServiceMap[klass] = blockImplWrapper 105 | } 106 | 107 | override fun queryService(block: AbstractBlock, klass: Class, activeIfNeed: Boolean): T? { 108 | return queryService(klass) 109 | } 110 | 111 | @Deprecated("ignore") 112 | override fun queryService(klass: Class, activeIfNeed: Boolean): T? { 113 | if (blockServiceMap.contains(klass)) { 114 | val target = blockServiceMap[klass]?.impl 115 | if(activeIfNeed) (target as? BaseBlock<*,*>)?.activeIfNeed() 116 | return target as T? 117 | } 118 | 119 | for (implWrapper in blockServiceMap.values) { 120 | if (klass.isInstance(implWrapper.impl)) { 121 | return implWrapper.impl as T? 122 | } 123 | } 124 | 125 | return null 126 | } 127 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/interaction/CoreTreeLayerBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.interaction 17 | 18 | interface CoreTreeLayerBlock { 19 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/interaction/EventManager.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.interaction 17 | 18 | import com.bytedance.blockframework.contract.AbstractBlock 19 | 20 | /** 21 | * @Author Wei Zijie 22 | * @Date 2020/12/23 23 | * @Description 24 | */ 25 | abstract class Event(val canIntercept: Boolean = false) 26 | 27 | interface IObserver { 28 | fun onEvent(event: T): Boolean 29 | } 30 | 31 | interface IEventManager { 32 | fun registerObserver(observer: IObserver, eventClass: Class) 33 | fun unregisterObserver(observer: IObserver) 34 | fun notifyEvent(event: Event): Boolean 35 | fun notifyEvent(block: StateAndEventModel, event: Event): Boolean 36 | fun notifyEventForResult(event: Event): Boolean 37 | } 38 | 39 | open class EventManager: IEventManager { 40 | private val TAG: String = "EventManager" 41 | 42 | protected val eventToObserverMap: MutableMap, MutableList>> = mutableMapOf() 43 | protected val observerToEventMap: MutableMap, MutableList>> = mutableMapOf() 44 | 45 | /** 46 | * Make observer observe event of Class type 47 | */ 48 | override fun registerObserver(observer: IObserver, eventClass: Class) { 49 | // update event to observer map 50 | val observers = eventToObserverMap[eventClass] ?: run { 51 | mutableListOf>().apply { 52 | eventToObserverMap[eventClass] = this 53 | } 54 | } 55 | if (!observers.contains(observer)) { 56 | observers.add(observer) 57 | } 58 | // update observer to event map 59 | val events = observerToEventMap[observer] ?: run { 60 | mutableListOf>().apply { 61 | observerToEventMap[observer] = this 62 | } 63 | } 64 | if (!events.contains(eventClass)) { 65 | events.add(eventClass) 66 | } 67 | } 68 | 69 | /** 70 | * Cancel observer observe any event 71 | */ 72 | override fun unregisterObserver(observer: IObserver) { 73 | if (!observerToEventMap.containsKey(observer)) { 74 | return 75 | } 76 | observerToEventMap[observer]?.forEach { 77 | eventToObserverMap[it]?.remove(observer) 78 | } 79 | observerToEventMap.remove(observer) 80 | } 81 | 82 | /** 83 | * Notify event 84 | */ 85 | override fun notifyEvent(event: Event): Boolean { 86 | val observers = eventToObserverMap[event.javaClass] ?: return false 87 | for (observer in observers) { 88 | if (observer is AbstractBlock) { 89 | if (!observer.isActive()) { 90 | continue 91 | } 92 | } 93 | val intercept = observer.onEvent(event) 94 | if (intercept && event.canIntercept) { 95 | break 96 | } 97 | } 98 | return true 99 | } 100 | 101 | override fun notifyEvent(block: StateAndEventModel, event: Event): Boolean { 102 | return notifyEvent(event) 103 | } 104 | 105 | override fun notifyEventForResult(event: Event): Boolean { 106 | val observers = eventToObserverMap[event.javaClass] ?: return false 107 | for (observer in observers) { 108 | if (observer is AbstractBlock) { 109 | if (!observer.isActive()) { 110 | continue 111 | } 112 | } 113 | val intercept = observer.onEvent(event) 114 | if (intercept && event.canIntercept) { 115 | return true 116 | } 117 | } 118 | return false 119 | } 120 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/interaction/IBlockMessageCenter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.interaction 17 | 18 | import com.bytedance.blockframework.contract.AbstractBlock 19 | import com.bytedance.blockframework.contract.BlockImplWrapper 20 | 21 | 22 | /** 23 | * @Author Wei Zijie 24 | * @Date 2020/12/20 25 | * @Description 26 | */ 27 | interface IBlockMessageCenter { 28 | 29 | val stateProviderMap: MutableMap, IStatusProvider> 30 | fun registerStateProvider(provider: IStatusProvider) 31 | fun unregisterStateProvider(provider: IStatusProvider) 32 | fun queryState(stateClass: Class): T? 33 | fun subscribeEvent(observer: IObserver, eventClass: Class) 34 | fun unregisterObserver(observer: IObserver) 35 | 36 | @Deprecated("use Block.notifyEvent(event: T) or notifyEvent(block: StateAndEventModel, event: T)") 37 | fun notifyEvent(event: T): Boolean 38 | fun notifyEvent(block: StateAndEventModel, event: T): Boolean 39 | fun notifyEventForResult(event: T): Boolean 40 | fun notifyEventForResult(block: StateAndEventModel, event: T): Boolean 41 | 42 | var blockServiceMap: MutableMap, BlockImplWrapper> 43 | fun registerService(klass: Class, blockImplWrapper: BlockImplWrapper) 44 | @Deprecated("use block.getBlockService(klass: Class) or queryService(block: AbstractBlock, klass: Class)") 45 | fun queryService(klass: Class, activeIfNeed: Boolean = true): T? 46 | fun queryService(block: AbstractBlock, klass: Class, activeIfNeed: Boolean = true): T? 47 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/interaction/State.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.interaction 17 | 18 | /** 19 | * @Author Wei Zijie 20 | * @Date 2020/12/23 21 | * @Description 22 | */ 23 | abstract class State 24 | 25 | interface IStatusProvider { 26 | val stateClass: Class 27 | val state: T 28 | } 29 | 30 | abstract class StatusProvider(private val _stateClass: Class) : IStatusProvider { 31 | final override val stateClass: Class 32 | get() = _stateClass 33 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/interaction/StateAndEventModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.interaction 17 | 18 | /** 19 | * @Author Wei Zijie 20 | * @Date 2020/12/28 21 | * @Description 22 | */ 23 | abstract class StateAndEventModel { 24 | 25 | open lateinit var blockBlockMessageCenter: IBlockMessageCenter 26 | 27 | abstract fun onPrepared() 28 | 29 | abstract fun onUnRegister() 30 | 31 | // 32 | val providers: MutableList> = mutableListOf() 33 | fun shareState(provider: IStatusProvider) { 34 | if (providers.contains(provider)) { 35 | throw RuntimeException("provider $provider already exists") 36 | } 37 | providers.add(provider as IStatusProvider) 38 | blockBlockMessageCenter.registerStateProvider(provider) 39 | } 40 | fun queryStatus(stateClass: Class): T? { 41 | return blockBlockMessageCenter.queryState(stateClass = stateClass) 42 | } 43 | // 44 | 45 | // 46 | val observers: MutableList> = mutableListOf() 47 | open fun subscribe(observer: IObserver, eventClass: Class) { 48 | if (!observers.contains(observer)) { 49 | observers.add(observer as IObserver) 50 | } 51 | blockBlockMessageCenter.subscribeEvent(observer, eventClass) 52 | } 53 | 54 | fun notifyEvent(event: Event) { 55 | blockBlockMessageCenter.notifyEvent(this, event = event) 56 | } 57 | // 58 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/interaction/TreeConstrainBlockMessageCenter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.interaction 17 | 18 | import com.bytedance.blockframework.contract.AbstractBlock 19 | import com.bytedance.blockframework.contract.BlockImplWrapper 20 | import com.bytedance.blockframework.framework.base.BaseBlock 21 | import com.bytedance.blockframework.framework.config.BlockInit 22 | import com.bytedance.blockframework.framework.monitor.BlockLogger 23 | import com.bytedance.blockframework.framework.utils.blockHandler 24 | import com.bytedance.blockframework.framework.utils.uploadException 25 | 26 | /** 27 | * Use [CoreTreeLayerBlock] as a hierarchical node 28 | * to restrict the [MessageCenter] with capability-dependent flow direction 29 | */ 30 | abstract class TreeConstrainBlockMessageCenter: BaseBlockMessageCenter() { 31 | 32 | 33 | override fun registerService(klass: Class, blockImplWrapper: BlockImplWrapper) { 34 | var block = blockImplWrapper.impl as? BaseBlock<*, *> 35 | if (block?.parent == null) { 36 | super.registerService(klass, blockImplWrapper) 37 | return 38 | } else { 39 | block.parent?.registerService(klass, blockImplWrapper) 40 | } 41 | } 42 | 43 | override fun queryService(block: AbstractBlock, klass: Class, activeIfNeed: Boolean): T? { 44 | if (block !is BaseBlock<*, *>) return super.queryService(klass, activeIfNeed) 45 | var res: T? = null 46 | if (block.parent == null) { 47 | res = queryService(klass, activeIfNeed) 48 | if(res != null) return res 49 | } 50 | 51 | res = block.parent?.queryService(klass, activeIfNeed) 52 | if(res != null) return res 53 | 54 | 55 | var nextTargetCoreTreeBlock: CoreTreeLayerBlock? = null 56 | (block.parent ?: block.blockHandler()).getChildBlocks().forEach { 57 | if(it is CoreTreeLayerBlock) { 58 | nextTargetCoreTreeBlock = it 59 | return@forEach 60 | } 61 | } 62 | res = (nextTargetCoreTreeBlock as? BaseBlock<*, *>)?.blockHandler()?.queryService(klass, activeIfNeed) 63 | if(res != null) return res 64 | 65 | val result = queryTargetCoreTreeService((nextTargetCoreTreeBlock as? BaseBlock<*, *>), klass, activeIfNeed) 66 | if (result == null) { 67 | if (!BlockInit.logOptEnable() || BlockLogger.debug()) { 68 | uploadException(RuntimeException("TreeConstrainBlockMessageCenter queryService $klass not find"), false) 69 | } 70 | if (BlockLogger.debug() && BlockInit.enableDebugDependencyCheck()) { 71 | var rootBlock = block.parent?.attachBlock 72 | while (rootBlock?.parent != null) { 73 | rootBlock = rootBlock.parent?.attachBlock 74 | } 75 | val resultFromAllBlock = rootBlock?.let { queryService(it, klass) } 76 | if (resultFromAllBlock != null) { 77 | handleUnexpectedServiceDepend(block, klass) 78 | } 79 | } 80 | } 81 | if (activeIfNeed) (result as? BaseBlock<*, *>)?.activeIfNeed() 82 | return result 83 | } 84 | 85 | open fun handleUnexpectedServiceDepend(block: BaseBlock<*, *>?, klass: Class<*>) {} 86 | 87 | private fun queryTargetCoreTreeService(block: BaseBlock<*, *>?, klass: Class, activeIfNeed: Boolean): T? { 88 | block ?: return null 89 | var res: T? = null 90 | var nextTargetCoreTreeBlock: CoreTreeLayerBlock? = null 91 | block.blockHandler()?.getChildBlocks()?.forEach { 92 | if(it is CoreTreeLayerBlock) { 93 | nextTargetCoreTreeBlock = it 94 | return@forEach 95 | } 96 | } 97 | nextTargetCoreTreeBlock ?: return null 98 | res = (nextTargetCoreTreeBlock as? BaseBlock<*, *>)?.blockHandler()?.queryService(klass, activeIfNeed) 99 | if(res != null) return res 100 | 101 | return queryTargetCoreTreeService(nextTargetCoreTreeBlock as? BaseBlock<*, *>, klass, activeIfNeed) 102 | } 103 | 104 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/interaction/TreeConstrainEventManager.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.interaction 17 | 18 | import com.bytedance.blockframework.framework.base.BaseBlock 19 | import com.bytedance.blockframework.framework.config.BlockInit 20 | import com.bytedance.blockframework.framework.monitor.BlockLogger 21 | import com.bytedance.blockframework.framework.utils.blockHandler 22 | 23 | /** 24 | * EventManager that restricts event flow 25 | */ 26 | open class TreeConstrainEventManager: EventManager() { 27 | 28 | override fun registerObserver(observer: IObserver, eventClass: Class) { 29 | if (observer !is BaseBlock<*, *>) { 30 | super.registerObserver(observer, eventClass) 31 | return 32 | } 33 | if (observer.parent == null) { 34 | observer.blockHandler().registerObserver(observer, eventClass) 35 | return 36 | } else { 37 | observer.parent?.registerObserver(observer, eventClass) 38 | } 39 | 40 | } 41 | 42 | override fun notifyEvent(block: StateAndEventModel, event: Event): Boolean { 43 | if (block !is BaseBlock<*, *>) return super.notifyEvent(block, event) 44 | var pointerBlock = block as? BaseBlock<*, *> 45 | while (pointerBlock?.parent != null) { 46 | pointerBlock.parent?.notifyEvent(event) 47 | pointerBlock = pointerBlock.parent?.attachBlock 48 | } 49 | if (BlockLogger.debug() && BlockInit.enableDebugDependencyCheck()) { 50 | //测试环境抛出不合理的Event依赖 51 | if (findLowerObserver(block, event)){ 52 | handleUnexpectedEventDepend(block, event) 53 | } 54 | } 55 | return true 56 | } 57 | 58 | open fun handleUnexpectedEventDepend(block: StateAndEventModel?, event: Event) {} 59 | 60 | private fun findLowerObserver(block: BaseBlock<*, *>?, event: Event): Boolean { 61 | block ?: return false 62 | var nextTargetCoreTreeBlock: CoreTreeLayerBlock? = null 63 | val supervisor = if (block is CoreTreeLayerBlock) block.blockHandler() else block.parent 64 | supervisor?.getChildBlocks()?.forEach { 65 | if(it is CoreTreeLayerBlock) { 66 | nextTargetCoreTreeBlock = it 67 | return@forEach 68 | } 69 | } 70 | nextTargetCoreTreeBlock ?: return false 71 | val observers = (nextTargetCoreTreeBlock as? BaseBlock<*, *>)?.blockHandler()?.eventToObserverMap?.get(event.javaClass) 72 | if (observers?.isNotEmpty() == true) return true 73 | 74 | return findLowerObserver(nextTargetCoreTreeBlock as? BaseBlock<*, *>, event) 75 | } 76 | } -------------------------------------------------------------------------------- /blockframework/src/main/java/com/bytedance/blockframework/utils/Logger.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.blockframework.utils 17 | 18 | import android.util.Log 19 | import com.bytedance.blockframework.BuildConfig 20 | 21 | /** 22 | * 23 | * @author Created by zhoujunjie on 2024/8/26 24 | * @mail zhoujunjie.9743@bytedance.com 25 | **/ 26 | 27 | object Logger { 28 | private const val TAG = "Logger" 29 | 30 | var logLevel = Log.DEBUG 31 | 32 | init { 33 | /** 34 | * Output less log when we are not in debug mode 35 | */ 36 | if (!BuildConfig.DEBUG) { 37 | logLevel = Log.ERROR 38 | } 39 | } 40 | 41 | fun v(msg: String) { 42 | v(TAG, msg) 43 | } 44 | 45 | fun v(tag: String, msg: String) { 46 | if (isVerboseLoggable()) { 47 | Log.v(tag, msg) 48 | } 49 | } 50 | 51 | fun isVerboseLoggable(): Boolean { 52 | return logLevel <= Log.VERBOSE 53 | } 54 | 55 | fun d(msg: String) { 56 | d(TAG, msg) 57 | } 58 | 59 | fun d(tag: String, msg: String) { 60 | if (isDebugLoggable()) { 61 | Log.d(tag, msg) 62 | } 63 | } 64 | 65 | fun isDebugLoggable(): Boolean { 66 | return logLevel <= Log.DEBUG 67 | } 68 | 69 | fun i(msg: String) { 70 | i(TAG, msg) 71 | } 72 | 73 | fun i(tag: String, msg: String) { 74 | if (isInfoLoggable()) { 75 | Log.i(tag, msg) 76 | } 77 | } 78 | 79 | fun isInfoLoggable(): Boolean { 80 | return logLevel <= Log.INFO 81 | } 82 | 83 | fun w(msg: String) { 84 | w(TAG, msg) 85 | } 86 | 87 | fun w(tag: String, msg: String) { 88 | if (isWarnLoggable()) { 89 | Log.w(tag, msg) 90 | } 91 | } 92 | 93 | fun isWarnLoggable(): Boolean { 94 | return logLevel <= Log.WARN 95 | } 96 | 97 | fun e(msg: String) { 98 | e(TAG, msg) 99 | } 100 | 101 | fun e(tag: String, msg: String) { 102 | if (isErrorLoggable()) { 103 | Log.e(tag, msg) 104 | } 105 | } 106 | 107 | fun isErrorLoggable(): Boolean { 108 | return logLevel <= Log.ERROR 109 | } 110 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | mavenCentral() 9 | } 10 | dependencies { 11 | classpath ANDROID_GRADLE_PLUGIN 12 | classpath KOTLIN_PLUGIN 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | google() 22 | jcenter() 23 | mavenCentral() 24 | } 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } -------------------------------------------------------------------------------- /demo/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | compileSdkVersion 31 8 | buildToolsVersion "30.0.1" 9 | 10 | defaultConfig { 11 | applicationId "com.bytedance.demo" 12 | minSdkVersion 24 13 | targetSdkVersion 30 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | kotlinOptions { 30 | jvmTarget = '1.8' 31 | } 32 | } 33 | 34 | dependencies { 35 | 36 | implementation KOTLIN_LIB 37 | implementation ANDROIDX_LIB 38 | implementation RECYCLER_VIEW 39 | implementation CONSTRAINT_LIB 40 | implementation KOTLIN_ANDROID_EXTENSIONS 41 | implementation project(':blockframework') 42 | } -------------------------------------------------------------------------------- /demo/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytedance/BlockFramework/d37e26ec8d28e95084ff5b7fb625d1a465fc43dd/demo/consumer-rules.pro -------------------------------------------------------------------------------- /demo/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/BlockScene.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo 17 | 18 | import com.bytedance.blockframework.framework.join.IBlockScene 19 | 20 | /** 21 | * description: 22 | * 23 | * @author Created by zhoujunjie on 2024/8/28 24 | * @mail zhoujunjie.9743@bytedance.com 25 | **/ 26 | 27 | enum class BlockScene : IBlockScene { 28 | DEMO_HOLDER_SCENE; 29 | 30 | override fun getName(): String { 31 | return name 32 | } 33 | } -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/DemoActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo 17 | 18 | import android.os.Bundle 19 | import androidx.appcompat.app.AppCompatActivity 20 | import androidx.recyclerview.widget.LinearLayoutManager 21 | import androidx.recyclerview.widget.PagerSnapHelper 22 | import androidx.recyclerview.widget.RecyclerView 23 | import com.bytedance.demo.data.DemoCardData 24 | import com.bytedance.demo.util.DemoTestUtil 25 | 26 | 27 | /** 28 | * description: 29 | * 30 | * @author Created by zhoujunjie on 2024/8/26 31 | * @mail zhoujunjie.9743@bytedance.com 32 | **/ 33 | 34 | class DemoActivity : AppCompatActivity() { 35 | 36 | private lateinit var recyclerView: RecyclerView 37 | private var adapter: DemoAdapter? = null 38 | 39 | override fun onCreate(savedInstanceState: Bundle?) { 40 | super.onCreate(savedInstanceState) 41 | setContentView(R.layout.demo_activity_layout) 42 | initView() 43 | } 44 | 45 | private fun initView() { 46 | recyclerView = findViewById(R.id.recycler_view) 47 | recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) 48 | val snapHelper = PagerSnapHelper() 49 | snapHelper.attachToRecyclerView(recyclerView) 50 | adapter = DemoAdapter(recyclerView) 51 | recyclerView.adapter = adapter 52 | } 53 | 54 | override fun onStart() { 55 | super.onStart() 56 | adapter?.setData(DemoTestUtil.getTestData()) 57 | } 58 | } -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/DemoAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo 17 | 18 | import android.annotation.SuppressLint 19 | import android.view.LayoutInflater 20 | import android.view.ViewGroup 21 | import androidx.recyclerview.widget.RecyclerView 22 | import com.bytedance.demo.data.DemoCardData 23 | 24 | /** 25 | * description: 26 | * 27 | * @author Created by zhoujunjie on 2024/8/27 28 | * @mail zhoujunjie.9743@bytedance.com 29 | **/ 30 | 31 | class DemoAdapter(val recyclerView: RecyclerView) : RecyclerView.Adapter() { 32 | 33 | private var data = arrayListOf() 34 | 35 | @SuppressLint("NotifyDataSetChanged") 36 | fun setData(data: ArrayList) { 37 | this.data = data 38 | notifyDataSetChanged() 39 | } 40 | 41 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DemoHolder { 42 | val itemView = LayoutInflater.from(parent.context).inflate(R.layout.demo_card_holder_layout, parent, false) 43 | return DemoHolder(itemView).apply { 44 | initCardBlock(recyclerView) 45 | } 46 | } 47 | 48 | override fun getItemCount(): Int { 49 | return data.size 50 | } 51 | 52 | override fun onBindViewHolder(holder: DemoHolder, position: Int) { 53 | holder.bindData(data[position]) 54 | } 55 | 56 | override fun onViewDetachedFromWindow(holder: DemoHolder) { 57 | holder.onDetachedFromWindow() 58 | } 59 | } -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/DemoHolder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo 17 | 18 | import android.view.View 19 | import androidx.lifecycle.Lifecycle 20 | import androidx.lifecycle.LifecycleEventObserver 21 | import androidx.lifecycle.LifecycleOwner 22 | import androidx.recyclerview.widget.RecyclerView 23 | import com.bytedance.blockframework.framework.join.IBlockContext 24 | import com.bytedance.blockframework.framework.join.IBlockJoin 25 | import com.bytedance.blockframework.framework.join.IBlockScene 26 | import com.bytedance.blockframework.framework.join.bindBlockModel 27 | import com.bytedance.blockframework.framework.join.dispatchOnCreate 28 | import com.bytedance.blockframework.framework.join.dispatchOnDestroy 29 | import com.bytedance.blockframework.framework.join.dispatchOnPause 30 | import com.bytedance.blockframework.framework.join.dispatchOnResume 31 | import com.bytedance.blockframework.framework.join.dispatchOnStart 32 | import com.bytedance.blockframework.framework.join.dispatchOnStop 33 | import com.bytedance.blockframework.framework.join.getBlockContext 34 | import com.bytedance.blockframework.framework.join.initRootBlock 35 | import com.bytedance.blockframework.framework.join.registerDepend 36 | import com.bytedance.demo.block.DemoCardRootBlock 37 | import com.bytedance.demo.data.DemoCardData 38 | import com.bytedance.demo.data.DemoModel 39 | import com.bytedance.demo.depend.IHolderBlockDepend 40 | 41 | /** 42 | * description: 43 | * 44 | * @author Created by zhoujunjie on 2024/8/28 45 | * @mail zhoujunjie.9743@bytedance.com 46 | **/ 47 | 48 | class DemoHolder(itemView: View) : RecyclerView.ViewHolder(itemView), IBlockJoin { 49 | 50 | private var ownerRecyclerView: RecyclerView? = null 51 | private lateinit var rootBlock: DemoCardRootBlock 52 | 53 | override val blockContext: IBlockContext by lazy { 54 | getBlockContext(blockScene) 55 | } 56 | 57 | override val blockScene: IBlockScene by lazy { 58 | BlockScene.DEMO_HOLDER_SCENE 59 | } 60 | 61 | private val holderDepend: IHolderBlockDepend = object : IHolderBlockDepend { 62 | override fun enableAsyncBind(): Boolean { 63 | return true 64 | } 65 | 66 | override fun getHolderView(): View { 67 | return itemView 68 | } 69 | } 70 | 71 | fun initCardBlock(recyclerView: RecyclerView) { 72 | ownerRecyclerView = recyclerView 73 | rootBlock = DemoCardRootBlock(itemView, blockContext).apply { 74 | registerDepend(holderDepend) 75 | } 76 | initRootBlock(itemView.context, rootBlock) 77 | dispatchOnCreate() 78 | dispatchOnStart() 79 | dispatchOnResume() 80 | observeLifeCycle() 81 | } 82 | 83 | private fun observeLifeCycle() { 84 | (itemView.context as? LifecycleOwner)?.lifecycle?.addObserver( 85 | object : LifecycleEventObserver { 86 | override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { 87 | when (event) { 88 | Lifecycle.Event.ON_CREATE -> dispatchOnCreate() 89 | Lifecycle.Event.ON_START -> dispatchOnStart() 90 | Lifecycle.Event.ON_RESUME -> dispatchOnResume() 91 | Lifecycle.Event.ON_PAUSE -> dispatchOnPause() 92 | Lifecycle.Event.ON_STOP -> dispatchOnStop() 93 | Lifecycle.Event.ON_DESTROY -> dispatchOnDestroy() 94 | else -> {} 95 | } 96 | } 97 | } 98 | ) 99 | } 100 | 101 | fun onDetachedFromWindow() { 102 | dispatchOnStop() 103 | dispatchOnDestroy() 104 | } 105 | 106 | fun bindData(data: DemoCardData) { 107 | bindBlockModel(DemoModel(data)) 108 | } 109 | 110 | } -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/block/BottomInfoBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo.block 17 | 18 | import android.annotation.SuppressLint 19 | import android.graphics.Typeface 20 | import android.view.View 21 | import android.widget.ImageView 22 | import android.widget.TextView 23 | import com.bytedance.blockframework.framework.async.AsyncUIBlock 24 | import com.bytedance.blockframework.framework.async.SyncInvoke 25 | import com.bytedance.blockframework.framework.join.IBlockContext 26 | import com.bytedance.blockframework.framework.utils.blockService 27 | import com.bytedance.blockframework.framework.utils.findDepend 28 | import com.bytedance.blockframework.interaction.Event 29 | import com.bytedance.demo.R 30 | import com.bytedance.demo.data.DemoCardData 31 | import com.bytedance.demo.data.DemoModel 32 | import com.bytedance.demo.depend.IHolderBlockDepend 33 | import com.bytedance.demo.event.ChangFontThemeEvent 34 | import com.bytedance.demo.service.IMainContentBlockService 35 | import com.bytedance.demo.util.FontType 36 | 37 | /** 38 | * description: 39 | * 40 | * @author Created by zhoujunjie on 2024/8/29 41 | * @mail zhoujunjie.9743@bytedance.com 42 | **/ 43 | 44 | class BottomInfoBlock(blockContext: IBlockContext) : 45 | AsyncUIBlock(blockContext) { 46 | 47 | private val holderDepend by findDepend() 48 | 49 | private val mainContentBlock : IMainContentBlockService? by blockService() 50 | 51 | private lateinit var avatarView: ImageView 52 | private lateinit var userView: TextView 53 | private lateinit var titleView: TextView 54 | 55 | override fun layoutResource(): Int { 56 | return R.layout.demo_bottom_info_block_layout 57 | } 58 | 59 | override fun onViewCreated(view: View) { 60 | super.onViewCreated(view) 61 | initView(view) 62 | } 63 | 64 | private fun initView(view: View) { 65 | avatarView = view.findViewById(R.id.avatar) 66 | userView = view.findViewById(R.id.user_name) 67 | titleView = view.findViewById(R.id.title) 68 | avatarView.setOnClickListener { 69 | mainContentBlock?.changeMainContent("On Avatar Click!") 70 | } 71 | userView.setOnClickListener { 72 | mainContentBlock?.changeMainContent("On UserName Click!") 73 | } 74 | titleView.setOnClickListener { 75 | mainContentBlock?.changeMainContent("On Title Click!") 76 | } 77 | } 78 | 79 | override fun enableAsyncBind(): Boolean { 80 | return holderDepend.enableAsyncBind() 81 | } 82 | 83 | @SuppressLint("UseCompatLoadingForDrawables") 84 | override fun asyncBind(model: DemoModel?, syncInvoke: SyncInvoke) { 85 | syncInvoke { 86 | avatarView.setImageDrawable( 87 | context.resources.getDrawable( 88 | model?.data?.avatarImg ?: R.color.content_1 89 | ) 90 | ) 91 | userView.text = model?.data?.userName 92 | titleView.text = model?.data?.title 93 | } 94 | } 95 | 96 | override fun onRegister() { 97 | subscribe(this, ChangFontThemeEvent::class.java) 98 | } 99 | 100 | override fun onEvent(event: Event): Boolean { 101 | when (event) { 102 | is ChangFontThemeEvent -> { 103 | when (event.type) { 104 | FontType.Normal -> { 105 | userView.typeface = Typeface.defaultFromStyle(Typeface.NORMAL) 106 | titleView.typeface = Typeface.defaultFromStyle(Typeface.NORMAL) 107 | } 108 | FontType.Bold -> { 109 | userView.typeface = Typeface.defaultFromStyle(Typeface.BOLD) 110 | titleView.typeface = Typeface.defaultFromStyle(Typeface.BOLD) 111 | } 112 | } 113 | } 114 | } 115 | return super.onEvent(event) 116 | } 117 | } -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/block/DemoCardRootBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo.block 17 | 18 | import android.view.View 19 | import com.bytedance.blockframework.framework.base.IUIBlock 20 | import com.bytedance.blockframework.framework.base.UIBlock 21 | import com.bytedance.blockframework.framework.core.BlockGenerator 22 | import com.bytedance.blockframework.framework.join.IBlockContext 23 | import com.bytedance.demo.R 24 | import com.bytedance.demo.data.DemoCardData 25 | import com.bytedance.demo.data.DemoModel 26 | 27 | /** 28 | * description: 29 | * 30 | * @author Created by zhoujunjie on 2024/8/29 31 | * @mail zhoujunjie.9743@bytedance.com 32 | **/ 33 | 34 | class DemoCardRootBlock(private val rootView: View, blockContext: IBlockContext) : 35 | UIBlock(blockContext) { 36 | 37 | override fun layoutResource(): Int { 38 | return IUIBlock.USE_PARENT_LAYOUT 39 | } 40 | 41 | override fun onCreateView(parent: View?): View { 42 | return rootView 43 | } 44 | 45 | override fun generateSubBlocks(generator: BlockGenerator) { 46 | generator.generate { 47 | addBlock { 48 | instance = { 49 | MainContentBlock(blockContext) 50 | } 51 | parentId = R.id.main_content_block_container 52 | // Indicates that the UI must be created on the main thread 53 | createUIOnMainThread = true 54 | } 55 | addBlock { 56 | instance = { 57 | BottomInfoBlock(blockContext) 58 | } 59 | parentId = R.id.bottom_info_block_container 60 | // reduce layout levels 61 | replaceParent = true 62 | } 63 | addBlock { 64 | instance = { 65 | RightInteractBlock(blockContext) 66 | } 67 | parentId = R.id.right_interact_block_container 68 | replaceParent = true 69 | } 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/block/MainContentBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo.block 17 | 18 | import android.annotation.SuppressLint 19 | import android.view.View 20 | import android.widget.Button 21 | import android.widget.TextView 22 | import android.widget.Toast 23 | import com.bytedance.blockframework.framework.async.AsyncUIBlock 24 | import com.bytedance.blockframework.framework.join.IBlockContext 25 | import com.bytedance.blockframework.framework.utils.findDepend 26 | import com.bytedance.demo.R 27 | import com.bytedance.demo.data.DemoCardData 28 | import com.bytedance.demo.data.DemoModel 29 | import com.bytedance.demo.depend.IHolderBlockDepend 30 | import com.bytedance.demo.event.ChangFontThemeEvent 31 | import com.bytedance.demo.service.IMainContentBlockService 32 | import com.bytedance.demo.util.FontType 33 | 34 | /** 35 | * description: 36 | * 37 | * @author Created by zhoujunjie on 2024/8/29 38 | * @mail zhoujunjie.9743@bytedance.com 39 | **/ 40 | 41 | class MainContentBlock(blockContext: IBlockContext) : 42 | AsyncUIBlock(blockContext), 43 | IMainContentBlockService 44 | { 45 | 46 | private val holderDepend by findDepend() 47 | 48 | private var mainContentRoot: View? = null 49 | private var mainContentText: TextView? = null 50 | private var mainButton: Button? = null 51 | private var currentFontType: FontType = FontType.Normal 52 | 53 | override fun layoutResource(): Int { 54 | return R.layout.demo_main_content_block_layout 55 | } 56 | 57 | override fun onViewCreated(view: View) { 58 | mainContentRoot = findViewById(R.id.main_content_root) 59 | mainContentText = findViewById(R.id.content) 60 | mainButton = findViewById(R.id.button) 61 | mainButton?.setOnClickListener { 62 | when (currentFontType) { 63 | FontType.Normal -> { 64 | notifyEvent(ChangFontThemeEvent(FontType.Bold)) 65 | currentFontType = FontType.Bold 66 | Toast.makeText(context, "Switch Bold Font Theme!", Toast.LENGTH_SHORT).show() 67 | } 68 | FontType.Bold -> { 69 | notifyEvent(ChangFontThemeEvent(FontType.Normal)) 70 | currentFontType = FontType.Normal 71 | Toast.makeText(context, "Switch Normal Font Theme!", Toast.LENGTH_SHORT).show() 72 | } 73 | } 74 | } 75 | } 76 | 77 | override fun enableAsyncBind(): Boolean { 78 | return holderDepend.enableAsyncBind() 79 | } 80 | 81 | override fun changeMainContent(content: String) { 82 | mainContentText?.text = content 83 | } 84 | 85 | @SuppressLint("SetTextI18n") 86 | override fun syncBind(model: DemoModel?) { 87 | mainContentRoot?.setBackgroundColor(context.resources.getColor(model?.data?.mainContentBg ?: 0)) 88 | mainContentText?.text = "This is Main Content!" 89 | } 90 | 91 | override fun defineBlockService(): Class<*>? { 92 | return IMainContentBlockService::class.java 93 | } 94 | } -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/block/RightInteractBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo.block 17 | 18 | import android.graphics.Typeface 19 | import android.view.View 20 | import android.widget.TextView 21 | import com.bytedance.blockframework.framework.async.AsyncUIBlock 22 | import com.bytedance.blockframework.framework.async.SyncInvoke 23 | import com.bytedance.blockframework.framework.join.IBlockContext 24 | import com.bytedance.blockframework.framework.utils.blockService 25 | import com.bytedance.blockframework.framework.utils.findDepend 26 | import com.bytedance.blockframework.interaction.Event 27 | import com.bytedance.demo.R 28 | import com.bytedance.demo.data.DemoCardData 29 | import com.bytedance.demo.data.DemoModel 30 | import com.bytedance.demo.depend.IHolderBlockDepend 31 | import com.bytedance.demo.event.ChangFontThemeEvent 32 | import com.bytedance.demo.service.IMainContentBlockService 33 | import com.bytedance.demo.util.FontType 34 | 35 | /** 36 | * description: 37 | * 38 | * @author Created by zhoujunjie on 2024/8/29 39 | * @mail zhoujunjie.9743@bytedance.com 40 | **/ 41 | 42 | class RightInteractBlock(blockContext: IBlockContext) : 43 | AsyncUIBlock(blockContext) { 44 | 45 | private val holderDepend by findDepend() 46 | 47 | private val mainContentBlock: IMainContentBlockService? by blockService() 48 | 49 | private lateinit var diggContainer: View 50 | private lateinit var commentContainer: View 51 | private lateinit var collectContainer: View 52 | private lateinit var moreContainer: View 53 | private lateinit var diggCount: TextView 54 | private lateinit var commentCount: TextView 55 | private lateinit var collectCount: TextView 56 | 57 | override fun layoutResource(): Int { 58 | return R.layout.demo_right_interact_block_layout 59 | } 60 | 61 | override fun onViewCreated(view: View) { 62 | diggContainer = view.findViewById(R.id.digg_container) 63 | commentContainer = view.findViewById(R.id.comment_container) 64 | collectContainer = view.findViewById(R.id.collect_container) 65 | moreContainer = view.findViewById(R.id.more_container) 66 | diggCount = view.findViewById(R.id.digg_text) 67 | commentCount = view.findViewById(R.id.comment_text) 68 | collectCount = view.findViewById(R.id.collect_text) 69 | diggContainer.setOnClickListener { 70 | mainContentBlock?.changeMainContent("On Click Praise!") 71 | } 72 | commentContainer.setOnClickListener { 73 | mainContentBlock?.changeMainContent("On Click Comment!") 74 | } 75 | collectContainer.setOnClickListener { 76 | mainContentBlock?.changeMainContent("On Click Collect!") 77 | } 78 | moreContainer.setOnClickListener { 79 | mainContentBlock?.changeMainContent("On Click More!") 80 | } 81 | } 82 | 83 | override fun enableAsyncBind(): Boolean { 84 | return holderDepend.enableAsyncBind() 85 | } 86 | 87 | override fun asyncBind(model: DemoModel?, syncInvoke: SyncInvoke) { 88 | syncInvoke { 89 | diggCount.text = model?.data?.praiseCount 90 | commentCount.text = model?.data?.commentCount 91 | collectCount.text = model?.data?.collectCount 92 | } 93 | } 94 | 95 | override fun onRegister() { 96 | subscribe(this, ChangFontThemeEvent::class.java) 97 | } 98 | 99 | override fun onEvent(event: Event): Boolean { 100 | when (event) { 101 | is ChangFontThemeEvent -> { 102 | when (event.type) { 103 | FontType.Normal -> { 104 | diggCount.typeface = Typeface.defaultFromStyle(Typeface.NORMAL) 105 | commentCount.typeface = Typeface.defaultFromStyle(Typeface.NORMAL) 106 | collectCount.typeface = Typeface.defaultFromStyle(Typeface.NORMAL) 107 | } 108 | 109 | FontType.Bold -> { 110 | diggCount.typeface = Typeface.defaultFromStyle(Typeface.BOLD) 111 | commentCount.typeface = Typeface.defaultFromStyle(Typeface.BOLD) 112 | collectCount.typeface = Typeface.defaultFromStyle(Typeface.BOLD) 113 | } 114 | } 115 | } 116 | } 117 | return super.onEvent(event) 118 | } 119 | } -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/data/DemoCardData.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo.data 17 | 18 | import androidx.annotation.DrawableRes 19 | 20 | /** 21 | * description: 22 | * 23 | * @author Created by zhoujunjie on 2024/8/28 24 | * @mail zhoujunjie.9743@bytedance.com 25 | **/ 26 | 27 | 28 | data class DemoCardData( 29 | @DrawableRes val avatarImg: Int = -1, 30 | val userName: String = "", 31 | val title: String = "", 32 | val praiseCount: String = "", 33 | val commentCount: String = "", 34 | val collectCount: String = "", 35 | val mainContentBg: Int = 0, 36 | ) -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/data/DemoModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo.data 17 | 18 | import com.bytedance.blockframework.framework.core.IBlockModel 19 | 20 | /** 21 | * description: 22 | * 23 | * @author Created by zhoujunjie on 2024/8/28 24 | * @mail zhoujunjie.9743@bytedance.com 25 | **/ 26 | 27 | data class DemoModel( 28 | override val data: DemoCardData 29 | ) : IBlockModel -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/depend/IHolderBlockDepend.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo.depend 17 | 18 | import android.view.View 19 | import com.bytedance.blockframework.framework.join.IBlockDepend 20 | 21 | /** 22 | * description: 23 | * 24 | * @author Created by zhoujunjie on 2024/8/29 25 | * @mail zhoujunjie.9743@bytedance.com 26 | **/ 27 | 28 | interface IHolderBlockDepend : IBlockDepend { 29 | fun enableAsyncBind(): Boolean 30 | fun getHolderView(): View 31 | } -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/event/ChangFontThemeEvent.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo.event 17 | 18 | import com.bytedance.blockframework.interaction.Event 19 | import com.bytedance.demo.util.FontType 20 | 21 | /** 22 | * description: 23 | * 24 | * @author Created by zhoujunjie on 2024/9/2 25 | * @mail zhoujunjie.9743@bytedance.com 26 | **/ 27 | 28 | class ChangFontThemeEvent(val type: FontType): Event() -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/service/IMainContentBlockService.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo.service 17 | 18 | /** 19 | * description: 20 | * 21 | * @author Created by zhoujunjie on 2024/8/30 22 | * @mail zhoujunjie.9743@bytedance.com 23 | **/ 24 | 25 | interface IMainContentBlockService { 26 | fun changeMainContent(content: String) 27 | } -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/util/DemoTestUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo.util 17 | 18 | import com.bytedance.demo.R 19 | import com.bytedance.demo.data.DemoCardData 20 | 21 | /** 22 | * description: 23 | * 24 | * @author Created by zhoujunjie on 2024/8/29 25 | * @mail zhoujunjie.9743@bytedance.com 26 | **/ 27 | 28 | object DemoTestUtil { 29 | 30 | fun getTestData(): ArrayList { 31 | return ArrayList().apply { 32 | add( 33 | DemoCardData( 34 | avatarImg = R.drawable.avatar_img1, 35 | userName = "Mary", 36 | title = "This is a message from Mary", 37 | praiseCount = "101", 38 | commentCount = "56", 39 | collectCount = "292", 40 | mainContentBg = R.color.content_2 41 | ) 42 | ) 43 | add( 44 | DemoCardData( 45 | avatarImg = R.drawable.avatar_img2, 46 | userName = "Jerry", 47 | title = "This is an essay from Jerry", 48 | praiseCount = "1.3w", 49 | commentCount = "6342", 50 | collectCount = "2.5w", 51 | mainContentBg = R.color.assist_1_dark 52 | ) 53 | ) 54 | add( 55 | DemoCardData( 56 | avatarImg = R.drawable.avatar_img1, 57 | userName = "Bob", 58 | title = "This is a picture from Bob", 59 | praiseCount = "2.4w", 60 | commentCount = "1.1w", 61 | collectCount = "3.2w", 62 | mainContentBg = R.color.assist_2_dark 63 | ) 64 | ) 65 | add( 66 | DemoCardData( 67 | avatarImg = R.drawable.avatar_img1, 68 | userName = "Cindy", 69 | title = "This is a diary from Cindy", 70 | praiseCount = "2.8w", 71 | commentCount = "9876", 72 | collectCount = "1.5w", 73 | mainContentBg = R.color.assist_3_dark 74 | ) 75 | ) 76 | add( 77 | DemoCardData( 78 | avatarImg = R.drawable.avatar_img1, 79 | userName = "Jack", 80 | title = "这是用户Jack的一条内容", 81 | praiseCount = "11.4w", 82 | commentCount = "1.1w", 83 | collectCount = "6.8w", 84 | mainContentBg = R.color.assist_4_dark 85 | ) 86 | ) 87 | add( 88 | DemoCardData( 89 | avatarImg = R.drawable.avatar_img1, 90 | userName = "Rem", 91 | title = "这是用户Rem的一条内容", 92 | praiseCount = "9300", 93 | commentCount = "2345", 94 | collectCount = "7542", 95 | mainContentBg = R.color.assist_5_dark 96 | ) 97 | ) 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /demo/src/main/java/com/bytedance/demo/util/FontType.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Bytedance Ltd. and/or its affiliates 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bytedance.demo.util 17 | 18 | /** 19 | * description: 20 | * 21 | * @author Created by zhoujunjie on 2024/9/2 22 | * @mail zhoujunjie.9743@bytedance.com 23 | **/ 24 | 25 | enum class FontType { 26 | Normal, 27 | Bold 28 | } -------------------------------------------------------------------------------- /demo/src/main/res/drawable/avatar_img1.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/avatar_img2.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/avatar_img3.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/avatar_img4.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/avatar_img5.xml: -------------------------------------------------------------------------------- 1 | 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 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/avatar_img6.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/ic_collect_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/ic_comment_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/ic_digg_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/ic_more_icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | 34 | 35 | 36 | 37 | 38 | 39 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/demo_activity_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/demo_bottom_info_block_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 24 | 25 | 35 | 36 | 37 | 38 | 49 | 50 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/demo_card_holder_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 27 | 28 | 39 | 40 | 48 | 49 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/demo_main_content_block_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 22 | 23 |