Android app更新方案:兼容6.0,7.0系统 包含强制更新,非强制后台更新,浏览器更新
项目中app更新功能是必不可少的 项目中遇到到的动态权限问题,文件权限问题
根据需求的不同 有时需要用户强制更新 有时更新对于用户是可选的 可以放在后台更新
最简单直接的方法直接打开浏览器更新 完全不用适配什么6.0 什么7.0 几行代码搞定 代码如下:
/** * 打开浏览器 * @param url */ public void browerUpdate(String url) { Intent intent = new Intent(); intent.setAction("android.intent.action.VIEW"); Uri content_url = Uri.parse(url); intent.setData(content_url); AppManager.getInstance().getTopActivity().startActivity(intent); }
强制更新 完整代码:
package com.langj.update.appconfig; import android.app.DownloadManager; import android.app.ProgressDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Handler; import android.os.Message; import android.widget.Toast; import com.langj.update.R; import com.langj.update.app.Constant; import com.langj.update.utils.AppManager; import com.langj.update.utils.SPUtil; import org.xutils.x; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; / public class DownloadUtils { private DownloadManager downloadManager; private Context mContext; private long downloadId; private ProgressDialog progressDialog; private DownloadChangeObserver downloadObserver; public static final int HANDLE_DOWNLOAD = 0x001; private ScheduledExecutorService scheduledExecutorService; public DownloadUtils(Context context) { this.mContext = context; } public void downloadAPK(String url) { downloadObserver = new DownloadChangeObserver(); showProcess(); registerContentObserver(); //创建下载任务 DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); //移动网络情况下是否允许漫游 request.setAllowedOverRoaming(false); request.setMimeType("application/vnd.android.package-archive"); //在通知栏中显示,默认就是显示的 request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN); request.setTitle("新版本" + x.app().getResources().getString(R.string.app_name)); request.setDescription("正在下载" + x.app().getResources().getString(R.string.app_name)); request.setVisibleInDownloadsUi(true); //设置下载的路径 // request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, " myApp.apk"); request.setDestinationInExternalFilesDir(mContext.getApplicationContext(), "phoenix", "phoenix.apk"); //获取DownloadManager downloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); //将下载请求加入下载队列,加入下载队列后会给该任务返回一个long型的id,通过该id可以取消任务,重启任务、获取下载的文件等等 downloadId = downloadManager.enqueue(request); registerBroadcast(); } /** * 正在下载中的进度条 */ public void showProcess() { progressDialog = new ProgressDialog(AppManager.getInstance().getTopActivity()); progressDialog.setCancelable(false); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDialog.setMax(100); progressDialog.setMessage("正在下载..."); progressDialog.show(); } /** * 注册广播 */ private void registerBroadcast() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE); intentFilter.addAction(DownloadManager.ACTION_NOTIFICATION_CLICKED); mContext.registerReceiver(receiver, intentFilter); } /** * 注册ContentObserver */ private void registerContentObserver() { /** observer download change **/ if (downloadObserver != null) { mContext.getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"), true, downloadObserver); } } private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { checkStatus(); } }; //检查下载状态 private void checkStatus() { DownloadManager.Query query = new DownloadManager.Query(); //通过下载的id查找 query.setFilterById(downloadId); Cursor c = downloadManager.query(query); if (c != null && c.moveToFirst()) { int state = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS)); switch (state) { //下载暂停 case DownloadManager.STATUS_PAUSED: break; case DownloadManager.ERROR_CANNOT_RESUME: //下载延迟 case DownloadManager.STATUS_PENDING: break; //正在下载 case DownloadManager.STATUS_RUNNING: break; //下载完成 case DownloadManager.STATUS_SUCCESSFUL: //下载完成安装APK close(); progressDialog.setProgress(100); unregisterBroadcast(); unregisterContentObserver(); Uri downloadFileUri = downloadManager.getUriForDownloadedFile(downloadId); SPUtil.put(Constant.SP_DOWNLOAD_PATH, downloadFileUri.getPath()); installApk(downloadFileUri); break; //下载失败 case DownloadManager.STATUS_FAILED: Toast.makeText(mContext, "下载失败", Toast.LENGTH_SHORT).show(); break; } } c.close(); } /** * 安装APK * * @param apkPath 安装包的路径 */ public void installApk(Uri apkPath) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setDataAndType(apkPath, "application/vnd.android.package-archive"); mContext.startActivity(intent); } public Handler downLoadHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (HANDLE_DOWNLOAD == msg.what) { float i = (msg.arg1 / (float) msg.arg2); int k = (int) (i * 100); if (k >= 0) { progressDialog.setProgress(k); } } } }; /** * 关闭定时器,线程等操作 */ private void close() { if (scheduledExecutorService != null && !scheduledExecutorService.isShutdown()) { scheduledExecutorService.shutdown(); } if (downLoadHandler != null) { downLoadHandler.removeCallbacksAndMessages(null); } } /** * 注销广播 */ private void unregisterBroadcast() { if (receiver != null) { mContext.unregisterReceiver(receiver); receiver = null; } } /** * 注销ContentObserver */ private void unregisterContentObserver() { if (downloadObserver != null) { mContext.getContentResolver().unregisterContentObserver(downloadObserver); } } private Runnable progressRunnable = new Runnable() { @Override public void run() { int[] bytesAndStatus = getBytesAndStatus(downloadId); downLoadHandler.sendMessage(downLoadHandler.obtainMessage(HANDLE_DOWNLOAD, bytesAndStatus[0], bytesAndStatus[1], bytesAndStatus[2])); } }; private int[] getBytesAndStatus(long downloadId) { int[] bytesAndStatus = new int[]{ -1, -1, 0 }; DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId); Cursor cursor = null; try { cursor = downloadManager.query(query); if (cursor != null && cursor.moveToFirst()) { //已经下载文件大小 bytesAndStatus[0] = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); //下载文件的总大小 bytesAndStatus[1] = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); //下载状态 bytesAndStatus[2] = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)); } } finally { if (cursor != null) { cursor.close(); } } return bytesAndStatus; } private class DownloadChangeObserver extends ContentObserver { public DownloadChangeObserver() { super(downLoadHandler); scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); } /** * 当所监听的Uri发生改变时,就会回调此方法 * * @param selfChange 此值意义不大, 一般情况下该回调值false */ @Override public void onChange(boolean selfChange) { scheduledExecutorService.scheduleAtFixedRate(progressRunnable, 0, 2, TimeUnit.SECONDS); } } }
后台更新:
package com.langj.update.appconfig; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.IBinder; import android.os.SystemClock; import android.support.annotation.Nullable; import android.support.v4.content.FileProvider; import com.langj.update.R; import com.langj.update.utils.AppManager; import org.xutils.x; import java.io.File; /** */ public class DownLoadService extends Service { String download_url; private int requestCode = (int) SystemClock.uptimeMillis(); @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { download_url = intent.getStringExtra("download_url"); Intent intent_noti = new Intent(); intent_noti.setAction(Intent.ACTION_VIEW); // intent_noti.setDataAndType(Uri.fromFile(mFile), "application/vnd.android.package-archive"); PendingIntent rightPendIntent = PendingIntent.getActivity(this, requestCode, intent_noti, PendingIntent.FLAG_UPDATE_CURRENT); int smallIcon = R.mipmap.ic_launcher; String ticker = "正在更新" + x.app().getResources().getString(R.string.app_name); NotifyUtil notify7 = new NotifyUtil(this, 7); notify7.notify_progress(rightPendIntent, smallIcon, ticker, x.app().getResources().getString(R.string.app_name) + "升级程序", "正在下载中", false, false, false, download_url, new NotifyUtil.DownLoadListener() { @Override public void OnSuccess(File file) { DownLoadService.this.stopSelf(); install(file); } @Override public void onFailure(Throwable t, int errorNo, String strMsg) { } }); return super.onStartCommand(intent, flags, startId); } /** * 安装APk * @param file */ public static void install(File file) { Uri apkUri; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { apkUri = FileProvider.getUriForFile(AppManager.getInstance().getTopActivity(), "com.langj.update.fileprovider", file); } else { apkUri = Uri.fromFile(file); } Intent intent = new Intent(Intent.ACTION_VIEW); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setDataAndType(apkUri, "application/vnd.android.package-archive"); AppManager.getInstance().getTopActivity().startActivity(intent); } }
后台更新用到的通知类“
package com.langj.update.appconfig; import android.annotation.SuppressLint; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Build; import android.os.SystemClock; import android.support.v7.app.NotificationCompat; import android.widget.RemoteViews; import android.widget.Toast; import com.langj.update.R; import com.langj.update.utils.AppManager; import com.langj.update.utils.FileManager; import org.xutils.common.Callback; import org.xutils.common.task.PriorityExecutor; import org.xutils.http.RequestParams; import org.xutils.x; import java.io.File; import java.util.ArrayList; @SuppressLint("NewApi") public class NotifyUtil { private static final int FLAG = Notification.FLAG_INSISTENT; int requestCode = (int) SystemClock.uptimeMillis(); private int NOTIFICATION_ID; private NotificationManager nm; private Notification notification; private NotificationCompat.Builder cBuilder; private Notification.Builder nBuilder; private Context mContext; public NotifyUtil(Context context, int ID) { this.NOTIFICATION_ID = ID; mContext = context; // 获取系统服务来初始化对象 nm = (NotificationManager) mContext .getSystemService(Activity.NOTIFICATION_SERVICE); cBuilder = new NotificationCompat.Builder(mContext); } /** * 设置在顶部通知栏中的各种信息 * * @param pendingIntent * @param smallIcon * @param ticker */ private void setCompatBuilder(PendingIntent pendingIntent, int smallIcon, String ticker, String title, String content, boolean sound, boolean vibrate, boolean lights) { // // 如果当前Activity启动在前台,则不开启新的Activity。 // intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); // // 当设置下面PendingIntent.FLAG_UPDATE_CURRENT这个参数的时候,常常使得点击通知栏没效果,你需要给notification设置一个独一无二的requestCode // // 将Intent封装进PendingIntent中,点击通知的消息后,就会启动对应的程序 // PendingIntent pIntent = PendingIntent.getActivity(mContext, // requestCode, intent, FLAG); cBuilder.setContentIntent(pendingIntent);// 该通知要启动的Intent cBuilder.setSmallIcon(smallIcon);// 设置顶部状态栏的小图标 cBuilder.setLargeIcon((BitmapFactory.decodeResource(AppManager.getInstance().getTopActivity().getResources(), R.mipmap.ic_launcher)));// 设置顶部状态栏的小图标 cBuilder.setTicker(ticker);// 在顶部状态栏中的提示信息 cBuilder.setContentTitle(title);// 设置通知中心的标题 cBuilder.setContentText(content);// 设置通知中心中的内容 cBuilder.setWhen(System.currentTimeMillis()); /* * 将AutoCancel设为true后,当你点击通知栏的notification后,它会自动被取消消失, * 不设置的话点击消息后也不清除,但可以滑动删除 */ cBuilder.setAutoCancel(true); // 将Ongoing设为true 那么notification将不能滑动删除 // notifyBuilder.setOngoing(true); /* * 从Android4.1开始,可以通过以下方法,设置notification的优先级, * 优先级越高的,通知排的越靠前,优先级低的,不会在手机最顶部的状态栏显示图标 */ cBuilder.setPriority(NotificationCompat.PRIORITY_MAX); /* * Notification.DEFAULT_ALL:铃声、闪光、震动均系统默认。 * Notification.DEFAULT_SOUND:系统默认铃声。 * Notification.DEFAULT_VIBRATE:系统默认震动。 * Notification.DEFAULT_LIGHTS:系统默认闪光。 * notifyBuilder.setDefaults(Notification.DEFAULT_ALL); */ int defaults = 0; if (sound) { defaults |= Notification.DEFAULT_SOUND; } if (vibrate) { defaults |= Notification.DEFAULT_VIBRATE; } if (lights) { defaults |= Notification.DEFAULT_LIGHTS; } cBuilder.setDefaults(defaults); } /** * 设置builder的信息,在用大文本时会用到这个 * * @param pendingIntent * @param smallIcon * @param ticker */ private void setBuilder(PendingIntent pendingIntent, int smallIcon, String ticker, boolean sound, boolean vibrate, boolean lights) { nBuilder = new Notification.Builder(mContext); // 如果当前Activity启动在前台,则不开启新的Activity。 // intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); // PendingIntent pIntent = PendingIntent.getActivity(mContext, // requestCode, intent, FLAG); nBuilder.setContentIntent(pendingIntent); nBuilder.setSmallIcon(smallIcon); nBuilder.setTicker(ticker); nBuilder.setWhen(System.currentTimeMillis()); nBuilder.setPriority(Notification.PRIORITY_MAX); int defaults = 0; if (sound) { defaults |= Notification.DEFAULT_SOUND; } if (vibrate) { defaults |= Notification.DEFAULT_VIBRATE; } if (lights) { defaults |= Notification.DEFAULT_LIGHTS; } nBuilder.setDefaults(defaults); } /** * 普通的通知 * * 1. 侧滑即消失,下拉通知菜单则在通知菜单显示 * * @param pendingIntent * @param smallIcon * @param ticker * @param title * @param content */ public void notify_normal_singline(PendingIntent pendingIntent, int smallIcon, String ticker, String title, String content, boolean sound, boolean vibrate, boolean lights) { setCompatBuilder(pendingIntent, smallIcon, ticker, title, content, sound, vibrate, lights); sent(); } /** * 进行多项设置的通知(在小米上似乎不能设置大图标,系统默认大图标为应用图标) * * @param pendingIntent * @param smallIcon * @param ticker * @param title * @param content */ public void notify_mailbox(PendingIntent pendingIntent, int smallIcon, int largeIcon, ArrayListmessageList, String ticker, String title, String content, boolean sound, boolean vibrate, boolean lights) { setCompatBuilder(pendingIntent, smallIcon, ticker, title, content, sound, vibrate, lights); // 将Ongoing设为true 那么notification将不能滑动删除 //cBuilder.setOngoing(true); Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), largeIcon); cBuilder.setLargeIcon(bitmap); cBuilder.setDefaults(Notification.DEFAULT_ALL);// 设置使用默认的声音 //cBuilder.setVibrate(new long[]{0, 100, 200, 300});// 设置自定义的振动 cBuilder.setAutoCancel(true); // builder.setSound(Uri.parse("file:///sdcard/click.mp3")); // 设置通知样式为收件箱样式,在通知中心中两指往外拉动,就能出线更多内容,但是很少见 //cBuilder.setNumber(messageList.size()); NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); for (String msg : messageList) { inboxStyle.addLine(msg); } inboxStyle.setSummaryText("[" + messageList.size() + "条]" + title); cBuilder.setStyle(inboxStyle); sent(); } /** * 自定义视图的通知 * * @param remoteViews * @param pendingIntent * @param smallIcon * @param ticker */ public void notify_customview(RemoteViews remoteViews, PendingIntent pendingIntent, int smallIcon, String ticker, boolean sound, boolean vibrate, boolean lights) { setCompatBuilder(pendingIntent, smallIcon, ticker, null, null, sound, vibrate, lights); notification = cBuilder.build(); notification.contentView = remoteViews; // 发送该通知 nm.notify(NOTIFICATION_ID, notification); } /** * 可以容纳多行提示文本的通知信息 (因为在高版本的系统中才支持,所以要进行判断) * * @param pendingIntent * @param smallIcon * @param ticker * @param title * @param content */ public void notify_normail_moreline(PendingIntent pendingIntent, int smallIcon, String ticker, String title, String content, boolean sound, boolean vibrate, boolean lights) { final int sdk = Build.VERSION.SDK_INT; if (sdk < Build.VERSION_CODES.JELLY_BEAN) { notify_normal_singline(pendingIntent, smallIcon, ticker, title, content, sound, vibrate, lights); Toast.makeText(mContext, "您的手机低于Android 4.1.2,不支持多行通知显示!!", Toast.LENGTH_SHORT).show(); } else { setBuilder(pendingIntent, smallIcon, ticker, true, true, false); nBuilder.setContentTitle(title); nBuilder.setContentText(content); nBuilder.setPriority(Notification.PRIORITY_HIGH); notification = new Notification.BigTextStyle(nBuilder).bigText(content).build(); // 发送该通知 nm.notify(NOTIFICATION_ID, notification); } } /** * 有进度条的通知,可以设置为模糊进度或者精确进度 * * @param pendingIntent * @param smallIcon * @param ticker * @param title * @param content */ public void notify_progress(PendingIntent pendingIntent, int smallIcon, String ticker, String title, String content, boolean sound, boolean vibrate, boolean lights, String download_url, final DownLoadListener listener) { setCompatBuilder(pendingIntent, smallIcon, ticker, title, content, sound, vibrate, lights); /* * 因为进度条要实时更新通知栏也就说要不断的发送新的提示,所以这里不建议开启通知声音。 * 这里是作为范例,给大家讲解下原理。所以发送通知后会听到多次的通知声音。 */ String path = FileManager.getCompressFilePath() + x.app().getResources().getString(R.string.app_name) + ".apk"; RequestParams params = new RequestParams(download_url); params.setSaveFilePath(path); params.setAutoResume(true); params.setExecutor(new PriorityExecutor(2, true)); params.setCancelFast(false); x.http().get(params, new Callback.ProgressCallback () { @Override public void onWaiting() { } @Override public void onStarted() { } @Override public void onLoading(long total, long current, boolean isDownloading) { double a = total; double b = current; double currentPro = (b / a) * 100; cBuilder.setProgress(100, (int) currentPro, false); sent(); } @Override public void onSuccess(File result) { cBuilder.setContentText("下载完成").setProgress(0, 0, false); sent(); listener.OnSuccess(result); } @Override public void onError(Throwable ex, boolean isOnCallback) { ex.printStackTrace(); } @Override public void onCancelled(CancelledException cex) { } @Override public void onFinished() { } }); } /** * 容纳大图片的通知 * * @param pendingIntent * @param smallIcon * @param ticker * @param title * @param bigPic */ public void notify_bigPic(PendingIntent pendingIntent, int smallIcon, String ticker, String title, String content, int bigPic, boolean sound, boolean vibrate, boolean lights) { setCompatBuilder(pendingIntent, smallIcon, ticker, title, null, sound, vibrate, lights); NotificationCompat.BigPictureStyle picStyle = new NotificationCompat.BigPictureStyle(); final BitmapFactory.Options options = new BitmapFactory.Options(); options.inScaled = true; options.inSampleSize = 2; Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), bigPic, options); picStyle.bigPicture(bitmap); picStyle.bigLargeIcon(bitmap); cBuilder.setContentText(content); cBuilder.setStyle(picStyle); sent(); } /** * 里面有两个按钮的通知 * * @param smallIcon * @param leftbtnicon * @param lefttext * @param leftPendIntent * @param rightbtnicon * @param righttext * @param rightPendIntent * @param ticker * @param title * @param content */ public void notify_button(int smallIcon, int leftbtnicon, String lefttext, PendingIntent leftPendIntent, int rightbtnicon, String righttext, PendingIntent rightPendIntent, String ticker, String title, String content, boolean sound, boolean vibrate, boolean lights) { requestCode = (int) SystemClock.uptimeMillis(); setCompatBuilder(rightPendIntent, smallIcon, ticker, title, content, sound, vibrate, lights); cBuilder.addAction(leftbtnicon, lefttext, leftPendIntent); cBuilder.addAction(rightbtnicon, righttext, rightPendIntent); sent(); } public void notify_HeadUp(PendingIntent pendingIntent, int smallIcon, int largeIcon, String ticker, String title, String content, int leftbtnicon, String lefttext, PendingIntent leftPendingIntent, int rightbtnicon, String righttext, PendingIntent rightPendingIntent, boolean sound, boolean vibrate, boolean lights) { setCompatBuilder(pendingIntent, smallIcon, ticker, title, content, sound, vibrate, lights); cBuilder.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), largeIcon)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { cBuilder.addAction(leftbtnicon, lefttext, leftPendingIntent); cBuilder.addAction(rightbtnicon, righttext, rightPendingIntent); } else { Toast.makeText(mContext, "版本低于Andriod5.0,无法体验HeadUp样式通知", Toast.LENGTH_SHORT).show(); } sent(); } /** * 发送通知 */ private void sent() { notification = cBuilder.build(); // 发送该通知 nm.notify(NOTIFICATION_ID, notification); } /** * 根据id清除通知 */ public void clear() { // 取消通知 nm.cancelAll(); } DownLoadListener listener; public void setOnDownLoadListener(DownLoadListener l) { listener = l; } public interface DownLoadListener { void OnSuccess(File file); void onFailure(Throwable t, int errorNo, String strMsg); } }
更新管理类:
package com.langj.update.appconfig; import android.Manifest; import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.view.View; import android.widget.TextView; import android.widget.Toast; import com.google.gson.Gson; import com.langj.update.R; import com.langj.update.app.Constant; import com.langj.update.net.ApiListener; import com.langj.update.net.ApiTool; import com.langj.update.utils.AppManager; import com.langj.update.utils.JSONUtils; import com.langj.update.utils.SPUtil; import com.tbruyelle.rxpermissions.RxPermissions; import org.xutils.common.Callback; import org.xutils.common.util.LogUtil; import org.xutils.http.RequestParams; import org.xutils.x; import java.io.File; import java.sql.Timestamp; import java.util.Map; import rx.functions.Action1; public class UpdateManager { /** * FoceUpdate 强制更新 * BrowSerUpdate 浏览器更新 * BackGroundUpdate 后台更新 */ public static final String FoceUpdate = "foceupdate"; public static final String BrowSerUpdate = "browserupdate"; public static final String BackGroundUpdate = "backgroundupdate"; private static UpdateManager manager = new UpdateManager(); private checkVersionListen checkVersionListen; /** * 检查更新 * * @param url 效验url * @param hasHint 如果是最新版本是否显示提示 * @param isFoceUpdate 是否强制更新 */ public static final void checkUpdate(String url, boolean hasHint, String isFoceUpdate) { manager.check(url, hasHint, isFoceUpdate); } public void setCheckVersionListen(checkVersionListen checkVersionListen) { this.checkVersionListen = checkVersionListen; } public interface checkVersionListen { void checkVerssion(String str); } public void check(String url, final boolean hasHint, final String updateType) { removeOldApk(); RequestParams params = new RequestParams(url); params.addBodyParameter("command", "config.app.update.check"); params.addBodyParameter("pubsJson", getPubJson()); ApiTool apiTool = new ApiTool(); apiTool.postApi(params, new ApiListener() { @Override public void onCancelled(Callback.CancelledException cex) { if (checkVersionListen != null) { checkVersionListen.checkVerssion("0"); } } @Override public void onComplete(RequestParams params, String result, String type) { Map, String> map = JSONUtils.parseKeyAndValueToMap(result); if (map.get("hasNewVersion").equals("1")) { if (checkVersionListen != null) { checkVersionListen.checkVerssion("1"); } switch (updateType) { case UpdateManager.FoceUpdate: startDownload(map.get("downloadUrl")); break; case UpdateManager.BackGroundUpdate: // showBuilder(result, ""); break; case UpdateManager.BrowSerUpdate: browerUpdate(map.get("downloadUrl")); break; } } else { if (checkVersionListen != null) { checkVersionListen.checkVerssion("0"); } if (hasHint) Toast.makeText(x.app(), "已是最新版本", Toast.LENGTH_SHORT).show(); } } @Override public void onError(Map , String> var1, RequestParams var2) { LogUtil.e(var1.get("message")); if (checkVersionListen != null) { checkVersionListen.checkVerssion("0"); } } @Override public void onExceptionType(Throwable var1, RequestParams params, String type) { if (checkVersionListen != null) { checkVersionListen.checkVerssion("0"); } } }, ""); } /** * 更新 * @param url * @param updateType */ public void update(final String url, String updateType) { switch (updateType) { case UpdateManager.FoceUpdate: startDownload(url); break; case UpdateManager.BackGroundUpdate: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { RxPermissions.getInstance(AppManager.getInstance().getTopActivity()) // 申请权限 .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) .subscribe(new Action1 () { @Override public void call(Boolean granted) { if (granted) { showBuilder("", url); } else { } } }); } else { showBuilder("", url); } break; case UpdateManager.BrowSerUpdate: browerUpdate(url); break; } } /** * 打开浏览器 * @param url */ public void browerUpdate(String url) { Intent intent = new Intent(); intent.setAction("android.intent.action.VIEW"); Uri content_url = Uri.parse(url); intent.setData(content_url); AppManager.getInstance().getTopActivity().startActivity(intent); } public void showBuilder(String result, final String url) { final Map , String> map = JSONUtils.parseKeyAndValueToMap(result); View view = View.inflate(AppManager.getInstance().getTopActivity(), R.layout.dlg_update, null); TextView tv_content = (TextView) view.findViewById(R.id.update_description); TextView tv_no = (TextView) view.findViewById(R.id.buildeexti_tv_no); TextView tv_ok = (TextView) view.findViewById(R.id.builderexit_tv_ok); final Dialog dialog = new Dialog(AppManager.getInstance().getTopActivity(), R.style.dialog); // tv_content.setText(map.get("new_features")); if (map != null && !TextUtils.isEmpty(map.get("newVersionDescribe"))) { tv_content.setText(map.get("newVersionDescribe")); } tv_no.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.cancel(); } }); tv_ok.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.cancel(); Intent intent = new Intent(AppManager.getInstance().getTopActivity(), DownLoadService.class); if (map != null && !TextUtils.isEmpty(map.get("downloadUrl"))) { intent.putExtra("download_url", map.get("downloadUrl")); } else { intent.putExtra("download_url", url); } AppManager.getInstance().getTopActivity().startService(intent); } }); dialog.setContentView(view); dialog.setCancelable(false); dialog.show(); } public void startDownload(final String url) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { RxPermissions.getInstance(AppManager.getInstance().getTopActivity()) // 申请权限 .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) .subscribe(new Action1 () { @Override public void call(Boolean granted) { if (granted) { downloadFile(url); } else { } } }); } else { downloadFile(url); } } /** * 更新之前先移除残留app文件 */ private void removeOldApk() { File fileName = new File(SPUtil.getString(Constant.SP_DOWNLOAD_PATH, "")); if (fileName != null && fileName.exists() && fileName.isFile()) { fileName.delete(); } } // 下载文件 private synchronized void downloadFile(String url) { DownloadUtils downloadUtils = new DownloadUtils(AppManager.getInstance().getTopActivity()); downloadUtils.downloadAPK(url); } // 获取版本号 private String getVersionCode() { try { PackageInfo packageInfo = x.app().getPackageManager().getPackageInfo(x.app().getPackageName(), 0); return packageInfo.versionName; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); return ""; } } public String getPubJson() { PubsJson pubsJson = new PubsJson(); pubsJson.setImei(getIMSI()); pubsJson.setOs("Android"); pubsJson.setOsVersion(Build.DISPLAY); pubsJson.setAppVersion(getVersionCode()); pubsJson.setTimestamp(getTimesTamp()); Gson gson = new Gson(); String s1 = gson.toJson(pubsJson); return s1; } /** * 获取手机IMSI号 */ public String getIMSI() { TelephonyManager mTelephonyMgr = (TelephonyManager) x.app().getSystemService(Context.TELEPHONY_SERVICE); String imsi = mTelephonyMgr.getSubscriberId(); return imsi; } public String getTimesTamp() { Timestamp now = new Timestamp(System.currentTimeMillis());//获取系统当前时间 return now.toString(); } }
compile 'org.xutils:xutils:3.3.36' //文件下载 compile 'com.zhy:autolayout:1.4.3' //适配 compile 'com.tbruyelle.rxpermissions:rxpermissions:0.7.0@aar' //权限申请 compile 'io.reactivex:rxjava:1.1.6' compile 'com.google.code.gson:gson:2.8.2'
最后在Activity中调用更新:
package com.langj.update; import android.os.Bundle; import android.view.View; import com.langj.update.appconfig.UpdateManager; import com.langj.update.utils.AppManager; import com.zhy.autolayout.AutoLayoutActivity; import org.xutils.view.annotation.Event; import org.xutils.x; public class MainActivity extends AutoLayoutActivity { private UpdateManager manager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); AppManager.getInstance().addActivity(this); x.view().inject(this); manager = new UpdateManager(); } @Event(value = {R.id.btn_01, R.id.btn_02, R.id.btn_03}) private void onTestBaidulClick(View view) { switch (view.getId()) { case R.id.btn_01: //浏览器更新 manager.update("http://dakaapp.troila.com/download/daka.apk?v=3.0", UpdateManager.BrowSerUpdate); break; case R.id.btn_02: //后台更新更新 manager.update("http://dakaapp.troila.com/download/daka.apk?v=3.0", UpdateManager.BackGroundUpdate); break; case R.id.btn_03: //强制更新 manager.update("http://dakaapp.troila.com/download/daka.apk?v=3.0", UpdateManager.FoceUpdate); break; } } }
核心代码到此结束,完整源码请后续看我GitHub地址