├── .gitignore ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── carman │ │ └── kotlin │ │ └── coroutine │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── carman │ │ │ └── kotlin │ │ │ └── coroutine │ │ │ ├── DemoApplication.kt │ │ │ ├── MainService.kt │ │ │ ├── base │ │ │ ├── BaseActivity.kt │ │ │ ├── BaseAdapter.kt │ │ │ ├── BaseBinding.kt │ │ │ ├── BaseDialogFragment.kt │ │ │ ├── BaseFragment.kt │ │ │ ├── BaseMultiTypeAdapter.kt │ │ │ ├── BaseService.kt │ │ │ └── BaseVBActivity.kt │ │ │ ├── bean │ │ │ ├── Person.kt │ │ │ ├── SysConfig.kt │ │ │ ├── User.kt │ │ │ └── Weather.kt │ │ │ ├── constant │ │ │ └── HttpConstant.kt │ │ │ ├── exception │ │ │ └── GlobalCoroutineExceptionHandler.kt │ │ │ ├── extensions │ │ │ ├── ActivityExtension.kt │ │ │ ├── ClassExtension.kt │ │ │ ├── CoilExtension.kt │ │ │ ├── ContextExtension.kt │ │ │ ├── CoroutineExtension.kt │ │ │ ├── RecyclerViewExtension.kt │ │ │ ├── StringExtension.kt │ │ │ └── ViewExtension.kt │ │ │ ├── interf │ │ │ ├── BindingAdapter.kt │ │ │ ├── CheckedChangedListener.kt │ │ │ ├── DoubleClickHelper.kt │ │ │ └── ItemClickListener.kt │ │ │ ├── remote │ │ │ ├── CResponse.kt │ │ │ ├── CommonInterceptor.kt │ │ │ ├── CoroutineService.kt │ │ │ ├── ErrorInterceptor.kt │ │ │ ├── OkHttpClientManager.kt │ │ │ └── ServerApi.kt │ │ │ ├── request │ │ │ ├── ModelFactory.kt │ │ │ ├── ViewModelUtils.kt │ │ │ ├── repository │ │ │ │ ├── BaseRepository.kt │ │ │ │ └── MainRepository.kt │ │ │ └── viewmodel │ │ │ │ ├── MainViewModel.kt │ │ │ │ └── TestViewModel.kt │ │ │ └── ui │ │ │ ├── HomeFragment.kt │ │ │ ├── MainActivity.kt │ │ │ ├── MainActivity04.kt │ │ │ ├── MainHiltActivity.kt │ │ │ ├── MainTestActivity.kt │ │ │ └── adapter │ │ │ ├── HomeAdapter.kt │ │ │ └── SecondAdapter.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_main04.xml │ │ ├── activity_main_test.xml │ │ ├── fragment_main.xml │ │ ├── item_home.xml │ │ ├── item_persion.xml │ │ ├── item_student.xml │ │ └── item_teacher.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── carman │ └── kotlin │ └── coroutine │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── 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 | /.idea/ 17 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | id 'kotlin-parcelize' 5 | id 'kotlin-kapt' 6 | id 'kotlinx-serialization' 7 | id 'dagger.hilt.android.plugin' 8 | } 9 | 10 | android { 11 | buildFeatures { 12 | dataBinding = true 13 | } 14 | compileSdkVersion 30 15 | buildToolsVersion "30.0.2" 16 | 17 | defaultConfig { 18 | applicationId "com.carman.kotlin.coroutine" 19 | minSdkVersion 21 20 | targetSdkVersion 30 21 | versionCode 1 22 | versionName "1.0" 23 | 24 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 25 | } 26 | 27 | buildTypes { 28 | release { 29 | minifyEnabled false 30 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 31 | } 32 | } 33 | compileOptions { 34 | sourceCompatibility JavaVersion.VERSION_1_8 35 | targetCompatibility JavaVersion.VERSION_1_8 36 | } 37 | kotlinOptions { 38 | jvmTarget = '1.8' 39 | } 40 | } 41 | 42 | dependencies { 43 | implementation "androidx.core:core-ktx:$androidx_version" 44 | implementation "androidx.appcompat:appcompat:$appcompat_version" 45 | implementation "com.google.android.material:material:$material_version" 46 | implementation "androidx.constraintlayout:constraintlayout:2.0.4" 47 | // Kotlin 48 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 49 | // 协程核心库 50 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" 51 | // 协程Android支持库 52 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" 53 | 54 | implementation "androidx.activity:activity-ktx:1.2.2" 55 | implementation "androidx.fragment:fragment-ktx:1.3.3" 56 | 57 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_KTX_version" 58 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_KTX_version" 59 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_KTX_version" 60 | implementation "androidx.room:room-ktx:$room_ktx_version" 61 | 62 | 63 | implementation "io.coil-kt:coil:$coil_version" 64 | implementation "io.coil-kt:coil-gif:$coil_version" 65 | implementation "io.coil-kt:coil-svg:$coil_version" 66 | 67 | // ok http 68 | implementation "com.squareup.okhttp3:okhttp:4.9.0" 69 | implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0' 70 | 71 | // retrofit 72 | implementation "com.squareup.retrofit2:retrofit:$retrofit_version" 73 | implementation "com.squareup.retrofit2:converter-scalars:$retrofit_version" 74 | implementation "com.squareup.retrofit2:converter-gson:$retrofit_version" 75 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' 76 | 77 | implementation "com.google.dagger:hilt-android:2.35.1" 78 | kapt "com.google.dagger:hilt-android-compiler:2.35.1" 79 | } -------------------------------------------------------------------------------- /app/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 -------------------------------------------------------------------------------- /app/src/androidTest/java/com/carman/kotlin/coroutine/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.carman.kotlin.coroutine", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/DemoApplication.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine 2 | 3 | import android.app.Application 4 | import dagger.hilt.android.HiltAndroidApp 5 | 6 | @HiltAndroidApp 7 | class DemoApplication : Application() { 8 | 9 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/MainService.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine 2 | 3 | import android.content.Intent 4 | import android.os.IBinder 5 | import com.carman.kotlin.coroutine.base.BaseService 6 | 7 | class MainService : BaseService() { 8 | 9 | override fun onBind(intent: Intent): IBinder? = null 10 | 11 | override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { 12 | requestIO { 13 | //网络加载 14 | } 15 | return super.onStartCommand(intent, flags, startId) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.base 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.databinding.ViewDataBinding 6 | import androidx.fragment.app.Fragment 7 | import androidx.lifecycle.ViewModel 8 | import androidx.lifecycle.ViewModelProvider 9 | import com.carman.kotlin.coroutine.extensions.getViewBinding 10 | import com.carman.kotlin.coroutine.request.ViewModelUtils 11 | 12 | /** 13 | * 14 | *@author carman 15 | * @time 2021-4-16 13:25 16 | */ 17 | abstract class BaseActivity( 18 | private val factory: ViewModelProvider.Factory? = null 19 | ) : AppCompatActivity(), BaseBinding { 20 | protected val mBinding: VB by lazy(mode = LazyThreadSafetyMode.NONE) { 21 | getViewBinding(layoutInflater) 22 | } 23 | protected lateinit var viewModel: VM 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | setContentView(mBinding.root) 28 | viewModel = ViewModelUtils.createViewModel(this, factory, 1) 29 | mBinding.initBinding() 30 | initObserve() 31 | } 32 | 33 | abstract fun initObserve() 34 | 35 | override fun onBackPressed() { 36 | super.onBackPressed() 37 | val fragments: List = supportFragmentManager.fragments 38 | if (!fragments.isNullOrEmpty()) { 39 | for (fragment in fragments) { 40 | if (fragment is BaseFragment<*, *>) { 41 | if (fragment.onBackPressed()) { 42 | return 43 | } 44 | } 45 | } 46 | } 47 | super.onBackPressed() 48 | } 49 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/base/BaseAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.base 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.databinding.ViewDataBinding 6 | import androidx.recyclerview.widget.DiffUtil 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.carman.kotlin.coroutine.extensions.getViewBinding 9 | import java.util.* 10 | 11 | /** 12 | * 13 | *@author carman 14 | * @time 2021-4-16 13:25 15 | */ 16 | 17 | 18 | abstract class BaseAdapter : RecyclerView.Adapter>() { 19 | 20 | private var mData: List = mutableListOf() 21 | 22 | fun setData(data: List?) { 23 | data?.let { 24 | val result = DiffUtil.calculateDiff(object : DiffUtil.Callback() { 25 | override fun getOldListSize(): Int { 26 | return mData.size 27 | } 28 | 29 | override fun getNewListSize(): Int { 30 | return it.size 31 | } 32 | 33 | override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 34 | val oldData: T = mData[oldItemPosition] 35 | val newData: T = it[newItemPosition] 36 | return this@BaseAdapter.areItemsTheSame(oldData, newData) 37 | } 38 | 39 | override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 40 | val oldData: T = mData[oldItemPosition] 41 | val newData: T = it[newItemPosition] 42 | return this@BaseAdapter.areItemContentsTheSame(oldData, newData, oldItemPosition, newItemPosition) 43 | } 44 | }) 45 | mData = data 46 | result.dispatchUpdatesTo(this) 47 | } ?: let { 48 | mData = mutableListOf() 49 | notifyItemRangeChanged(0, mData.size) 50 | } 51 | 52 | } 53 | 54 | fun addData(data: List?, position: Int? = null) { 55 | if (!data.isNullOrEmpty()) { 56 | with(LinkedList(mData)){ 57 | position?.let { 58 | val startPosition = when { 59 | it < 0 -> 0 60 | it >= size -> size 61 | else -> it 62 | } 63 | addAll(startPosition, data) 64 | }?: addAll(data) 65 | setData(this) 66 | } 67 | } 68 | } 69 | 70 | protected open fun areItemContentsTheSame(oldItem: T, newItem: T, oldItemPosition: Int, newItemPosition: Int): Boolean { 71 | return oldItem == newItem 72 | } 73 | 74 | protected open fun areItemsTheSame(oldItem: T, newItem: T): Boolean { 75 | return oldItem == newItem 76 | } 77 | 78 | fun getData(): List { 79 | return mData 80 | } 81 | 82 | fun getItem(position: Int): T { 83 | return mData[position] 84 | } 85 | 86 | fun getActualPosition(data: T): Int { 87 | return mData.indexOf(data) 88 | } 89 | 90 | override fun getItemCount(): Int { 91 | return mData.size 92 | } 93 | 94 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindViewHolder { 95 | return with(getViewBinding(LayoutInflater.from(parent.context), parent,1)) { 96 | setListener() 97 | BindViewHolder(this) 98 | } 99 | } 100 | 101 | override fun onBindViewHolder(holder: BindViewHolder, position: Int) { 102 | with(holder.binding){ 103 | onBindViewHolder(getItem(position), position) 104 | executePendingBindings() 105 | } 106 | } 107 | 108 | open fun VB.setListener() {} 109 | 110 | abstract fun VB.onBindViewHolder(bean: T, position: Int) 111 | 112 | class BindViewHolder(var binding: M) : 113 | RecyclerView.ViewHolder(binding.root) 114 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/base/BaseBinding.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.base 2 | 3 | import androidx.databinding.ViewDataBinding 4 | 5 | interface BaseBinding { 6 | fun VB.initBinding() 7 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/base/BaseDialogFragment.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.base 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.view.WindowManager 8 | import androidx.databinding.ViewDataBinding 9 | import androidx.fragment.app.DialogFragment 10 | import com.carman.kotlin.coroutine.extensions.getViewBinding 11 | 12 | /** 13 | * 14 | *@author carman 15 | * @time 2021-4-16 13:25 16 | */ 17 | abstract class BaseDialogFragment : DialogFragment(),BaseBinding{ 18 | protected lateinit var mBinding:VB 19 | 20 | override fun onCreateView( 21 | inflater: LayoutInflater, 22 | container: ViewGroup?, 23 | savedInstanceState: Bundle? 24 | ): View? { 25 | mBinding = getViewBinding(inflater,container) 26 | return mBinding.root 27 | } 28 | 29 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 30 | super.onViewCreated(view, savedInstanceState) 31 | mBinding.initBinding() 32 | } 33 | 34 | override fun onResume() { 35 | dialog?.let { 36 | val params: ViewGroup.LayoutParams? = it.window?.attributes 37 | params?.run { 38 | width = WindowManager.LayoutParams.MATCH_PARENT 39 | height = WindowManager.LayoutParams.MATCH_PARENT 40 | it.window?.attributes = this as WindowManager.LayoutParams 41 | } 42 | } 43 | super.onResume() 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.base 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.databinding.ViewDataBinding 8 | import androidx.fragment.app.Fragment 9 | import androidx.lifecycle.ViewModel 10 | import androidx.lifecycle.ViewModelProvider 11 | import androidx.lifecycle.lifecycleScope 12 | import com.carman.kotlin.coroutine.extensions.getViewBinding 13 | import com.carman.kotlin.coroutine.request.ViewModelUtils 14 | 15 | /** 16 | * 17 | *@author carman 18 | * @time 2021-4-16 13:25 19 | */ 20 | abstract class BaseFragment( 21 | private val share: Boolean = false, 22 | private val factory: ViewModelProvider.Factory? = null 23 | ) : Fragment(), BaseBinding { 24 | protected lateinit var mBinding: VB 25 | private set 26 | protected lateinit var viewModel: VM 27 | override fun onCreateView( 28 | inflater: LayoutInflater, 29 | container: ViewGroup?, 30 | savedInstanceState: Bundle? 31 | ): View? { 32 | mBinding = getViewBinding(inflater, container) 33 | viewModel = if (share) ViewModelUtils.createActivityViewModel(this, factory, 1) 34 | else ViewModelUtils.createViewModel(this, factory, 1) 35 | return mBinding.root 36 | } 37 | 38 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 39 | super.onViewCreated(view, savedInstanceState) 40 | mBinding.initBinding() 41 | } 42 | 43 | 44 | open fun onBackPressed(): Boolean { 45 | return false 46 | } 47 | 48 | override fun onDestroy() { 49 | super.onDestroy() 50 | if (::mBinding.isInitialized) { 51 | mBinding.unbind() 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/base/BaseMultiTypeAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.base 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.databinding.ViewDataBinding 6 | import androidx.recyclerview.widget.DiffUtil 7 | import androidx.recyclerview.widget.RecyclerView 8 | import java.util.* 9 | 10 | /** 11 | * 12 | *@author carman 13 | * @time 2021-4-16 13:25 14 | */ 15 | abstract class BaseMultiTypeAdapter : RecyclerView.Adapter() { 16 | 17 | private var mData: List = mutableListOf() 18 | 19 | fun setData(data: List?) { 20 | data?.let { 21 | val result = DiffUtil.calculateDiff(object : DiffUtil.Callback() { 22 | override fun getOldListSize(): Int { 23 | return mData.size 24 | } 25 | 26 | override fun getNewListSize(): Int { 27 | return it.size 28 | } 29 | 30 | override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 31 | val oldData: T = mData[oldItemPosition] 32 | val newData: T = it[newItemPosition] 33 | return this@BaseMultiTypeAdapter.areItemsTheSame(oldData, newData) 34 | } 35 | 36 | override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 37 | val oldData: T = mData[oldItemPosition] 38 | val newData: T = it[newItemPosition] 39 | return this@BaseMultiTypeAdapter.areItemContentsTheSame(oldData, newData, oldItemPosition, newItemPosition) 40 | } 41 | }) 42 | mData = data 43 | result.dispatchUpdatesTo(this) 44 | } ?: let { 45 | mData = mutableListOf() 46 | notifyItemRangeChanged(0, mData.size) 47 | } 48 | 49 | } 50 | 51 | fun addData(data: List?, position: Int? = null) { 52 | if (!data.isNullOrEmpty()) { 53 | with(LinkedList(mData)) { 54 | position?.let { 55 | val startPosition = when { 56 | it < 0 -> 0 57 | it >= size -> size 58 | else -> it 59 | } 60 | addAll(startPosition, data) 61 | } ?: addAll(data) 62 | setData(this) 63 | } 64 | } 65 | } 66 | 67 | protected open fun areItemContentsTheSame(oldItem: T, newItem: T, oldItemPosition: Int, newItemPosition: Int): Boolean { 68 | return oldItem == newItem 69 | } 70 | 71 | protected open fun areItemsTheSame(oldItem: T, newItem: T): Boolean { 72 | return oldItem == newItem 73 | } 74 | 75 | fun getData(): List { 76 | return mData 77 | } 78 | 79 | fun getItem(position: Int): T { 80 | return mData[position] 81 | } 82 | 83 | fun getActualPosition(data: T): Int { 84 | return mData.indexOf(data) 85 | } 86 | 87 | override fun getItemCount(): Int { 88 | return mData.size 89 | } 90 | 91 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MultiTypeViewHolder { 92 | return MultiTypeViewHolder(onCreateMultiViewHolder(parent, viewType)) 93 | } 94 | 95 | override fun onBindViewHolder(holder: MultiTypeViewHolder, position: Int) { 96 | holder.onBindViewHolder(holder, getItem(position), position) 97 | holder.binding.executePendingBindings() 98 | } 99 | 100 | abstract fun MultiTypeViewHolder.onBindViewHolder(holder: MultiTypeViewHolder, item: T, position: Int) 101 | 102 | abstract fun onCreateMultiViewHolder(parent: ViewGroup, viewType: Int): ViewDataBinding 103 | 104 | protected fun loadLayout(vbClass: Class,parent: ViewGroup): VB { 105 | val inflate = vbClass.getDeclaredMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.java) 106 | return inflate.invoke(null, LayoutInflater.from(parent.context), parent, false) as VB 107 | } 108 | 109 | class MultiTypeViewHolder(var binding: ViewDataBinding) : 110 | RecyclerView.ViewHolder(binding.root) 111 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/base/BaseService.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.base 2 | 3 | import android.app.Service 4 | import com.carman.kotlin.coroutine.exception.GlobalCoroutineExceptionHandler 5 | import com.carman.kotlin.coroutine.extensions.NormalScope 6 | import kotlinx.coroutines.* 7 | 8 | abstract class BaseService :Service(){ 9 | private val normalScope = NormalScope() 10 | 11 | override fun onDestroy() { 12 | normalScope.cancel() 13 | super.onDestroy() 14 | } 15 | 16 | protected fun requestMain( 17 | errCode: Int = -1, errMsg: String = "", report: Boolean = false, 18 | block: suspend CoroutineScope.() -> Unit) { 19 | normalScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) { 20 | block.invoke(this) 21 | } 22 | } 23 | 24 | 25 | protected fun requestIO( 26 | errCode: Int = -1, errMsg: String = "", report: Boolean = false, 27 | block: suspend CoroutineScope.() -> Unit): Job { 28 | return normalScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) { 29 | block.invoke(this) 30 | } 31 | } 32 | 33 | protected fun delayMain( 34 | delayTime: Long,errCode: Int = -1, errMsg: String = "", report: Boolean = false, 35 | block: suspend CoroutineScope.() -> Unit) { 36 | normalScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) { 37 | withContext(Dispatchers.IO) { 38 | delay(delayTime) 39 | } 40 | block.invoke(this) 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/base/BaseVBActivity.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.base 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.databinding.ViewDataBinding 6 | import com.carman.kotlin.coroutine.extensions.getViewBinding 7 | 8 | /** 9 | * 10 | *@author carman 11 | * @time 2021-4-16 13:25 12 | */ 13 | abstract class BaseVBActivity : AppCompatActivity(), BaseBinding { 14 | 15 | protected val mBinding: VB by lazy(mode = LazyThreadSafetyMode.NONE) { 16 | getViewBinding(layoutInflater) 17 | } 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | setContentView(mBinding.root) 22 | initObserve() 23 | mBinding.initBinding() 24 | } 25 | 26 | abstract fun initObserve() 27 | 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/bean/Person.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.bean 2 | 3 | 4 | sealed class Person(open val id :Int, open val name:String) 5 | 6 | data class Student( 7 | override val id:Int, 8 | override val name:String, 9 | val grade:String):Person(id, name) 10 | 11 | data class Teacher( 12 | override val id:Int, 13 | override val name:String, 14 | val subject:String):Person(id, name) -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/bean/SysConfig.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.bean 2 | 3 | data class SysConfig( 4 | var splashBg:String? = null, 5 | var hotFix:Boolean = false 6 | ) 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/bean/User.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.bean 2 | 3 | data class User(val id:Int, val name:String) 4 | -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/bean/Weather.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.bean 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | 6 | data class Weather( 7 | val now: WeatherDetail, 8 | val time: String 9 | ) 10 | 11 | data class WeatherDetail( 12 | val aqi: String, 13 | val rain: String, 14 | val sd: String, 15 | val temperature: String, 16 | @SerializedName("temperature_time") 17 | val temperatureTime: String, 18 | val weather: String, 19 | @SerializedName("weather_pic") 20 | val weatherPic: String, 21 | @SerializedName("wind_direction") 22 | val windDirection: String, 23 | @SerializedName("windPower") 24 | val windPower: String 25 | ) 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/constant/HttpConstant.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.constant 2 | 3 | object HttpConstant { 4 | 5 | internal val SERVER_HOST = "route.showapi.com" 6 | internal val HTTP_SERVER = "https://$SERVER_HOST" 7 | internal val SHOW_API_APPID = "628308" 8 | internal val SHOW_API_SIGN = "a7a8d4b8f23b432e9d66cabc9619a216" 9 | 10 | const val OK = 200 11 | const val ACCOUNT_FROZEN = 10001 12 | const val INVALID_TOKEN = 10005 13 | const val TOKEN_EXPIRED = 10006 14 | 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/exception/GlobalCoroutineExceptionHandler.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.exception 2 | 3 | import android.util.Log 4 | import kotlinx.coroutines.CoroutineExceptionHandler 5 | import kotlin.coroutines.CoroutineContext 6 | 7 | /** 8 | * @param errCode 错误码 9 | * @param errMsg 简要错误信息 10 | * @param report 是否需要上报 11 | * 12 | */ 13 | class GlobalCoroutineExceptionHandler(private val errCode: Int, private val errMsg: String? = "", private val report: Boolean = false) : CoroutineExceptionHandler { 14 | override val key: CoroutineContext.Key<*> 15 | get() = CoroutineExceptionHandler 16 | 17 | override fun handleException(context: CoroutineContext, exception: Throwable) { 18 | Log.e("$errCode","----------------异常警告----------------") 19 | Log.e("$errCode","---------------------------------------") 20 | Log.e("$errCode","---------------------------------------") 21 | val msg = exception.stackTraceToString() 22 | Log.e("$errCode","GlobalCoroutineExceptionHandler:${msg}") 23 | if (report){ 24 | //上报日志 25 | } 26 | Log.e("$errCode","---------------------------------------") 27 | Log.e("$errCode","---------------------------------------") 28 | Log.e("$errCode","----------------异常警告----------------") 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/extensions/ActivityExtension.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.extensions 2 | 3 | import android.app.Activity 4 | import android.app.ActivityManager 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.graphics.Color 8 | import android.os.Build 9 | import android.view.View 10 | import android.view.WindowManager 11 | import androidx.core.os.bundleOf 12 | import androidx.fragment.app.Fragment 13 | import androidx.fragment.app.FragmentActivity 14 | 15 | inline fun Context.startActivity(vararg params: Pair) { 16 | startActivity(Intent(this, T::class.java).putExtras(bundleOf(*params))) 17 | } 18 | inline fun Context.getIntent(vararg params: Pair):Intent { 19 | return Intent(this, T::class.java).putExtras(bundleOf(*params)) 20 | } 21 | 22 | inline fun FragmentActivity.startActivity(vararg params: Pair) { 23 | startActivity(Intent(this, T::class.java).putExtras(bundleOf(*params))) 24 | } 25 | 26 | inline fun FragmentActivity.startActivityForResult(requestCode:Int,vararg params: Pair) { 27 | startActivityForResult(Intent(this, T::class.java).putExtras(bundleOf(*params)),requestCode) 28 | } 29 | 30 | inline fun Fragment.startActivity(vararg params: Pair) { 31 | startActivity(Intent(this.context, T::class.java).putExtras(bundleOf(*params))) 32 | } 33 | 34 | inline fun Fragment.startActivity(requestCode:Int,vararg params: Pair) { 35 | startActivityForResult(Intent(this.context, T::class.java).putExtras(bundleOf(*params)),requestCode) 36 | } 37 | 38 | 39 | inline fun Context.getRunningActivityName(): String { 40 | val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager 41 | return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 42 | val listInfos: List = activityManager.getRunningTasks(1) 43 | if (!listInfos.isNullOrEmpty()){ 44 | listInfos[0].topActivity?.className?:"" 45 | }else "" 46 | }else{ 47 | val listInfos = activityManager.appTasks 48 | if (!listInfos.isNullOrEmpty()){ 49 | listInfos[0].taskInfo.topActivity?.className?:"" 50 | }else "" 51 | } 52 | } 53 | 54 | /** 55 | * 重置状态栏 56 | */ 57 | inline fun Activity.resetTopStatusBarColor(isLightStatusBar: Boolean) { 58 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 59 | window.decorView.let { 60 | it.systemUiVisibility = 61 | if (isLightStatusBar) it.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() else it.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 62 | } 63 | } else { 64 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) 65 | window.statusBarColor = Color.TRANSPARENT 66 | if (isLightStatusBar) { 67 | window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) 68 | } else { 69 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/extensions/ClassExtension.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.extensions 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.activity.ComponentActivity 7 | import androidx.activity.viewModels 8 | import androidx.lifecycle.ViewModel 9 | import androidx.lifecycle.ViewModelLazy 10 | import androidx.lifecycle.ViewModelProvider 11 | import androidx.viewbinding.ViewBinding 12 | import com.carman.kotlin.coroutine.base.BaseMultiTypeAdapter 13 | import com.carman.kotlin.coroutine.request.ViewModelUtils 14 | import com.carman.kotlin.coroutine.request.viewmodel.MainViewModel 15 | import java.lang.reflect.ParameterizedType 16 | import java.lang.reflect.Type 17 | 18 | 19 | inline fun Any.getViewBinding(inflater: LayoutInflater,position:Int = 0):VB{ 20 | val vbClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments.filterIsInstance>() 21 | val inflate = vbClass[position].getDeclaredMethod("inflate", LayoutInflater::class.java) 22 | return inflate.invoke(null, inflater) as VB 23 | } 24 | 25 | 26 | inline fun Any.getViewBinding(inflater: LayoutInflater, container: ViewGroup?,position:Int = 0):VB{ 27 | val vbClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments.filterIsInstance>() 28 | val inflate = vbClass[position].getDeclaredMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.java) 29 | return inflate.invoke(null, inflater, container, false) as VB 30 | } 31 | 32 | inline fun ComponentActivity.createViewModel(position:Int): VM { 33 | val vbClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments.filterIsInstance>() 34 | val viewModel = vbClass[position] as Class 35 | return ViewModelProvider(this).get(viewModel) 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/extensions/CoilExtension.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.extensions 2 | 3 | import android.content.Context 4 | import android.graphics.drawable.Animatable 5 | import android.graphics.drawable.Drawable 6 | import android.os.Build 7 | import android.view.View 8 | import android.widget.ImageView 9 | import androidx.annotation.DrawableRes 10 | import androidx.lifecycle.DefaultLifecycleObserver 11 | import androidx.lifecycle.LifecycleOwner 12 | import coil.Coil.imageLoader 13 | import coil.ImageLoader 14 | import coil.decode.GifDecoder 15 | import coil.decode.ImageDecoderDecoder 16 | import coil.decode.SvgDecoder 17 | import coil.imageLoader 18 | import coil.loadAny 19 | import coil.request.Disposable 20 | import coil.request.ImageRequest 21 | import coil.size.PixelSize 22 | import coil.target.PoolableViewTarget 23 | import coil.transform.CircleCropTransformation 24 | import coil.transform.GrayscaleTransformation 25 | import coil.transform.RoundedCornersTransformation 26 | import coil.transition.TransitionTarget 27 | 28 | inline fun ImageRequest.Builder.configPlaceholder(@DrawableRes placeholder: Int) { 29 | if (placeholder != 0) { 30 | placeholder(placeholder) 31 | } 32 | } 33 | 34 | inline fun ImageRequest.Builder.configErrorHolder(@DrawableRes errorId: Int) { 35 | if (errorId != 0) { 36 | error(errorId) 37 | } 38 | } 39 | 40 | inline fun getGifImageLoader(context:Context):ImageLoader{ 41 | return ImageLoader.Builder(context).componentRegistry { 42 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 43 | add(ImageDecoderDecoder()) 44 | } else { 45 | add(GifDecoder()) 46 | } 47 | }.build() 48 | } 49 | 50 | 51 | inline fun getSvgImageLoader(context:Context):ImageLoader{ 52 | return ImageLoader.Builder(context).componentRegistry { 53 | add(SvgDecoder(context)) 54 | }.build() 55 | } 56 | 57 | fun ImageView.load( 58 | source: Any?, 59 | @DrawableRes placeholder: Int = 0, 60 | @DrawableRes errorId: Int = 0, 61 | skipMemoryCache: Boolean = false, 62 | isGrayscale: Boolean = false 63 | ) { 64 | loadAny(source) { 65 | configPlaceholder(placeholder) 66 | configErrorHolder(errorId) 67 | if (isGrayscale) { 68 | transformations( 69 | GrayscaleTransformation() 70 | ) 71 | } 72 | } 73 | } 74 | 75 | fun ImageView.loadGif( 76 | source: Any?, 77 | @DrawableRes placeholder: Int = 0, 78 | @DrawableRes errorId: Int = 0, 79 | skipMemoryCache: Boolean = false, 80 | isGrayscale: Boolean = false, 81 | ) { 82 | loadAny(source, getGifImageLoader(context)) { 83 | configPlaceholder(placeholder) 84 | configErrorHolder(errorId) 85 | if (isGrayscale) { 86 | transformations( 87 | GrayscaleTransformation() 88 | ) 89 | } 90 | } 91 | } 92 | 93 | 94 | fun ImageView.loadCircle( 95 | source: Any?, 96 | @DrawableRes placeholder: Int = 0, 97 | @DrawableRes errorId: Int = 0, 98 | skipMemoryCache: Boolean = false, 99 | isGrayscale: Boolean = false 100 | ): Disposable { 101 | return loadAny(source) { 102 | configPlaceholder(placeholder) 103 | configErrorHolder(errorId) 104 | if (isGrayscale) { 105 | transformations( 106 | CircleCropTransformation(), GrayscaleTransformation() 107 | ) 108 | } else { 109 | transformations( 110 | CircleCropTransformation() 111 | ) 112 | } 113 | } 114 | } 115 | 116 | fun ImageView.loadRoundedCorner( 117 | source: Any?, 118 | @DrawableRes placeholder: Int = 0, 119 | @DrawableRes errorId: Int = 0, 120 | skipMemoryCache: Boolean = false, 121 | isGrayscale: Boolean = false, 122 | radius: Int = 0 123 | ): Disposable { 124 | return loadRoundedCorner( 125 | source, 126 | errorId, 127 | placeholder, 128 | skipMemoryCache, 129 | isGrayscale, 130 | radius.toFloat(), 131 | radius.toFloat(), 132 | radius.toFloat(), 133 | radius.toFloat() 134 | ) 135 | } 136 | 137 | fun ImageView.loadRoundedCorner( 138 | source: Any?, 139 | @DrawableRes placeholder: Int = 0, 140 | @DrawableRes errorId: Int = 0, 141 | skipMemoryCache: Boolean = false, 142 | isGrayscale: Boolean = false, 143 | topLeft: Float = 0f, 144 | topRight: Float = 0f, 145 | bottomRight: Float = 0f, 146 | bottomLeft: Float = 0f 147 | ): Disposable { 148 | return loadAny(source) { 149 | configPlaceholder(placeholder) 150 | configErrorHolder(errorId) 151 | if (isGrayscale) { 152 | transformations( 153 | CircleCropTransformation(), GrayscaleTransformation() 154 | ) 155 | } else { 156 | transformations( 157 | RoundedCornersTransformation(topLeft, topRight, bottomLeft, bottomRight) 158 | ) 159 | } 160 | } 161 | } 162 | 163 | 164 | fun ImageView.loadRoundedCorner( 165 | source: Any?, 166 | @DrawableRes placeholder: Int = 0, 167 | @DrawableRes errorId: Int = 0, 168 | skipMemoryCache: Boolean = false, 169 | isGrayscale: Boolean = false, 170 | width: Int, 171 | height: Int, 172 | radius: Int = 0 173 | ): Disposable { 174 | return loadRoundedCorner( 175 | source, 176 | errorId, 177 | placeholder, 178 | skipMemoryCache, 179 | isGrayscale, 180 | width, 181 | height, 182 | radius.toFloat(), 183 | radius.toFloat(), 184 | radius.toFloat(), 185 | radius.toFloat() 186 | ) 187 | } 188 | 189 | 190 | fun ImageView.loadRoundedCorner( 191 | source: Any?, 192 | @DrawableRes placeholder: Int = 0, 193 | @DrawableRes errorId: Int = 0, 194 | skipMemoryCache: Boolean = false, 195 | isGrayscale: Boolean = false, 196 | width: Int, 197 | height: Int, 198 | topLeft: Float = 0f, 199 | topRight: Float = 0f, 200 | bottomRight: Float = 0f, 201 | bottomLeft: Float = 0f 202 | ): Disposable { 203 | return loadAny(source) { 204 | configPlaceholder(placeholder) 205 | configErrorHolder(errorId) 206 | size(PixelSize(width, height)) 207 | if (isGrayscale) { 208 | transformations( 209 | CircleCropTransformation(), GrayscaleTransformation() 210 | ) 211 | } else { 212 | transformations( 213 | RoundedCornersTransformation(topLeft, topRight, bottomLeft, bottomRight) 214 | ) 215 | } 216 | } 217 | } 218 | 219 | 220 | fun View.load( 221 | source: Any?, 222 | @DrawableRes placeholder: Int = 0, 223 | @DrawableRes errorId: Int = 0, 224 | skipMemoryCache: Boolean = false, 225 | isGrayscale: Boolean = false 226 | ): Disposable { 227 | val builder: ImageRequest.Builder.() -> Unit = { 228 | configPlaceholder(placeholder) 229 | configErrorHolder(errorId) 230 | if (isGrayscale) { 231 | transformations( 232 | GrayscaleTransformation() 233 | ) 234 | } 235 | } 236 | val request = ImageRequest.Builder(context) 237 | .data(source) 238 | .target(CoilViewTarget(this)) 239 | .apply(builder) 240 | .build() 241 | return context.imageLoader.enqueue(request) 242 | } 243 | 244 | 245 | fun View.loadRoundedCorner( 246 | source: Any?, 247 | @DrawableRes placeholder: Int = 0, 248 | @DrawableRes errorId: Int = 0, 249 | skipMemoryCache: Boolean = false, 250 | isGrayscale: Boolean = false, 251 | radius: Int = 0 252 | ): Disposable { 253 | return loadRoundedCorner( 254 | source, 255 | errorId, 256 | placeholder, 257 | skipMemoryCache, 258 | isGrayscale, 259 | radius.toFloat(), 260 | radius.toFloat(), 261 | radius.toFloat(), 262 | radius.toFloat() 263 | ) 264 | } 265 | 266 | fun View.loadRoundedCorner( 267 | source: Any?, 268 | @DrawableRes placeholder: Int = 0, 269 | @DrawableRes errorId: Int = 0, 270 | skipMemoryCache: Boolean = false, 271 | isGrayscale: Boolean = false, 272 | topLeft: Float = 0f, 273 | topRight: Float = 0f, 274 | bottomRight: Float = 0f, 275 | bottomLeft: Float = 0f 276 | ): Disposable { 277 | val builder: ImageRequest.Builder.() -> Unit = { 278 | configPlaceholder(placeholder) 279 | configErrorHolder(errorId) 280 | if (isGrayscale) { 281 | transformations( 282 | CircleCropTransformation(), GrayscaleTransformation() 283 | ) 284 | } else { 285 | transformations( 286 | RoundedCornersTransformation(topLeft, topRight, bottomLeft, bottomRight) 287 | ) 288 | } 289 | } 290 | val request = ImageRequest.Builder(context) 291 | .data(source) 292 | .target(CoilViewTarget(this)) 293 | .apply(builder) 294 | .build() 295 | return context.imageLoader.enqueue(request) 296 | } 297 | 298 | 299 | fun View.loadRoundedCorner( 300 | source: Any?, 301 | @DrawableRes placeholder: Int = 0, 302 | @DrawableRes errorId: Int = 0, 303 | skipMemoryCache: Boolean = false, 304 | isGrayscale: Boolean = false, 305 | width: Int, 306 | height: Int, 307 | radius: Int = 0 308 | ): Disposable { 309 | return loadRoundedCorner( 310 | source, 311 | errorId, 312 | placeholder, 313 | skipMemoryCache, 314 | isGrayscale, 315 | width, 316 | height, 317 | radius.toFloat(), 318 | radius.toFloat(), 319 | radius.toFloat(), 320 | radius.toFloat() 321 | ) 322 | } 323 | 324 | 325 | fun View.loadRoundedCorner( 326 | source: Any?, 327 | @DrawableRes placeholder: Int = 0, 328 | @DrawableRes errorId: Int = 0, 329 | skipMemoryCache: Boolean = false, 330 | isGrayscale: Boolean = false, 331 | width: Int, 332 | height: Int, 333 | topLeft: Float = 0f, 334 | topRight: Float = 0f, 335 | bottomRight: Float = 0f, 336 | bottomLeft: Float = 0f 337 | ): Disposable { 338 | val builder: ImageRequest.Builder.() -> Unit = { 339 | configPlaceholder(placeholder) 340 | configErrorHolder(errorId) 341 | size(PixelSize(width, height)) 342 | if (isGrayscale) { 343 | transformations( 344 | CircleCropTransformation(), GrayscaleTransformation() 345 | ) 346 | } else { 347 | transformations( 348 | RoundedCornersTransformation(topLeft, topRight, bottomLeft, bottomRight) 349 | ) 350 | } 351 | } 352 | val request = ImageRequest.Builder(context) 353 | .data(source) 354 | .target(CoilViewTarget(this)) 355 | .apply(builder) 356 | .build() 357 | return context.imageLoader.enqueue(request) 358 | } 359 | 360 | class CoilViewTarget( 361 | override val view: View 362 | ) : PoolableViewTarget, TransitionTarget, DefaultLifecycleObserver { 363 | 364 | private var isStarted = false 365 | 366 | override val drawable: Drawable? get() = view.background 367 | 368 | override fun onStart(placeholder: Drawable?) = setDrawable(placeholder) 369 | 370 | override fun onError(error: Drawable?) = setDrawable(error) 371 | 372 | override fun onSuccess(result: Drawable) = setDrawable(result) 373 | 374 | override fun onClear() = setDrawable(null) 375 | 376 | override fun onStart(owner: LifecycleOwner) { 377 | isStarted = true 378 | updateAnimation() 379 | } 380 | 381 | override fun onStop(owner: LifecycleOwner) { 382 | isStarted = false 383 | updateAnimation() 384 | } 385 | 386 | /** Replace the [ImageView]'s current drawable with [drawable]. */ 387 | protected open fun setDrawable(drawable: Drawable?) { 388 | (view.background as? Animatable)?.stop() 389 | view.background = drawable 390 | updateAnimation() 391 | } 392 | 393 | /** Start/stop the current [Drawable]'s animation based on the current lifecycle state. */ 394 | protected open fun updateAnimation() { 395 | val animatable = view.background as? Animatable ?: return 396 | if (isStarted) animatable.start() else animatable.stop() 397 | } 398 | 399 | override fun equals(other: Any?): Boolean { 400 | return (this === other) || (other is CoilViewTarget && view == other.view) 401 | } 402 | 403 | override fun hashCode() = view.hashCode() 404 | 405 | override fun toString() = "CoilViewTarget(view=$view)" 406 | } 407 | -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/extensions/ContextExtension.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.extensions 2 | 3 | import android.app.ActivityManager 4 | import android.content.Context 5 | import android.content.pm.PackageInfo 6 | import android.content.pm.PackageManager 7 | import android.os.Process 8 | 9 | 10 | fun Context.isAPPInTop(): Boolean { 11 | val packageName: String = getProcessName() ?:"" 12 | val activityManager: ActivityManager = 13 | getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager 14 | val listInfos: List = activityManager.getRunningTasks(20) 15 | // 判断程序是否在栈顶 16 | return if (!listInfos.isNullOrEmpty()){ 17 | listInfos[0].topActivity?.packageName == packageName 18 | }else false 19 | } 20 | 21 | /** 22 | * 返回app运行状态 23 | * @param context 一个context 24 | * @return int 1:前台 2:后台 0:不存在 25 | */ 26 | fun Context.isAppAlive(): Int { 27 | val packageName: String = getProcessName() ?:"" 28 | val activityManager: ActivityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager 29 | val listInfos: List = activityManager.getRunningTasks(20) 30 | 31 | return if (listInfos[0].topActivity?.packageName == packageName) { 32 | 1 33 | } else { 34 | for (info in listInfos) { 35 | if (info.topActivity?.packageName == packageName) { 36 | return 2 37 | } 38 | } 39 | 0 40 | } 41 | } 42 | 43 | fun Context.getProcessName(): String? { 44 | var manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager 45 | for (processInfo in manager.runningAppProcesses) { 46 | if (processInfo.pid == Process.myPid()) { 47 | return processInfo.processName 48 | } 49 | } 50 | return null 51 | } 52 | 53 | fun Context.checkAppInstalled(pckName: String): Boolean { 54 | var packageInfo: PackageInfo? = null 55 | try { 56 | packageInfo = packageManager.getPackageInfo(pckName, 0) 57 | } catch (e: PackageManager.NameNotFoundException) { 58 | } 59 | return packageInfo != null 60 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/extensions/CoroutineExtension.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.extensions 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import androidx.fragment.app.Fragment 5 | import androidx.lifecycle.LifecycleCoroutineScope 6 | import androidx.lifecycle.ViewModel 7 | import androidx.lifecycle.lifecycleScope 8 | import androidx.lifecycle.viewModelScope 9 | import com.carman.kotlin.coroutine.exception.GlobalCoroutineExceptionHandler 10 | import kotlinx.coroutines.* 11 | import kotlin.coroutines.CoroutineContext 12 | 13 | @Suppress("FunctionName") 14 | public fun NormalScope(): CoroutineScope = CoroutineScope(Dispatchers.Main) 15 | 16 | /** 17 | * @param errCode 错误码 18 | * @param errMsg 简要错误信息 19 | * @param report 是否需要上报 20 | * @param block 需要执行的任务 21 | */ 22 | inline fun AppCompatActivity.requestMain( 23 | errCode: Int = -1, errMsg: String = "", report: Boolean = false, 24 | noinline block: suspend CoroutineScope.() -> Unit) { 25 | lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) { 26 | block.invoke(this) 27 | } 28 | } 29 | 30 | 31 | /** 32 | * @param errCode 错误码 33 | * @param errMsg 简要错误信息 34 | * @param report 是否需要上报 35 | * @param block 需要执行的任务 36 | */ 37 | inline fun AppCompatActivity.requestIO( 38 | errCode: Int = -1, errMsg: String = "", report: Boolean = false, 39 | noinline block: suspend CoroutineScope.() -> Unit): Job { 40 | return lifecycleScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) { 41 | block.invoke(this) 42 | } 43 | } 44 | 45 | /** 46 | * @param errCode 错误码 47 | * @param errMsg 简要错误信息 48 | * @param report 是否需要上报 49 | * @param block 需要执行的任务 50 | */ 51 | inline fun AppCompatActivity.delayMain( 52 | delayTime: Long,errCode: Int = -1, errMsg: String = "", report: Boolean = false, 53 | noinline block: suspend CoroutineScope.() -> Unit) { 54 | lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) { 55 | withContext(Dispatchers.IO) { 56 | delay(delayTime) 57 | } 58 | block.invoke(this) 59 | } 60 | } 61 | 62 | /** 63 | * @param errCode 错误码 64 | * @param errMsg 简要错误信息 65 | * @param report 是否需要上报 66 | * @param block 需要执行的任务 67 | */ 68 | inline fun Fragment.requestMain( 69 | errCode: Int = -1, errMsg: String = "", report: Boolean = false, 70 | noinline block: suspend CoroutineScope.() -> Unit) { 71 | lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) { 72 | block.invoke(this) 73 | } 74 | } 75 | 76 | /** 77 | * @param errCode 错误码 78 | * @param errMsg 简要错误信息 79 | * @param report 是否需要上报 80 | * @param block 需要执行的任务 81 | */ 82 | inline fun Fragment.requestIO( 83 | errCode: Int = -1, errMsg: String = "", report: Boolean = false, 84 | noinline block: suspend CoroutineScope.() -> Unit) { 85 | lifecycleScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) { 86 | block.invoke(this) 87 | } 88 | } 89 | 90 | 91 | /** 92 | * @param errCode 错误码 93 | * @param errMsg 简要错误信息 94 | * @param report 是否需要上报 95 | * @param delayTime 延时时间 96 | * @param block 需要执行的任务 97 | */ 98 | inline fun Fragment.delayMain( 99 | delayTime: Long, errCode: Int = -1, errMsg: String = "", report: Boolean = false, 100 | noinline block: suspend CoroutineScope.() -> Unit) { 101 | lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) { 102 | withContext(Dispatchers.IO) { 103 | delay(delayTime) 104 | } 105 | block.invoke(this) 106 | } 107 | } 108 | 109 | 110 | 111 | 112 | /** 113 | * @param errCode 错误码 114 | * @param errMsg 简要错误信息 115 | * @param report 是否需要上报 116 | * @param block 需要执行的任务 117 | */ 118 | inline fun ViewModel.requestMain( 119 | errCode: Int = -1, errMsg: String = "", report: Boolean = false, 120 | noinline block: suspend CoroutineScope.() -> Unit) { 121 | viewModelScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) { 122 | block.invoke(this) 123 | } 124 | } 125 | 126 | /** 127 | * @param errCode 错误码 128 | * @param errMsg 简要错误信息 129 | * @param report 是否需要上报 130 | * @param block 需要执行的任务 131 | */ 132 | inline fun ViewModel.requestIO( 133 | errCode: Int = -1, errMsg: String = "", report: Boolean = false, 134 | noinline block: suspend CoroutineScope.() -> Unit) { 135 | viewModelScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) { 136 | block.invoke(this) 137 | } 138 | } 139 | 140 | 141 | /** 142 | * @param errCode 错误码 143 | * @param errMsg 简要错误信息 144 | * @param report 是否需要上报 145 | * @param delayTime 延时时间 146 | * @param block 需要执行的任务 147 | */ 148 | inline fun ViewModel.delayMain( 149 | delayTime: Long, errCode: Int = -1, errMsg: String = "", report: Boolean = false, 150 | noinline block: suspend CoroutineScope.() -> Unit) { 151 | viewModelScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) { 152 | withContext(Dispatchers.IO) { 153 | delay(delayTime) 154 | } 155 | block.invoke(this) 156 | } 157 | } 158 | 159 | inline fun LifecycleCoroutineScope.requestMain( 160 | errCode: Int = -1, errMsg: String = "", report: Boolean = false, 161 | noinline block: suspend CoroutineScope.() -> Unit) { 162 | launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) { 163 | block.invoke(this) 164 | } 165 | } 166 | 167 | @ExperimentalStdlibApi 168 | suspend inline fun CoroutineScope.delayWithContext(delayTime: Long, context: CoroutineContext, noinline block: suspend CoroutineScope.() -> T) { 169 | if (context[CoroutineDispatcher] is MainCoroutineDispatcher) { 170 | withContext(Dispatchers.IO) { 171 | delay(delayTime) 172 | } 173 | } else { 174 | delay(delayTime) 175 | } 176 | block.invoke(this) 177 | 178 | } 179 | -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/extensions/RecyclerViewExtension.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.extensions 2 | 3 | import android.graphics.Rect 4 | import android.view.View 5 | import androidx.recyclerview.widget.RecyclerView 6 | import androidx.recyclerview.widget.SimpleItemAnimator 7 | 8 | 9 | fun RecyclerView.setVerticalTransparentDivider(height: Int) { 10 | 11 | this.addItemDecoration(object : RecyclerView.ItemDecoration() { 12 | 13 | override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { 14 | val position = parent.getChildAdapterPosition(view) 15 | outRect.top = if (position == 0) 0 else height 16 | } 17 | }) 18 | } 19 | 20 | fun RecyclerView.setHorizontalTransparentDivider(width: Int) { 21 | this.addItemDecoration(object : RecyclerView.ItemDecoration() { 22 | override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { 23 | val position = parent.getChildAdapterPosition(view) 24 | outRect.left = if (position == 0) 0 else width 25 | } 26 | }) 27 | } 28 | 29 | /** 30 | * 设置首页的list的marginBottom,最后一项需要加个很大的marginBottom 31 | * @param marginBottom 设置0~最后一项的marginBottom 32 | * @param lastItemMarginBottom 设置最后一项的marginBottom 33 | */ 34 | fun RecyclerView.setHomePartyMarginBottom(marginBottom: Int, lastItemMarginBottom: Int) { 35 | this.addItemDecoration(object : RecyclerView.ItemDecoration() { // 列表垂直间距 36 | override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { 37 | val itemCount = parent.adapter?.itemCount ?: 0 38 | if (itemCount == 0) { 39 | outRect.bottom = 0 40 | } else { 41 | val lastItemPosition = itemCount - 1 42 | when (parent.getChildAdapterPosition(view)) { 43 | lastItemPosition -> outRect.bottom = lastItemMarginBottom 44 | else -> outRect.bottom = marginBottom 45 | } 46 | } 47 | } 48 | }) 49 | } 50 | 51 | /** 52 | * 打开默认局部刷新动画 53 | */ 54 | fun RecyclerView.openDefaultAnimator() { 55 | this.itemAnimator?.addDuration = 120 56 | this.itemAnimator?.changeDuration = 250 57 | this.itemAnimator?.moveDuration = 250 58 | this.itemAnimator?.removeDuration = 120 59 | (this.itemAnimator as? SimpleItemAnimator)?.supportsChangeAnimations = true 60 | } 61 | 62 | /** 63 | * 关闭默认局部刷新动画 64 | */ 65 | fun RecyclerView.closeDefaultAnimator() { 66 | this.itemAnimator?.addDuration = 0 67 | this.itemAnimator?.changeDuration = 0 68 | this.itemAnimator?.moveDuration = 0 69 | this.itemAnimator?.removeDuration = 0 70 | (this.itemAnimator as? SimpleItemAnimator)?.supportsChangeAnimations = false 71 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/extensions/StringExtension.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.extensions 2 | 3 | import java.nio.charset.Charset 4 | import java.util.regex.Matcher 5 | import java.util.regex.Pattern 6 | 7 | fun String.toUtf8Bytes():ByteArray{ 8 | return this.toByteArray(Charset.forName("UTF-8")) 9 | } 10 | 11 | fun String.toSBC(): String { 12 | val c = this.toCharArray() 13 | for (i in c.indices) { 14 | if (c[i] == ' ') { 15 | c[i] = '\u3000' 16 | } else if (c[i] < '\u007f') { 17 | c[i] = (c[i] + 65248) 18 | } 19 | } 20 | return String(c) 21 | } 22 | 23 | inline fun String.replaceBlank(): String { 24 | val p: Pattern = Pattern.compile("\\s*|\t|\r|\n") 25 | val m: Matcher = p.matcher(this) 26 | return m.replaceAll("") 27 | } 28 | 29 | infix fun Int.toList(x:Int): Array { 30 | var array = mutableListOf() 31 | for (index in this..x){ 32 | array = (array + index) as MutableList 33 | } 34 | return array.toTypedArray() 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/extensions/ViewExtension.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.extensions 2 | 3 | import android.content.Context 4 | import android.util.DisplayMetrics 5 | import android.view.View 6 | import androidx.annotation.DimenRes 7 | import androidx.viewbinding.ViewBinding 8 | 9 | inline fun View.visible(){ 10 | this.visibility = View.VISIBLE 11 | } 12 | 13 | inline fun View.invisible(){ 14 | this.visibility = View.INVISIBLE 15 | } 16 | 17 | inline fun View.gone(){ 18 | this.visibility = View.GONE 19 | } 20 | 21 | const val LDPI: Int = DisplayMetrics.DENSITY_LOW 22 | const val MDPI: Int = DisplayMetrics.DENSITY_MEDIUM 23 | const val HDPI: Int = DisplayMetrics.DENSITY_HIGH 24 | 25 | const val TVDPI: Int = DisplayMetrics.DENSITY_TV 26 | const val XHDPI: Int = DisplayMetrics.DENSITY_XHIGH 27 | const val XXHDPI: Int = DisplayMetrics.DENSITY_XXHIGH 28 | const val XXXHDPI: Int = DisplayMetrics.DENSITY_XXXHIGH 29 | 30 | const val MAXDPI: Int = 0xfffe 31 | 32 | //returns dip(dp) dimension value in pixels 33 | fun Context.dip(value: Int): Int = (value * resources.displayMetrics.density).toInt() 34 | fun Context.dip(value: Float): Int = (value * resources.displayMetrics.density).toInt() 35 | 36 | //return sp dimension value in pixels 37 | fun Context.sp(value: Int): Int = (value * resources.displayMetrics.scaledDensity).toInt() 38 | fun Context.sp(value: Float): Int = (value * resources.displayMetrics.scaledDensity).toInt() 39 | 40 | //converts px value into dip or sp 41 | fun Context.px2dip(px: Int): Float = px.toFloat() / resources.displayMetrics.density 42 | fun Context.px2sp(px: Int): Float = px.toFloat() / resources.displayMetrics.scaledDensity 43 | 44 | fun Context.dimen(@DimenRes resource: Int): Int = resources.getDimensionPixelSize(resource) 45 | 46 | //the same for the views 47 | inline fun View.dip(value: Int): Int = context.dip(value) 48 | inline fun View.dip(value: Float): Int = context.dip(value) 49 | inline fun View.sp(value: Int): Int = context.sp(value) 50 | inline fun View.sp(value: Float): Int = context.sp(value) 51 | inline fun View.px2dip(px: Int): Float = context.px2dip(px) 52 | inline fun View.px2sp(px: Int): Float = context.px2sp(px) 53 | inline fun View.dimen(@DimenRes resource: Int): Int = context.dimen(resource) 54 | 55 | 56 | 57 | inline fun ViewBinding.dip(value: Int): Int = root.dip(value) 58 | inline fun ViewBinding.dip(value: Float): Int = root.dip(value) 59 | inline fun ViewBinding.sp(value: Int): Int = root.sp(value) 60 | inline fun ViewBinding.sp(value: Float): Int = root.sp(value) 61 | inline fun ViewBinding.px2dip(px: Int): Float = root.px2dip(px) 62 | inline fun ViewBinding.px2sp(px: Int): Float = root.px2sp(px) 63 | inline fun ViewBinding.dimen(@DimenRes resource: Int): Int = root.dimen(resource) -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/interf/BindingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.interf 2 | 3 | import android.view.View 4 | import androidx.databinding.BindingAdapter 5 | import com.carman.kotlin.coroutine.interf.DoubleClickHelper 6 | 7 | 8 | @BindingAdapter("bindOnClick") 9 | fun bindOnClick(view: View?, listener: View.OnClickListener?) { 10 | DoubleClickHelper.click(view, listener) 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/interf/CheckedChangedListener.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.interf 2 | 3 | import android.widget.CompoundButton 4 | 5 | interface CheckedChangedListener { 6 | fun onCheckedChanged(buttonView: CompoundButton, isChecked:Boolean,data: T) 7 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/interf/DoubleClickHelper.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.interf 2 | 3 | import android.view.View 4 | //import io.reactivex.Observable 5 | //import io.reactivex.ObservableEmitter 6 | //import io.reactivex.ObservableOnSubscribe 7 | //import java.util.concurrent.TimeUnit 8 | 9 | object DoubleClickHelper { 10 | private const val windowDuration = 1 11 | @JvmOverloads 12 | fun click(view: View?, r: Runnable?, durationSeconds: Int = windowDuration) { 13 | if (view == null || r == null) { 14 | return 15 | } 16 | // Observable.create(ClickObservable(view)) 17 | // .throttleFirst(durationSeconds.toLong(), TimeUnit.SECONDS) 18 | // .subscribe { o: View? -> r.run() } 19 | } 20 | 21 | @JvmOverloads 22 | fun click(view: View?, listener: View.OnClickListener?, durationSeconds: Int = windowDuration) { 23 | if (view == null || listener == null) { 24 | return 25 | } 26 | // Observable.create(ClickObservable(view)) 27 | // .throttleFirst(durationSeconds.toLong(), TimeUnit.SECONDS) 28 | // .subscribe { o: View? -> listener.onClick(view) } 29 | } 30 | } 31 | 32 | //internal class ClickObservable(view: View) : ObservableOnSubscribe { 33 | // private var mEmitter: ObservableEmitter? = null 34 | // @Throws(Exception::class) 35 | // override fun subscribe(e: ObservableEmitter) { 36 | // mEmitter = e 37 | // } 38 | // 39 | // init { 40 | // view.setOnClickListener { view -> 41 | // if (mEmitter != null) { 42 | // mEmitter!!.onNext(view) 43 | // } 44 | // } 45 | // } 46 | //} -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/interf/ItemClickListener.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.interf 2 | 3 | import android.view.View 4 | 5 | interface ItemClickListener { 6 | fun onItemClick(view: View,position:Int, data: T){} 7 | fun onItemClick(view: View, data: T){} 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/remote/CResponse.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.remote 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class CResponse( 6 | @SerializedName("showapi_res_code") 7 | val code: Int, 8 | @SerializedName("showapi_res_error") 9 | val msg: String? = null, 10 | @SerializedName("showapi_res_body") 11 | val data: T 12 | ) 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/remote/CommonInterceptor.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.remote 2 | 3 | import android.os.Build 4 | import com.carman.kotlin.coroutine.constant.HttpConstant 5 | import com.carman.kotlin.coroutine.constant.HttpConstant.SHOW_API_APPID 6 | import com.carman.kotlin.coroutine.constant.HttpConstant.SHOW_API_SIGN 7 | import okhttp3.Interceptor 8 | import okhttp3.Response 9 | import java.io.IOException 10 | 11 | 12 | class CommonInterceptor : Interceptor { 13 | 14 | @Throws(IOException::class) 15 | override fun intercept(chain: Interceptor.Chain): Response { 16 | val oldRequest = chain.request() 17 | val httpUrl = oldRequest.url 18 | val host = httpUrl.host 19 | if (HttpConstant.SERVER_HOST != host ) { 20 | return chain.proceed(oldRequest) 21 | } 22 | val urlBuilder = httpUrl.newBuilder() 23 | urlBuilder.addQueryParameter("showapi_appid", SHOW_API_APPID) 24 | urlBuilder.addQueryParameter("showapi_sign", SHOW_API_SIGN) 25 | 26 | val request = oldRequest 27 | .newBuilder() 28 | .url(urlBuilder.build()) 29 | .build() 30 | return chain.proceed(request) 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/remote/CoroutineService.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.remote 2 | 3 | import com.carman.kotlin.coroutine.bean.SysConfig 4 | import com.carman.kotlin.coroutine.bean.Weather 5 | import retrofit2.http.* 6 | 7 | interface CoroutineService { 8 | 9 | @GET("/9-2") 10 | suspend fun getWeather(@Query("area") area: String): CResponse 11 | 12 | 13 | @FormUrlEncoded 14 | @POST("/9-2") 15 | suspend fun postWeather( 16 | @Field("area") area: String 17 | ): CResponse 18 | 19 | 20 | /** 21 | * 获取系统全局配置 22 | */ 23 | @GET("sys/config") 24 | suspend fun getSysConfig(): CResponse 25 | 26 | @FormUrlEncoded 27 | @POST("/user/push/reg") 28 | suspend fun registerPushId( 29 | @Field("plat") plat: String, 30 | @Field("identifier") identifier: String 31 | ): CResponse 32 | 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/remote/ErrorInterceptor.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.remote 2 | 3 | import com.carman.kotlin.coroutine.constant.HttpConstant 4 | import okhttp3.Interceptor 5 | import okhttp3.Response 6 | import okio.Buffer 7 | import okio.GzipSource 8 | import org.json.JSONObject 9 | import java.io.IOException 10 | 11 | class ErrorInterceptor : Interceptor { 12 | 13 | @Throws(IOException::class) 14 | override fun intercept(chain: Interceptor.Chain): Response { 15 | val response = chain.proceed(chain.request()) 16 | val oldRequest = chain.request() 17 | val host = oldRequest.url.host 18 | if (HttpConstant.HTTP_SERVER != host) { 19 | return response 20 | } 21 | 22 | if (response.isSuccessful) { 23 | val headers = response.headers 24 | val responseStr = response.body?.let { 25 | val source = it.source() 26 | source.request(java.lang.Long.MAX_VALUE) // Buffer the entire body. 27 | var buffer = source.buffer 28 | 29 | if ("gzip".equals(headers.get("Content-Encoding"), ignoreCase = true)) { 30 | GzipSource(buffer.clone()).use { gzippedResponseBody -> 31 | buffer = Buffer() 32 | buffer.writeAll(gzippedResponseBody) 33 | } 34 | } 35 | buffer.clone().readString(Charsets.UTF_8) 36 | } 37 | if (!responseStr.isNullOrEmpty()) { 38 | val responseJson = try { 39 | JSONObject(responseStr) 40 | } catch (e: Exception) { 41 | return response 42 | } 43 | when (responseJson.getInt("code")) { 44 | HttpConstant.TOKEN_EXPIRED -> { 45 | // token过期,刷新token 46 | return response 47 | } 48 | HttpConstant.INVALID_TOKEN -> { 49 | // token 错误,登出账户 50 | return response 51 | } 52 | } 53 | } 54 | } 55 | return response 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/remote/OkHttpClientManager.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.remote 2 | 3 | import com.carman.kotlin.coroutine.BuildConfig 4 | import okhttp3.OkHttpClient 5 | import okhttp3.logging.HttpLoggingInterceptor 6 | 7 | object OkHttpClientManager { 8 | 9 | val mClient: OkHttpClient by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { 10 | buildClient() 11 | } 12 | 13 | private fun buildClient(): OkHttpClient { 14 | val logging = HttpLoggingInterceptor() 15 | logging.level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE 16 | return OkHttpClient.Builder().apply { 17 | addInterceptor(CommonInterceptor()) 18 | addInterceptor(logging) 19 | // addInterceptor(ErrorInterceptor()) 20 | followSslRedirects(true) 21 | }.build() 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/remote/ServerApi.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.remote 2 | 3 | import com.carman.kotlin.coroutine.constant.HttpConstant 4 | import retrofit2.Retrofit 5 | import retrofit2.converter.gson.GsonConverterFactory 6 | import retrofit2.converter.scalars.ScalarsConverterFactory 7 | 8 | object ServerApi { 9 | val service: CoroutineService by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { 10 | build() 11 | } 12 | private fun build():CoroutineService{ 13 | val retrofit = Retrofit.Builder().apply { 14 | baseUrl(HttpConstant.HTTP_SERVER) 15 | client(OkHttpClientManager.mClient) 16 | addConverterFactory(ScalarsConverterFactory.create()) 17 | addConverterFactory(GsonConverterFactory.create()) 18 | }.build() 19 | return retrofit.create(CoroutineService::class.java) 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/request/ModelFactory.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.request 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.ViewModelProvider 5 | import com.carman.kotlin.coroutine.request.repository.MainRepository 6 | import com.carman.kotlin.coroutine.request.viewmodel.MainViewModel 7 | import javax.inject.Inject 8 | 9 | 10 | fun provideMainViewModelFactory( 11 | ): MainViewModelFactory { 12 | return MainViewModelFactory(MainRepository()) 13 | } 14 | 15 | class MainViewModelFactory @Inject constructor( 16 | private val repository: MainRepository 17 | ) : ViewModelProvider.Factory { 18 | 19 | @Suppress("UNCHECKED_CAST") 20 | override fun create(modelClass: Class): T { 21 | return MainViewModel(repository) as T 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/request/ViewModelUtils.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.request 2 | 3 | import androidx.activity.ComponentActivity 4 | import androidx.activity.viewModels 5 | import androidx.fragment.app.Fragment 6 | import androidx.lifecycle.ViewModel 7 | import androidx.lifecycle.ViewModelProvider 8 | import com.carman.kotlin.coroutine.extensions.createViewModel 9 | import com.carman.kotlin.coroutine.request.repository.MainRepository 10 | import com.carman.kotlin.coroutine.request.viewmodel.MainViewModel 11 | import java.lang.NullPointerException 12 | import java.lang.RuntimeException 13 | import java.lang.reflect.ParameterizedType 14 | 15 | object ViewModelUtils { 16 | 17 | fun createViewModel( 18 | activity: ComponentActivity, 19 | factory: ViewModelProvider.Factory? = null, 20 | position: Int 21 | ): VM { 22 | val vbClass = 23 | (activity.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments.filterIsInstance>() 24 | val viewModel = vbClass[position] as Class 25 | return factory?.let { 26 | ViewModelProvider( 27 | activity, 28 | factory 29 | ).get(viewModel) 30 | } ?: let { 31 | ViewModelProvider(activity).get(viewModel) 32 | } 33 | } 34 | 35 | fun createViewModel( 36 | fragment: Fragment, 37 | factory: ViewModelProvider.Factory? = null, 38 | position: Int 39 | ): VM { 40 | val vbClass = 41 | (fragment.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments.filterIsInstance>() 42 | val viewModel = vbClass[position] as Class 43 | return factory?.let { 44 | ViewModelProvider( 45 | fragment, 46 | factory 47 | ).get(viewModel) 48 | } ?: let { 49 | ViewModelProvider(fragment).get(viewModel) 50 | } 51 | } 52 | 53 | /** 54 | * 直接创建 55 | */ 56 | fun createActivityViewModel( 57 | fragment: Fragment, 58 | factory: ViewModelProvider.Factory? = null, 59 | position: Int 60 | ): VM { 61 | val vbClass = 62 | (fragment.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments.filterIsInstance>() 63 | val viewModel = vbClass[position] as Class 64 | return factory?.let { 65 | ViewModelProvider( 66 | fragment.requireActivity(), 67 | factory 68 | ).get(viewModel) 69 | } ?: let { 70 | ViewModelProvider(fragment.requireActivity()).get(viewModel) 71 | } 72 | } 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/request/repository/BaseRepository.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.request.repository 2 | 3 | import com.carman.kotlin.coroutine.constant.HttpConstant 4 | import com.carman.kotlin.coroutine.remote.CResponse 5 | 6 | open class BaseRepository { 7 | suspend fun handResponse(response: CResponse, onSuccess: suspend () -> Unit, errorBlock:suspend () -> Unit){ 8 | when{ 9 | response == null -> errorBlock() 10 | response.code == HttpConstant.OK -> onSuccess() 11 | else -> errorBlock() 12 | } 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/request/repository/MainRepository.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.request.repository 2 | 3 | import com.carman.kotlin.coroutine.bean.Weather 4 | import com.carman.kotlin.coroutine.remote.CResponse 5 | import com.carman.kotlin.coroutine.remote.ServerApi 6 | import javax.inject.Inject 7 | 8 | class MainRepository @Inject constructor():BaseRepository(){ 9 | 10 | suspend fun getWeather( 11 | area: String 12 | ): CResponse{ 13 | return ServerApi.service.getWeather(area) 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/request/viewmodel/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.request.viewmodel 2 | 3 | import android.view.View 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.SavedStateHandle 7 | import androidx.lifecycle.ViewModel 8 | import com.carman.kotlin.coroutine.R 9 | import com.carman.kotlin.coroutine.bean.User 10 | import com.carman.kotlin.coroutine.bean.Weather 11 | import com.carman.kotlin.coroutine.extensions.requestMain 12 | import com.carman.kotlin.coroutine.request.repository.MainRepository 13 | import dagger.assisted.Assisted 14 | import dagger.hilt.android.lifecycle.HiltViewModel 15 | import javax.inject.Inject 16 | 17 | @HiltViewModel 18 | class MainViewModel @Inject constructor( 19 | private val repository: MainRepository 20 | ):ViewModel() { 21 | private val _user: MutableLiveData = MutableLiveData(User(1,"测试")) 22 | val mUser: LiveData = _user 23 | 24 | 25 | private val _weather:MutableLiveData = MutableLiveData() 26 | val mWeather: LiveData = _weather 27 | 28 | fun getWeather( area: String){ 29 | requestMain { 30 | val result = try { 31 | repository.getWeather(area) 32 | } catch (e: Exception) { 33 | e.printStackTrace() 34 | null 35 | } 36 | _weather.postValue(result?.data) 37 | } 38 | } 39 | 40 | fun onClick(v:View){ 41 | when (v.id){ 42 | R.id.request_tbn ->{ 43 | getWeather("深圳") 44 | } 45 | } 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/request/viewmodel/TestViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.request.viewmodel 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import com.carman.kotlin.coroutine.bean.User 7 | 8 | class TestViewModel: ViewModel() { 9 | 10 | private val _user: MutableLiveData = MutableLiveData(User(1,"测试")) 11 | val mUser: LiveData = _user 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/ui/HomeFragment.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.ui 2 | 3 | import android.os.Bundle 4 | import android.util.Log 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.Toast 9 | import androidx.fragment.app.Fragment 10 | import androidx.fragment.app.activityViewModels 11 | import androidx.fragment.app.viewModels 12 | import androidx.lifecycle.lifecycleScope 13 | import com.carman.kotlin.coroutine.R 14 | import com.carman.kotlin.coroutine.base.BaseFragment 15 | import com.carman.kotlin.coroutine.databinding.FragmentMainBinding 16 | import com.carman.kotlin.coroutine.extensions.delayMain 17 | import com.carman.kotlin.coroutine.extensions.requestIO 18 | import com.carman.kotlin.coroutine.extensions.requestMain 19 | import com.carman.kotlin.coroutine.request.viewmodel.MainViewModel 20 | 21 | class HomeFragment:BaseFragment() { 22 | override fun FragmentMainBinding.initBinding() { 23 | 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/ui/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.ui 2 | 3 | import android.app.Dialog 4 | import android.os.Bundle 5 | import android.util.Log 6 | import android.view.View 7 | import android.widget.LinearLayout 8 | import android.widget.Toast 9 | import androidx.activity.viewModels 10 | import androidx.appcompat.app.AppCompatActivity 11 | import androidx.databinding.DataBindingUtil 12 | import androidx.lifecycle.LifecycleOwner 13 | import androidx.lifecycle.Observer 14 | import androidx.lifecycle.ViewModel 15 | import androidx.lifecycle.ViewModelProvider 16 | import androidx.lifecycle.ViewModelProviders 17 | import androidx.lifecycle.lifecycleScope 18 | import androidx.recyclerview.widget.LinearLayoutManager 19 | import androidx.recyclerview.widget.RecyclerView 20 | import com.carman.kotlin.coroutine.R 21 | import com.carman.kotlin.coroutine.base.BaseActivity 22 | import com.carman.kotlin.coroutine.bean.Person 23 | import com.carman.kotlin.coroutine.bean.Student 24 | import com.carman.kotlin.coroutine.bean.Teacher 25 | import com.carman.kotlin.coroutine.databinding.ActivityMainBinding 26 | import com.carman.kotlin.coroutine.extensions.* 27 | import com.carman.kotlin.coroutine.interf.ItemClickListener 28 | import com.carman.kotlin.coroutine.request.ViewModelUtils 29 | import com.carman.kotlin.coroutine.request.provideMainViewModelFactory 30 | import com.carman.kotlin.coroutine.request.repository.MainRepository 31 | import com.carman.kotlin.coroutine.request.viewmodel.MainViewModel 32 | import com.carman.kotlin.coroutine.request.viewmodel.TestViewModel 33 | import com.carman.kotlin.coroutine.ui.adapter.HomeAdapter 34 | import com.carman.kotlin.coroutine.ui.adapter.SecondAdapter 35 | import dagger.hilt.android.AndroidEntryPoint 36 | import kotlinx.coroutines.* 37 | 38 | /** 39 | * 项目实战主入口 40 | * @author carman 41 | * @time 2021-4-26 12:11 42 | */ 43 | class MainActivity : BaseActivity(provideMainViewModelFactory()) { 44 | 45 | val mainViewModel:MainViewModel by viewModels(){ 46 | provideMainViewModelFactory() 47 | } 48 | override fun initObserve() { 49 | viewModel.mUser.observe(this){ 50 | Log.d("MainViewModel","user: $it") 51 | } 52 | // viewModel.getWeather("深圳") 53 | // viewModel.mWeather.observe(this){ 54 | // mBinding.contentTv.text ="$it" 55 | // } 56 | } 57 | 58 | override fun ActivityMainBinding.initBinding() { 59 | // this.mainViewModel = viewModel 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/ui/MainActivity04.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.ui 2 | 3 | import android.app.Dialog 4 | import android.os.Bundle 5 | import android.util.Log 6 | import android.view.View 7 | import android.widget.LinearLayout 8 | import android.widget.Toast 9 | import androidx.activity.viewModels 10 | import androidx.appcompat.app.AppCompatActivity 11 | import androidx.databinding.DataBindingUtil 12 | import androidx.lifecycle.LifecycleOwner 13 | import androidx.lifecycle.Observer 14 | import androidx.lifecycle.ViewModel 15 | import androidx.lifecycle.ViewModelProvider 16 | import androidx.lifecycle.lifecycleScope 17 | import androidx.recyclerview.widget.LinearLayoutManager 18 | import androidx.recyclerview.widget.RecyclerView 19 | import com.carman.kotlin.coroutine.R 20 | import com.carman.kotlin.coroutine.base.BaseActivity 21 | import com.carman.kotlin.coroutine.base.BaseVBActivity 22 | import com.carman.kotlin.coroutine.bean.Person 23 | import com.carman.kotlin.coroutine.bean.Student 24 | import com.carman.kotlin.coroutine.bean.Teacher 25 | import com.carman.kotlin.coroutine.databinding.ActivityMain04Binding 26 | import com.carman.kotlin.coroutine.databinding.ActivityMainBinding 27 | import com.carman.kotlin.coroutine.extensions.* 28 | import com.carman.kotlin.coroutine.interf.ItemClickListener 29 | import com.carman.kotlin.coroutine.request.repository.MainRepository 30 | import com.carman.kotlin.coroutine.request.viewmodel.MainViewModel 31 | import com.carman.kotlin.coroutine.ui.adapter.HomeAdapter 32 | import com.carman.kotlin.coroutine.ui.adapter.SecondAdapter 33 | import kotlinx.coroutines.* 34 | 35 | /** 36 | * 第四章节案例 37 | */ 38 | class MainActivity04 : BaseVBActivity() { 39 | 40 | override fun initObserve() { 41 | 42 | } 43 | lateinit var homeAdapter:HomeAdapter 44 | override fun ActivityMain04Binding.initBinding() { 45 | // homeAdapter = HomeAdapter(itemClickListener) 46 | // with(recyclerView){ 47 | // layoutManager = LinearLayoutManager(this@MainActivity).apply { 48 | // orientation = RecyclerView.VERTICAL 49 | // } 50 | // adapter = homeAdapter 51 | // } 52 | // homeAdapter.setData(listOf("a","b","c","d","e","f")) 53 | // btn.setOnClickListener { 54 | // homeAdapter.setData(listOf("c","d","e","f")) 55 | // } 56 | 57 | val secondAdapter = SecondAdapter() 58 | with(recyclerView){ 59 | layoutManager = LinearLayoutManager(this@MainActivity04).apply { 60 | orientation = RecyclerView.VERTICAL 61 | } 62 | adapter = secondAdapter 63 | } 64 | secondAdapter.setData( 65 | listOf( 66 | Teacher(1,"Person","语文"), 67 | Student(2,"Person","一年级"), 68 | Teacher(3,"Person","数学"), 69 | )) 70 | } 71 | 72 | 73 | private val itemClickListener = object : ItemClickListener { 74 | override fun onItemClick(view: View, position: Int, data: String) { 75 | Log.d("onItemClick", "data:$data position:${homeAdapter.getActualPosition(data)}") 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/ui/MainHiltActivity.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.ui 2 | 3 | import android.util.Log 4 | import androidx.activity.viewModels 5 | import com.carman.kotlin.coroutine.base.BaseVBActivity 6 | import com.carman.kotlin.coroutine.databinding.ActivityMainBinding 7 | import com.carman.kotlin.coroutine.request.viewmodel.MainViewModel 8 | import dagger.hilt.android.AndroidEntryPoint 9 | 10 | /** 11 | * 项目实战主入口 12 | * @author carman 13 | * @time 2021-4-26 12:11 14 | */ 15 | @AndroidEntryPoint 16 | class MainHiltActivity : BaseVBActivity(){ 17 | val viewModel:MainViewModel by viewModels() 18 | 19 | override fun initObserve() { 20 | viewModel.mUser.observe(this){ 21 | Log.d("MainViewModel","user: $it") //MainViewModel: user: User(id=1, name=测试) 22 | } 23 | } 24 | 25 | override fun ActivityMainBinding.initBinding() { 26 | this.mainViewModel = viewModel 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/ui/MainTestActivity.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.ui 2 | 3 | import android.os.Bundle 4 | import android.util.Log 5 | import android.widget.Button 6 | import androidx.appcompat.app.AppCompatActivity 7 | import androidx.lifecycle.lifecycleScope 8 | import com.carman.kotlin.coroutine.R 9 | import kotlinx.coroutines.* 10 | import kotlin.coroutines.ContinuationInterceptor 11 | import kotlin.coroutines.CoroutineContext 12 | 13 | /** 14 | * 协程基础演示实验类 15 | * @author carman 16 | * @time 2021-4-10 16:11 17 | */ 18 | class MainTestActivity : AppCompatActivity() { 19 | 20 | init { 21 | lifecycleScope.launchWhenResumed { 22 | Log.d("init", "在类初始化位置启动协程") 23 | } 24 | } 25 | 26 | companion object Key : CoroutineContext.Key 27 | 28 | private lateinit var btn: Button 29 | private val mainScope = MainScope() 30 | 31 | 32 | override fun onCreate(savedInstanceState: Bundle?) { 33 | super.onCreate(savedInstanceState) 34 | setContentView(R.layout.activity_main_test) 35 | btn = findViewById(R.id.btn) 36 | 37 | btn.setOnClickListener { 38 | start() 39 | // testCoroutineContext() 40 | // testCoroutineStart() 41 | // testUnDispatched() 42 | // testCoroutineScope() 43 | // testCoroutineScope2() 44 | // testCoroutineScope3() 45 | // testCoroutineScope4() 46 | // testException() 47 | } 48 | } 49 | 50 | override fun onResume() { 51 | super.onResume() 52 | Log.d("onResume", "onResume") 53 | } 54 | 55 | override fun onDestroy() { 56 | super.onDestroy() 57 | job?.cancel() 58 | } 59 | private var job:Job?= null 60 | 61 | private fun start() { 62 | mainScope.launch { 63 | launch { 64 | throw NullPointerException("空指针") 65 | } 66 | val result = withContext(Dispatchers.IO) { 67 | //网络请求... 68 | "请求结果" 69 | } 70 | launch { 71 | //网络请求3... 72 | } 73 | btn.text = result 74 | } 75 | 76 | 77 | // job = GlobalScope.launch(Dispatchers.Main + SupervisorJob(),CoroutineStart.UNDISPATCHED) { 78 | // launch { 79 | //// throw NullPointerException("空指针") 80 | // } 81 | // val result = withContext(Dispatchers.IO) { 82 | // //网络请求... 83 | // "请求结果" 84 | // } 85 | // launch { 86 | // //网络请求3... 87 | // } 88 | // btn.text = result 89 | // } 90 | 91 | 92 | /* GlobalScope.launch(Dispatchers.Main) { 93 | for (index in 1 until 10) { 94 | //同步执行 95 | launch { 96 | Log.d("launch$index", "启动一个协程") 97 | } 98 | } 99 | }*/ 100 | 101 | // GlobalScope.launch { 102 | // for (index in 1 until 10) { 103 | // //并发执行 104 | // launch { 105 | // Log.d("launch$index", "启动一个协程") 106 | // } 107 | // } 108 | // } 109 | 110 | 111 | /*GlobalScope.launch{ 112 | val launchJob = launch{ 113 | Log.d("launch", "启动一个协程") 114 | } 115 | Log.d("launchJob", "$launchJob") 116 | val asyncJob = async{ 117 | Log.d("async", "启动一个协程") 118 | "我是async返回值" 119 | } 120 | Log.d("asyncJob.await", ":${asyncJob.await()}") 121 | Log.d("asyncJob", "$asyncJob") 122 | }*/ 123 | 124 | /* val runBlockingJob = runBlocking { 125 | Log.d("runBlocking", "启动一个协程") 126 | } 127 | Log.d("runBlockingJob", "$runBlockingJob") 128 | val launchJob = GlobalScope.launch{ 129 | Log.d("launch", "启动一个协程") 130 | } 131 | Log.d("launchJob", "$launchJob") 132 | val asyncJob = GlobalScope.async{ 133 | Log.d("async", "启动一个协程") 134 | "我是返回值" 135 | } 136 | Log.d("asyncJob", "$asyncJob")*/ 137 | 138 | 139 | // runBlocking { 140 | // Log.d("runBlocking", "启动一个协程") 141 | // } 142 | // GlobalScope.launch{ 143 | // Log.d("launch", "启动一个协程") 144 | // } 145 | // GlobalScope.async{ 146 | // Log.d("async", "启动一个协程") 147 | // } 148 | } 149 | 150 | private fun testCoroutineContext() { 151 | val coroutineContext1 = Job() + CoroutineName("这是第一个上下文") 152 | Log.d("coroutineContext1", "$coroutineContext1") 153 | val coroutineContext2 = coroutineContext1 + Dispatchers.Default + CoroutineName("这是第二个上下文") 154 | Log.d("coroutineContext2", "$coroutineContext2") 155 | val coroutineContext3 = coroutineContext2 + Dispatchers.Main + CoroutineName("这是第三个上下文") 156 | Log.d("coroutineContext3", "$coroutineContext3") 157 | } 158 | 159 | private fun testCoroutineStart() { 160 | val defaultJob = GlobalScope.launch { 161 | Log.d("defaultJob", "CoroutineStart.DEFAULT") 162 | } 163 | defaultJob.cancel() 164 | 165 | val lazyJob = GlobalScope.launch(start = CoroutineStart.LAZY) { 166 | Log.d("lazyJob", "CoroutineStart.LAZY") 167 | } 168 | 169 | val atomicJob = GlobalScope.launch(start = CoroutineStart.ATOMIC) { 170 | Log.d("atomicJob", "CoroutineStart.ATOMIC挂起前") 171 | delay(100) 172 | Log.d("atomicJob", "CoroutineStart.ATOMIC挂起后") 173 | } 174 | 175 | atomicJob.cancel() 176 | 177 | val undispatchedJob = GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) { 178 | Log.d("undispatchedJob", "CoroutineStart.UNDISPATCHED挂起前") 179 | delay(100) 180 | Log.d("atomicJob", "CoroutineStart.UNDISPATCHED挂起后") 181 | } 182 | undispatchedJob.cancel() 183 | } 184 | 185 | private fun testUnDispatched() { 186 | GlobalScope.launch(Dispatchers.Main) { 187 | val job = launch(start = CoroutineStart.UNDISPATCHED) { 188 | Log.d("${Thread.currentThread().name}线程", "-> 挂起前") 189 | delay(100) 190 | Log.d("${Thread.currentThread().name}线程", "-> 挂起后") 191 | } 192 | Log.d("${Thread.currentThread().name}线程", "-> join前") 193 | job.join() 194 | Log.d("${Thread.currentThread().name}线程", "-> join后") 195 | } 196 | } 197 | 198 | private fun testCoroutineScope() { 199 | GlobalScope.launch(Dispatchers.Main) { 200 | Log.d("父协程上下文", "$coroutineContext") 201 | launch(CoroutineName("第一个子协程")) { 202 | Log.d("第一个子协程上下文", "$coroutineContext") 203 | } 204 | launch(Dispatchers.Unconfined) { 205 | Log.d("第二个子协程协程上下文", "$coroutineContext") 206 | } 207 | } 208 | } 209 | 210 | 211 | private fun testCoroutineScope2() { 212 | val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> 213 | Log.d("exceptionHandler", "${coroutineContext[CoroutineName]} $throwable") 214 | } 215 | GlobalScope.launch(Dispatchers.Main + CoroutineName("scope1") + exceptionHandler) { 216 | Log.d("scope", "--------- 1") 217 | launch(CoroutineName("scope2") + exceptionHandler) { 218 | Log.d("scope", "--------- 2") 219 | throw NullPointerException("空指针") 220 | Log.d("scope", "--------- 3") 221 | } 222 | val scope3 = launch(CoroutineName("scope3") + exceptionHandler) { 223 | Log.d("scope", "--------- 4") 224 | delay(2000) 225 | Log.d("scope", "--------- 5") 226 | } 227 | scope3.join() 228 | Log.d("scope", "--------- 6") 229 | } 230 | } 231 | 232 | 233 | private fun testCoroutineScope3() { 234 | val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> 235 | Log.d("exceptionHandler", "${coroutineContext[CoroutineName]} $throwable") 236 | } 237 | GlobalScope.launch(Dispatchers.Main + CoroutineName("scope1") + exceptionHandler) { 238 | supervisorScope { 239 | Log.d("scope", "--------- 1") 240 | launch(CoroutineName("scope2")) { 241 | Log.d("scope", "--------- 2") 242 | throw NullPointerException("空指针") 243 | Log.d("scope", "--------- 3") 244 | val scope3 = launch(CoroutineName("scope3")) { 245 | Log.d("scope", "--------- 4") 246 | delay(2000) 247 | Log.d("scope", "--------- 5") 248 | } 249 | scope3.join() 250 | } 251 | val scope4 = launch(CoroutineName("scope4")) { 252 | Log.d("scope", "--------- 6") 253 | delay(2000) 254 | Log.d("scope", "--------- 7") 255 | } 256 | scope4.join() 257 | Log.d("scope", "--------- 8") 258 | } 259 | } 260 | } 261 | 262 | private fun testCoroutineScope4() { 263 | val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> 264 | Log.d("exceptionHandler", "${coroutineContext[CoroutineName]} $throwable") 265 | } 266 | val coroutineScope = CoroutineScope(SupervisorJob() + CoroutineName("coroutineScope")) 267 | GlobalScope.launch(Dispatchers.Main + CoroutineName("scope1") + exceptionHandler) { 268 | with(coroutineScope){ 269 | val scope2 = launch(CoroutineName("scope2") + exceptionHandler) { 270 | Log.d("scope", "1--------- ${coroutineContext[CoroutineName]}") 271 | throw NullPointerException("空指针") 272 | } 273 | val scope3 = launch(CoroutineName("scope3") + exceptionHandler) { 274 | scope2.join() 275 | Log.d("scope", "2--------- ${coroutineContext[CoroutineName]}") 276 | delay(2000) 277 | Log.d("scope", "3--------- ${coroutineContext[CoroutineName]}") 278 | } 279 | scope2.join() 280 | Log.d("scope", "4--------- ${coroutineContext[CoroutineName]}") 281 | coroutineScope.cancel() 282 | scope3.join() 283 | Log.d("scope", "5---------${coroutineContext[CoroutineName]}") 284 | } 285 | Log.d("scope", "6--------- ${coroutineContext[CoroutineName]}") 286 | } 287 | } 288 | 289 | private suspend fun test(){ 290 | 291 | } 292 | 293 | private fun testException(){ 294 | // var a:MutableList = mutableListOf(1,2,3) 295 | // GlobalScope.launch{ 296 | // launch { 297 | // Log.d("${Thread.currentThread().name}","我要开始抛异常了" ) 298 | // try { 299 | // launch{ 300 | // Log.d("${Thread.currentThread().name}", "${a[1]}") 301 | // } 302 | // a.clear() 303 | // } catch (e: Exception) { 304 | // e.printStackTrace() 305 | // } 306 | // 307 | // } 308 | // Log.d("${Thread.currentThread().name}", "end") 309 | // } 310 | 311 | // val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> 312 | // Log.d("exceptionHandler", "${coroutineContext[CoroutineName]} :$throwable") 313 | // } 314 | // GlobalScope.launch(CoroutineName("异常处理") + exceptionHandler){ 315 | // launch { 316 | // Log.d("${Thread.currentThread().name}","我要开始抛异常了" ) 317 | // throw NullPointerException("异常测试") 318 | // } 319 | // Log.d("${Thread.currentThread().name}", "end") 320 | // } 321 | 322 | 323 | /* GlobalScope.launch{ 324 | launch(start = CoroutineStart.UNDISPATCHED) { 325 | Log.d("${Thread.currentThread().name}", " 我要开始抛异常了") 326 | try { 327 | throw NullPointerException("异常测试") 328 | } catch (e: Exception) { 329 | e.printStackTrace() 330 | } 331 | } 332 | Log.d("${Thread.currentThread().name}", "end") 333 | } 334 | val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> 335 | Log.d("exceptionHandler", "${coroutineContext[CoroutineName].toString()} 处理异常 :$throwable") 336 | } 337 | GlobalScope.launch(CoroutineName("父协程") + exceptionHandler){ 338 | val job = launch(CoroutineName("子协程")) { 339 | Log.d("${Thread.currentThread().name}","我要开始抛异常了" ) 340 | throw NullPointerException("空指针异常") 341 | } 342 | 343 | for (index in 0..10){ 344 | launch(CoroutineName("子协程$index")) { 345 | Log.d("${Thread.currentThread().name}","${coroutineContext[CoroutineName]}" ) 346 | } 347 | } 348 | 349 | try { 350 | job.join() 351 | } catch (e: Exception) { 352 | e.printStackTrace() 353 | } 354 | Log.d("${Thread.currentThread().name}", "end") 355 | } */ 356 | 357 | 358 | /*val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> 359 | Log.d("exceptionHandler", "${coroutineContext[CoroutineName].toString()} 处理异常 :$throwable") 360 | } 361 | GlobalScope.launch(exceptionHandler) { 362 | supervisorScope { 363 | launch(CoroutineName("异常子协程")) { 364 | Log.d("${Thread.currentThread().name}", "我要开始抛异常了") 365 | throw NullPointerException("空指针异常") 366 | } 367 | for (index in 0..10) { 368 | launch(CoroutineName("子协程$index")) { 369 | Log.d("${Thread.currentThread().name}正常执行", "$index") 370 | if (index %3 == 0){ 371 | throw NullPointerException("子协程${index}空指针异常") 372 | } 373 | } 374 | } 375 | } 376 | }*/ 377 | 378 | val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> 379 | Log.d("exceptionHandler", "${coroutineContext[CoroutineName].toString()} 处理异常 :$throwable") 380 | } 381 | val supervisorScope = CoroutineScope(SupervisorJob() + exceptionHandler) 382 | with(supervisorScope) { 383 | launch(CoroutineName("异常子协程")) { 384 | Log.d("${Thread.currentThread().name}", "我要开始抛异常了") 385 | throw NullPointerException("空指针异常") 386 | } 387 | for (index in 0..10) { 388 | launch(CoroutineName("子协程$index")) { 389 | Log.d("${Thread.currentThread().name}正常执行", "$index") 390 | if (index % 3 == 0) { 391 | throw NullPointerException("子协程${index}空指针异常") 392 | } 393 | } 394 | } 395 | } 396 | } 397 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/ui/adapter/HomeAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.ui.adapter 2 | 3 | import com.carman.kotlin.coroutine.base.BaseAdapter 4 | import com.carman.kotlin.coroutine.databinding.ItemHomeBinding 5 | import com.carman.kotlin.coroutine.interf.ItemClickListener 6 | 7 | 8 | class HomeAdapter(private val listener: ItemClickListener) : BaseAdapter() { 9 | 10 | override fun ItemHomeBinding.setListener() { 11 | itemClickListener = listener 12 | } 13 | 14 | override fun ItemHomeBinding.onBindViewHolder(bean: String, position: Int) { 15 | this.bean = bean 16 | this.position = position 17 | tv.text = bean 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/carman/kotlin/coroutine/ui/adapter/SecondAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.carman.kotlin.coroutine.ui.adapter 2 | 3 | import android.util.Log 4 | import android.view.ViewGroup 5 | import androidx.databinding.ViewDataBinding 6 | import com.carman.kotlin.coroutine.base.BaseMultiTypeAdapter 7 | import com.carman.kotlin.coroutine.bean.Person 8 | import com.carman.kotlin.coroutine.bean.Student 9 | import com.carman.kotlin.coroutine.bean.Teacher 10 | import com.carman.kotlin.coroutine.databinding.ItemPersionBinding 11 | import com.carman.kotlin.coroutine.databinding.ItemStudentBinding 12 | import com.carman.kotlin.coroutine.databinding.ItemTeacherBinding 13 | 14 | class SecondAdapter: BaseMultiTypeAdapter() { 15 | 16 | companion object{ 17 | private const val ITEM_DEFAULT_TYPE = 0 18 | private const val ITEM_STUDENT_TYPE = 1 19 | private const val ITEM_TEACHER_TYPE = 2 20 | } 21 | 22 | override fun getItemViewType(position: Int): Int { 23 | return when(getItem(position)){ 24 | is Student -> ITEM_STUDENT_TYPE 25 | is Teacher -> ITEM_TEACHER_TYPE 26 | else -> ITEM_DEFAULT_TYPE 27 | } 28 | } 29 | 30 | override fun onCreateMultiViewHolder(parent: ViewGroup, viewType: Int): ViewDataBinding { 31 | return when(viewType){ 32 | ITEM_STUDENT_TYPE -> loadLayout(ItemStudentBinding::class.java,parent) 33 | ITEM_TEACHER_TYPE -> loadLayout(ItemTeacherBinding::class.java,parent) 34 | else -> loadLayout(ItemPersionBinding::class.java,parent) 35 | } 36 | } 37 | 38 | override fun MultiTypeViewHolder.onBindViewHolder(holder: MultiTypeViewHolder, item: Person, position: Int) { 39 | when(holder.binding){ 40 | is ItemStudentBinding ->{ 41 | Log.d("ItemStudentBinding","item : $item position : $position") 42 | } 43 | is ItemTeacherBinding ->{ 44 | Log.d("ItemTeacherBinding","item : $item position : $position") 45 | } 46 | } 47 | } 48 | 49 | override fun areItemContentsTheSame(oldItem: Person, newItem: Person, oldItemPosition: Int, newItemPosition: Int): Boolean { 50 | return super.areItemContentsTheSame(oldItem, newItem, oldItemPosition, newItemPosition) 51 | } 52 | 53 | override fun areItemsTheSame(oldItem: Person, newItem: Person): Boolean { 54 | return super.areItemsTheSame(oldItem, newItem) 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 18 | 19 | 29 | 30 | 31 |