手把手实现andriod应用增量升级

近期研究了android应用增量升级的应用。当中用到了android NDK编程,先说下为什么要使用增量升级。当我们的应用达到一定大小的时候,比方眼下有30M。假设新版本号35M仅仅是添加了几个功能,而之前都保持不变,那么这个时候我们能够生成一个差分包,这个差分包一般就6M左右大小,大大的为用户节省了流量。

增量升级原理

android增量升级,首先在server端生成差分包,然后用户下载差分包。在手机端,将该差分包和旧版本号的apk合成为新的版本号。

使用到的开源项目

bsdiff :是一个二进制差分工具。 用于生成差分包

bspatch :是一个开源的合成工具,android通过ndk将其编译为so文件然后调用其代码就可以实现:差分包和旧版本号apk的合成。

另外还须要下载:bzip2-1.0.6

http://www.bzip.org/downloads.html

增量升级的实现

increaseone1.0.apk

在正式实现之前,当然是先准备我们的apk了,这里我编写一个简单的project,执行效果例如以下:

将该该projectbin文件夹下生成的apk复制到”D:\increase”文件夹下。而且又一次命名为”increaseone1.0.apk”

increaseone2.0.apk

如今加入一个activity,而且在该projectmainactivity中加入一个button能够跳转到该activity。主要是为了差别。此时执行后的效果例如以下:

相同将bin文件夹下的最新的apk文件拷贝至”d:\increase”文件夹下。重命名为”increaseone2.0.apk”

生成差分包

准备好不同版本号的apk后,就须要生成差分包。这里通常是在服务端来实现的。这里用到了”bsdiff4.3-win32”工具:

进入命令行下输入例如以下命令:”bsdiff.exe 旧版本号的apk路径 新版本号的apk路径 生成的差分包的路径”,例如以下图:

此时在”d:\increase”文件夹下回生成一个increase.patch的差分包。

手机端实现patch合成

创建androidproject

新建一个androidprojectselfincrease

新建类:”PatchUpdate.java”用来调用底层的c代码实现patch的合成。

package com.example.selfincrease;

public class PatchUpdate {
    public  native  int patch(String oldApkPath,    String  newApkPath, String  patchPath);
}

调用javac和javah生成头文件:

具体的步骤能够參考一步一步学习androidNDK编程(hello world)

创建jni实现c代码

在该androidproject文件夹下,新建一个jni文件夹,将中的例如以下文件复制到该文件夹下:

blocksort.c

bzip2.c

bzip2recover.c

bzlib_private.h

bzlib.c

bzlib.h

com_example_selfincrease_PatchUpdate.h

compress.c

crctable.c

decompress.c

dlltest.c

huffman.c

mk251.c

randtable.c

spewG.c

unzcrash.c

期中”com_example_selfincrease_PatchUpdate.h”是刚才生成的头文件。

在jni文件夹下新建”com_example_selfincrease_PatchUpdate.c”文件,内容例如以下:

#include <stdio.h>

#include "bzlib_private.h"
#include "bzlib.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <err.h>
#include <unistd.h>
#include <fcntl.h>
#include <android/log.h>
#include <jni.h>
#include "com_example_selfincrease_PatchUpdate.h"

/*---------------------------------------------------*/
void BZ2_hbCreateDecodeTables ( Int32 *limit,
                                Int32 *base,
                                Int32 *perm,
                                UChar *length,
                                Int32 minLen,
                                Int32 maxLen,
                                Int32 alphaSize )
{
   Int32 pp, i, j, vec;

   pp = 0;
   for (i = minLen; i <= maxLen; i++)
      for (j = 0; j < alphaSize; j++)
         if (length[j] == i) { perm[pp] = j; pp++; };

   for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0;
   for (i = 0; i < alphaSize; i++) base[length[i]+1]++;

   for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1];

   for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0;
   vec = 0;

   for (i = minLen; i <= maxLen; i++) {
      vec += (base[i+1] - base[i]);
      limit[i] = vec-1;
      vec <<= 1;
   }
   for (i = minLen + 1; i <= maxLen; i++)
      base[i] = ((limit[i-1] + 1) << 1) - base[i];
}

static
void makeMaps_d ( DState* s )
{
   Int32 i;
   s->nInUse = 0;
   for (i = 0; i < 256; i++)
      if (s->inUse[i]) {
         s->seqToUnseq[s->nInUse] = i;
         s->nInUse++;
      }
}

/*---------------------------------------------------*/
#define RETURN(rrr)                               \
   { retVal = rrr; goto save_state_and_return; };

#define GET_BITS(lll,vvv,nnn)                     \
{                                                    case lll: s->state = lll;                         while (True) {                                       if (s->bsLive >= nnn) {                              UInt32 v;                                         v = (s->bsBuff >>                                     (s->bsLive-nnn)) & ((1 << nnn)-1);            s->bsLive -= nnn;                                 vvv = v;                                          break;                                         }                                                 if (s->strm->avail_in == 0) RETURN(BZ_OK);        s->bsBuff                                            = (s->bsBuff << 8) |                                ((UInt32)                                            (*((UChar*)(s->strm->next_in))));         s->bsLive += 8;                                   s->strm->next_in++;                               s->strm->avail_in--;                        \
      s->strm->total_in_lo32++;                         if (s->strm->total_in_lo32 == 0)                     s->strm->total_in_hi32++;                   }                                              }

#define GET_UCHAR(lll,uuu)                        \
   GET_BITS(lll,uuu,8)

#define GET_BIT(lll,uuu)                          \
   GET_BITS(lll,uuu,1)

/*---------------------------------------------------*/
#define GET_MTF_VAL(label1,label2,lval)           \
{                                                    if (groupPos == 0) {                                 groupNo++;                                        if (groupNo >= nSelectors)                           RETURN(BZ_DATA_ERROR);                         groupPos = BZ_G_SIZE;                             gSel = s->selector[groupNo];                      gMinlen = s->minLens[gSel];                       gLimit = &(s->limit[gSel][0]);                    gPerm = &(s->perm[gSel][0]);                      gBase = &(s->base[gSel][0]);                   }                                                 groupPos--;                                    \
   zn = gMinlen;                                     GET_BITS(label1, zvec, zn);                       while (1) {                                          if (zn > 20 /* the longest code */)                  RETURN(BZ_DATA_ERROR);                         if (zvec <= gLimit[zn]) break;                    zn++;                                             GET_BIT(label2, zj);                              zvec = (zvec << 1) | zj;                       };                                                if (zvec - gBase[zn] < 0                              || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE)        RETURN(BZ_DATA_ERROR);                         lval = gPerm[zvec - gBase[zn]];                }

//#define BZ_OK                0

Int32 BZ2_rNums[512] = {
   619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
   985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
   733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
   419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
   878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
   862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
   150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
   170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
   73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
   909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
   641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
   161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
   382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
   98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
   227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
   469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
   184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
   715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
   951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
   652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
   645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
   609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
   653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
   411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
   170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
   857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
   669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
   944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
   344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
   897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
   433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
   686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
   946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
   978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
   680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
   707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
   297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
   134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
   343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
   140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
   170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
   369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
   804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
   896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
   661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
   768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
   61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
   372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
   780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
   920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
   645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
   936, 638
};

