Android NDK開發(fā)兩部曲(二)之應(yīng)用篇(增量更新也就那樣)

目錄

Android NDK開發(fā)兩部曲(一)之初識篇(JNI通識與NDK配置)
Android NDK開發(fā)兩部曲(二)之應(yīng)用篇(增量更新也就那樣)

前言

之前發(fā)過一篇在Eclipse上關(guān)于JNI和NDK的博客Android JNI與Android NDK掃盲, 里面只是籠統(tǒng)地介紹了一下, 文章本身并沒有深入學習的意思. 那么現(xiàn)在我們就開始在這篇文章的基礎(chǔ)上, 在Android Studio上面去詳細學習NDK開發(fā)吧.

本文參考鴻洋大神的Android 增量更新完全解析 是增量不是熱修復, 學習過程中. 發(fā)現(xiàn)Android Studio對NDK的支持越來越友好, 并不需要像以前那樣一個個地去新建文件.太多的原理就不重復了, 主要演示一下新版本下的操作變化

建議先看看鴻洋大神的博客再回來看這里

如果還沒有NDK環(huán)境, 那么請參考Android NDK開發(fā)三部曲(一)之配置篇(AS對NDK友好集成)

新建項目

這里我的項目就叫BadPatchDemo

android_studio_new_native_project.png

同時勾選Include C++ support

如果沒有安裝cmake(一個跨平臺的C/C++項目編譯配置工具), 那么AS會提示安裝cmake, 按照提示完成安裝就可以了.

android_studio_install_cmake.png

NDK開發(fā)

我們發(fā)現(xiàn)app/src/main/多了一個cpp的文件夾, 這里就是存放我們源文件的目錄.

添加第三方代碼

前提是你按照鴻洋大神的博客里面那樣, 安裝了bsdiff. 因為生成差分包的工作應(yīng)該是在服務(wù)器, 所以我們就只集成bspatch來對差分包跟舊安裝包合并.

大家可以下載代碼, 這里是篩選了某些源代碼的

鏈接: https://pan.baidu.com/s/1dEHNhx3 密碼: abnf

解壓后我們直接把bspatch.cbzip拷貝進去cpp文件夾, 然后切換到project視圖編輯app/CMakeLists.txt

然后加入需要編譯的代碼

src/main/cpp/bzip2/blocksort.csrc/main/cpp/bzip2/bzip2.csrc/main/cpp/bzip2/bzip2recover.csrc/main/cpp/bzip2/bzlib.csrc/main/cpp/bzip2/compress.csrc/main/cpp/bzip2/crctable.csrc/main/cpp/bzip2/decompress.csrc/main/cpp/bzip2/huffman.csrc/main/cpp/bzip2/randtable.csrc/main/cpp/bspatch.c
android_studio_add_sourcefiles.png

最后點擊同步按鈕

暴露出接口

我們在cpp目錄郵件下新建C/C++ Header File->bspatch.h, 加入下面代碼

#ifndef BSDPATCHDEMO_BSPATCH_H
#define BSDPATCHDEMO_BSPATCH_H
#endif 
//BSDPATCHDEMO_BSPATCH_Hint 
doPatch(int argc, char *argv[]);

其中doPatch是bspatch.c的main函數(shù)改名而來, 因為我們在命令行下面執(zhí)行命令的時候, 其實就是調(diào)用命令行工具的main函數(shù). 后面的參數(shù)會作為字符串的形式傳入到main函數(shù)中. 其中argc表示參數(shù)的個數(shù),argv存著參數(shù)的具體內(nèi)容

例如:

mv test /root/
argc = 3
argv[0] = mv
argv[1] = test
argv[2] = /root/

編寫native代碼

我們新建BsdUtil

package com.example.bsdpatchdemo;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;

import java.io.File;

/**
 * Created by August on 2017/3/18.
 */

public class BsdUtil {

    static {
        System.loadLibrary("native-lib");
    }

    /**
     * 原生打補丁方法
     *
     * @param oldApk
     * @param patchFile
     * @param newApk
     */
    public static native void patch(String oldApk, String patchFile, String newApk);


    /**
     * 獲取本身APK的路徑
     *
     * @param context
     * @return
     */
    public static String getOriginalApk(Context context) {
        return context.getApplicationInfo().sourceDir;
    }

    /**
     * 安裝指定apk
     *
     * @param context
     * @param apk
     */
    public static void install(Context context, String apk) {
        Intent intent = new Intent();
        intent.setDataAndType(Uri.fromFile(new File(apk)),
                "application/vnd.android.package-archive");
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }
}

