Skip to content

Commit

Permalink
- 解决Activity内存泄漏
Browse files Browse the repository at this point in the history
- 通过`setPermissionRequester`方法自定义悬浮窗权限申请器
  • Loading branch information
kongxiaojun committed Nov 17, 2023
1 parent d930714 commit 64cbc95
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 57 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ allprojects {
- **在应用模块的`build.gradle`添加:**
```
dependencies {
implementation 'com.github.kongxiaojun:EasyFloat:3.0.0'
implementation 'com.github.kongxiaojun:EasyFloat:3.0.1'
}
```

Expand Down
4 changes: 4 additions & 0 deletions UpdateDoc.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## 版本更新日志

#### v 3.0.1:
- 解决Activity内存泄漏
- 通过`setPermissionRequester`方法自定义悬浮窗权限申请器

#### v 3.0.0:
- 升级kotlin版本至1.7.21
- 支持设置拖拽的viewid,设置后触摸此View才支持拖拽,触摸其他地方则不能拖拽
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ class MainActivity : BaseActivity(), View.OnClickListener {
.setImmersionStatusBar(true)
.setGravity(Gravity.CENTER, -20, 10)
.setDragEnable(true)
.setPermissionRequester {
PermissionUtils.requestPermission(this@MainActivity, it)
}
.setLayout(R.layout.float_app) {
it.findViewById<ImageView>(R.id.ivClose).setOnClickListener {
EasyFloat.dismiss()
Expand Down
4 changes: 2 additions & 2 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ afterEvaluate {
from components.release
groupId = 'com.github.kongxiaojun'
artifactId = 'EasyFloat'
version = '3.0'
version = '3.0.1'
}
debug(MavenPublication) {
from components.debug
groupId = 'com.github.kongxiaojun'
artifactId = 'EasyFloat'
version = '3.0'
version = '3.0.1'
}
}
}
Expand Down
59 changes: 40 additions & 19 deletions library/src/main/java/com/lzf/easyfloat/EasyFloat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ import com.lzf.easyfloat.core.FloatingWindowManager
import com.lzf.easyfloat.data.FloatConfig
import com.lzf.easyfloat.enums.ShowPattern
import com.lzf.easyfloat.enums.SidePattern
import com.lzf.easyfloat.interfaces.*
import com.lzf.easyfloat.interfaces.FloatCallbacks
import com.lzf.easyfloat.interfaces.OnDisplayHeight
import com.lzf.easyfloat.interfaces.OnFloatAnimator
import com.lzf.easyfloat.interfaces.OnFloatCallbacks
import com.lzf.easyfloat.interfaces.OnInvokeView
import com.lzf.easyfloat.interfaces.OnPermissionResult
import com.lzf.easyfloat.interfaces.PermissionRequester
import com.lzf.easyfloat.permission.PermissionUtils
import com.lzf.easyfloat.utils.LifecycleUtils
import com.lzf.easyfloat.interfaces.FloatCallbacks
import com.lzf.easyfloat.utils.DisplayUtils
import com.lzf.easyfloat.utils.LifecycleUtils
import com.lzf.easyfloat.utils.Logger
import java.lang.Exception

/**
* @author: liuzhenfeng
Expand All @@ -28,12 +31,11 @@ class EasyFloat {

/**
* 通过上下文,创建浮窗的构建者信息,使浮窗拥有一些默认属性
* @param activity 上下文信息,优先使用Activity上下文,因为系统浮窗权限的自动申请,需要使用Activity信息
* @param context 上下文信息
* @return 浮窗属性构建者
*/
@JvmStatic
fun with(activity: Context): Builder = if (activity is Activity) Builder(activity)
else Builder(LifecycleUtils.getTopActivity() ?: activity)
fun with(context: Context): Builder = Builder(context.applicationContext)

/**
* 关闭当前浮窗
Expand Down Expand Up @@ -146,7 +148,7 @@ class EasyFloat {
@JvmStatic
@JvmOverloads
fun removeFilters(tag: String? = null, vararg clazz: Class<*>) =
getFilterSet(tag)?.removeAll(clazz.map { it.name })
getFilterSet(tag)?.removeAll(clazz.map { it.name }.toSet())

/**
* 清除当前浮窗的所有过滤信息
Expand All @@ -173,7 +175,7 @@ class EasyFloat {
/**
* 浮窗的属性构建类,支持链式调用
*/
class Builder(private val activity: Context) : OnPermissionResult {
class Builder(private val context: Context) : OnPermissionResult {

// 创建浮窗数据类,方便管理配置
private val config = FloatConfig()
Expand Down Expand Up @@ -251,9 +253,9 @@ class EasyFloat {
@JvmOverloads
fun setBorder(
left: Int = 0,
top: Int = -DisplayUtils.getStatusBarHeight(activity),
right: Int = DisplayUtils.getScreenWidth(activity),
bottom: Int = DisplayUtils.getScreenHeight(activity)
top: Int = -DisplayUtils.getStatusBarHeight(context),
right: Int = DisplayUtils.getScreenWidth(context),
bottom: Int = DisplayUtils.getScreenHeight(context)
) = apply {
config.leftBorder = left
config.topBorder = top
Expand Down Expand Up @@ -340,9 +342,25 @@ class EasyFloat {
fun setFilter(vararg clazz: Class<*>) = apply {
clazz.forEach {
config.filterSet.add(it.name)
if (activity is Activity) {
// 过滤掉当前Activity
if (it.name == activity.componentName.className) config.filterSelf = true
}
}

/**
* 设置权限申请器,当需要申请悬浮窗权限时优先使用此权限申请器,满足合规和自定义权限弹窗需求
* @param permissionRequester 权限申请器
*/
fun setPermissionRequester(permissionRequester: PermissionRequester) = apply {
config.permissionRequester = permissionRequester
}

/**
* 设置权限申请器,当需要申请悬浮窗权限时优先使用此权限申请器,满足合规和自定义权限弹窗需求
* @param permissionRequester 权限申请器
*/
fun setPermissionRequester(permissionRequester: (OnPermissionResult) -> Unit) = apply {
config.permissionRequester = object : PermissionRequester {
override fun requestPermission(resultCallback: OnPermissionResult) {
permissionRequester.invoke(resultCallback)
}
}
}
Expand All @@ -357,22 +375,25 @@ class EasyFloat {
// 仅当页显示,则直接创建activity浮窗
config.showPattern == ShowPattern.CURRENT_ACTIVITY -> createFloat()
// 系统浮窗需要先进行权限审核,有权限则创建app浮窗
PermissionUtils.checkPermission(activity) -> createFloat()
PermissionUtils.checkPermission(context) -> createFloat()
// 申请浮窗权限
else -> requestPermission()
}

/**
* 通过浮窗管理类,统一创建浮窗
*/
private fun createFloat() = FloatingWindowManager.create(activity, config)
private fun createFloat() = FloatingWindowManager.create(context, config)

/**
* 通过Fragment去申请系统悬浮窗权限
*/
private fun requestPermission() =
if (activity is Activity) PermissionUtils.requestPermission(activity, this)
else callbackCreateFailed(WARN_CONTEXT_REQUEST)
config.permissionRequester?.requestPermission(this) ?: LifecycleUtils.topActivity
?.let { PermissionUtils.requestPermission(it, this) } ?: callbackCreateFailed(
WARN_CONTEXT_REQUEST
)


/**
* 申请浮窗权限的结果回调
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.lzf.easyfloat.core

import android.animation.Animator
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Service
import android.content.Context
import android.graphics.PixelFormat
Expand Down Expand Up @@ -103,8 +102,7 @@ internal class FloatingWindowHelper(val context: Context, var config: FloatConfi
}
}

private fun getActivity() =
if (context is Activity) context else LifecycleUtils.getTopActivity()
private fun getActivity() = LifecycleUtils.topActivity

private fun getToken(): IBinder? = getActivity()?.window?.decorView?.windowToken

Expand Down Expand Up @@ -153,7 +151,7 @@ internal class FloatingWindowHelper(val context: Context, var config: FloatConfi
lastLayoutMeasureHeight = frameLayout?.measuredHeight ?: -1
config.apply {
// 如果设置了过滤当前页,或者后台显示前台创建、前台显示后台创建,隐藏浮窗,否则执行入场动画
if (filterSelf
if (this.filterSet.contains(LifecycleUtils.topActivity?.componentName?.className)
|| (showPattern == ShowPattern.BACKGROUND && LifecycleUtils.isForeground())
|| (showPattern == ShowPattern.FOREGROUND && !LifecycleUtils.isForeground())
) {
Expand Down
7 changes: 4 additions & 3 deletions library/src/main/java/com/lzf/easyfloat/data/FloatConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,15 @@ data class FloatConfig(

// 不需要显示系统浮窗的页面集合,参数为类名
val filterSet: MutableSet<String> = mutableSetOf(),
// 是否设置,当前创建的页面也被过滤
internal var filterSelf: Boolean = false,
// 是否需要显示,当过滤信息匹配上时,该值为false(用户手动调用隐藏,该值也为false,相当于手动过滤)
internal var needShow: Boolean = true,

// 当layout大小变化后,整体view的位置的摆放
var layoutChangedGravity: Int = Gravity.TOP.or(Gravity.START),

// 支持拖拽的viewid,设置后触摸此View才支持拖拽,触摸其他地方则不能拖拽
var dragViewId: Int? = null
var dragViewId: Int? = null,

// 权限申请器,实现后优先使用此申请器申请权限,满足用户的合规需求和自定义需求
var permissionRequester: PermissionRequester? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.lzf.easyfloat.interfaces

/**
* @author kongxiaojun
* @date 2023/11/17
* @description 权限申请器
*/
interface PermissionRequester {

fun requestPermission(resultCallback: OnPermissionResult)

}
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,7 @@ object PermissionUtils {
*/
private fun commonROMPermissionCheck(context: Context): Boolean =
if (RomUtils.checkIsMeizuRom()) meizuPermissionCheck(context) else {
var result = true
if (Build.VERSION.SDK_INT >= 23) try {
val clazz = Settings::class.java
val canDrawOverlays =
clazz.getDeclaredMethod("canDrawOverlays", Context::class.java)
result = canDrawOverlays.invoke(null, context) as Boolean
} catch (e: Exception) {
Log.e(TAG, Log.getStackTraceString(e))
}
result
if (Build.VERSION.SDK_INT >= 23) Settings.canDrawOverlays(context) else true
}

/**
Expand Down
37 changes: 19 additions & 18 deletions library/src/main/java/com/lzf/easyfloat/utils/LifecycleUtils.kt
Original file line number Diff line number Diff line change
@@ -1,57 +1,58 @@
package com.lzf.easyfloat.utils

import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.os.Bundle
import com.lzf.easyfloat.core.FloatingWindowManager
import com.lzf.easyfloat.enums.ShowPattern
import java.lang.ref.WeakReference

/**
* @author: liuzhenfeng
* @function: 通过生命周期回调,判断系统浮窗的过滤信息,以及app是否位于前台,控制浮窗显隐
* @date: 2019-07-11 15:51
*/
@SuppressLint("StaticFieldLeak")
internal object LifecycleUtils {

lateinit var application: Application
private var activityCount = 0
private var mTopActivity: WeakReference<Activity>? = null

fun getTopActivity(): Activity? = mTopActivity?.get()
var topActivity: Activity? = null
private set

fun setLifecycleCallbacks(application: Application) {
this.application = application
application.registerActivityLifecycleCallbacks(object :
Application.ActivityLifecycleCallbacks {

override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
topActivity = activity
}

override fun onActivityStarted(activity: Activity) {
// 计算启动的activity数目
activity?.let { activityCount++ }
activityCount++
}

override fun onActivityResumed(activity: Activity) {
activity?.let {
mTopActivity?.clear()
mTopActivity = WeakReference<Activity>(it)
// 每次都要判断当前页面是否需要显示
checkShow(it)
}
// 每次都要判断当前页面是否需要显示
checkShow(activity)
}

override fun onActivityPaused(activity: Activity) {}

override fun onActivityStopped(activity: Activity) {
activity?.let {
// 计算关闭的activity数目,并判断当前App是否处于后台
activityCount--
checkHide(it)
}
// 计算关闭的activity数目,并判断当前App是否处于后台
activityCount--
checkHide(activity)
}

override fun onActivityDestroyed(activity: Activity) {}
@SuppressLint("StaticFieldLeak")
override fun onActivityDestroyed(activity: Activity) {
if (activity == topActivity) {
topActivity = null
}
}

override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
})
Expand Down

0 comments on commit 64cbc95

Please sign in to comment.