Int32 BZ2_decompress ( DState* s )
{
   UChar      uc;
   Int32      retVal;
   Int32      minLen, maxLen;
   bz_stream* strm = s->strm;

   /* stuff that needs to be saved/restored */
   Int32  i;
   Int32  j;
   Int32  t;
   Int32  alphaSize;
   Int32  nGroups;
   Int32  nSelectors;
   Int32  EOB;
   Int32  groupNo;
   Int32  groupPos;
   Int32  nextSym;
   Int32  nblockMAX;
   Int32  nblock;
   Int32  es;
   Int32  N;
   Int32  curr;
   Int32  zt;
   Int32  zn;
   Int32  zvec;
   Int32  zj;
   Int32  gSel;
   Int32  gMinlen;
   Int32* gLimit;
   Int32* gBase;
   Int32* gPerm;

   if (s->state == BZ_X_MAGIC_1) {
      /*initialise the save area*/
      s->save_i           = 0;
      s->save_j           = 0;
      s->save_t           = 0;
      s->save_alphaSize   = 0;
      s->save_nGroups     = 0;
      s->save_nSelectors  = 0;
      s->save_EOB         = 0;
      s->save_groupNo     = 0;
      s->save_groupPos    = 0;
      s->save_nextSym     = 0;
      s->save_nblockMAX   = 0;
      s->save_nblock      = 0;
      s->save_es          = 0;
      s->save_N           = 0;
      s->save_curr        = 0;
      s->save_zt          = 0;
      s->save_zn          = 0;
      s->save_zvec        = 0;
      s->save_zj          = 0;
      s->save_gSel        = 0;
      s->save_gMinlen     = 0;
      s->save_gLimit      = NULL;
      s->save_gBase       = NULL;
      s->save_gPerm       = NULL;
   }

   /*restore from the save area*/
   i           = s->save_i;
   j           = s->save_j;
   t           = s->save_t;
   alphaSize   = s->save_alphaSize;
   nGroups     = s->save_nGroups;
   nSelectors  = s->save_nSelectors;
   EOB         = s->save_EOB;
   groupNo     = s->save_groupNo;
   groupPos    = s->save_groupPos;
   nextSym     = s->save_nextSym;
   nblockMAX   = s->save_nblockMAX;
   nblock      = s->save_nblock;
   es          = s->save_es;
   N           = s->save_N;
   curr        = s->save_curr;
   zt          = s->save_zt;
   zn          = s->save_zn;
   zvec        = s->save_zvec;
   zj          = s->save_zj;
   gSel        = s->save_gSel;
   gMinlen     = s->save_gMinlen;
   gLimit      = s->save_gLimit;
   gBase       = s->save_gBase;
   gPerm       = s->save_gPerm;

   retVal = BZ_OK;

   switch (s->state) {

      GET_UCHAR(BZ_X_MAGIC_1, uc);
      if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC);

      GET_UCHAR(BZ_X_MAGIC_2, uc);
      if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC);

      GET_UCHAR(BZ_X_MAGIC_3, uc);
      if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC);

      GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8);
      if (s->blockSize100k < (BZ_HDR_0 + 1) ||
          s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC);
      s->blockSize100k -= BZ_HDR_0;

      if (s->smallDecompress) {
         s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) );
         s->ll4  = BZALLOC(
                      ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar)
                   );
         if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR);
      } else {
         s->tt  = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) );
         if (s->tt == NULL) RETURN(BZ_MEM_ERROR);
      }

      GET_UCHAR(BZ_X_BLKHDR_1, uc);

      if (uc == 0x17) goto endhdr_2;
      if (uc != 0x31) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_2, uc);
      if (uc != 0x41) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_3, uc);
      if (uc != 0x59) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_4, uc);
      if (uc != 0x26) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_5, uc);
      if (uc != 0x53) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_6, uc);
      if (uc != 0x59) RETURN(BZ_DATA_ERROR);

      s->currBlockNo++;
      if (s->verbosity >= 2)
         VPrintf1 ( "\n    [%d: huff+mtf ", s->currBlockNo );

      s->storedBlockCRC = 0;
      GET_UCHAR(BZ_X_BCRC_1, uc);
      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_BCRC_2, uc);
      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_BCRC_3, uc);
      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_BCRC_4, uc);
      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);

      GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1);

      s->origPtr = 0;
      GET_UCHAR(BZ_X_ORIGPTR_1, uc);
      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
      GET_UCHAR(BZ_X_ORIGPTR_2, uc);
      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
      GET_UCHAR(BZ_X_ORIGPTR_3, uc);
      s->origPtr = (s->origPtr << 8) | ((Int32)uc);

      if (s->origPtr < 0)
         RETURN(BZ_DATA_ERROR);
      if (s->origPtr > 10 + 100000*s->blockSize100k)
         RETURN(BZ_DATA_ERROR);

      /*--- Receive the mapping table ---*/
      for (i = 0; i < 16; i++) {
         GET_BIT(BZ_X_MAPPING_1, uc);
         if (uc == 1)
            s->inUse16[i] = True; else
            s->inUse16[i] = False;
      }

      for (i = 0; i < 256; i++) s->inUse[i] = False;

      for (i = 0; i < 16; i++)
         if (s->inUse16[i])
            for (j = 0; j < 16; j++) {
               GET_BIT(BZ_X_MAPPING_2, uc);
               if (uc == 1) s->inUse[i * 16 + j] = True;
            }
      makeMaps_d ( s );
      if (s->nInUse == 0) RETURN(BZ_DATA_ERROR);
      alphaSize = s->nInUse+2;

      /*--- Now the selectors ---*/
      GET_BITS(BZ_X_SELECTOR_1, nGroups, 3);
      if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR);
      GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15);
      if (nSelectors < 1) RETURN(BZ_DATA_ERROR);
      for (i = 0; i < nSelectors; i++) {
         j = 0;
         while (True) {
            GET_BIT(BZ_X_SELECTOR_3, uc);
            if (uc == 0) break;
            j++;
            if (j >= nGroups) RETURN(BZ_DATA_ERROR);
         }
         s->selectorMtf[i] = j;
      }

      /*--- Undo the MTF values for the selectors. ---*/
      {
         UChar pos[BZ_N_GROUPS], tmp, v;
         for (v = 0; v < nGroups; v++) pos[v] = v;

         for (i = 0; i < nSelectors; i++) {
            v = s->selectorMtf[i];
            tmp = pos[v];
            while (v > 0) { pos[v] = pos[v-1]; v--; }
            pos[0] = tmp;
            s->selector[i] = tmp;
         }
      }

      /*--- Now the coding tables ---*/
      for (t = 0; t < nGroups; t++) {
         GET_BITS(BZ_X_CODING_1, curr, 5);
         for (i = 0; i < alphaSize; i++) {
            while (True) {
               if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR);
               GET_BIT(BZ_X_CODING_2, uc);
               if (uc == 0) break;
               GET_BIT(BZ_X_CODING_3, uc);
               if (uc == 0) curr++; else curr--;
            }
            s->len[t][i] = curr;
         }
      }

      /*--- Create the Huffman decoding tables ---*/
      for (t = 0; t < nGroups; t++) {
         minLen = 32;
         maxLen = 0;
         for (i = 0; i < alphaSize; i++) {
            if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
            if (s->len[t][i] < minLen) minLen = s->len[t][i];
         }
         BZ2_hbCreateDecodeTables (
            &(s->limit[t][0]),
            &(s->base[t][0]),
            &(s->perm[t][0]),
            &(s->len[t][0]),
            minLen, maxLen, alphaSize
         );
         s->minLens[t] = minLen;
      }

      /*--- Now the MTF values ---*/

      EOB      = s->nInUse+1;
      nblockMAX = 100000 * s->blockSize100k;
      groupNo  = -1;
      groupPos = 0;

      for (i = 0; i <= 255; i++) s->unzftab[i] = 0;

      /*-- MTF init --*/
      {
         Int32 ii, jj, kk;
         kk = MTFA_SIZE-1;
         for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) {
            for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
               s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj);
               kk--;
            }
            s->mtfbase[ii] = kk + 1;
         }
      }
      /*-- end MTF init --*/

      nblock = 0;
      GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym);

      while (True) {

         if (nextSym == EOB) break;

         if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) {

            es = -1;
            N = 1;
            do {
               /* Check that N doesn‘t get too big, so that es doesn‘t
                  go negative.  The maximum value that can be
                  RUNA/RUNB encoded is equal to the block size (post
                  the initial RLE), viz, 900k, so bounding N at 2
                  million should guard against overflow without
                  rejecting any legitimate inputs. */
               if (N >= 2*1024*1024) RETURN(BZ_DATA_ERROR);
               if (nextSym == BZ_RUNA) es = es + (0+1) * N; else
               if (nextSym == BZ_RUNB) es = es + (1+1) * N;
               N = N * 2;
               GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym);
            }
               while (nextSym == BZ_RUNA || nextSym == BZ_RUNB);

            es++;
            uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ];
            s->unzftab[uc] += es;

            if (s->smallDecompress)
               while (es > 0) {
                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
                  s->ll16[nblock] = (UInt16)uc;
                  nblock++;
                  es--;
               }
            else
               while (es > 0) {
                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
                  s->tt[nblock] = (UInt32)uc;
                  nblock++;
                  es--;
               };

            continue;

         } else {

            if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);

            /*-- uc = MTF ( nextSym-1 ) --*/
            {
               Int32 ii, jj, kk, pp, lno, off;
               UInt32 nn;
               nn = (UInt32)(nextSym - 1);

               if (nn < MTFL_SIZE) {
                  /* avoid general-case expense */
                  pp = s->mtfbase[0];
                  uc = s->mtfa[pp+nn];
                  while (nn > 3) {
                     Int32 z = pp+nn;
                     s->mtfa[(z)  ] = s->mtfa[(z)-1];
                     s->mtfa[(z)-1] = s->mtfa[(z)-2];
                     s->mtfa[(z)-2] = s->mtfa[(z)-3];
                     s->mtfa[(z)-3] = s->mtfa[(z)-4];
                     nn -= 4;
                  }
                  while (nn > 0) {
                     s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--;
                  };
                  s->mtfa[pp] = uc;
               } else {
                  /* general case */
                  lno = nn / MTFL_SIZE;
                  off = nn % MTFL_SIZE;
                  pp = s->mtfbase[lno] + off;
                  uc = s->mtfa[pp];
                  while (pp > s->mtfbase[lno]) {
                     s->mtfa[pp] = s->mtfa[pp-1]; pp--;
                  };
                  s->mtfbase[lno]++;
                  while (lno > 0) {
                     s->mtfbase[lno]--;
                     s->mtfa[s->mtfbase[lno]]
                        = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1];
                     lno--;
                  }
                  s->mtfbase[0]--;
                  s->mtfa[s->mtfbase[0]] = uc;
                  if (s->mtfbase[0] == 0) {
                     kk = MTFA_SIZE-1;
                     for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) {
                        for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
                           s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj];
                           kk--;
                        }
                        s->mtfbase[ii] = kk + 1;
                     }
                  }
               }
            }
            /*-- end uc = MTF ( nextSym-1 ) --*/

            s->unzftab[s->seqToUnseq[uc]]++;
            if (s->smallDecompress)
               s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else
               s->tt[nblock]   = (UInt32)(s->seqToUnseq[uc]);
            nblock++;

            GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym);
            continue;
         }
      }

      /* Now we know what nblock is, we can do a better sanity
         check on s->origPtr.
      */
      if (s->origPtr < 0 || s->origPtr >= nblock)
         RETURN(BZ_DATA_ERROR);

      /*-- Set up cftab to facilitate generation of T^(-1) --*/
      /* Check: unzftab entries in range. */
      for (i = 0; i <= 255; i++) {
         if (s->unzftab[i] < 0 || s->unzftab[i] > nblock)
            RETURN(BZ_DATA_ERROR);
      }
      /* Actually generate cftab. */
      s->cftab[0] = 0;
      for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1];
      for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1];
      /* Check: cftab entries in range. */
      for (i = 0; i <= 256; i++) {
         if (s->cftab[i] < 0 || s->cftab[i] > nblock) {
            /* s->cftab[i] can legitimately be == nblock */
            RETURN(BZ_DATA_ERROR);
         }
      }
      /* Check: cftab entries non-descending. */
      for (i = 1; i <= 256; i++) {
         if (s->cftab[i-1] > s->cftab[i]) {
            RETURN(BZ_DATA_ERROR);
         }
      }

      s->state_out_len = 0;
      s->state_out_ch  = 0;
      BZ_INITIALISE_CRC ( s->calculatedBlockCRC );
      s->state = BZ_X_OUTPUT;
      if (s->verbosity >= 2) VPrintf0 ( "rt+rld" );

      if (s->smallDecompress) {

         /*-- Make a copy of cftab, used in generation of T --*/
         for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i];

         /*-- compute the T vector --*/
         for (i = 0; i < nblock; i++) {
            uc = (UChar)(s->ll16[i]);
            SET_LL(i, s->cftabCopy[uc]);
            s->cftabCopy[uc]++;
         }

         /*-- Compute T^(-1) by pointer reversal on T --*/
         i = s->origPtr;
         j = GET_LL(i);
         do {
            Int32 tmp = GET_LL(j);
            SET_LL(j, i);
            i = j;
            j = tmp;
         }
            while (i != s->origPtr);

         s->tPos = s->origPtr;
         s->nblock_used = 0;
         if (s->blockRandomised) {
            BZ_RAND_INIT_MASK;
            BZ_GET_SMALL(s->k0); s->nblock_used++;
            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK;
         } else {
            BZ_GET_SMALL(s->k0); s->nblock_used++;
         }

      } else {

         /*-- compute the T^(-1) vector --*/
         for (i = 0; i < nblock; i++) {
            uc = (UChar)(s->tt[i] & 0xff);
            s->tt[s->cftab[uc]] |= (i << 8);
            s->cftab[uc]++;
         }

         s->tPos = s->tt[s->origPtr] >> 8;
         s->nblock_used = 0;
         if (s->blockRandomised) {
            BZ_RAND_INIT_MASK;
            BZ_GET_FAST(s->k0); s->nblock_used++;
            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK;
         } else {
            BZ_GET_FAST(s->k0); s->nblock_used++;
         }

      }

      RETURN(BZ_OK);

    endhdr_2:

      GET_UCHAR(BZ_X_ENDHDR_2, uc);
      if (uc != 0x72) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_ENDHDR_3, uc);
      if (uc != 0x45) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_ENDHDR_4, uc);
      if (uc != 0x38) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_ENDHDR_5, uc);
      if (uc != 0x50) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_ENDHDR_6, uc);
      if (uc != 0x90) RETURN(BZ_DATA_ERROR);

      s->storedCombinedCRC = 0;
      GET_UCHAR(BZ_X_CCRC_1, uc);
      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_CCRC_2, uc);
      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_CCRC_3, uc);
      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_CCRC_4, uc);
      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);

      s->state = BZ_X_IDLE;
      RETURN(BZ_STREAM_END);

      default: AssertH ( False, 4001 );
