当遇到直接通过Google play上的版本名称来更新我们自己的App时,这时我们可以通过访问Google play上目标App的版本信息,通知自己的App,并跳转到Google play。下面来看具体实现。
implementation 'org.jsoup:jsoup:1.10.2'
ps:jsoup官网、github源码、文档这个工具主要是java代码,作为抓取HTML的工具,有很高的效率,如果有兴趣可以通过文档详细了解这个工具。我们可以通过他获得网页上想要的信息,正如现在我们需要“current version”信息。
val document: Document = Jsoup.connect(
"https://play.google.com/store/apps/details?id=" + (if (!BuildConfig.DEBUG) BuildConfig.APPLICATION_ID else testPackageName) + "&hl=en"
).timeout(30000).userAgent("Mozilla/5.0 (Windows; U; WindowsNT 5.1; en-US; rv1.8.1.6) Gecko/20070725 Firefox/2.0.0.6").referrer("http://www.google.com").get()
其中connect里面为要访问的目标url,拼接的id为访问目标的app id,通常为包名。这里可以直接获取BuildConfig.APPLICATION_ID当前项目id,如果在测试的时候没有上架到google play,可以找个已上架的app,作为测试id。
timeout为链接超时时间,userAgent为当前链接需要设置的代理信息。
val element: Elements = document.getElementsContainingOwnText("Current Version")
for (ele in element) {
Logg.d(tag, "ele:$ele")
if (ele.siblingElements() != null) {
Logg.d(tag, "ele.siblingElements:${ele.siblingElements()}")
val sibElemets: Elements = ele.siblingElements()
for (sibElemet in sibElemets) {
newVersion = sibElemet.text()
}
}
}
通过第二步的操作,成功了的话我们可以获得所有的HTML信息并放入document中。此时只需要输入我们需要元素key值,再轮巡取出目标text,这就是本次所要的version值。当然,你也可以根据需要分析document,在这些信息取出需要的信息。
activity?.let { it ->
val intentData = Intent(Intent.ACTION_VIEW).setData(
Uri.parse(
"market://details?id=" + if (!BuildConfig.DEBUG) it.packageName else testPackageName
)
)
if (it.packageManager?.let { intentData.resolveActivity(it) } != null) {
it.startActivity(intentData)
} else {
intentData.data = Uri.parse(
"https://play.google.com/store/apps/details?id=" + if (!BuildConfig.DEBUG) it.packageName else testPackageName
)
it.startActivity(intentData)
}
}
如果顺利的话,第三步我们已经拿到了关键的version name,此时只要根据自己的逻辑判断,跳转到Google play就可以了。当然,如果当前设备没有安装Google play,那就跳转浏览器。这里的id便是跳转目标app的包名packageName.
object VersionUpdateUtil {
private val tag = "${this.javaClass.simpleName}=="
private var newVersion: String? = null
//test app package name
private const val testPackageName = "com.embrace_2redbeans"
private fun getVersionGooglePlay(): String? {
try {
val document: Document = Jsoup.connect(
"https://play.google.com/store/apps/details?id=" + (if (!BuildConfig.DEBUG) BuildConfig.APPLICATION_ID else testPackageName) + "&hl=en"
).timeout(30000).userAgent("Mozilla/5.0 (Windows; U; WindowsNT 5.1; en-US; rv1.8.1.6) Gecko/20070725 Firefox/2.0.0.6").referrer("http://www.google.com").get()
// d(tag,"document:$document")
val element: Elements = document.getElementsContainingOwnText("Current Version")
for (ele in element) {
Logg.d(tag, "ele:$ele")
if (ele.siblingElements() != null) {
Logg.d(tag, "ele.siblingElements:${ele.siblingElements()}")
val sibElemets: Elements = ele.siblingElements()
for (sibElemet in sibElemets) {
newVersion = sibElemet.text()
}
}
}
} catch (e: IOException) {
e.printStackTrace()
}
return newVersion
}
/**
* first->currentVersionName
* second->mLatestVersionName
* third->is not equal
*/
fun startCheckVersion(): Triple<String?, String?, Boolean> {
return try {
val mLatestVersionName = getVersionGooglePlay()
val currentVersionName = BuildConfig.VERSION_NAME
Logg.d(tag, "versionName:$mLatestVersionName")
Logg.d(tag, "VERSION_CODE:$currentVersionName")
Triple(currentVersionName, mLatestVersionName, mLatestVersionName != currentVersionName)
} catch (e: Exception) {
e.printStackTrace()
Triple(null, null, false)
}
}
@SuppressLint("QueryPermissionsNeeded")
fun jumpToGooglePlay(activity: Activity?) {
activity?.let { it ->
val intentData = Intent(Intent.ACTION_VIEW).setData(
Uri.parse(
"market://details?id=" + if (!BuildConfig.DEBUG) it.packageName else testPackageName
)
)
if (it.packageManager?.let { intentData.resolveActivity(it) } != null) {
it.startActivity(intentData)
} else {
intentData.data = Uri.parse(
"https://play.google.com/store/apps/details?id=" + if (!BuildConfig.DEBUG) it.packageName else testPackageName
)
it.startActivity(intentData)
}
}
}
}
coroutineScope?.launch(context = ioDispatcherDispatchers.IO) {
try {
onGetResponse(resultCallback, VersionUpdateUtil.startCheckVersion())
} catch (e: Exception) {
e.printStackTrace()
onGetResponse(resultCallback, null)
}
}
suspend fun <T> withNonCancellable(block: suspend CoroutineScope.() -> T): T {
return withContext(NonCancellable, block)
}
suspend fun <T> withMain(block: suspend CoroutineScope.() -> T): T {
return withContext(mainDispatcher, block)
}
suspend fun <Data> onGetResponse(callback: (Data?) -> Unit, httpData: Data?) {
callback.let {
withNonCancellable {
withMain {
it.invoke(httpData)
}
}
}
}
获取HTML数据时,耗时操作要放入协程里面。目前的这个更新方式也有小缺点:google play哪天改了current version,那我们的app就没法获取最新版本更新了。