是否可以从 base64String API 响应调用生成 APK 文件?

Is it possible to generate an APK file from a base64String API response call?

我目前正在研究如何以编程方式更新和安装 APK 文件的解决方案 - 与 this 问题非常相似。我的应用程序 不使用 Google 服务。

出于更新目的,具有升序版本的 APK 文件存储在内部服务器和 C# Web 服务上 returns 最新 版本的 APK 文件。

我用 Retrofit2 做这个:

    @Streaming
    @GET("/ws/webservice.svc/getUpdate")
    fun getUpdate(
        @Query("Programm") program: String,
        @Query("version") version: String,
        @Query("test") test: Boolean
    ): Single<String>

和实时数据:

override fun getUpdate() {
        disposables += api.getUpdate(
            program = context.getString(R.string.app_name),
            version = context.getString(R.string.app_version),
            test = isTest
        )
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeBy(
                onSuccess = {
                    liveData.value = GetUpdate(it)
                },
                onError = {
                    liveData.value = Error("Error getUpdate: " + it.message)
                }
            )
    }

我面临的问题是 API 调用的响应(这意味着 - 最新的 APK 文件)具有如图所示的 base64String 表示在下图中 - 例如,当在浏览器中进行 API 调用时,这只是服务器响应的一部分。

是否有可能在下载后从这个字符串表示中生成一个 "real" APK 文件,这样我就可以在设备上安装它了?我知道这很奇怪,但客户希望我为此重新使用相同的 Web 服务。

我发现了一个类似的问题 here。这在 Kotlin 中如何完成?提前致谢。

是的,您需要将 base64 解码为 ByteArray,然后将字节写入后缀为 .apk 的位置。您拥有的是 String,其中 bytesencoded,使用 base64 编码方案。

自从您使用 kotlin 后,您可能需要查看此处的内容才能从 String 获取 ByteArray![1]。然后确保您写入的文件具有 .apk 扩展名。

[1] https://developer.android.com/reference/kotlin/java/util/Base64.Decoder#decode(kotlin.String)

AndroidManifest.xml

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

带 LiveData 的 MVVM:

    override fun getUpdate() {
        disposables += api.getUpdate(
            program = context.getString(R.string.app_name) + ".apk",
            version = context.getString(R.string.app_version),
            test = isTest
        )
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .map(::decodeToAPK)
            .subscribe({
                liveData.value = GetUpdate 
            }, {
                liveData.value = Error("Error getUpdate: " + it.message)
            })
    }


    private fun decodeToAPK(base64String: String) {

        val byteArrayAPK = Base64.decode(base64String, Base64.DEFAULT)

        val file =
            File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "yourApp.apk")
        try {
            val fileOutputStream = FileOutputStream(file)
            fileOutputStream.write(byteArrayAPK)
            fileOutputStream.flush()
            fileOutputStream.close()
        } catch (e: UnsupportedEncodingException) {
            Log.d(null, "error writing APK")
            e.printStackTrace()
        }
   }

   sealed class SomeAction : Action {
     data class Error(val message: String) : SomeAction()
     object GetUpdate : SomeAction()
   }