//      default:RETURN(BZ_OK);
   }

   AssertH ( False, 4002 );

   save_state_and_return:

   s->save_i           = i;
   s->save_j           = j;
   s->save_t           = t;
   s->save_alphaSize   = alphaSize;
   s->save_nGroups     = nGroups;
   s->save_nSelectors  = nSelectors;
   s->save_EOB         = EOB;
   s->save_groupNo     = groupNo;
   s->save_groupPos    = groupPos;
   s->save_nextSym     = nextSym;
   s->save_nblockMAX   = nblockMAX;
   s->save_nblock      = nblock;
   s->save_es          = es;
   s->save_N           = N;
   s->save_curr        = curr;
   s->save_zt          = zt;
   s->save_zn          = zn;
   s->save_zvec        = zvec;
   s->save_zj          = zj;
   s->save_gSel        = gSel;
   s->save_gMinlen     = gMinlen;
   s->save_gLimit      = gLimit;
   s->save_gBase       = gBase;
   s->save_gPerm       = gPerm;

   return retVal;
}

#define BZ_SETERR(eee)                    \
{                                            if (bzerror != NULL) *bzerror = eee;      if (bzf != NULL) bzf->lastErr = eee;   }

typedef
   struct {
      FILE*     handle;
      Char      buf[BZ_MAX_UNUSED];
      Int32     bufN;
      Bool      writing;
      bz_stream strm;
      Int32     lastErr;
      Bool      initialisedOk;
   }
   bzFile;

   static
   void* default_bzalloc ( void* opaque, Int32 items, Int32 size )
   {
      void* v = malloc ( items * size );
      return v;
   }

   static
   void default_bzfree ( void* opaque, void* addr )
   {
      if (addr != NULL) free ( addr );
   }

   static Bool myfeof ( FILE* f )
   {
      Int32 c = fgetc ( f );
      if (c == EOF) return True;
      ungetc ( c, f );
      return False;
   }

   static
   int bz_config_ok ( void )
   {
      if (sizeof(int)   != 4) return 0;
      if (sizeof(short) != 2) return 0;
      if (sizeof(char)  != 1) return 0;
      return 1;
   }

   UInt32 BZ2_crc32Table[256] = {

      /*-- Ugly, innit?

--*/

      0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L,
      0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L,
      0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L,
      0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL,
      0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L,
      0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L,
      0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L,
      0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL,
      0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L,
      0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L,
      0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L,
      0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL,
      0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L,
      0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L,
      0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L,
      0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL,
      0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL,
      0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L,
      0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L,
      0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL,
      0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL,
      0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L,
      0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L,
      0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL,
      0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL,
      0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L,
      0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L,
      0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL,
      0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL,
      0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L,
      0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L,
      0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL,
      0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L,
      0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL,
      0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL,
      0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L,
      0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L,
      0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL,
      0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL,
      0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L,
      0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L,
      0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL,
      0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL,
      0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L,
      0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L,
      0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL,
      0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL,
      0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L,
      0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L,
      0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL,
      0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L,
      0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L,
      0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L,
      0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL,
      0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L,
      0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L,
      0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L,
      0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL,
      0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L,
      0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L,
      0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L,
      0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL,
      0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L,
      0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L
   };

   const char * BZ_API(BZ2_bzlibVersion)(void)
   {
      return BZ_VERSION;
   }

   __inline__ Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab )
   {
      Int32 nb, na, mid;
      nb = 0;
      na = 256;
      do {
         mid = (nb + na) >> 1;
         if (indx >= cftab[mid]) nb = mid; else na = mid;
      }
      while (na - nb != 1);
      return nb;
   }

   static
   Bool unRLE_obuf_to_output_SMALL ( DState* s )
   {
      UChar k1;

      if (s->blockRandomised) {

         while (True) {
            /* try to finish existing run */
            while (True) {
               if (s->strm->avail_out == 0) return False;
               if (s->state_out_len == 0) break;
               *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
               BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
               s->state_out_len--;
               s->strm->next_out++;
               s->strm->avail_out--;
               s->strm->total_out_lo32++;
               if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
            }

            /* can a new run be started?

*/
            if (s->nblock_used == s->save_nblock+1) return False;

            /* Only caused by corrupt data stream? */
            if (s->nblock_used > s->save_nblock+1)
               return True;

            s->state_out_len = 1;
            s->state_out_ch = s->k0;
            BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
            k1 ^= BZ_RAND_MASK; s->nblock_used++;
            if (s->nblock_used == s->save_nblock+1) continue;
            if (k1 != s->k0) { s->k0 = k1; continue; };

            s->state_out_len = 2;
            BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
            k1 ^= BZ_RAND_MASK; s->nblock_used++;
            if (s->nblock_used == s->save_nblock+1) continue;
            if (k1 != s->k0) { s->k0 = k1; continue; };

            s->state_out_len = 3;
            BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
            k1 ^= BZ_RAND_MASK; s->nblock_used++;
            if (s->nblock_used == s->save_nblock+1) continue;
            if (k1 != s->k0) { s->k0 = k1; continue; };

            BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
            k1 ^= BZ_RAND_MASK; s->nblock_used++;
            s->state_out_len = ((Int32)k1) + 4;
            BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK;
            s->k0 ^= BZ_RAND_MASK; s->nblock_used++;
         }

      } else {

         while (True) {
            /* try to finish existing run */
            while (True) {
               if (s->strm->avail_out == 0) return False;
               if (s->state_out_len == 0) break;
               *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
               BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
               s->state_out_len--;
               s->strm->next_out++;
               s->strm->avail_out--;
               s->strm->total_out_lo32++;
               if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
            }

            /* can a new run be started? */
            if (s->nblock_used == s->save_nblock+1) return False;

            /* Only caused by corrupt data stream? */
            if (s->nblock_used > s->save_nblock+1)
               return True;

            s->state_out_len = 1;
            s->state_out_ch = s->k0;
            BZ_GET_SMALL(k1); s->nblock_used++;
            if (s->nblock_used == s->save_nblock+1) continue;
            if (k1 != s->k0) { s->k0 = k1; continue; };

            s->state_out_len = 2;
            BZ_GET_SMALL(k1); s->nblock_used++;
            if (s->nblock_used == s->save_nblock+1) continue;
            if (k1 != s->k0) { s->k0 = k1; continue; };

            s->state_out_len = 3;
            BZ_GET_SMALL(k1); s->nblock_used++;
            if (s->nblock_used == s->save_nblock+1) continue;
            if (k1 != s->k0) { s->k0 = k1; continue; };

            BZ_GET_SMALL(k1); s->nblock_used++;
            s->state_out_len = ((Int32)k1) + 4;
            BZ_GET_SMALL(s->k0); s->nblock_used++;
         }

      }
   }

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) {
    //if (vm->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        __android_log_print(ANDROID_LOG_INFO, "JNIMsg", "ERROR: GetEnv failed");
        goto bail;
    }
