編輯:關於Android編程
別看本文看上去很簡單,實際在實驗過程中遇到了很多問題,比如andorid studio下ndk編譯報錯,而本文呈現給大家的都是最終可行的方法.
下載生成差分包所需依賴文件
bsdiff bzip2新建項目並調用native代碼
新建java層類
package cn.edu.zafu.util;
public class DiffAndPatch {
static {
System.loadLibrary(DiffAndPatch);
}
public static native int generateDiff(String oldApkPath, String newApkPath,
String patchPath);
public static native int applyPatch(String oldApkPath, String newApkPath,
String patchPath);
}
進入項目bin目錄,使用javah命令創建頭文件
javah cn.edu.zafu.util.DiffAndPatch
將頭文件復制到jni文件夾下(自己創建),復制一份修改後綴名為.c
將裡面的內容清除,復制下面的代碼進去
#include cn_edu_zafu_util_DiffAndPatch.h
#include
#ifdef __cplusplus
extern C {
#endif
/*
* Class: cn_edu_zafu_util_DiffAndPatch
* Method: generateDiff
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_cn_edu_zafu_util_DiffAndPatch_generateDiff
(JNIEnv *env, jclass arg, jstring old, jstring new, jstring patch){
printf(-----generateDiff start-----
);
clock_t start, finish;
double duration;
int argc = 4;
char * argv[argc];
argv[0] = bsdiff;
argv[1] = (char*) ((*env)->GetStringUTFChars(env, old, 0));
argv[2] = (char*) ((*env)->GetStringUTFChars(env, new, 0));
argv[3] = (char*) ((*env)->GetStringUTFChars(env, patch, 0));
printf(old apk = %s
, argv[1]);
printf(new apk = %s
, argv[2]);
printf(patch = %s
, argv[3]);
printf(start to generate patch file
);
start = clock();
int ret = generatePatch(argc, argv);
finish = clock();
printf(finish to generate patch file
);
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf(generateDiff result = %d
, ret);
printf(time spended %f s
,duration);
(*env)->ReleaseStringUTFChars(env, old, argv[1]);
(*env)->ReleaseStringUTFChars(env, new, argv[2]);
(*env)->ReleaseStringUTFChars(env, patch, argv[3]);
printf(-----generateDiff end-----
);
return ret;
}
/*
* Class: cn_edu_zafu_util_PatchUtil
* Method: applyPatch
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_cn_edu_zafu_util_DiffAndPatch_applyPatch
(JNIEnv *env, jclass arg, jstring old, jstring new, jstring patch){
printf(-----applyPatch start-----
);
clock_t start, finish;
double duration;
int argc = 4;
char * argv[argc];
argv[0] = bspatch;
argv[1] = (char*) ((*env)->GetStringUTFChars(env, old, 0));
argv[2] = (char*) ((*env)->GetStringUTFChars(env, new, 0));
argv[3] = (char*) ((*env)->GetStringUTFChars(env, patch, 0));
printf(old apk = %s
, argv[1]);
printf(patch = %s
, argv[3]);
printf(new apk = %s
, argv[2]);
printf(start to apply patch file
);
start = clock();
int ret = applyPatch(argc, argv);
finish = clock();
printf(finish to apply patch file
);
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf(applyPatch result = %d
, ret);
printf(time spended %f s
,duration);
(*env)->ReleaseStringUTFChars(env, old, argv[1]);
(*env)->ReleaseStringUTFChars(env, new, argv[2]);
(*env)->ReleaseStringUTFChars(env, patch, argv[3]);
printf(-----applyPatch end-----
);
return ret;
}
#ifdef __cplusplus
}
#endif
將相關依賴文件復制到jni目錄下,對應的文件從下載下來的bsdiff和bzip2中尋找,文件清單如圖
並在jni文件下新建Makefile文件,內容如下,對應的jdk目錄修改為自己電腦的目錄.
CC := gcc
CFLAGS := -I /opt/java/jdk1.8.0_25/include -I /opt/java/jdk1.8.0_25/include/linux -I /usr/include
LDFLAGS := -fPIC -shared
SOURCE := $(wildcard *.c *.h)
libDiffAndPatch.so: $(SOURCE)
$(CC) $(LDFLAGS) $(CFLAGS) $^ -o libDiffAndPatch.so
clean:
rm libDiffAndPatch.so
增加native library location.項目文件右鍵Propreties->Java Build Path->Libraryies->JRE System Library->Native Library Location雙擊後指向jni目錄
命令行進入jni目錄進行編譯
編寫測試代碼,將apk文件置於對應文件夾下,這裡是/home/lizhangqu/apk/這個目錄下
package cn.edu.zafu;
import cn.edu.zafu.util.DiffAndPatch;
public class Test {
// 舊版本
public static final String OLD_APK = /home/lizhangqu/apk/ApkPatchTestVersion_1.apk;
// 新版本
public static final String NEW_APK = /home/lizhangqu/apk/ApkPatchTestVersion_2.apk;
// patch存儲路徑
public static final String PATCH_FILE = /home/lizhangqu/apk/version_1_to_version_2_.patch;
// 使用舊版本+patch包,合成的新版本
public static final String OLD_TO_NEW_APK = /home/lizhangqu/apk/generated.apk;
public static void main(String[] args) {
System.out.println(正在生成文件,可能需要一段時間,請稍後...);
DiffAndPatch.generateDiff(OLD_APK, NEW_APK, PATCH_FILE);
}
}
最終會輸出以下信息
正在生成文件,可能需要一段時間,請稍後...
-----generateDiff start-----
old apk = /home/lizhangqu/apk/ApkPatchTestVersion_1.apk
new apk = /home/lizhangqu/apk/ApkPatchTestVersion_2.apk
patch = /home/lizhangqu/apk/version_1_to_version_2_.patch
start to generate patch file
finish to generate patch file
generateDiff result = 0
time spended 2.595576 s
-----generateDiff end-----
將舊版本安裝在手機上,將生成的patch拷到手機上.這裡拷到手機內存卡根路徑下apk文件夾裡
使用android studio新建項目,創建java層調用native層代碼
package cn.edu.zafu.util;
/**
* 類說明: apk 合成工具類
*
* @author lizhangqu
* @date 2015-5-29
* @version 1.0
*/
public class PatchUtil {
static {
System.loadLibrary(Patch);
}
/**
* @param oldApkPath 舊包路徑
* @param newApkPath 生成的包路徑
* @param patchPath patch路徑
* @return
*/
public static native int applyPatch(String oldApkPath, String newApkPath,
String patchPath);
}
build後打開android studio裡的終端使用javah命令生成頭文件
cd app/src/main
javah -d jni -classpath /media/lizhangqu/windows/sdk/platforms/android-22/android.jar:../../build/intermediates/classes/debug/ cn.edu.zafu.util.PatchUtil
之後再jni目錄裡會有頭文件生成,復制一份改後綴為.c,裡面實現的內容如下
#include
#include
JNIEXPORT jint JNICALL Java_cn_edu_zafu_util_PatchUtil_applyPatch
(JNIEnv *env, jclass arg, jstring oldApk, jstring newApk, jstring patch){
printf(-----applyPatch start-----
);
clock_t start, finish;
double duration;
int argc = 4;
char * argv[argc];
argv[0] = bspatch;
argv[1] = (char*) ((*env)->GetStringUTFChars(env, oldApk, 0));
argv[2] = (char*) ((*env)->GetStringUTFChars(env, newApk, 0));
argv[3] = (char*) ((*env)->GetStringUTFChars(env, patch, 0));
printf(old apk = %s
, argv[1]);
printf(patch = %s
, argv[3]);
printf(new apk = %s
, argv[2]);
printf(start to apply patch file
);
start = clock();
int ret = applyPatch(argc, argv);
finish = clock();
printf(finish to apply patch file
);
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf(applyPatch result = %d
, ret);
printf(time spended %f s
,duration);
(*env)->ReleaseStringUTFChars(env, oldApk, argv[1]);
(*env)->ReleaseStringUTFChars(env, newApk, argv[2]);
(*env)->ReleaseStringUTFChars(env, patch, argv[3]);
printf(-----applyPatch end-----
);
return ret;
}
將之前的生成差分包的項目jni目錄下除了Makefile文件和自己生成的兩個文件外,其余文件都拷到android studio 的jni目錄下.
在jni文件下創建Android.mk,內容如下
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Patch
LOCAL_SRC_FILES :=
blocksort.c
bspatch.c
bzip2.c
bzlib.c
bzlib.h
bzlib_private.h
cn_edu_zafu_util_PatchUtil.c
cn_edu_zafu_util_PatchUtil.h
compress.c
crctable.c
decompress.c
huffman.c
randtable.c
include $(BUILD_SHARED_LIBRARY)
在jni目錄下創建Application.mk,內容如下
APP_ABI := armeabi
APP_PLATFORM :=android-15
APP_PLATFORM一定要,否則編譯的時候會報錯
禁用android studio自帶的ndk build並使用自己的Android.mk和Application.mk文件
在local.properties裡加入ndk目錄
ndk.dir=/media/lizhangqu/windows/sdk/android-ndk-r10e
修改app目錄下的build.gradle文件,最終內容如下
import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion 22.0.1
defaultConfig {
applicationId cn.edu.zafu.patch
minSdkVersion 15
targetSdkVersion 22
versionCode 1
versionName 1.0
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets.main.jni.srcDirs = [] // disable automatic ndk-build call, which ignore our Android.mk
sourceSets.main.jniLibs.srcDir 'src/main/libs'
// call regular ndk-build(.cmd) script from app directory
task ndkBuild(type: Exec) {
workingDir file('src/main')
commandLine getNdkBuildCmd()
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
task cleanNative(type: Exec) {
workingDir file('src/main')
commandLine getNdkBuildCmd(), 'clean'
}
clean.dependsOn cleanNative
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.1.1'
}
def getNdkDir() {
if (System.env.ANDROID_NDK_ROOT != null)
return System.env.ANDROID_NDK_ROOT
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def ndkdir = properties.getProperty('ndk.dir', null)
if (ndkdir == null)
throw new GradleException(NDK location not found. Define location with ndk.dir in the local.properties file or with an ANDROID_NDK_ROOT environment variable.)
return ndkdir
}
def getNdkBuildCmd() {
def ndkbuild = getNdkDir() + /ndk-build
if (Os.isFamily(Os.FAMILY_WINDOWS))
ndkbuild += .cmd
return ndkbuild
}
編寫更新代碼,假設已經將patch下載到本地
package cn.edu.zafu;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import cn.edu.zafu.util.ApkUtil;
import cn.edu.zafu.util.PatchUtil;
import cn.edu.zafu.util.SignUtil;
public class MainActivity extends AppCompatActivity {
private static final String ROOT_PATH= Environment.getExternalStorageDirectory().getAbsolutePath();
private static final String PACKAGE_NAME=com.sina.weibo;
// patch存儲路徑
public static final String PATCH_FILE = /apk/weibo.patch;
// 使用舊版本+patch包,合成的新版本
public static final String OLD_TO_NEW_APK = /apk/generated.apk;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
boolean isInstalled=ApkUtil.isInstalled(getApplicationContext(), PACKAGE_NAME);
if(isInstalled){
String source=ApkUtil.getSourceApkPath(getApplicationContext(),PACKAGE_NAME);
Log.d(TAG,source);
Log.d(TAG,ROOT_PATH+OLD_TO_NEW_APK);
Log.d(TAG,ROOT_PATH+PATCH_FILE);
applyPatch(source,ROOT_PATH+OLD_TO_NEW_APK,ROOT_PATH+PATCH_FILE);
}
}
private void applyPatch(final String oldApk,final String newApk,final String patch) {
new Thread(new Runnable() {
@Override
public void run() {
Log.d(TAG, 正在合成文件,可能需要一段時間,請稍後...);
PatchUtil.applyPatch(oldApk, newApk, patch);
String oldSign=SignUtil.getInstalledApkSignature(getApplicationContext(), PACKAGE_NAME);
String newSign=SignUtil.getUnInstalledApkSignature(newApk);
if(oldSign.equals(newSign)){
Log.d(TAG, 簽名相同);
ApkUtil.installApk(MainActivity.this, newApk);
}else{
Log.d(TAG,簽名錯誤);
}
}
}).start();
}
}
工具類代碼就不貼了,其作用就跟名字一樣.還有注意的就是記得加讀取內存卡的權限.
這時候build一下,再運行程序,記得把patch文件放在對應的文件目錄下,不出意外,幾秒鐘之後就會合成新包並進行安裝.
本文實例講述了Android編程使用ListView實現數據列表顯示的方法。分享給大家供大家參考,具體如下:要將數據庫中的數據列表顯示在屏幕上,我們要使用ListView
首先是Get: 布局很簡單,就一個按鈕,主要看一下MainActivity吧: package com.francis.httpget; import android
如果圖片資源是靜態的,當我們要在View上顯示圖片時,只需要簡單的將圖片賦值給ImageView就可以了,但如果需要浏覽網絡上的圖片時該如何做呢?有可能圖片很大,有可能網
dom 我的理解就是先把整個文檔讀取到內存中,然後才解析,讀取大點的文件的話這樣效率就會很低。而 sax和pull 它們是基於事件解析的。一行一行去解析,效率會高點。下面