在返回值之前如何等待函数执行完毕?

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)

如有任何问题,请随时提出,我们将不胜感激。

svk*_*aka 5

最简单的方法是使用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)

再说一遍,它是非阻塞的,就应该如此!