我目前正在研究如何以编程方式更新和安装APK文件的解决方案-非常类似于this问题。我的应用程序不使用 Google服务。
出于更新目的,具有递增版本的APK文件存储在内部服务器上,并且C#Web服务返回APK文件的最新版本。
我使用Retrofit2:
@Streaming
@GET("/ws/webservice.svc/getUpdate")
fun getUpdate(
@Query("Programm") program: String,
@Query("version") version: String,
@Query("test") test: Boolean
): Single<String>
和LiveData:
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时的服务器响应。
下载后,是否可以通过某种方式从此String表示形式生成“真实的” APK文件,因此之后我可以将其安装在设备上了吗?我知道这很奇怪,但是客户希望我为此目的重复使用相同的Web服务。
我发现了类似的问题here。如何在Kotlin中完成?预先感谢。
是,您需要将base64
解码为ByteArray
,然后将字节写入带有后缀.apk
的位置。您所拥有的是一个String
,其中使用bytes
编码方案将encoded
设为base64
。
由于您使用了kotlin
,因此您可能会在这里看到如何从ByteArray!
中获得String
[1]。然后,只需确保您写入的文件具有.apk
扩展名即可。
[1] https://developer.android.com/reference/kotlin/java/util/Base64.Decoder#decode(kotlin.String)
这应该是这个问题的答案。由于硬件限制,我无法为我的项目使用代码-我在Zebra VC80x上使用了该代码-根据Zebra的说法,不可能在内部存储上写入数据-因此,我总是收到错误消息“ Permission否认”。但这也许会帮助别人...
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 path = Environment.getExternalStorageDirectory().path
Log.d(null, path)
val dir = File(path)
if (!dir.exists()) {
dir.mkdirs()
Log.d(null, "dir created")
}
val fullName = "$path/appName.apk"
val file = File(fullName)
Log.d(null, "fullname: $fullName not yet created")
if (!file.exists()) {
file.createNewFile()
Log.d(null, "fullname: $fullName created")
}
if (byteArrayAPK.isNotEmpty()) {
Log.d(null, "byteArrayAPK is not empty")
try {
val fileOutputStream = FileOutputStream(file)
Log.d(null, "init FileOutputStream")
fileOutputStream.write(byteArrayAPK)
fileOutputStream.close()
Log.d(null, "fileOutputStream.close() $file")
} catch (e: UnsupportedEncodingException) {
Log.d(null, "FileOutputStream error")
e.printStackTrace()
}
}
sealed class SomeAction : Action {
data class Error(val message: String) : SomeAction()
object GetUpdate : SomeAction()
}