//    assert(env != NULL);
//    if (register_android_boa(env) < 0) {
//      __android_log_print(ANDROID_LOG_INFO,"JNIMsg", "ERROR: Boa Server native registration failed");
//        goto bail;
//    }

    /* success -- return valid version number */
    result = JNI_VERSION_1_4;

bail:
    return result;
}

int BZ_API(BZ2_bzDecompressEnd)  ( bz_stream *strm )
{
   DState* s;
   if (strm == NULL) return BZ_PARAM_ERROR;
   s = strm->state;
   if (s == NULL) return BZ_PARAM_ERROR;
   if (s->strm != strm) return BZ_PARAM_ERROR;

   if (s->tt   != NULL) BZFREE(s->tt);
   if (s->ll16 != NULL) BZFREE(s->ll16);
   if (s->ll4  != NULL) BZFREE(s->ll4);

   BZFREE(strm->state);
   strm->state = NULL;

   return BZ_OK;
}

void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b )
{
   bzFile* bzf = (bzFile*)b;

   BZ_SETERR(BZ_OK);
   if (bzf == NULL)
      { BZ_SETERR(BZ_OK); return; };

   if (bzf->writing)
      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };

   if (bzf->initialisedOk)
      (void)BZ2_bzDecompressEnd ( &(bzf->strm) );
   free ( bzf );
}

