我对这段代码有一个问题,其中返回PackageSize
语句是在onGetStatsComplated
函数之前触发的,它返回0而不是正确的值。有没有办法在返回PackageSize
之前强制onGetStatsComplated
完成?我知道这是一个逻辑问题,因为如果我删除//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
}
}
编辑代码:
这是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
}
}
}
从接口调用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)
我还尝试了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
欢迎提出任何问题,任何帮助都将不胜感激。
使用线程。睡眠(…)不仅不建议使用,它还可能会锁定UI,并且不会产生所需的结果(如果getPackageSizeInfo方法运行时间超过1秒)。我强烈建议使用AsyncTask或协同程序获取后台线程的信息,就像@Luciano Ferruzzi建议的那样。既然您已经在使用kotlin,我会选择本机解决方案并使用协同程序,它可能看起来像这样:
GlobalScope.launch(Dispatchers.Main){
val getPackageSizeInfo = withContext(Dispacthers.IO) {
pm.javaClass.getMethod(
"getPackageSizeInfo", String::class.java, IPackageStatsObserver::class.java)
getPackageSizeInfo.invoke(pm, context.packageName,
object : CachePackState() {//Call inner class
})
}
}
正如您所看到的,这基本上不会对代码进行任何更改,除了明确说明代码特定部分将使用的线程。
*很抱歉出现了代码错误,我并没有真正编译它。
我强烈建议您使用RxJava、协同程序或异步任务在后台线程上完成这项工作。但是你可以用一个ContdownLatch做一个快速修复。
//Ugly global variable
val countdownLatch = CountdownLatch(1) //-------CHANGE HERE--------
/**
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()
}
countDownLatch.await(1_000, TimeUnit.MILLISECONDS) //-------CHANGE HERE--------
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
countDownLatch.countDown() //-------CHANGE HERE--------
}
}
有关其工作原理的更多信息,请点击此处查看这个很棒的答案:https://stackoverflow.com/a/17827339/7926889
最简单的方法是使用kotlin协程及其挂起函数。
首先将它们添加到您的项目中
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1'
然后,您只需在方法签名中添加suspend
修饰符,如下所示。
suspend fun getPackageSize(): Long {...}
然后你可以像这样得到它
fun collectAndShow(){
launch(Dispatchers.IO){
val size = getPackageSize()
withContext(Dispatchers.Main){
textView.text = "App size is: $size"
}
}
}
我建议您让您的活动、服务、视图模型实现CoroutineScope,这可以帮助您防止内存泄漏。如果不想这样做,请使用GlobalScope。启动
,但你肯定应该使用第一种方法。
看起来是这样的
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
}
}
使用kotlin协同程序的另一个原因是,一些jetpack库将要或已经支持suspend
函数。
编辑:如果您无法公开挂起函数,那么您可以使用回调来处理它
fun getPackageSize(callback: (Long) -> Unit) {
launch(Dispatchers.IO) {
...
val size = StorageInformation(getApplicationContext()).getPackageSize()
callback(size)
}
}
然后在你的另一节课上这样称呼它
//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"
}
}
再一次,它是非阻塞的,它应该是这样的!
我仍在为承诺而挣扎,但多亏了这里的社区,我取得了一些进步。 我有一个简单的JS函数,它查询解析数据库。它应该返回结果数组,但显然由于查询的异步性质(因此有承诺),函数在结果之前返回,给我留下一个未定义的数组。 我需要做什么才能让这个函数等待承诺的结果呢? 这是我的代码:
问题内容: 我正在尝试在Node.js / Express中创建一个路由,该路由从两个查询中读取数据,然后基于来自查询的数据来增加计数。由于Node.js是异步的,因此在读取所有数据之前会显示总计。 我创建了一个简单的示例,以达到我目前正在做的事情 我不确定要等到两个功能都完成后才能打印总计,我该做什么。我是否需要创建一个自定义事件发射器来实现这一目标? 问题答案: 拥抱异步性: 或使用Promi
问题内容: 我有以下 我有一个运行一些ajax的函数,然后根据ajax是否成功返回true或false。我从代码的多个位置调用了这个ajax函数。 因为该函数在ajax完成之前结束,所以它总是返回false。如何避免这种情况? 我读到一些建议我在函数中执行的操作,然后将和函数移至我的和函数。但是,在我的方法中,我进行了大量的计算。很多代码。因此,问题在于,如果我将功能移至其他功能,那么我将复制一堆
问题内容: 这是我的情况: 在this.handleFormSubmit()上,我正在执行this.setState() 在this.handleFormSubmit()内部,我正在调用this.findRoutes(); -取决于this.setState()的成功完成 this.setState(); 在this.findRoutes被调用之前没有完成… 在调用this.findRoutes(
我想返回用户的电话号码,但我得到的是[对象对象]。我在想,如果它在等待完成之前返回值。这是我的代码。 输出12345678,以便我可以在异步函数外部访问它。例如,要在个人资料页面中显示它,
我正在尝试使用返回Mono的Spring反应器webclient执行多个HTTP调用。我正在使用Mono.block()等待所有monos完成。这不是等待直到订阅()完成。 有人能帮我解决这个问题,在subscribe()完成后返回更新的用户对象吗?