Android 开发工具类 35_PatchUtils

时间:2023-03-09 05:02:04
Android 开发工具类 35_PatchUtils

增量更新工具类【https://github.com/cundong/SmartAppUpdates】

 import java.io.File;

 import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast; import com.cundong.utils.PatchUtils;
import com.example.test.util.ApkUtils;
import com.example.test.util.SignUtils;
import com.example.test2.R; /**
* 类说明: ApkPatchLibrary 使用 Demo
*
* @author Cundong
* @version 1.5
*/
public class MainActivity extends Activity { // 合成成功
private static final int WHAT_SUCCESS = 1; // 合成的APK签名和已安装的签名不一致
private static final int WHAT_FAIL_SING = -1; // 合成失败
private static final int WHAT_FAIL_ERROR = -2; // 获取源文件失败
private static final int WHAT_FAIL_GET_SOURCE = -3; private Context mContext = null; private ProgressDialog mProgressDialog;
private TextView mResultView;
private Button mStartButton, mGithubButton; private long mBeginTime, mEndTime; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); mContext = getApplicationContext(); mProgressDialog = new ProgressDialog(this);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setMessage("doing..");
mProgressDialog.setCancelable(false); mResultView = (TextView) findViewById(R.id.textview4);
mStartButton = (Button) findViewById(R.id.start_btn);
mGithubButton = (Button) findViewById(R.id.github_btn); mStartButton.setOnClickListener(new OnClickListener() { @Override
public void onClick(View arg0) { File patchFile = new File(Constants.PATCH_PATH); if (!ApkUtils.isInstalled(mContext, Constants.TEST_PACKAGENAME)) {
Toast.makeText(mContext, getString(R.string.demo_info1),
Toast.LENGTH_LONG).show();
} else if (!patchFile.exists()) {
Toast.makeText(mContext, getString(R.string.demo_info2),
Toast.LENGTH_LONG).show();
} else {
new PatchApkTask().execute();
}
}
}); mGithubButton.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
Intent intent = new Intent("android.intent.action.VIEW", Uri.parse("https://github.com/cundong/SmartAppUpdates"));
startActivity(intent);
}
});
} private class PatchApkTask extends AsyncTask<String, Void, Integer> { @Override
protected void onPreExecute() {
super.onPreExecute(); mProgressDialog.show(); mResultView.setText("");
mBeginTime = System.currentTimeMillis();
} @Override
protected Integer doInBackground(String... params) { String oldApkSource = ApkUtils.getSourceApkPath(mContext,
Constants.TEST_PACKAGENAME); if (!TextUtils.isEmpty(oldApkSource)) { int patchResult = PatchUtils.patch(oldApkSource,
Constants.NEW_APK_PATH, Constants.PATCH_PATH); if (patchResult == 0) { String signatureNew = SignUtils
.getUnInstalledApkSignature(Constants.NEW_APK_PATH); String signatureSource = SignUtils
.getInstalledApkSignature(mContext,
Constants.TEST_PACKAGENAME); if (!TextUtils.isEmpty(signatureNew)
&& !TextUtils.isEmpty(signatureSource)
&& signatureNew.equals(signatureSource)) {
return WHAT_SUCCESS;
} else {
return WHAT_FAIL_SING;
}
} else {
return WHAT_FAIL_ERROR;
}
} else {
return WHAT_FAIL_GET_SOURCE;
}
} @Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result); if (mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
} mEndTime = System.currentTimeMillis();
mResultView.setText("耗时: " + (mEndTime - mBeginTime) + "ms"); switch (result) {
case WHAT_SUCCESS: { String text = "新apk已合成成功:" + Constants.NEW_APK_PATH;
showShortToast(text); ApkUtils.installApk(MainActivity.this, Constants.NEW_APK_PATH);
break;
}
case WHAT_FAIL_SING: {
String text = "新apk已合成失败,签名不一致";
showShortToast(text);
break;
}
case WHAT_FAIL_ERROR: {
String text = "新apk已合成失败";
showShortToast(text);
break;
}
case WHAT_FAIL_GET_SOURCE: {
String text = "无法获取packageName为" + Constants.TEST_PACKAGENAME
+ "的源apk文件,只能整包更新了!";
showShortToast(text);
break;
}
}
}
} private void showShortToast(final String text) { Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show();
} static {
System.loadLibrary("ApkPatchLibrary");
}
}