static
Bool unRLE_obuf_to_output_FAST ( DState* s )
{
   UChar k1;

   if (s->blockRandomised) {

      while (True) {
         /* try to finish existing run */
         while (True) {
            if (s->strm->avail_out == 0) return False;
            if (s->state_out_len == 0) break;
            *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
            BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
            s->state_out_len--;
            s->strm->next_out++;
            s->strm->avail_out--;
            s->strm->total_out_lo32++;
            if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
         }

         /* can a new run be started? */
         if (s->nblock_used == s->save_nblock+1) return False;

         /* Only caused by corrupt data stream? */
         if (s->nblock_used > s->save_nblock+1)
            return True;

         s->state_out_len = 1;
         s->state_out_ch = s->k0;
         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
         k1 ^= BZ_RAND_MASK; s->nblock_used++;
         if (s->nblock_used == s->save_nblock+1) continue;
         if (k1 != s->k0) { s->k0 = k1; continue; };

         s->state_out_len = 2;
         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
         k1 ^= BZ_RAND_MASK; s->nblock_used++;
         if (s->nblock_used == s->save_nblock+1) continue;
         if (k1 != s->k0) { s->k0 = k1; continue; };

         s->state_out_len = 3;
         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
         k1 ^= BZ_RAND_MASK; s->nblock_used++;
         if (s->nblock_used == s->save_nblock+1) continue;
         if (k1 != s->k0) { s->k0 = k1; continue; };

         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
         k1 ^= BZ_RAND_MASK; s->nblock_used++;
         s->state_out_len = ((Int32)k1) + 4;
         BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK;
         s->k0 ^= BZ_RAND_MASK; s->nblock_used++;
      }

   } else {

      /* restore */
      UInt32        c_calculatedBlockCRC = s->calculatedBlockCRC;
      UChar         c_state_out_ch       = s->state_out_ch;
      Int32         c_state_out_len      = s->state_out_len;
      Int32         c_nblock_used        = s->nblock_used;
      Int32         c_k0                 = s->k0;
      UInt32*       c_tt                 = s->tt;
      UInt32        c_tPos               = s->tPos;
      char*         cs_next_out          = s->strm->next_out;
      unsigned int  cs_avail_out         = s->strm->avail_out;
      Int32         ro_blockSize100k     = s->blockSize100k;
      /* end restore */

      UInt32       avail_out_INIT = cs_avail_out;
      Int32        s_save_nblockPP = s->save_nblock+1;
      unsigned int total_out_lo32_old;

      while (True) {

         /* try to finish existing run */
         if (c_state_out_len > 0) {
            while (True) {
               if (cs_avail_out == 0) goto return_notr;
               if (c_state_out_len == 1) break;
               *( (UChar*)(cs_next_out) ) = c_state_out_ch;
               BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch );
               c_state_out_len--;
               cs_next_out++;
               cs_avail_out--;
            }
            s_state_out_len_eq_one:
            {
               if (cs_avail_out == 0) {
                  c_state_out_len = 1; goto return_notr;
               };
               *( (UChar*)(cs_next_out) ) = c_state_out_ch;
               BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch );
               cs_next_out++;
               cs_avail_out--;
            }
         }
         /* Only caused by corrupt data stream?

*/
         if (c_nblock_used > s_save_nblockPP)
            return True;

         /* can a new run be started?

*/
         if (c_nblock_used == s_save_nblockPP) {
            c_state_out_len = 0; goto return_notr;
         };
         c_state_out_ch = c_k0;
         BZ_GET_FAST_C(k1); c_nblock_used++;
         if (k1 != c_k0) {
            c_k0 = k1; goto s_state_out_len_eq_one;
         };
         if (c_nblock_used == s_save_nblockPP)
            goto s_state_out_len_eq_one;

         c_state_out_len = 2;
         BZ_GET_FAST_C(k1); c_nblock_used++;
         if (c_nblock_used == s_save_nblockPP) continue;
         if (k1 != c_k0) { c_k0 = k1; continue; };

         c_state_out_len = 3;
         BZ_GET_FAST_C(k1); c_nblock_used++;
         if (c_nblock_used == s_save_nblockPP) continue;
         if (k1 != c_k0) { c_k0 = k1; continue; };

         BZ_GET_FAST_C(k1); c_nblock_used++;
         c_state_out_len = ((Int32)k1) + 4;
         BZ_GET_FAST_C(c_k0); c_nblock_used++;
      }

      return_notr:
      total_out_lo32_old = s->strm->total_out_lo32;
      s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out);
      if (s->strm->total_out_lo32 < total_out_lo32_old)
         s->strm->total_out_hi32++;

      /* save */
      s->calculatedBlockCRC = c_calculatedBlockCRC;
      s->state_out_ch       = c_state_out_ch;
      s->state_out_len      = c_state_out_len;
      s->nblock_used        = c_nblock_used;
      s->k0                 = c_k0;
      s->tt                 = c_tt;
      s->tPos               = c_tPos;
      s->strm->next_out     = cs_next_out;
      s->strm->avail_out    = cs_avail_out;
      /* end save */
   }
   return False;
}

void BZ2_bz__AssertH__fail ( int errcode )
{
   fprintf(stderr,
      "\n\nbzip2/libbzip2: internal error number %d.\n"
      "This is a bug in bzip2/libbzip2, %s.\n"
      "Please report it to me at: [email protected]  If this happened\n"
      "when you were using some program which uses libbzip2 as a\n"
      "component, you should also report this bug to the author(s)\n"
      "of that program.  Please make an effort to report this bug;\n"
      "timely and accurate bug reports eventually lead to higher\n"
      "quality software.  Thanks.  Julian Seward, 10 December 2007.\n\n",
      errcode,
      BZ2_bzlibVersion()
   );

   if (errcode == 1007) {
   fprintf(stderr,
      "\n*** A special note about internal error number 1007 ***\n"
      "\n"
      "Experience suggests that a common cause of i.e. 1007\n"
      "is unreliable memory or other hardware.  The 1007 assertion\n"
      "just happens to cross-check the results of huge numbers of\n"
      "memory reads/writes, and so acts (unintendedly) as a stress\n"
      "test of your memory system.\n"
      "\n"
      "I suggest the following: try compressing the file again,\n"
      "possibly monitoring progress in detail with the -vv flag.\n"
      "\n"
      "* If the error cannot be reproduced, and/or happens at different\n"
      "  points in compression, you may have a flaky memory system.\n"
      "  Try a memory-test program.  I have used Memtest86\n"
      "  (www.memtest86.com).  At the time of writing it is free (GPLd).\n"
      "  Memtest86 tests memory much more thorougly than your BIOSs\n"
      "  power-on test, and may find failures that the BIOS doesn‘t.\n"
      "\n"
      "* If the error can be repeatably reproduced, this is a bug in\n"
      "  bzip2, and I would very much like to hear about it.  Please\n"
      "  let me know, and, ideally, save a copy of the file causing the\n"
      "  problem -- without which I will be unable to investigate it.\n"
      "\n"
   );
   }

   exit(3);
}

