Modify: ARouter、MMKV

This commit is contained in:
Quyunshuo
2020-08-28 20:01:45 +08:00
parent 76d957cad8
commit 03960ef66c
20 changed files with 333 additions and 7 deletions

View File

@ -67,6 +67,7 @@ dependencies {
api GitHub.Glide api GitHub.Glide
api GitHub.ARoute api GitHub.ARoute
api GitHub.RecyclerViewAdapter api GitHub.RecyclerViewAdapter
api GitHub.StatusBar
kapt GitHub.GlideCompiler kapt GitHub.GlideCompiler
kapt GitHub.ARouteCompiler kapt GitHub.ARouteCompiler

View File

@ -1,6 +1,8 @@
package com.quyunshuo.base package com.quyunshuo.base
import android.app.Application import android.app.Application
import com.alibaba.android.arouter.launcher.ARouter
import com.quyunshuo.base.utils.SpUtils
/** /**
* @Author: QuYunShuo * @Author: QuYunShuo
@ -12,5 +14,21 @@ open class BaseApplication : Application() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
initialize()
}
/**
* 进行一些初始化的操作
*/
protected open fun initialize() {
// 腾讯 MMKV 初始化
SpUtils.initMMKV(this)
// 阿里路由 ARouter 初始化
if (BuildConfig.DEBUG) {
ARouter.openLog() // 打印日志
ARouter.openDebug() // 开启调试模式(如果在InstantRun模式下运行必须开启调试模式线上版本需要关闭,否则有安全风险)
}
ARouter.init(this)
} }
} }

View File

@ -0,0 +1,81 @@
package com.quyunshuo.base.ktx
import android.content.Context
import android.view.Gravity
import android.widget.Toast
import androidx.annotation.StringRes
/**
* @Author: QuYunShuo
* @Time: 2020/8/17
* @Class: ContextKtx
* @Remark: Context相关的扩展方法
*/
/**
* Toast
* @param text CharSequence 类型文本
*/
fun Context.toast(text: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, text, duration).show()
}
/**
* Toast
* @param resId String 类型资源id
*/
fun Context.toast(@StringRes resId: Int, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, resId, duration).show()
}
/**
* 居中Toast
* @param text CharSequence 类型文本
*/
fun Context.centerToast(text: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
val toast = Toast.makeText(this, text, duration)
toast.setGravity(Gravity.CENTER, 0, 0)
toast.show()
}
/**
* 居中Toast
* @param resId String 类型资源id
*/
fun Context.centerToast(@StringRes resId: Int, duration: Int = Toast.LENGTH_SHORT) {
val toast = Toast.makeText(this, resId, duration)
toast.setGravity(Gravity.CENTER, 0, 0)
toast.show()
}
/**
* dp 转 px
*/
fun Context.dp2px(dpValue: Float): Int {
val scale = resources.displayMetrics.density
return (dpValue * scale + 0.5f).toInt()
}
/**
* px 转 dp
*/
fun Context.px2dp(pxValue: Float): Int {
val scale = resources.displayMetrics.density
return (pxValue / scale + 0.5f).toInt()
}
/**
* sp 转 px
*/
fun Context.sp2px(spValue: Float): Int {
val scale = resources.displayMetrics.scaledDensity
return (spValue * scale + 0.5f).toInt()
}
/**
* px 转 sp
*/
fun Context.px2sp(pxValue: Float): Int {
val scale = resources.displayMetrics.scaledDensity
return (pxValue / scale + 0.5f).toInt()
}

View File

@ -0,0 +1,23 @@
package com.quyunshuo.base.ktx
import android.view.View
/**
* @Author: QuYunShuo
* @Time: 2020/8/17
* @Class: ViewKtx
* @Remark: View相关的扩展方法
*/
fun View.gone() {
visibility = View.GONE
}
fun View.visible() {
visibility = View.VISIBLE
}
fun View.invisible() {
visibility = View.INVISIBLE
}

View File

