package com.ecjia.update; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.os.AsyncTask; import android.text.Html; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.Toast; import com.ecjia.hamster.model.UPDATE_BEAN; import com.ecjia.mydialog.CustomDialog; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; /** * Created by Jongey on 2018/3/27 0027. * CheckVersionInfoTask.java * 从服务器取得版本信息,与本地apk对比版本号,判断是否有更新,然后用Dialog让用户选择是否更新 * 若用户选择更新,则调用服务去完成apk文件的下载 */ public class CheckVersionInfoTask extends AsyncTask<Void, Void, String> { private static final String TAG = "CheckVersionInfoTask"; private ProgressDialog dialog; private Context mContext; private boolean mShowProgressDialog; private CustomDialog.Builder builder; private CustomDialog mDialog; private UPDATE_BEAN update_bean; private String apkUrl ; private String updateMessage ; private int apkCode ; //服务器端地址 private static final String VERSION_INFO_URL = "xxxxxx"; public CheckVersionInfoTask(Context context, boolean showProgressDialog) { this.mContext = context; this.mShowProgressDialog = showProgressDialog; } //初始化显示Dialog protected void onPreExecute() { if (mShowProgressDialog) { dialog = new ProgressDialog(mContext); dialog.setMessage("正在检测新版本"); dialog.show(); } } //在后台任务(子线程)中检查服务器的版本信息 @Override protected String doInBackground(Void... params) { return getVersionInfo(VERSION_INFO_URL); } //后台任务执行完毕后,解除Dialog并且解析return返回的结果 @Override protected void onPostExecute(String result) { if (dialog != null && dialog.isShowing()) { dialog.dismiss(); } if (!TextUtils.isEmpty(result)) { parseJson(result); } } /** * 从服务器取得版本信息 * { "url":"http://crazyfzw.github.io/demo/auto-update-version/new-version-v2.0.apk", "versionName":2, "updateMessage":"[1]新增视频弹幕功能<br/>[2]优化离线缓存功能<br/>[3]增强了稳定性" } * @return */ public String getVersionInfo(String urlStr){ HttpURLConnection uRLConnection = null; InputStream is = null; BufferedReader buffer = null; String result = null; try { URL url = new URL(urlStr); uRLConnection = (HttpURLConnection) url.openConnection(); uRLConnection.setRequestMethod("GET"); is = uRLConnection.getInputStream(); buffer = new BufferedReader(new InputStreamReader(is)); StringBuilder strBuilder = new StringBuilder(); String line; while ((line = buffer.readLine()) != null) { strBuilder.append(line); } result = strBuilder.toString(); } catch (Exception e) { Log.e(TAG, "http post error"); } finally { if (buffer != null) { try { buffer.close(); } catch (IOException ignored) { } } if (is != null) { try { is.close(); } catch (IOException ignored) { } } if (uRLConnection != null) { uRLConnection.disconnect(); } } return result; } /** * * @param result */ private void parseJson(String result) { Gson gson = new Gson(); java.lang.reflect.Type type = new TypeToken<UPDATE_BEAN>() {}.getType(); update_bean = gson.fromJson(result, type); apkUrl=update_bean.getData().getApk_url();//APK下载路径 updateMessage=update_bean.getData().getApp_new_text();//版本更新说明 apkCode = update_bean.getData().getApp_version_num(); //新版APK对于的版本号 //JSONObject obj = new JSONObject(result); //String apkUrl = obj.getString("apk_url"); //String updateMessage = obj.getString("app_new_text"); //String apkCode = obj.getString("app_version_num"); //取得已经安装在手机的APP的版本号 versionCode final int versionCode = getCurrentVersionCode(); //对比版本号判断是否需要更新 if (apkCode > versionCode) { //showDialog(updateMessage, apkUrl); showTwoButtonDialog(updateMessage,"版本更新提示","立即更新","取消",new View.OnClickListener() { @Override public void onClick(View v) { goToDownloadApk(apkUrl); mDialog.dismiss(); } }, new View.OnClickListener() { @Override public void onClick(View v) { mDialog.dismiss(); //这里写自定义处理XXX } }); } else if (mShowProgressDialog) { Toast.makeText(mContext, "当前已经是最新版本了哦", Toast.LENGTH_SHORT).show(); } } /** * 取得当前版本号 * @return */ public int getCurrentVersionCode() { try { return mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), PackageManager.GET_CONFIGURATIONS).versionCode; } catch (PackageManager.NameNotFoundException ignored) { } return 0; } /** * 显示对话框提示用户有新版本,并且让用户选择是否更新版本 * @param alertText 提示信息 * @param confirmText 升级 * @param cancelText 忽略 * @param conFirmListener 升级按钮监听 * @param cancelListener 忽略按钮监听 */ private void showTwoButtonDialog(String alertText,String title,String confirmText, String cancelText, View.OnClickListener conFirmListener, View.OnClickListener cancelListener) { builder = new CustomDialog.Builder(mContext); mDialog = builder.setTitle(title) .setMessage(Html.fromHtml(alertText)) .setPositiveButton(confirmText, conFirmListener) .setNegativeButton(cancelText, cancelListener) .createTwoButtonDialog(); mDialog.show(); } /** * 显示对话框提示用户有新版本,并且让用户选择是否更新版本 * @param content 提示信息 * @param downloadUrl apk下载地址 */ public void showDialog(String content, final String downloadUrl) { AlertDialog.Builder builder = new AlertDialog.Builder(mContext); builder.setTitle("发现了新版本"); builder.setMessage(Html.fromHtml(content)) .setPositiveButton("升级", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { //下载apk文件 goToDownloadApk(downloadUrl); } }) .setNegativeButton("忽略", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { } }); AlertDialog dialog = builder.create(); //点击对话框外面,对话框不消失 dialog.setCanceledOnTouchOutside(false); dialog.show(); } /** * 用intent启用DownloadService服务去下载AKP文件 * @param downloadUrl */ private void goToDownloadApk(String downloadUrl) { Intent intent = new Intent(mContext, DownloadApkService.class); intent.putExtra("apkUrl", downloadUrl); mContext.startService(intent); } } /** *后台下载apk服务 */
package com.ecjia.update; import android.app.IntentService; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.support.v4.app.NotificationCompat; import android.util.Log; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; /** * Created by Jongey on 2018/3/27 0027. * 创建服务完成apk文件的下载,下载完成后调用系统的安装程序完成安装 */ public class DownloadApkService extends IntentService{ private static final int BUFFER_SIZE = 10 * 1024; //缓存大小 private static final String TAG = "DownloadService"; private static final int NOTIFICATION_ID = 0; private NotificationManager mNotifyManager; private NotificationCompat.Builder mBuilder; public DownloadApkService() { super("DownloadApkService"); } /** * 在onHandleIntent中下载apk文件 * @param intent */ @Override protected void onHandleIntent(Intent intent) { //初始化通知,用于显示下载进度 mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mBuilder = new NotificationCompat.Builder(this); String appName = getString(getApplicationInfo().labelRes); int icon = getApplicationInfo().icon; mBuilder.setContentTitle(appName).setSmallIcon(icon); String urlStr = intent.getStringExtra("apkUrl"); //从intent中取得apk下载路径 InputStream in = null; FileOutputStream out = null; try { //建立下载连接 URL url = new URL(urlStr); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.setRequestMethod("GET"); urlConnection.setDoOutput(false); urlConnection.setConnectTimeout(10 * 1000); urlConnection.setReadTimeout(10 * 1000); urlConnection.setRequestProperty("Connection", "Keep-Alive"); urlConnection.setRequestProperty("Charset", "UTF-8"); urlConnection.setRequestProperty("Accept-Encoding", "gzip, deflate"); urlConnection.connect(); //以文件流读取数据 long bytetotal = urlConnection.getContentLength(); //取得文件长度 long bytesum = 0; int byteread = 0; in = urlConnection.getInputStream(); File dir = StorageUtils.getCacheDirectory(this); //取得应用缓存目录 String apkName = urlStr.substring(urlStr.lastIndexOf("/") + 1, urlStr.length());//取得apK文件名 File apkFile = new File(dir, apkName); out = new FileOutputStream(apkFile); byte[] buffer = new byte[BUFFER_SIZE]; int limit = 0; int oldProgress = 0; while ((byteread = in.read(buffer)) != -1) { bytesum += byteread; out.write(buffer, 0, byteread); int progress = (int) (bytesum * 100L / bytetotal); // 如果进度与之前进度相等,则不更新,如果更新太频繁,则会造成界面卡顿 if (progress != oldProgress) { updateProgress(progress); } oldProgress = progress; } // 下载完成,调用installAPK开始安装文件 installAPk(apkFile); Log.d("调试","download apk finish"); mNotifyManager.cancel(NOTIFICATION_ID); } catch (Exception e) { Log.e(TAG, "download apk file error"); } finally { if (out != null) { try { out.close(); } catch (IOException ignored) { } } if (in != null) { try { in.close(); } catch (IOException ignored) { } } } } /** * 实时更新下载进度条显示 * @param progress */ private void updateProgress(int progress) { //"正在下载:" + progress + "%" mBuilder.setContentText("升级").setProgress(100, progress, false); PendingIntent pendingintent = PendingIntent.getActivity(this, 0, new Intent(), PendingIntent.FLAG_CANCEL_CURRENT); mBuilder.setContentIntent(pendingintent); mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build()); } /** * 调用系统安装程序安装下载好的apk * @param apkFile */ private void installAPk(File apkFile) { Intent intent = new Intent(Intent.ACTION_VIEW); //如果没有设置SDCard写权限,或者没有sdcard,apk文件保存在内存中,需要授予权限才能安装 try { String[] command = {"chmod", "777", apkFile.toString()}; //777代表权限 rwxrwxrwx ProcessBuilder builder = new ProcessBuilder(command); builder.start(); } catch (IOException ignored) { } intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } }/**
*工具类
*/
package com.ecjia.update; import android.content.Context; import android.content.pm.PackageManager; import android.os.Environment; import android.util.Log; import java.io.File; import java.io.IOException; import static android.os.Environment.MEDIA_MOUNTED; /** * Provides application storage paths */ final class StorageUtils { private static final String TAG = "StorageUtils"; private static final String EXTERNAL_STORAGE_PERMISSION = "android.permission.WRITE_EXTERNAL_STORAGE"; private StorageUtils() { } /** * Returns application cache directory. Cache directory will be created on SD card * <i>("/Android/data/[app_package_name]/cache")</i> if card is mounted and app has appropriate permission. Else - * Android defines cache directory on device's file system. * * @param context Application context * @return Cache {@link File directory} */ public static File getCacheDirectory(Context context) { File appCacheDir = null; if (MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) { appCacheDir = getExternalCacheDir(context); } if (appCacheDir == null) { appCacheDir = context.getCacheDir(); } if (appCacheDir == null) { Log.w(TAG, "Can't define system cache directory! The app should be re-installed."); } return appCacheDir; } private static File getExternalCacheDir(Context context) { File dataDir = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data"); File appCacheDir = new File(new File(dataDir, context.getPackageName()), "cache"); if (!appCacheDir.exists()) { if (!appCacheDir.mkdirs()) { Log.w(TAG, "Unable to create external cache directory"); return null; } try { new File(appCacheDir, ".nomedia").createNewFile(); } catch (IOException e) { Log.i(TAG, "Can't create \".nomedia\" file in application external cache directory"); } } return appCacheDir; } private static boolean hasExternalStoragePermission(Context context) { int perm = context.checkCallingOrSelfPermission(EXTERNAL_STORAGE_PERMISSION); return perm == PackageManager.PERMISSION_GRANTED; } }