APK 工具类

 import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.text.TextUtils; /**
* 类说明: Apk工具类
*
* @author Cundong
* @date 2013-9-6
* @version 1.0
*/
public class ApkUtils { public static boolean isInstalled(Context context, String packageName) {
PackageManager pm = context.getPackageManager();
boolean installed = false;
try {
pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
installed = true;
} catch (Exception e) {
e.printStackTrace();
} return installed;
} /**
* 获取已安装Apk文件的源Apk文件
* 如:/data/app/com.sina.weibo-1.apk
*
* @param context
* @param packageName
* @return
*/
public static String getSourceApkPath(Context context, String packageName) {
if (TextUtils.isEmpty(packageName))
return null; try {
ApplicationInfo appInfo = context.getPackageManager()
.getApplicationInfo(packageName, 0);
return appInfo.sourceDir;
} catch (NameNotFoundException e) {
e.printStackTrace();
} return null;
} /**
* 安装Apk
*
* @param context
* @param apkPath
*/
public static void installApk(Context context, String apkPath) { Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file://" + apkPath),
"application/vnd.android.package-archive"); context.startActivity(intent);
}
}

apk 签名信息获取工具类

 import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List; import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.util.DisplayMetrics; /**
* 类说明: apk 签名信息获取工具类
*
* @author Cundong
* @date 2013-9-6
* @version 1.0
*/
public class SignUtils { /**
* 获取未安装Apk的签名
*
* @param apkPath
* @return
*/
public static String getUnInstalledApkSignature(String apkPath) {
String PATH_PackageParser = "android.content.pm.PackageParser"; try {
Class<?> pkgParserCls = Class.forName(PATH_PackageParser);
Class<?>[] typeArgs = new Class[1];
typeArgs[0] = String.class;
Constructor<?> pkgParserCt = pkgParserCls.getConstructor(typeArgs);
Object[] valueArgs = new Object[1];
valueArgs[0] = apkPath;
Object pkgParser = pkgParserCt.newInstance(valueArgs); DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults(); typeArgs = new Class[4];
typeArgs[0] = File.class;
typeArgs[1] = String.class;
typeArgs[2] = DisplayMetrics.class;
typeArgs[3] = Integer.TYPE; Method pkgParser_parsePackageMtd = pkgParserCls.getDeclaredMethod(
"parsePackage", typeArgs);
valueArgs = new Object[4];
valueArgs[0] = new File(apkPath);
valueArgs[1] = apkPath;
valueArgs[2] = metrics;
valueArgs[3] = PackageManager.GET_SIGNATURES;
Object pkgParserPkg = pkgParser_parsePackageMtd.invoke(pkgParser,
valueArgs); typeArgs = new Class[2];
typeArgs[0] = pkgParserPkg.getClass();
typeArgs[1] = Integer.TYPE; Method pkgParser_collectCertificatesMtd = pkgParserCls
.getDeclaredMethod("collectCertificates", typeArgs);
valueArgs = new Object[2];
valueArgs[0] = pkgParserPkg;
valueArgs[1] = PackageManager.GET_SIGNATURES;
pkgParser_collectCertificatesMtd.invoke(pkgParser, valueArgs); Field packageInfoFld = pkgParserPkg.getClass().getDeclaredField(
"mSignatures");
Signature[] info = (Signature[]) packageInfoFld.get(pkgParserPkg);
return info[0].toCharsString();
} catch (Exception e) {
e.printStackTrace();
} return null;
} /**
* 获取已安装apk签名
*
* @param context
* @param packageName
* @return
*/
public static String getInstalledApkSignature(Context context,
String packageName) {
PackageManager pm = context.getPackageManager();
List<PackageInfo> apps = pm
.getInstalledPackages(PackageManager.GET_SIGNATURES); Iterator<PackageInfo> iter = apps.iterator();
while (iter.hasNext()) {
PackageInfo packageinfo = iter.next();
String thisName = packageinfo.packageName;
if (thisName.equals(packageName)) {
return packageinfo.signatures[0].toCharsString();
}
} return null;
}
}