int BZ_API(BZ2_bzDecompress) ( bz_stream *strm )
{
   Bool    corrupt;
   DState* s;
   if (strm == NULL) return BZ_PARAM_ERROR;
   s = strm->state;
   if (s == NULL) return BZ_PARAM_ERROR;
   if (s->strm != strm) return BZ_PARAM_ERROR;

   while (True) {
      if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR;
      if (s->state == BZ_X_OUTPUT) {
         if (s->smallDecompress)
            corrupt = unRLE_obuf_to_output_SMALL ( s ); else
            corrupt = unRLE_obuf_to_output_FAST  ( s );
         if (corrupt) return BZ_DATA_ERROR;
         if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) {
            BZ_FINALISE_CRC ( s->calculatedBlockCRC );
            if (s->verbosity >= 3)
               VPrintf2 ( " {0x%08x, 0x%08x}", s->storedBlockCRC,
                          s->calculatedBlockCRC );
            if (s->verbosity >= 2) VPrintf0 ( "]" );
            if (s->calculatedBlockCRC != s->storedBlockCRC)
               return BZ_DATA_ERROR;
            s->calculatedCombinedCRC
               = (s->calculatedCombinedCRC << 1) |
                    (s->calculatedCombinedCRC >> 31);
            s->calculatedCombinedCRC ^= s->calculatedBlockCRC;
            s->state = BZ_X_BLKHDR_1;
         } else {
            return BZ_OK;
         }
      }
      if (s->state >= BZ_X_MAGIC_1) {
         Int32 r = BZ2_decompress ( s );
         if (r == BZ_STREAM_END) {
            if (s->verbosity >= 3)
               VPrintf2 ( "\n    combined CRCs: stored = 0x%08x, computed = 0x%08x",
                          s->storedCombinedCRC, s->calculatedCombinedCRC );
            if (s->calculatedCombinedCRC != s->storedCombinedCRC)
               return BZ_DATA_ERROR;
            return r;
         }
         if (s->state != BZ_X_OUTPUT) return r;
      }
   }

   AssertH ( 0, 6001 );

   return 0;  /*NOTREACHED*/
}

int BZ_API(BZ2_bzDecompressInit)
                     ( bz_stream* strm,
                       int        verbosity,
                       int        small )
{
   DState* s;

   if (!bz_config_ok()) return BZ_CONFIG_ERROR;

   if (strm == NULL) return BZ_PARAM_ERROR;
   if (small != 0 && small != 1) return BZ_PARAM_ERROR;
   if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR;

   if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc;
   if (strm->bzfree == NULL) strm->bzfree = default_bzfree;

   s = BZALLOC( sizeof(DState) );
   if (s == NULL) return BZ_MEM_ERROR;
   s->strm                  = strm;
   strm->state              = s;
   s->state                 = BZ_X_MAGIC_1;
   s->bsLive                = 0;
   s->bsBuff                = 0;
   s->calculatedCombinedCRC = 0;
   strm->total_in_lo32      = 0;
   strm->total_in_hi32      = 0;
   strm->total_out_lo32     = 0;
   strm->total_out_hi32     = 0;
   s->smallDecompress       = (Bool)small;
   s->ll4                   = NULL;
   s->ll16                  = NULL;
   s->tt                    = NULL;
   s->currBlockNo           = 0;
   s->verbosity             = verbosity;

   return BZ_OK;
}

int BZ_API(BZ2_bzRead)
           ( int*    bzerror,
             BZFILE* b,
             void*   buf,
             int     len )
{
   Int32   n, ret;
   bzFile* bzf = (bzFile*)b;

   BZ_SETERR(BZ_OK);

   if (bzf == NULL || buf == NULL || len < 0)
      { BZ_SETERR(BZ_PARAM_ERROR); return 0; };

   if (bzf->writing)
      { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; };

   if (len == 0)
      { BZ_SETERR(BZ_OK); return 0; };

   bzf->strm.avail_out = len;
   bzf->strm.next_out = buf;

   while (1) {

      if (ferror(bzf->handle))
         { BZ_SETERR(BZ_IO_ERROR); return 0; };

      if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) {
         n = fread ( bzf->buf, sizeof(UChar),
                     BZ_MAX_UNUSED, bzf->handle );
         if (ferror(bzf->handle))
            { BZ_SETERR(BZ_IO_ERROR); return 0; };
         bzf->bufN = n;
         bzf->strm.avail_in = bzf->bufN;
         bzf->strm.next_in = bzf->buf;
      }

      ret = BZ2_bzDecompress ( &(bzf->strm) );

      if (ret != BZ_OK && ret != BZ_STREAM_END)
         { BZ_SETERR(ret); return 0; };

      if (ret == BZ_OK && myfeof(bzf->handle) &&
          bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0)
         { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; };

      if (ret == BZ_STREAM_END)
         { BZ_SETERR(BZ_STREAM_END);
           return len - bzf->strm.avail_out; };
      if (bzf->strm.avail_out == 0)
         { BZ_SETERR(BZ_OK); return len; };
   }

   return -1; /*not reached*/
}

BZFILE* BZ_API(BZ2_bzReadOpen)
                   ( int*  bzerror,
                     FILE* f,
                     int   verbosity,
                     int   small,
                     void* unused,
                     int   nUnused )
{
   bzFile* bzf = NULL;
   int     ret;

   BZ_SETERR(BZ_OK);

   if (f == NULL ||
       (small != 0 && small != 1) ||
       (verbosity < 0 || verbosity > 4) ||
       (unused == NULL && nUnused != 0) ||
       (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED)))
      { BZ_SETERR(BZ_PARAM_ERROR); return NULL; };

   if (ferror(f))
      { BZ_SETERR(BZ_IO_ERROR); return NULL; };

   bzf = malloc ( sizeof(bzFile) );
   if (bzf == NULL)
      { BZ_SETERR(BZ_MEM_ERROR); return NULL; };

   BZ_SETERR(BZ_OK);

   bzf->initialisedOk = False;
   bzf->handle        = f;
   bzf->bufN          = 0;
   bzf->writing       = False;
   bzf->strm.bzalloc  = NULL;
   bzf->strm.bzfree   = NULL;
   bzf->strm.opaque   = NULL;

   while (nUnused > 0) {
      bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++;
      unused = ((void*)( 1 + ((UChar*)(unused))  ));
      nUnused--;
   }

   ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small );
   if (ret != BZ_OK)
      { BZ_SETERR(ret); free(bzf); return NULL; };

   bzf->strm.avail_in = bzf->bufN;
   bzf->strm.next_in  = bzf->buf;

   bzf->initialisedOk = True;
   return bzf;
}

