Android 开发工具类 35_PatchUtils

增量更新工具类【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<String, Void, Integer> {
 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         @Override
108         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 = SignUtils
121                             .getUnInstalledApkSignature(Constants.NEW_APK_PATH);
122
123                     String signatureSource = SignUtils
124                             .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         @Override
143         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_PACKAGENAME
174                             + "的源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     Cundong
13  * @date     2013-9-6
14  * @version 1.0
15  */
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.apk
34      *
35      * @param context
36      * @param packageName
37      * @return
38      */
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      * 安装Apk
56      *
57      * @param context
58      * @param apkPath
59      */
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         List<PackageInfo> apps = pm
 93                 .getInstalledPackages(PackageManager.GET_SIGNATURES);
 94
 95         Iterator<PackageInfo> 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 }
时间: 2024-08-07 16:08:05

Android 开发工具类 35_PatchUtils的相关文章

android开发工具类总结(一)

一.日志工具类 Log.java 1 public class L 2 { 3 private L() 4 { 5 /* 不可被实例化 */ 6 throw new UnsupportedOperationException("Cannot be instantiated!"); 7 } 8 // 是否需要打印bug,可以在application的onCreate函数里面初始化 9 public static boolean isDebug = true; 10 private sta

Android 开发工具类 13_ SaxService

网络 xml 解析方式 1 package com.example.dashu_saxxml; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.util.HashMap; 6 import java.util.List; 7 8 import javax.xml.parsers.SAXParser; 9 import javax.xml.parsers.SAXParserFactory; 10

Android开发工具类之DownloadManagerPro

这个工具类就是Android系统下载管理DownloadManager的一个增强类,提供了一些增强方法.或许大家不太了解这个安卓系统自带的DownloadManager这个类,我先做一个简单介绍吧.DownloadManager是系统开放给第三方应用使用的类,包含两个静态内部类DownloadManager.Query和DownloadManager.Request. DownloadManager.Request用来请求一个下载,DownloadManager.Query用来查询下载信息.用d

Android 开发工具类 03_HttpUtils

Http 请求的工具类: 1.异步的 Get 请求: 2.异步的 Post 请求: 3.Get 请求,获得返回数据: 4.向指定 URL 发送 POST方法的请求. 1 import java.io.BufferedReader; 2 import java.io.ByteArrayOutputStream; 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.InputStreamReader

Android 开发工具类 19_NetworkStateReceiver

检测网络状态改变类: 1.注册网络状态广播: 2.检查网络状态: 3.注销网络状态广播: 4.获取当前网络状态,true为网络连接成功,否则网络连接失败: 5.注册网络连接观察者: 6.注销网络连接观察者. 1 import java.util.ArrayList; 2 3 import android.content.BroadcastReceiver; 4 import android.content.Context; 5 import android.content.Intent; 6 i

Android 开发工具类 10_Toast 统一管理类

Toast 统一管理类: 1.短时间显示Toast: 2.长时间显示 Toast: 3.自定义显示 Toast 时间. 1 import android.content.Context; 2 import android.widget.Toast; 3 4 // Toast 统一管理类 5 public class T 6 { 7 8 private T() 9 { 10 /* cannot be instantiated */ 11 throw new UnsupportedOperation

Android 开发工具类 09_SPUtils

SharedPreferences 辅助类: 1.保存在手机里面的文件名: 2.保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法: 3.得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值: 4.移除某个 key 值已经对应的值: 5.清除所有数据: 6.查询某个 key 是否已经存在: 7.返回所有的键值对: 8.创建一个解决 SharedPreferencesCompat.apply 方法的一个兼容类: 1 import jav

Android 开发工具类 33_开机自运行

原理:该类派生自 BroadcastReceiver,重载方法 onReceive ,检测接收到的 Intent 是否符合 BOOT_COMPLETED,如果符合,则启动用户Activity. 1 import android.content.BroadcastReceiver; 2 import android.content.Context; 3 import android.content.Intent; 4 5 public class BootBroadcastReceiver ext

Android 开发工具类 31_WebService 获取手机号码归属地

AndroidInteractWithWebService.xml 1 <?xml version="1.0" encoding="utf-8"?> 2 <soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12=