我們看到patch爆紅, 這時候我們點一下提示, 然后讓as幫我們創(chuàng)建方法. 然后我們就直接調(diào)到對應(yīng)的C/C++文件里面了

現(xiàn)在我們需要引入的剛剛暴露方法的頭文件bspatch.h

#include <jni.h>
#include <string>
#include "bspatch.h"

JNIEXPORT void JNICALL
Java_com_example_bsdpatchdemo_BsdUtil_patch(JNIEnv *env, jclass type, jstring oldApk_,
                                            jstring patchFile_, jstring newApk_) {
    const char *oldApk = env->GetStringUTFChars(oldApk_, 0);
    const char *patchFile = env->GetStringUTFChars(patchFile_, 0);
    const char *newApk = env->GetStringUTFChars(newApk_, 0);

    // TODO
    char *argv[] = {(char *) "bspatch", (char *) oldApk, (char *) newApk, (char *) patchFile};
    doPatch(4, argv);


    env->ReleaseStringUTFChars(oldApk_, oldApk);
    env->ReleaseStringUTFChars(patchFile_, patchFile);
    env->ReleaseStringUTFChars(newApk_, newApk);
}

編寫調(diào)用代碼

activity_main

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:onClick="patch"
    android:text="old app"
    android:textSize="20sp" />

MainActivity

package com.example.bsdpatchdemo;

import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import java.io.File;

public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void patch(View v) {
        String oldApk = BsdUtil.getOriginalApk(this);
        String newApk = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "newApk.apk";
        String patchFile = "/storage/sdcard0/p.patch";
        BsdUtil.patch(oldApk, patchFile, newApk);
        BsdUtil.install(this, newApk);
    }

}

涉及到讀寫權(quán)限的, 不要忘了在AndroidManifest中加入

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

編譯

Build->Build APK,編譯的時候我這邊爆了一個鏈接錯誤, 估計是C++跟C語言混編配置不當造成的.

解決方法

  • native-lib.cpp改成native-lib.c

  • 同時把CMakeLists.txt里面的native-lib.cpp改成native-lib.c

  • 然后把native-lib.c中面向?qū)ο蟮膶懛ǜ幕谻語言面向過程的寫法

#include <jni.h>
#include "bspatch.h"

JNIEXPORT void JNICALL
Java_com_example_bsdpatchdemo_BsdUtil_patch(JNIEnv *env, jclass type, jstring oldApk_,
                                            jstring patchFile_, jstring newApk_) {
    const char *oldApk = (*env)->GetStringUTFChars(env, oldApk_, 0);
    const char *patchFile = (*env)->GetStringUTFChars(env, patchFile_, 0);
    const char *newApk = (*env)->GetStringUTFChars(env, newApk_, 0);

    char *argv[] = {(char *) "bspatch", (char *) oldApk, (char *) newApk, (char *) patchFile};
    doPatch(4, argv);


    (*env)->ReleaseStringUTFChars(env, oldApk_, oldApk);
    (*env)->ReleaseStringUTFChars(env, patchFile_, patchFile);
    (*env)->ReleaseStringUTFChars(env, newApk_, newApk);
}

再次編譯APK

測試

  • 現(xiàn)在我們得到的是舊的APK改名為old.apk, 給手機安裝上

  • 然后我們把activity_main中的old app改成new app, 然后再build出新的apk, 改名為new.apk.

  • 有了兩個apk我們可以生成差分包

yubindeMacBook-Pro:Desktop August$ bsdiff old.apk new.apk p.patch
yubindeMacBook-Pro:Desktop August$ adb push p.patch /storage/sdcard0/p.patch
[100%] /storage/sdcard0/p.patch
android_bspatch.gif

使用熱修復雖然不需要安裝, 但幾乎市面上的熱修復框架都是基于運行時修復的, 所以存在很大兼容性問題. 但是增量更新是通過合并差分包來更新的, 只要差分包是對的, 那么幾乎不會存在兼容性問題. 很多時候應(yīng)用只需要修改代碼, 但是資源文件是不變的. 這時候用增量更新, 就能減去資源文件的大小, 從而達到省流量更新.

說在最后

其實本博文主要目的是說明新版的Android Studio的NDK開發(fā)方式, 增量更新只是作為其中一個應(yīng)用分析. 其實這個過程也成為移植. 大家也可以去找一些C庫, 圖片壓縮 聲音視頻變換之類的, 然后移植到Android上, 也是挺有趣的.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容