static off_t offtin(u_char *buf)
{
    off_t y;

    y=buf[7]&0x7F;
    y=y*256;y+=buf[6];
    y=y*256;y+=buf[5];
    y=y*256;y+=buf[4];
    y=y*256;y+=buf[3];
    y=y*256;y+=buf[2];
    y=y*256;y+=buf[1];
    y=y*256;y+=buf[0];

    if(buf[7]&0x80) y=-y;

    return y;
}

int applypatch(int argc,char * argv[])
{
    FILE * f, * cpf, * dpf, * epf;
    BZFILE * cpfbz2, * dpfbz2, * epfbz2;
    int cbz2err, dbz2err, ebz2err;
    int fd;
    ssize_t oldsize,newsize;
    ssize_t bzctrllen,bzdatalen;
    u_char header[32],buf[8];
    u_char *old, *new;
    off_t oldpos,newpos;
    off_t ctrl[3];
    off_t lenread;
    off_t i;

    /* Open patch file */
    if ((f = fopen(argv[3], "r")) == NULL)
        err(1, "fopen(%s)", argv[3]);

    /* Read header */
    if (fread(header, 1, 32, f) < 32) {
        if (feof(f))
            errx(1, "Corrupt patch\n");
        err(1, "fread(%s)", argv[3]);
    }

    /* Check for appropriate magic */
    if (memcmp(header, "BSDIFF40", 8) != 0)
        errx(1, "Corrupt patch\n");

    /* Read lengths from header */
    bzctrllen=offtin(header+8);
    bzdatalen=offtin(header+16);
    newsize=offtin(header+24);

    if((bzctrllen<0) || (bzdatalen<0) || (newsize<0))
        errx(1,"Corrupt patch\n");

    /* Close patch file and re-open it via libbzip2 at the right places */
    if (fclose(f))
        err(1, "fclose(%s)", argv[3]);

    if ((cpf = fopen(argv[3], "r")) == NULL)
        err(1, "fopen(%s)", argv[3]);

    if (fseeko(cpf, 32, SEEK_SET))
        err(1, "fseeko(%s, %lld)", argv[3],
            (long long)32);

    cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0);

    if ((cpfbz2) == NULL)
        errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err);

    if ((dpf = fopen(argv[3], "r")) == NULL)
        err(1, "fopen(%s)", argv[3]);

    if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))
        err(1, "fseeko(%s, %lld)", argv[3],
            (long long)(32 + bzctrllen));

    if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)
        errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err);
    if ((epf = fopen(argv[3], "r")) == NULL)
        err(1, "fopen(%s)", argv[3]);

    if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))
        err(1, "fseeko(%s, %lld)", argv[3],
            (long long)(32 + bzctrllen + bzdatalen));

    if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)
        errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err);

    if(((fd=open(argv[1],O_RDONLY,0))<0) ||
        ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
        ((old=malloc(oldsize+1))==NULL) ||
        (lseek(fd,0,SEEK_SET)!=0) ||
        (read(fd,old,oldsize)!=oldsize) ||
        (close(fd)==-1)) err(1,"%s",argv[1]);
    if((new=malloc(newsize+1))==NULL) err(1,NULL);

    oldpos=0;newpos=0;
    while(newpos<newsize) {
        /* Read control data */
        for(i=0;i<=2;i++) {
            lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);
            if ((lenread < 8) || ((cbz2err != BZ_OK) &&
                (cbz2err != BZ_STREAM_END)))
                errx(1, "Corrupt patch\n");
            ctrl[i]=offtin(buf);
        }

        /* Sanity-check */
        if(newpos+ctrl[0]>newsize)
            errx(1,"Corrupt patch\n");

        /* Read diff string */
        lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);
        if ((lenread < ctrl[0]) ||
            ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))
            errx(1, "Corrupt patch\n");

        /* Add old data to diff string */
        for(i=0;i<ctrl[0];i++)
            if((oldpos+i>=0) && (oldpos+i<oldsize))
                new[newpos+i]+=old[oldpos+i];

        /* Adjust pointers */
        newpos+=ctrl[0];
        oldpos+=ctrl[0];

        /* Sanity-check */
        if(newpos+ctrl[1]>newsize)
            errx(1,"Corrupt patch\n");

        /* Read extra string */
        lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);
        if ((lenread < ctrl[1]) ||
            ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))
            errx(1, "Corrupt patch\n");

        /* Adjust pointers */
        newpos+=ctrl[1];
        oldpos+=ctrl[2];
    }

    /* Clean up the bzip2 reads */
    BZ2_bzReadClose(&cbz2err, cpfbz2);
    BZ2_bzReadClose(&dbz2err, dpfbz2);
    BZ2_bzReadClose(&ebz2err, epfbz2);
    if (fclose(cpf) || fclose(dpf) || fclose(epf)){
        err(1, "fclose(%s)", argv[3]);
    }

    if((fd=open(argv[2],O_CREAT|O_RDWR|O_TRUNC,0777))<0){
    }
    if(write(fd,new,newsize)!=newsize){
    }
    if(close(fd)==-1){
    }

    free(new);
    free(old);

    return 0;
}

JNIEXPORT jint JNICALL Java_com_example_selfincrease_PatchUpdate_patch(JNIEnv *env,
        jobject obj, jstring old, jstring new , jstring patch)
{
      char * ch[4];
      ch[0]="bspatch";
      ch[1]=(char*)((*env)->GetStringUTFChars(env,old, 0));
      ch[2]=(char*)((*env)->GetStringUTFChars(env,new, 0));
      ch[3]=(char*)((*env)->GetStringUTFChars(env,patch, 0));

      int ret=applypatch(4, ch);
      (*env)->ReleaseStringUTFChars(env,old,ch[1]);
      (*env)->ReleaseStringUTFChars(env,new,ch[2]);
      (*env)->ReleaseStringUTFChars(env,patch,ch[3]);

      //return (*env)->NewStringUTF(env,"success");
      return ret;
}

注意两点:

1. 引入”头文件”

#include “com_example_selfincrease_PatchUpdate.h”

2. 实现生命的本地方法patch

Java_com_example_selfincrease_PatchUpdate_patch

JNIEXPORT jint JNICALL Java_com_example_selfincrease_PatchUpdate_patch(JNIEnv *env,
        jobject obj, jstring old, jstring new , jstring patch)
{
      char * ch[4];
      ch[0]="bspatch";
      ch[1]=(char*)((*env)->GetStringUTFChars(env,old, 0));
      ch[2]=(char*)((*env)->GetStringUTFChars(env,new, 0));
      ch[3]=(char*)((*env)->GetStringUTFChars(env,patch, 0));

      int ret=applypatch(4, ch);
      (*env)->ReleaseStringUTFChars(env,old,ch[1]);
      (*env)->ReleaseStringUTFChars(env,new,ch[2]);
      (*env)->ReleaseStringUTFChars(env,patch,ch[3]);

      //return (*env)->NewStringUTF(env,"success");
      return ret;
}

编写Android.mk文件:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := increase
LOCAL_CXXFLAGS :=
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_SRC_FILES := com_example_selfincrease_PatchUpdate.c
LOCAL_LDLIBS := -lz -llog
include $(BUILD_SHARED_LIBRARY)

这里我声明编译出来的模块是increase。

上层代码调用

使用loadLibrary引入模块

底层的c代码以及Android.mk文件都实现了以后。就是在上层载入了,这里我们在PatchUpdate类中载入:

static{
        System.loadLibrary("increase");
    }

