發
最近在學習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>
如果還有疑問或者還不成功的小夥伴可以取下載源碼。