phi*_*652 0 logic android return kotlin
我对这段代码有一个问题,其中该return packageSize语句在函数之前触发onGetStatsCompleted,并且返回 0 而不是正确的值。有什么办法可以让我onGetStatsCompleted在返回之前强制完成packageSize吗?我知道这是一个逻辑问题,因为如果我删除评论,//Thread.sleep它就可以正常工作。
如何Thread.sleep在应用程序中不使用超时或任何其他类型的超时来解决此问题?
原始代码:
/**
Get the size of the app for API < 26
*/
@Throws(InterruptedException::class)
fun getPackageSize(): Long {
val pm = context.packageManager
try {
val getPackageSizeInfo = pm.javaClass.getMethod(
"getPackageSizeInfo", String::class.java, IPackageStatsObserver::class.java)
getPackageSizeInfo.invoke(pm, context.packageName,
object : CachePackState() {//Call inner class
})
} catch (e: Exception) {
e.printStackTrace()
}
//Thread.sleep(1000)
return packageSize
}
/**
Inner class which will get the data size for the application
*/
open inner class CachePackState : IPackageStatsObserver.Stub() {
override fun onGetStatsCompleted(pStats: PackageStats, succeeded: Boolean) {
//here the pStats has all the details of the package
dataSize = pStats.dataSize
cacheSize = pStats.cacheSize
apkSize = pStats.codeSize
packageSize = cacheSize + apkSize
}
}
Run Code Online (Sandbox Code Playgroud)
编辑代码:
这是 StorageInformation 类
import android.annotation.SuppressLint
import android.app.usage.StorageStatsManager
import android.content.Context
import android.content.pm.IPackageStatsObserver
import android.content.pm.PackageManager
import android.content.pm.PackageStats
/**
This class will perform data operation
*/
internal class StorageInformation(internal var context: Context) {
private var packageSize: Long = 0
private var dataSize: Long = 0
private var cacheSize: Long = 0
private var apkSize: Long = 0
/**
Get the size of the app
*/
@Throws(InterruptedException::class)
suspend fun getPackageSize(): Long {
val pm = context.packageManager
@SuppressLint("WrongConstant")
val storageStatsManager: StorageStatsManager
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
storageStatsManager = context.getSystemService(Context.STORAGE_STATS_SERVICE) as StorageStatsManager
try {
val ai = context.packageManager.getApplicationInfo(context.packageName, 0)
val storageStats = storageStatsManager.queryStatsForUid(ai.storageUuid, pm.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA).uid)
cacheSize = storageStats.cacheBytes
apkSize = storageStats.appBytes
packageSize = cacheSize + apkSize
} catch (e: Exception) {
e.printStackTrace()
}
} else {
try {
val getPackageSizeInfo = pm.javaClass.getMethod(
"getPackageSizeInfo", String::class.java, IPackageStatsObserver::class.java)
getPackageSizeInfo.invoke(pm, context.packageName,
object : CachePackState() {//Call inner class
})
} catch (e: Exception) {
e.printStackTrace()
}
}
return packageSize
}
/**
Inner class which will get the data size for the application
*/
open inner class CachePackState : IPackageStatsObserver.Stub() {
override fun onGetStatsCompleted(pStats: PackageStats, succeeded: Boolean) {
//here the pStats has all the details of the package
dataSize = pStats.dataSize
cacheSize = pStats.cacheSize
apkSize = pStats.codeSize
packageSize = cacheSize + apkSize
}
}
}
Run Code Online (Sandbox Code Playgroud)
从接口调用StorageInformation
var appSize=""
fun getPackageSize(callback: (Long) -> Unit) {
launch(Dispatchers.IO) {
val size = StorageInformation(getApplicationContext()).getPackageSize()
callback(size)
}
}
fun handlePackageSize(size: Long) {
launch(Dispatchers.Main) {
appSize = formatFileSize(getApplicationContext(), size)
}
}
getPackageSize(::handlePackageSize)
Run Code Online (Sandbox Code Playgroud)
我也尝试了 r2rek 的解决方案并得到相同的结果
try {
GlobalScope.launch(Dispatchers.Main){
var getPackageSizeInfo = withContext(coroutineContext) {
pm.javaClass.getMethod(
"getPackageSizeInfo", String::class.java, IPackageStatsObserver::class.java)
}
getPackageSizeInfo.invoke(pm, context.packageName,
object : CachePackState() {//Call inner class
})
}
} catch (e: Exception) {
e.printStackTrace()
}
}
return packageSize
Run Code Online (Sandbox Code Playgroud)
如有任何问题,请随时提出,我们将不胜感激。
最简单的方法是使用kotlin 协程及其挂起函数。
首先将它们添加到您的项目中
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1'
Run Code Online (Sandbox Code Playgroud)
然后你需要做的就是suspend在你的方法签名中添加修饰符,所以它看起来像这样。
suspend fun getPackageSize(): Long {...}
Run Code Online (Sandbox Code Playgroud)
然后你可以这样获取它
fun collectAndShow(){
launch(Dispatchers.IO){
val size = getPackageSize()
withContext(Dispatchers.Main){
textView.text = "App size is: $size"
}
}
}
Run Code Online (Sandbox Code Playgroud)
我建议你让你的 Activity、Service、ViewModel 实现CoroutineScope,这可以帮助你防止内存泄漏。如果您不想这样做,GlobalScope.launch但您绝对应该使用第一种方法。
所以看起来像这样
class MainActivity : AppCompatActivity(), CoroutineScope {
override val coroutineContext: CoroutineContext
get() = Job()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
launch(Dispatchers.IO) {
val size= getPackageSize()
withContext(Dispatchers.Main){
findViewById<TextView>(R.id.textView).text="App size is: $size"
}
}
}
suspend fun getPackageSize(): Long {
//do your stuff
}
}
Run Code Online (Sandbox Code Playgroud)
使用 kotlin 协程的另一个原因是一些 jetpack 库即将或已经支持suspend功能。
编辑: 如果您无法公开挂起函数,那么您可以使用回调来处理它
fun getPackageSize(callback: (Long) -> Unit) {
launch(Dispatchers.IO) {
...
val size = StorageInformation(getApplicationContext()).getPackageSize()
callback(size)
}
}
Run Code Online (Sandbox Code Playgroud)
然后在你的其他班级中这样称呼它
//wherever you want to get size
....
getPackageSize(::handlePackageSize)
....
fun handlePackageSize(size: Long) {
//do whatever you want with size
launch(Dispatchers.Main) {
findViewById<TextView>(R.id.textView).text = "APP SIZE= $size"
}
}
Run Code Online (Sandbox Code Playgroud)
再说一遍,它是非阻塞的,就应该如此!
| 归档时间: |
|
| 查看次数: |
2629 次 |
| 最近记录: |