能够看到这里载入的是increase模块。就是在Android.mk文件里声明的。

MainActivity中调用

在调用之前,当然须要将之前生成好的”increaseone1.0.apk”以及increase.patch文件拷贝至手机里,这里我复制到”storage/sdcard0/123”文件夹下:

MainActivity.java

package com.example.selfincrease;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.widget.Toast;

public class MainActivity extends Activity {
     String sd  =   Environment.getExternalStorageDirectory()   +   "/123/";
     String patch   =   "increase.patch";
     String oldapk  =   "increaseone1.0.apk";

     String oldapk_filepath =   sd  +   oldapk;
     String newapk_savepath =   sd  +   "new.apk";
     String patchpath = sd  +   patch;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        PatchUpdate patchInterence  =   new PatchUpdate();
        /**
         * oldapk_filepath  旧apk的路径
         * newapk_savepath  合成的apk路径
         * patchpath        查分包patch的路径
         */
        patchInterence.patch(oldapk_filepath, newapk_savepath, patchpath);
        Toast.makeText(MainActivity.this, "新的apk已经合成"   +   sd  +   "文件夹以下", Toast.LENGTH_LONG).show();
    }

}

能够看到这里主要是调用的patchInterence.patch(oldapk_filepath, newapk_savepath, patchpath);来实现patch的合成,也就是核心的工作是由底层的c代码来实现的。

使用ndk-build编译

接下来就是使用ndk-build来编译生成so文件。

这里我在window下使用的是cygwine。不清楚的能够參考一步一步学习androidNDK编程(搭建开发环境)

编译好之后,会生成一个libincrease.so文件,此时执行selfincreaseproject,就会在指定文件夹下生成新的合成后的new.apk文件。

注意:

因为须要给sdcard上写入新的apk文件,所以须要在manifest中声明一下权限:

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

此时安装执行new.apk,如图:

和我们最新版本号的apk执行效果是相同的。说明我们已经合成新的apk成功了。

总结

  1. 使用bsdiff4.3对新旧apk生成差分包
  2. 依据差分包合成新的apk文件。

源代码下载

时间: 2024-08-06 18:27:06

手把手实现andriod应用增量升级的相关文章

军规14 增量升级必不可少

作为一个用户,测试过程中要注意APP升级时是否必须先卸载,才能安装:还有就是安装了最新版的,却发现之前的登陆信息全没了,还需要重新登陆:还有这就是最新版的安装后会不会崩溃. 14.1 测试APP的增量升级 对于增量升级,测试员不能只为了方便只进行全新安装的测试,还需要对APP升级安装也进行测试.不过可以对全新安装的APP进行重点测试,对APP升级的进行冒烟测试,或者对改变的功能进行有重点的测试.对于降级可以不考虑测试. 测试APP升级,需要注意以下细节: 1.在APP升级前登陆的用户信息在APP

react native 增量升级方案(转)

前言 facebook的react-native给我们带来了用js写出原生应用的同时,也使得使用RN编写的代码的在线升级变得可能,终于可以不通过应用市场来进行升级,极大的提升了app修bug和赋予新功能的能力.----使用h5的方式也可以做到,但是rn的用户体验可要远远超过h5啊. 一般使用RN编写的app的线上使用方式,是将react-native bundle命令打出bundle文件和assets文件夹,直接内置到app中,app在viewcontroller或者activity中直接加载a

Android应用市场省流量更新(增量升级)原理解析

一.前言 最近在看热修复相关的框架,之前我们已经看过了阿里的Dexposed和AndFix这两个框架了,不了解的同学可以点击这里进行查看:Dexposed框架原理解析 和 AndFix热修复框架原理解析,然后还有最近很火的一个是腾讯的Tinker热修复框架,再看他的原理实现的时候,发现了他使用到了开源的文件差分工具bsdiff/bspatch,所以就单独用这篇文章来详细介绍一下这个工具,因为这个工具有一个很大的用途就是增量更新,也就是我们看到现在大部分的应用市场推出的省流量更新应用的效果: 看到

Mac平台与Windows平台下AndroidStudio增量升级

Android Studio增量升级什么情况下使用最合适呢? 比如现在的as版本是2.2版本,而你的as版本2.0版本,这个时候点Check For Updates就没有反应了,因为你已经2个有版本没升级了(版本跨度太大 ),所以这样就不能在线升级了. 重要的信息 谷歌更新地址:https://dl.google.com/android/studio/patches/updates.xml 增加升级Jar下载:(windodws为win—linux为unix—mac os 为mac.对号入座)

Android增量升级简单实现(附源码)

随着现在手机硬件不断的提升,分辨率提高手机的安装包也是越来越大了.当年NOKIA,MOTO时代,一个手机APP如果有1MB那都是算大的,2MB已经不得了了.虽然网络.存储都已经大大提升,但是流量还不至于廉价到APP改了一个标题要去下载一个几兆的程序安装包.今天就介绍安卓增量下载的实现.有耐心的先看原理,后面实践! 增量升级的原理 今天我们就来实现类似的应用的增量升级.其实增量升级的原理很简单,即首先将应用的旧版本Apk与新版本Apk做差分,得到更新的部分的补丁,例如旧版本的APK有5M,新版的有

Android studio下增量升级功能的NDK开发

最近研究Android应用的增量升级功能,期间涉及到了NDK开发的内容,整理记录在此. 先说几个问题. 一.NDK开发就是JNI开发啊,卧槽我原来都不知道啊,一直以为是两个东西啊...... 二.关于开发环境,老的Eclipse版本要安装的工具比较多,什么NDK啊,cygwin啊什么,AS(Android studio首字母,以下皆以AS代替)就简单多了,直接装一个NDK就可以了.貌似AS从1.3版本就可以不装cygwin了,而我装的是2.0版本的,更不需要装.而且神奇的是,我安装NDK的时候没

android黑科技系列——应用市场省流量更新(增量升级)原理解析

一.前言 最近在看热修复相关的框架,之前我们已经看过了阿里的Dexposed和AndFix这两个框架了,不了解的同学可以点击这里进行查看:Dexposed框架原理解析 和 AndFix热修复框架原理解析,然后还有最近很火的一个是腾讯的Tinker热修复框架,再看他的原理实现的时候,发现了他使用到了开源的文件差分工具bsdiff/bspatch,所以就单独用这篇文章来详细介绍一下这个工具,因为这个工具有一个很大的用途就是增量更新,也就是我们看到现在大部分的应用市场推出的省流量更新应用的效果: 看到

包含MANIFEST.MF的jar可执行应用指定classpath及spring boot应用增量升级打包实现

对于不包含MANIFEST.MF,或jar包中的MANIFEST.MF未指定MainClass的jar,可以通过java命令行选项-classpath指定classpath.但是如果是包含MainClass的jar,例如: Manifest-Version: 1.0Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txtBundle-SymbolicName: org.mybatis.generator.mybatis-gene

增量升级(省流量更新)的Android客户端实现

转载与 zhouhuiah的专栏 http://blog.csdn.net/zhouhuiah/article/details/16939937 本文在以上两篇博客的基础上再增加了异常处理,并将生成的so库和Native代码一起打包. 1.准备工具 (1)bspatch源码(点击下载).某个应用的两个不同版本.或者直接下载上面提到的第一个博主提供的工具和素材.点击打开链接 这里面包括了我们需要用到的bsdiff源码和apk等. (2)除此之外,还需要下载bzip2.点击打开链接 2.编译环境 L