搜索
您的当前位置:首页正文

Android NDK开发两部曲(二)之应用篇(增量更新也就那样

来源:二三娱乐

目录

前言

建议先看看鸿洋大神的博客再回来看这里

新建项目

这里我的项目就叫BadPatchDemo

android_studio_new_native_project.png

同时勾选Include C++ support

如果没有安装cmake(一个跨平台的C/C++项目编译配置工具), 那么AS会提示安装cmake, 按照提示完成安装就可以了.

android_studio_install_cmake.png

NDK开发

我们发现app/src/main/多了一个cpp的文件夹, 这里就是存放我们源文件的目录.

添加第三方代码

前提是你按照鸿洋大神的博客里面那样, 安装了bsdiff. 因为生成差分包的工作应该是在服务器, 所以我们就只集成bspatch来对差分包跟旧安装包合并.

大家可以下载代码, 这里是筛选了某些源代码的

链接:  密码: 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函数改名而来, 因为我们在命令行下面执行命令的时候, 其实就是调用命令行工具的main函数. 后面的参数会作为字符串的形式传入到main函数中. 其中argc表示参数的个数,argv存着参数的具体内容

例如:

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 

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帮我们创建方法. 然后我们就直接调到对应的C/C++文件里面了

现在我们需要引入的刚刚暴露方法的头文件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);
}

编写调用代码

activity_main

<?xml version="1.0" encoding="utf-8"?>
<TextView 
    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);
    }

}

涉及到读写权限的, 不要忘了在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中面向对象的写法改回C语言面向过程的写法

#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

测试

  • 现在我们得到的是旧的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

使用热修复虽然不需要安装, 但几乎市面上的热修复框架都是基于运行时修复的, 所以存在很大兼容性问题. 但是增量更新是通过合并差分包来更新的, 只要差分包是对的, 那么几乎不会存在兼容性问题. 很多时候应用只需要修改代码, 但是资源文件是不变的. 这时候用增量更新, 就能减去资源文件的大小, 从而达到省流量更新.

说在最后

其实本博文主要目的是说明新版的Android Studio的NDK开发方式, 增量更新只是作为其中一个应用分析. 其实这个过程也成为移植. 大家也可以去找一些C库, 图片压缩 声音视频变换之类的, 然后移植到Android上, 也是挺有趣的.

Top