@ -5,6 +5,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import com.alibaba.android.arouter.launcher.ARouter
/** /**
* @Author: QuYunShuo * @Author: QuYunShuo
@ -24,6 +25,8 @@ abstract class BaseFrameActivity<VB : ViewBinding, VM : ViewModel>(private val v
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(mBinding.root) setContentView(mBinding.root)
// ARouter 依赖注入
ARouter.getInstance().inject(this)
initView() initView()
} }

View File

@ -0,0 +1,59 @@
package com.quyunshuo.base.utils
import android.content.Context
import com.tencent.mmkv.MMKV
/**
* @Author: QuYunShuo
* @Time: 2020/8/28
* @Class: SpUtils
* @Remark: MMKV使用封装
*/
object SpUtils {
/**
* 初始化
*/
fun initMMKV(context: Context): String? = MMKV.initialize(context)
/**
* 保存数据(简化)
* 根据value类型自动匹配需要执行的方法
*/
fun put(key: String, value: Any) =
when (value) {
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Float -> putFloat(key, value)
is Double -> putDouble(key, value)
is String -> putString(key, value)
is Boolean -> putBoolean(key, value)
else -> false
}
fun putString(key: String, value: String): Boolean = MMKV.defaultMMKV().encode(key, value)
fun getString(key: String, defValue: String): String = MMKV.defaultMMKV().decodeString(key, defValue)
fun putInt(key: String, value: Int): Boolean = MMKV.defaultMMKV().encode(key, value)
fun getInt(key: String, defValue: Int): Int = MMKV.defaultMMKV().decodeInt(key, defValue)
fun putLong(key: String, value: Long): Boolean = MMKV.defaultMMKV().encode(key, value)
fun getLong(key: String, defValue: Long): Long = MMKV.defaultMMKV().decodeLong(key, defValue)
fun putDouble(key: String, value: Double): Boolean = MMKV.defaultMMKV().encode(key, value)
fun getDouble(key: String, defValue: Double): Double = MMKV.defaultMMKV().decodeDouble(key, defValue)
fun putFloat(key: String, value: Float): Boolean = MMKV.defaultMMKV().encode(key, value)
fun getFloat(key: String, defValue: Float): Float = MMKV.defaultMMKV().decodeFloat(key, defValue)
fun putBoolean(key: String, value: Boolean): Boolean = MMKV.defaultMMKV().encode(key, value)
fun getBoolean(key: String, defValue: Boolean): Boolean = MMKV.defaultMMKV().decodeBool(key, defValue)
fun contains(key: String): Boolean = MMKV.defaultMMKV().contains(key)
}

View File

@ -0,0 +1,35 @@
package com.quyunshuo.base.utils
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
/**
* 以顶层函数存在的常用工具方法
* startPolling() -> 开启一个轮询
*/
/**
* 使用 Flow 做的简单的轮询
* 请使用单独的协程来进行管理该 Flow
* Flow 仍有一些操作符是实验性的 使用时需添加 @InternalCoroutinesApi 注解
* @param intervals 轮询间隔时间/毫秒
* @param block 需要执行的代码块
*/
@InternalCoroutinesApi
suspend fun startPolling(intervals: Long, block: () -> Unit) {
flow {
while (true) {
delay(intervals)
emit(0)
}
}
.catch { Log.e("flow", "startPolling: $it") }
.flowOn(Dispatchers.Main)
.collect { block.invoke() }
}

View File

@ -1,7 +1,7 @@
<resources> <resources>
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="base_AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <style name="base_AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
<item name="colorPrimary">@color/base_colorPrimary</item> <item name="colorPrimary">@color/base_colorPrimary</item>
<item name="colorPrimaryDark">@color/base_colorPrimaryDark</item> <item name="colorPrimaryDark">@color/base_colorPrimaryDark</item>

View File

@ -8,5 +8,4 @@ import com.quyunshuo.base.BaseApplication
* @Class: CommonApplication * @Class: CommonApplication
* @Remark: 项目相关的Application * @Remark: 项目相关的Application
*/ */
open class CommonApplication : BaseApplication() { open class CommonApplication : BaseApplication()
}

View File

@ -0,0 +1,11 @@
package com.quyunshuo.common.constant
/**
* @Author: QuYunShuo
* @Time: 2020/8/28
* @Class: RouteKey
* @Remark: 路由使用中 用到的Key 统一写在此类中
*/
object RouteKey {
const val KEY_NAME = "key_name"
}

View File

@ -0,0 +1,16 @@
package com.quyunshuo.common.constant
/**
* @Author: QuYunShuo
* @Time: 2020/8/28
* @Class: RoutePath
* @Remark: 路由地址
*/
object RouteUrl {
// MainActivity
const val MainActivity: String = "/lib_splash/SplashActivity"
// MainActivity2
const val MainActivity2: String = "/lib_splash/SplashActivity2"
}

View File

@ -3,7 +3,10 @@
package="com.quyunshuo.main"> package="com.quyunshuo.main">
<application> <application>
<activity android:name=".MainActivity"> <activity android:name=".MainActivity2"></activity>
<activity
android:name=".MainActivity"
android:screenOrientation="sensorLandscape">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

View File

@ -1,6 +1,10 @@
package com.quyunshuo.main package com.quyunshuo.main
import android.widget.Toast import android.widget.Toast
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter
import com.quyunshuo.common.constant.RouteKey
import com.quyunshuo.common.constant.RouteUrl
import com.quyunshuo.common.ui.BaseActivity import com.quyunshuo.common.ui.BaseActivity
import com.quyunshuo.main.databinding.MainActivityMainBinding import com.quyunshuo.main.databinding.MainActivityMainBinding
@ -10,6 +14,7 @@ import com.quyunshuo.main.databinding.MainActivityMainBinding
* @Class: MainActivity * @Class: MainActivity
* @Remark: 主界面Activity * @Remark: 主界面Activity
*/ */
@Route(path = RouteUrl.MainActivity)
class MainActivity : class MainActivity :
BaseActivity<MainActivityMainBinding, MainViewModel>(MainViewModel::class.java) { BaseActivity<MainActivityMainBinding, MainViewModel>(MainViewModel::class.java) {
@ -22,5 +27,9 @@ class MainActivity :
Toast.makeText(this, it, Toast.LENGTH_SHORT).show() Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
}) })
mBinding.mBtn.setOnClickListener { mViewModel.getString() } mBinding.mBtn.setOnClickListener { mViewModel.getString() }
mBinding.mIntentBtn.setOnClickListener {
ARouter.getInstance().build(RouteUrl.MainActivity2)
.withString(RouteKey.KEY_NAME, "ARouter").navigation()
}
} }
} }

