增量更新

 

            最近在學習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>

如果還有疑問或者還不成功的小夥伴可以取下載源碼。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章