增量更新工具类【https://github.com/cundong/SmartAppUpdates】
1 import java.io.File; 2 3 import android.app.Activity; 4 import android.app.ProgressDialog; 5 import android.content.Context; 6 import android.content.Intent; 7 import android.net.Uri; 8 import android.os.AsyncTask; 9 import android.os.Bundle; 10 import android.text.TextUtils; 11 import android.view.View; 12 import android.view.View.OnClickListener; 13 import android.widget.Button; 14 import android.widget.TextView; 15 import android.widget.Toast; 16 17 import com.cundong.utils.PatchUtils; 18 import com.example.test.util.ApkUtils; 19 import com.example.test.util.SignUtils; 20 import com.example.test2.R; 21 22 /** 23 * 类说明: ApkPatchLibrary 使用 Demo 24 * 25 * @author Cundong 26 * @version 1.5 27 */ 28 public class MainActivity extends Activity { 29 30 // 合成成功 31 private static final int WHAT_SUCCESS = 1; 32 33 // 合成的APK签名和已安装的签名不一致 34 private static final int WHAT_FAIL_SING = -1; 35 36 // 合成失败 37 private static final int WHAT_FAIL_ERROR = -2; 38 39 // 获取源文件失败 40 private static final int WHAT_FAIL_GET_SOURCE = -3; 41 42 private Context mContext = null; 43 44 private ProgressDialog mProgressDialog; 45 private TextView mResultView; 46 private Button mStartButton, mGithubButton; 47 48 private long mBeginTime, mEndTime; 49 50 @Override 51 protected void onCreate(Bundle savedInstanceState) { 52 super.onCreate(savedInstanceState); 53 setContentView(R.layout.activity_main); 54 55 mContext = getApplicationContext(); 56 57 mProgressDialog = new ProgressDialog(this); 58 mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); 59 mProgressDialog.setMessage("doing.."); 60 mProgressDialog.setCancelable(false); 61 62 mResultView = (TextView) findViewById(R.id.textview4); 63 mStartButton = (Button) findViewById(R.id.start_btn); 64 mGithubButton = (Button) findViewById(R.id.github_btn); 65 66 mStartButton.setOnClickListener(new OnClickListener() { 67 68 @Override 69 public void onClick(View arg0) { 70 71 File patchFile = new File(Constants.PATCH_PATH); 72 73 if (!ApkUtils.isInstalled(mContext, Constants.TEST_PACKAGENAME)) { 74 Toast.makeText(mContext, getString(R.string.demo_info1), 75 Toast.LENGTH_LONG).show(); 76 } else if (!patchFile.exists()) { 77 Toast.makeText(mContext, getString(R.string.demo_info2), 78 Toast.LENGTH_LONG).show(); 79 } else { 80 new PatchApkTask().execute(); 81 } 82 } 83 }); 84 85 mGithubButton.setOnClickListener(new OnClickListener() { 86 87 @Override 88 public void onClick(View v) { 89 Intent intent = new Intent("android.intent.action.VIEW", Uri.parse("https://github.com/cundong/SmartAppUpdates")); 90 startActivity(intent); 91 } 92 }); 93 } 94 95 private class PatchApkTask extends AsyncTask{ 96 97 @Override 98 protected void onPreExecute() { 99 super.onPreExecute();100 101 mProgressDialog.show();102 103 mResultView.setText("");104 mBeginTime = System.currentTimeMillis();105 }106 107 @Override108 protected Integer doInBackground(String... params) {109 110 String oldApkSource = ApkUtils.getSourceApkPath(mContext,111 Constants.TEST_PACKAGENAME);112 113 if (!TextUtils.isEmpty(oldApkSource)) {114 115 int patchResult = PatchUtils.patch(oldApkSource,116 Constants.NEW_APK_PATH, Constants.PATCH_PATH);117 118 if (patchResult == 0) {119 120 String signatureNew = SignUtils121 .getUnInstalledApkSignature(Constants.NEW_APK_PATH);122 123 String signatureSource = SignUtils124 .getInstalledApkSignature(mContext,125 Constants.TEST_PACKAGENAME);126 127 if (!TextUtils.isEmpty(signatureNew)128 && !TextUtils.isEmpty(signatureSource)129 && signatureNew.equals(signatureSource)) {130 return WHAT_SUCCESS;131 } else {132 return WHAT_FAIL_SING;133 }134 } else {135 return WHAT_FAIL_ERROR;136 }137 } else {138 return WHAT_FAIL_GET_SOURCE;139 }140 }141 142 @Override143 protected void onPostExecute(Integer result) {144 super.onPostExecute(result);145 146 if (mProgressDialog.isShowing()) {147 mProgressDialog.dismiss();148 }149 150 mEndTime = System.currentTimeMillis();151 mResultView.setText("耗时: " + (mEndTime - mBeginTime) + "ms");152 153 switch (result) {154 case WHAT_SUCCESS: {155 156 String text = "新apk已合成成功:" + Constants.NEW_APK_PATH;157 showShortToast(text);158 159 ApkUtils.installApk(MainActivity.this, Constants.NEW_APK_PATH);160 break;161 }162 case WHAT_FAIL_SING: {163 String text = "新apk已合成失败,签名不一致";164 showShortToast(text);165 break;166 }167 case WHAT_FAIL_ERROR: {168 String text = "新apk已合成失败";169 showShortToast(text);170 break;171 }172 case WHAT_FAIL_GET_SOURCE: {173 String text = "无法获取packageName为" + Constants.TEST_PACKAGENAME174 + "的源apk文件,只能整包更新了!";175 showShortToast(text);176 break;177 }178 }179 }180 }181 182 private void showShortToast(final String text) {183 184 Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show();185 }186 187 static {188 System.loadLibrary("ApkPatchLibrary");189 }190 }
APK 工具类
1 import android.content.Context; 2 import android.content.Intent; 3 import android.content.pm.ApplicationInfo; 4 import android.content.pm.PackageManager; 5 import android.content.pm.PackageManager.NameNotFoundException; 6 import android.net.Uri; 7 import android.text.TextUtils; 8 9 /**10 * 类说明: Apk工具类11 * 12 * @author Cundong13 * @date 2013-9-614 * @version 1.015 */16 public class ApkUtils {17 18 public static boolean isInstalled(Context context, String packageName) {19 PackageManager pm = context.getPackageManager();20 boolean installed = false;21 try {22 pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);23 installed = true;24 } catch (Exception e) {25 e.printStackTrace();26 }27 28 return installed;29 }30 31 /**32 * 获取已安装Apk文件的源Apk文件33 * 如:/data/app/com.sina.weibo-1.apk34 * 35 * @param context36 * @param packageName37 * @return38 */39 public static String getSourceApkPath(Context context, String packageName) {40 if (TextUtils.isEmpty(packageName))41 return null;42 43 try {44 ApplicationInfo appInfo = context.getPackageManager()45 .getApplicationInfo(packageName, 0);46 return appInfo.sourceDir;47 } catch (NameNotFoundException e) {48 e.printStackTrace();49 }50 51 return null;52 }53 54 /**55 * 安装Apk56 * 57 * @param context58 * @param apkPath59 */60 public static void installApk(Context context, String apkPath) {61 62 Intent intent = new Intent(Intent.ACTION_VIEW);63 intent.setDataAndType(Uri.parse("file://" + apkPath),64 "application/vnd.android.package-archive");65 66 context.startActivity(intent);67 }68 }
apk 签名信息获取工具类
1 import java.io.File; 2 import java.lang.reflect.Constructor; 3 import java.lang.reflect.Field; 4 import java.lang.reflect.Method; 5 import java.util.Iterator; 6 import java.util.List; 7 8 import android.content.Context; 9 import android.content.pm.PackageInfo; 10 import android.content.pm.PackageManager; 11 import android.content.pm.Signature; 12 import android.util.DisplayMetrics; 13 14 /** 15 * 类说明: apk 签名信息获取工具类 16 * 17 * @author Cundong 18 * @date 2013-9-6 19 * @version 1.0 20 */ 21 public class SignUtils { 22 23 /** 24 * 获取未安装Apk的签名 25 * 26 * @param apkPath 27 * @return 28 */ 29 public static String getUnInstalledApkSignature(String apkPath) { 30 String PATH_PackageParser = "android.content.pm.PackageParser"; 31 32 try { 33 Class pkgParserCls = Class.forName(PATH_PackageParser); 34 Class [] typeArgs = new Class[1]; 35 typeArgs[0] = String.class; 36 Constructor pkgParserCt = pkgParserCls.getConstructor(typeArgs); 37 Object[] valueArgs = new Object[1]; 38 valueArgs[0] = apkPath; 39 Object pkgParser = pkgParserCt.newInstance(valueArgs); 40 41 DisplayMetrics metrics = new DisplayMetrics(); 42 metrics.setToDefaults(); 43 44 typeArgs = new Class[4]; 45 typeArgs[0] = File.class; 46 typeArgs[1] = String.class; 47 typeArgs[2] = DisplayMetrics.class; 48 typeArgs[3] = Integer.TYPE; 49 50 Method pkgParser_parsePackageMtd = pkgParserCls.getDeclaredMethod( 51 "parsePackage", typeArgs); 52 valueArgs = new Object[4]; 53 valueArgs[0] = new File(apkPath); 54 valueArgs[1] = apkPath; 55 valueArgs[2] = metrics; 56 valueArgs[3] = PackageManager.GET_SIGNATURES; 57 Object pkgParserPkg = pkgParser_parsePackageMtd.invoke(pkgParser, 58 valueArgs); 59 60 typeArgs = new Class[2]; 61 typeArgs[0] = pkgParserPkg.getClass(); 62 typeArgs[1] = Integer.TYPE; 63 64 Method pkgParser_collectCertificatesMtd = pkgParserCls 65 .getDeclaredMethod("collectCertificates", typeArgs); 66 valueArgs = new Object[2]; 67 valueArgs[0] = pkgParserPkg; 68 valueArgs[1] = PackageManager.GET_SIGNATURES; 69 pkgParser_collectCertificatesMtd.invoke(pkgParser, valueArgs); 70 71 Field packageInfoFld = pkgParserPkg.getClass().getDeclaredField( 72 "mSignatures"); 73 Signature[] info = (Signature[]) packageInfoFld.get(pkgParserPkg); 74 return info[0].toCharsString(); 75 } catch (Exception e) { 76 e.printStackTrace(); 77 } 78 79 return null; 80 } 81 82 /** 83 * 获取已安装apk签名 84 * 85 * @param context 86 * @param packageName 87 * @return 88 */ 89 public static String getInstalledApkSignature(Context context, 90 String packageName) { 91 PackageManager pm = context.getPackageManager(); 92 Listapps = pm 93 .getInstalledPackages(PackageManager.GET_SIGNATURES); 94 95 Iterator iter = apps.iterator(); 96 while (iter.hasNext()) { 97 PackageInfo packageinfo = iter.next(); 98 String thisName = packageinfo.packageName; 99 if (thisName.equals(packageName)) {100 return packageinfo.signatures[0].toCharsString();101 }102 }103 104 return null;105 }106 }