View File

@ -0,0 +1,25 @@
package com.quyunshuo.main
import com.alibaba.android.arouter.facade.annotation.Autowired
import com.alibaba.android.arouter.facade.annotation.Route
import com.quyunshuo.base.ktx.toast
import com.quyunshuo.common.constant.RouteKey
import com.quyunshuo.common.constant.RouteUrl
import com.quyunshuo.common.ui.BaseActivity
import com.quyunshuo.main.databinding.MainActivityMain2Binding
@Route(path = RouteUrl.MainActivity2)
class MainActivity2 :
BaseActivity<MainActivityMain2Binding, MainViewModel>(MainViewModel::class.java) {
// 通过name来映射URL中的不同参数
@Autowired(name = RouteKey.KEY_NAME)
lateinit var name: String
override fun initViewBinding(): MainActivityMain2Binding =
MainActivityMain2Binding.inflate(layoutInflater)
override fun initView() {
toast(name)
}
}

View File

@ -24,4 +24,22 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/mTv" /> app:layout_constraintTop_toBottomOf="@+id/mTv" />
<Button
android:id="@+id/mIntentBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="跳转"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/mBtn" />
<FrameLayout
android:layout_width="300dp"
android:layout_height="300dp"
android:background="#333"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity2">
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,6 +1,12 @@
# Android项目框架(组件化 + Kotlin + MVVM + Jetpack ) # Android项目框架(组件化 + Kotlin + MVVM + Jetpack )
note: 1. 测试打包脚本 note: 1. 测试打包脚本
资源文件相关{
资源相关文件属于项目相关的因此需要放在Common组件内不要放在Base组件里
String、Color、Style、layout、drawable、mipmap 公用的 放在Common组件内不公用的放在各自组件内命名以各组件的规则命名(build文件设置了规则)
}
协程相关{ 协程相关{
Activity/Fragment 中 可以使用 lifecycleScope,与Activity/Fragment绑定了生命周期 无需手动取消 Activity/Fragment 中 可以使用 lifecycleScope,与Activity/Fragment绑定了生命周期 无需手动取消
ViewModel 中 可以使用 viewModelScope,它与ViewModel绑定了生命周期 无需手动取消 ViewModel 中 可以使用 viewModelScope,它与ViewModel绑定了生命周期 无需手动取消

View File

@ -9,6 +9,14 @@
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/base_AppTheme" /> android:theme="@style/base_AppTheme">
<!-- &lt;!&ndash; 屏幕适配基准DP &ndash;&gt;-->
<!-- <meta-data-->
<!-- android:name="design_width_in_dp"-->
<!-- android:value="640" />-->
<!-- <meta-data-->
<!-- android:name="design_height_in_dp"-->
<!-- android:value="400" />-->
</application>
</manifest> </manifest>

View File

@ -11,8 +11,8 @@ import com.quyunshuo.common.CommonApplication
*/ */
class AppApplication : CommonApplication() { class AppApplication : CommonApplication() {
override fun onCreate() { override fun initialize() {
MultiDex.install(this) MultiDex.install(this)
super.onCreate() super.initialize()
} }
} }

View File

@ -38,6 +38,7 @@ object Version {
const val ARoute = "1.5.0" // 阿里路由 const val ARoute = "1.5.0" // 阿里路由
const val ARouteCompiler = "1.2.2" // 阿里路由 APT const val ARouteCompiler = "1.2.2" // 阿里路由 APT
const val RecyclerViewAdapter = "3.0.4" // RecyclerViewAdapter const val RecyclerViewAdapter = "3.0.4" // RecyclerViewAdapter
const val StatusBar = "1.5.1" // 状态栏
} }
object AndroidX { object AndroidX {
@ -89,4 +90,5 @@ object GitHub {
const val ARouteCompiler = "com.alibaba:arouter-compiler:${Version.ARouteCompiler}" const val ARouteCompiler = "com.alibaba:arouter-compiler:${Version.ARouteCompiler}"
const val RecyclerViewAdapter = const val RecyclerViewAdapter =
"com.github.CymChad:BaseRecyclerViewAdapterHelper:${Version.RecyclerViewAdapter}" "com.github.CymChad:BaseRecyclerViewAdapterHelper:${Version.RecyclerViewAdapter}"
const val StatusBar = "com.jaeger.statusbarutil:library:${Version.StatusBar}"
} }