Android更新app就不需要像iOS那样依赖于AppStore了,完全可以将 apk 安装包放在自己的服务器,每次启动app时去检查服务器的版本信息。如果高于本地版本就下载安装新版本。
版本控制
versionCode:类似iOS里的Build构建号,不对外显示,只是内部使用和应用商店更新时去修改构建号,一般每次更新版本就递增1。
versionName:类似iOS里的Version版本号,这个才是展示在应用商店和app界面上的版本号,一般我们更新版本时比较这个就行了。
请求更新版本接口后,我们可以获取到类似的返回信息,然后和本地应用版本进行比较。
php脚本
1 2 3 4 5 6 |
<?php exit(json_encode([ "version" => "1.0.1", "apk_url" => "https://blog.6ag.cn/study/Mobilesafe.apk", "des" => "新版本上线了,快来下载吧!!!" ];)); |
返回的json
1 2 3 4 5 |
{ "code": "1.0.1", "apkurl": "https://blog.6ag.cn/study/Mobilesafe.apk", "des": "新版本上线了,快来下载吧!!!" } |
获取本地版本号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/** * 获取当前应用的版本号 * * @return 版本号 */ private String getVersionName() { PackageManager pm = getPackageManager(); // 第一个参数:应用程序的包名 // 第二个参数:指定信息的标签,0表示获取基础信息,比如包名、版本号。要想获取权限等信息必须要通过标签指定。 try { PackageInfo info = pm.getPackageInfo(getPackageName(), 0); return info.versionName; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return ""; } |
安装APK
如果有则开启子线程去下载apk安装文件,并获取到apk文件下载后的保存路径,然后启动Android系统自带的应用 PackageInstaller 去进行安装,通过分析这个应用程序的 AndroidManifest ,我们可以使用隐式 Intent 来启动 PackageInstaller 安装apk的 activity 。
1 2 3 4 5 6 7 8 9 10 11 |
/** * 安装下载的新版本apk * @param apkPath apk存放路径 */ private void installAPK(String apkPath) { Intent intent = new Intent(); intent.setAction("android.intent.action.VIEW"); intent.addCategory("android.intent.category.DEFAULT"); intent.setDataAndType(Uri.fromFile(new File(apkPath)), "application/vnd.android.package-archive"); startActivityForResult(intent, MY_INSTALL_REQUEST); } |
注意我们需要使用带响应结果的启动方法:startActivityForResult(..),因为用户有可能会取消安装。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/** * 跳转activity后的回调结果 */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case MY_INSTALL_REQUEST: if (resultCode == RESULT_CANCELED) { // 应用安装被取消,跳转到主界面 } break; } } |
安装完毕后会自动退出应用程序,如果取消安装则进入主界面或继续使用当前版本,这样就完成了整个升级过程。
完整代码:
PHP部分
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php $result = [ "err_msg" => "success", "info" => "获取版本信息成功", "data" => [ "version" => "1.0.1", "description" => "1.修复已知bug\n2.兼容Android4.2系统", "url" => "http://download.baokan.tv/baokan.apk" ] ]; exit(json_encode($result)); |
Java部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
private static final String TAG = "MainActivity"; private static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 10000; // 写入SD卡权限 private ProgressDialog mDownloadDialog; // 加载进度 private String serverVersion; // 服务器版本号 private String description; // 新版本更新描述 private String apkUrl; // 新版本apk下载地址 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 检查版本更新 checkVersion(); } /** * 检查是否有新版本 */ private void checkVersion() { NetworkUtils.shared.get(APIs.UPDATE, new HashMap<String, String>(), new NetworkUtils.StringCallback() { @Override public void onError(Call call, Exception e, int id) { ProgressHUD.showInfo(mContext, "您的网络不给力"); } @Override public void onResponse(String response, int id) { try { JSONObject jsonObject = new JSONObject(response); if (jsonObject.getString("err_msg").equals("success")) { JSONObject versionInfo = jsonObject.getJSONObject("data"); serverVersion = versionInfo.getString("version"); description = versionInfo.getString("description"); apkUrl = versionInfo.getString("url"); // 更新版本 showUpdateDialog(); } } catch (JSONException e) { e.printStackTrace(); ProgressHUD.showInfo(mContext, "数据解析异常"); } } }); } /** * 弹出对话框更新app版本 */ protected void showUpdateDialog() { // 检查是否是新版本 String currentVersion = BaoKanApp.app.getVersionName(); if (currentVersion.equals(serverVersion)) { return; } AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("发现新版本:" + serverVersion); builder.setIcon(R.mipmap.ic_launcher); builder.setMessage(description); builder.setCancelable(false); builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 判断是否有写入SD权限 if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // 申请权限 ActivityCompat.requestPermissions(mContext, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); } else { // 有写入权限直接下载apk downloadAPK(); } } }); builder.setNegativeButton("取消", null); builder.show(); } /** * 运行时权限请求回调结果 */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { downloadAPK(); } else { Toast.makeText(getApplicationContext(), "你没有文件写入权限", Toast.LENGTH_SHORT).show(); } break; } } /** * 下载新版本 */ protected void downloadAPK() { // apk文件保存路径 String apkPath = null; String apkName = "baokan" + serverVersion + ".apk"; if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { apkPath = Environment.getExternalStorageDirectory().getAbsolutePath(); } if (apkPath == null) { ProgressHUD.showInfo(mContext, "您的手机没有SD卡"); return; } // 弹出下载进度会话框 showDownloadDialog(); // 下载文件 OkHttpUtils .get() .url(apkUrl) .build() .execute(new FileCallBack(apkPath, apkName) { @Override public void onResponse(File arg0, int arg1) { mDownloadDialog.dismiss(); // 下载完成安装apk installAPK(arg0.getAbsolutePath()); } @Override public void onError(Call arg0, Exception arg1, int arg2) { mDownloadDialog.dismiss(); ProgressHUD.showInfo(mContext, "您的网络不给力哦"); } @Override public void inProgress(float progress, long total, int id) { // 更新下载进度 mDownloadDialog.setProgress(Math.round(progress * 100)); } }); } /** * 弹出下载对话框 */ public void showDownloadDialog() { mDownloadDialog = new ProgressDialog(mContext); mDownloadDialog.setIcon(R.mipmap.ic_launcher); mDownloadDialog.setTitle("版本更新"); mDownloadDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); mDownloadDialog.setMessage("正在玩命下载中......"); mDownloadDialog.getWindow().setGravity(Gravity.CENTER); mDownloadDialog.setMax(100); mDownloadDialog.show(); } /** * 安装下载的新版本apk * * @param apkPath apk存放路径 */ private void installAPK(String apkPath) { Intent intent = new Intent(); intent.setAction("android.intent.action.VIEW"); intent.addCategory("android.intent.category.DEFAULT"); intent.setDataAndType(Uri.fromFile(new File(apkPath)), "application/vnd.android.package-archive"); startActivity(intent); } |
2017年03月23日 21:00 沙发