发
最近在学习kotlin这是写的第一篇kotlin相关的博客。平时我们做更新都是下载版本的apk,然后在安装。这样子很浪费用户的流量。今天来讲一种增量更新的方法。就是新旧版本的apk对比,讲两版的不一样的地方生成一个差分包。然后每次下载差分包,和旧版本的apk合并成新的apk,这样子可以帮用户节约好多流量。
一,得到差分包。
要生成差分包,我们需要借助一个工具bsdiff.exe,想详细了解这个工具的可以自己取百度,我们这里只告诉大家怎么使用。先下载工具bsdiff4.3-win32.rar,然后解压。解压之后里面有测试的新旧版本的apk(1.0.apk和2.0.apk) 和生成的差分包(demo.patch)。你可以先备份一份,然后删掉换上自己的apk,也可以直接用这个测试。
解压bsdiff4.3-win32.rar之后,在文件夹里打开cmd命令框。输入命名 bsdiff oldfile newfile patchfile,然后会生成新的差分包。
二,添加相关的引用。
kotlin开发为了开发方便,我们可以先集成anko到自己的项目(https://github.com/Kotlin/anko)。在Module的build.gradle添加依赖implementation "org.jetbrains.anko:anko:0.10.8"。就可以了。
和并差分包还需要添加一个依赖,他在github上的地址https://github.com/jiyouliang2/SmartUpdateDemo。添加的方法很简单。
1. 在project的build.gradle添加如下代码(如下图)
allprojects {
repositories {
maven { url "https://jitpack.io" }
}
}
2. 在Module的build.gradle添加依赖(如下图)
compile 'com.github.jiyouliang2:SmartUpdateDemo:1.0.1'
三,直接上代码。
只要的工作都在MainActivity里面。注释写的很详细,不在啰嗦。
class MainActivity : AppCompatActivity() {
//6.0以上的需要动态获取权限
private val REQUEST_EXTERNAL_STORAGE = 1
private var PERMISSIONS_STORAGE = arrayOf("android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE")
private val mDialog: ProgressDialog by lazy { ProgressDialog(this) }//懒加载
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
verifyStoragePermissions()//6.0以上要动态加载权限
//动态升级
updata.onClick {
//8.0以上的版本要先判断是否有安装应用的权限。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //兼容8.0
if (!packageManager.canRequestPackageInstalls()) {
startInstallPermissionSettingActivity()
return@onClick
}
}
//https://github.com/jiyouliang2/SmartUpdateDemo github上合并差分包和旧版本的项目地址。
var pm: PackageManager = packageManager//获得包管理器
var appInfo = pm.getApplicationInfo(packageName, 0)//通过包管理器得到app信息
var oldPath = appInfo.sourceDir;//旧版本路径
val newApkFile = File(getExternalStorageDirectory(), "2.0.apk")//新版本保存路径(将要生成的新版本的apk存放的路径)
val patchFile = File(Environment.getExternalStorageDirectory(), "demo.patch")//更新包路径 手动的讲demo.patch放到该路径,或者网上下载到该路径
if (patchFile.exists()) {
doAsync {
val result = PatchUtil.patch(oldPath, newApkFile.absolutePath, patchFile.absolutePath)//合并patch和旧的apk,生成新的apk
if (result == 0) {
//合并成功,安装apk
runOnUiThread {
mDialog.dismiss()
install(newApkFile)//安装apk
}
} else {
toast("合并失败")
}
}
} else {
toast("请将差分包demo.patch保存到sdcard")
}
}
}
private fun install(apkFile: File) {
try {
intent = Intent(Intent.ACTION_VIEW)
//兼容7.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
var contentUri = FileProvider.getUriForFile(this, "$packageName.fileProvider", apkFile)
intent.setDataAndType(contentUri, "application/vnd.android.package-archive")
} else {
intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
startActivity(intent)
} catch (e: Throwable) {
e.printStackTrace()
}
}
/**
* 8.0以上的版本需要判断是否有权限安装应用
*/
@RequiresApi(api = Build.VERSION_CODES.O)
fun startInstallPermissionSettingActivity() {
var packageURI = Uri.parse("package:" + getPackageName())
//注意这个是8.0新API
var intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI)
startActivityForResult(intent, 10086)
}
/**
* android 动态权限申请
*/
private fun verifyStoragePermissions() {
try {
//检测是否有写的权限
var permission = ActivityCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE")
if (permission != PackageManager.PERMISSION_GRANTED) {
// 没有写的权限,去申请写的权限,会弹出对话框
ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
这个是布局文件。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:text="v2.0"
android:textSize="20sp"
android:layout_height="wrap_content" />
<Button
android:id="@+id/updata"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="更新"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
6.0以上的安卓版本,访问sd卡是要动态添加权限的。8.0以上的安装apk也要做处理,这些代码中都处理了,不知道的小伙伴可以自己百度下。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.administrator.demokt1">
<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"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.administrator.demokt1.fileProvider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
</application>
</manifest>
如果还有疑问或者还不成功的小伙伴可以取下载源码。