前言
最近开发有版本更新的需求,然后就研究了下,找到了某位大神的代码 开发中应用版本更新功能 (链接在这里),并在基础上改进了6.0的权限问题,代码仅供参考
简介
大体的思路就是拿到我们本地的版本号去对比后台的版本号,一致不做处理,不一致弹出对话框提示更新
使用
public class AppInnerDownLoder { public final static String SD_FOLDER = Environment.getExternalStorageDirectory()+ "/VersionChecker/"; private static final String TAG = AppInnerDownLoder.class.getSimpleName(); /** * 从服务器中下载APK */ @SuppressWarnings("unused") public static void downLoadApk(final Context mContext, final String downURL, final String appName ) { final ProgressDialog pd; // 进度条对话框 pd = new ProgressDialog(mContext); pd.setCancelable(false);// 必须一直下载完,不可取消 pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pd.setMessage("正在下载安装包,请稍后"); pd.setTitle("版本升级"); pd.show(); new Thread() { @Override public void run() { try { File file = downloadFile(downURL,appName, pd); sleep(3000); installApk(mContext, file); // 结束掉进度条对话框 pd.dismiss(); } catch (Exception e) { pd.dismiss(); } } }.start(); } /** * 从服务器下载最新更新文件 * * @param path * 下载路径 * @param pd * 进度条 * @return * @throws Exception */ private static File downloadFile(String path,String appName ,ProgressDialog pd) throws Exception { // 如果相等的话表示当前的sdcard挂载在手机上并且是可用的 if (Environment.MEDIA_MOUNTED.equals(Environment .getExternalStorageState())) { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); // 获取到文件的大小 pd.setMax(conn.getContentLength()); InputStream is = conn.getInputStream(); String fileName = SD_FOLDER + appName+".apk"; File file = new File(fileName); // 目录不存在创建目录 if (!file.getParentFile().exists()) file.getParentFile().mkdirs(); FileOutputStream fos = new FileOutputStream(file); BufferedInputStream bis = new BufferedInputStream(is); byte[] buffer = new byte[1024]; int len; int total = 0; while ((len = bis.read(buffer)) != -1) { fos.write(buffer, 0, len); total += len; // 获取当前下载量 pd.setProgress(total); } fos.close(); bis.close(); is.close(); return file; } else { throw new IOException("未发现有SD卡"); } } /** * 安装apk */ private static void installApk(Context mContext, File file) { Uri fileUri = Uri.fromFile(file); Intent it = new Intent(); it.setAction(Intent.ACTION_VIEW); it.setDataAndType(fileUri, "application/vnd.android.package-archive"); it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// 防止打不开应用 mContext.startActivity(it); } /** * 获取应用程序版本(versionName) * * @return 当前应用的版本号 */ private static double getLocalVersion(Context context) { PackageManager manager = context.getPackageManager(); PackageInfo info = null; try { info = manager.getPackageInfo(context.getPackageName(), 0); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "获取应用程序版本失败,原因:" + e.getMessage()); return 0.0; } return Double.valueOf(info.versionName); } /** * byte(字节)根据长度转成kb(千字节)和mb(兆字节) * * @param bytes * @return */ public static String bytes2kb(long bytes) { BigDecimal filesize = new BigDecimal(bytes); BigDecimal megabyte = new BigDecimal(1024 * 1024); float returnValue = filesize.divide(megabyte, 2, BigDecimal.ROUND_UP) .floatValue(); if (returnValue > 1) return (returnValue + "MB"); BigDecimal kilobyte = new BigDecimal(1024); returnValue = filesize.divide(kilobyte, 2, BigDecimal.ROUND_UP) .floatValue(); return (returnValue + "KB"); } }
主要的实现功能就在这一个类里面,看懂的朋友恭喜你直接搬走就好了,没看懂的朋友没关系我们一步一步来,详细的在下面:
效果图如下:
我用到的是retrofit+rxjava MVP+Retrofit+RxJava网络请求框架(这里提供一下我的自学博客)
配置如下://retrofit2.0 compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.3.0' compile 'com.squareup.okhttp3:logging-interceptor:3.8.0' //rxJava compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0' compile 'io.reactivex.rxjava2:rxandroid:2.0.1' // RxJava2支持(根据需要选择) compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
Manifest里权限配置
<!--网络权限--> <uses-permission android:name="android.permission.INTERNET" /> <!-- 读写权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
请求封装
public class HttpUtil { private static Retrofit retrofit = new Retrofit.Builder() .baseUrl(Constant.LINK_MAIN) .addConverterFactory(ScalarsConverterFactory.create()) .addCallAdapterFactory(retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory.create()) .build(); //get封装 public static void get(Consumer<String> onNext, Consumer<Throwable> onError, String url) { ApiServer api = retrofit.create(ApiServer.class); Observable<String> observable = api.getTest(url); observable.observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe(onNext, onError); } }
请求接口拼接
public interface ApiServer { //get请求 @Headers({"User-Agent:okhttp/3.9.1/Android"}) @GET() Observable<String> getTest(@Url String url); }
返回数据并进行封装
public class GetPresenter extends BasePresenter<DataView> { public <T> void getData(final Class<T> cla, String url) { HttpUtil.get(new Consumer<String>() { @Override public void accept(String s) throws Exception { Log.e(TAG, "get:"+s); T t = Constant.GsonToBean(s, cla); getView().callBackData(t); } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { } }, url); } }
提供常用接口和方法的类
public class Constant { //常用网络访问路径地址 public static final String LINK_MAIN = "http://127.0.0.1:8081/update"; public static final String HEAD_PR = "http://releases.b0.upaiyun.com/hoolay.apk"; //gson封装 public static <T> T GsonToBean(String gsonString, Class<T> cls) { T t = null; if (!TextUtils.isEmpty(gsonString)) { t = gson.fromJson(gsonString, cls); } return t; } }
封装的BasePresenter
public abstract class BasePresenter <V extends BaseView> { protected V mView; public V getView() { return mView; } public void attachView(V view){ this.mView = view; } public void detachView(){ this.mView = null; } }
回调父类接口
public interface BaseView { }
回调的子类接口
public interface DataView <T> extends BaseView { void callBackData(T value); void callBackDataError(Throwable throwable); }
网络获取成功封装成bean类(自己根据后台返回的数据封装)
public class VersionUpdateBean { /** * error_code : 200 * msg : 成功 * data : {"version":"1.0.1","content":["更新视频上传功能","更新视频上传功能","更新视频上传功能"]} */ private int error_code; private String msg; private DataBean data; public int getError_code() { return error_code; } public void setError_code(int error_code) { this.error_code = error_code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public DataBean getData() { return data; } public void setData(DataBean data) { this.data = data; } ... ... }
获取当前应用的版本号及版本名称(可直接复制提供使用)
public class APKVersionCodeUtils { /** * 获取当前本地apk的版本 * * @param mContext * @return */ public static int getVersionCode(Context mContext) { int versionCode = 0; try { //获取软件版本号,对应AndroidManifest.xml下android:versionCode versionCode = mContext.getPackageManager(). getPackageInfo(mContext.getPackageName(), 0).versionCode; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return versionCode; } /** * 获取版本号名称 * * @param context 上下文 * @return */ public static String getVerName(Context context) { String verName = ""; try { verName = context.getPackageManager(). getPackageInfo(context.getPackageName(), 0).versionName; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return verName; } }
6.0动态权限封装(可直接复制提供使用)
public class PermissionUtil { /** * 是否需要检查权限 */ private static boolean needCheckPermission() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; } /** * 获取sd存储卡读写权限 * * @return 是否已经获取权限,没有自动申请 */ public static boolean getExternalStoragePermissions(@NonNull Activity activity, int requestCode) { return requestPerssions(activity, requestCode, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE); } /** * 获取拍照权限 * * @return 是否已经获取权限,没有自动申请 */ public static boolean getCameraPermissions(@NonNull Activity activity, int requestCode) { return requestPerssions(activity, requestCode, Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE); } /** * 获取麦克风权限 * * @return 是否已经获取权限,没有自动申请 */ public static boolean getAudioPermissions(@NonNull Activity activity, int requestCode) { return requestPerssions(activity, requestCode, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE); } /** * 获取定位权限 * * @return 是否已经获取权限,没有自动申请 */ public static boolean getLocationPermissions(@NonNull Activity activity, int requestCode) { return requestPerssions(activity, requestCode, Manifest.permission.ACCESS_COARSE_LOCATION); } /** * 获取读取联系人权限 * * @return 是否已经获取权限,没有自动申请 */ public static boolean getContactsPermissions(@NonNull Activity activity, int requestCode) { return requestPerssions(activity, requestCode, Manifest.permission.READ_CONTACTS); } /** * 获取发送短信权限 * * @return 是否已经获取权限,没有自动申请 */ public static boolean getSendSMSPermissions(@NonNull Activity activity, int requestCode) { return requestPerssions(activity, requestCode, Manifest.permission.SEND_SMS); } /** * 获取拨打电话权限 * * @return 是否已经获取权限,没有自动申请 */ public static boolean getCallPhonePermissions(@NonNull Activity activity, int requestCode) { return requestPerssions(activity, requestCode, Manifest.permission.CALL_PHONE); } public static List<String> getDeniedPermissions(@NonNull Activity activity, @NonNull String... permissions) { if (!needCheckPermission()) { return null; } List<String> deniedPermissions = new ArrayList<>(); for (String permission : permissions) { if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) { deniedPermissions.add(permission); } } if (!deniedPermissions.isEmpty()) { return deniedPermissions; } return null; } /** * 是否拥有权限 */ public static boolean hasPermissons(@NonNull Activity activity, @NonNull String... permissions) { if (!needCheckPermission()) { return true; } for (String permission : permissions) { if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) { return false; } } return true; } /** * 是否拒绝了再次申请权限的请求(点击了不再询问) */ public static boolean deniedRequestPermissonsAgain(@NonNull Activity activity, @NonNull String... permissions) { if (!needCheckPermission()) { return false; } List<String> deniedPermissions = getDeniedPermissions(activity, permissions); for (String permission : deniedPermissions) { if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_DENIED) { if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) { //当用户之前已经请求过该权限并且拒绝了授权这个方法返回true return true; } } } return false; } /** * 打开app详细设置界面<br/> * <p> * 在 onActivityResult() 中没有必要对 resultCode 进行判断,因为用户只能通过返回键才能回到我们的 App 中,<br/> * 所以 resultCode 总是为 RESULT_CANCEL,所以不能根据返回码进行判断。<br/> * 在 onActivityResult() 中还需要对权限进行判断,因为用户有可能没有授权就返回了!<br/> */ public static void startApplicationDetailsSettings(@NonNull Activity activity, int requestCode) { Toast.makeText(activity, "点击权限,并打开全部权限", Toast.LENGTH_SHORT).show(); Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", activity.getPackageName(), null); intent.setData(uri); activity.startActivityForResult(intent, requestCode); } /** * 申请权限<br/> * 使用onRequestPermissionsResult方法,实现回调结果或者自己普通处理 * * @return 是否已经获取权限 */ public static boolean requestPerssions(Activity activity, int requestCode, String... permissions) { if (!needCheckPermission()) { return true; } if (!hasPermissons(activity, permissions)) { if (deniedRequestPermissonsAgain(activity, permissions)) { startApplicationDetailsSettings(activity, requestCode); //返回结果onActivityResult } else { List<String> deniedPermissions = getDeniedPermissions(activity, permissions); if (deniedPermissions != null) { ActivityCompat.requestPermissions(activity, deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode); //返回结果onRequestPermissionsResult } } return false; } return true; } /** * 申请权限返回方法 */ public static void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults, @NonNull OnRequestPermissionsResultCallbacks callBack) { // Make a collection of granted and denied permissions from the request. List<String> granted = new ArrayList<>(); List<String> denied = new ArrayList<>(); for (int i = 0; i < permissions.length; i++) { String perm = permissions[i]; if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { granted.add(perm); } else { denied.add(perm); } } if (null != callBack) { if (!granted.isEmpty()) { callBack.onPermissionsGranted(requestCode, granted, denied.isEmpty()); } if (!denied.isEmpty()) { callBack.onPermissionsDenied(requestCode, denied, granted.isEmpty()); } } } /** * 申请权限返回 */ // public interface OnRequestPermissionsResultCallbacks extends ActivityCompat.OnRequestPermissionsResultCallback { public interface OnRequestPermissionsResultCallbacks { /** * @param isAllGranted 是否全部同意 */ void onPermissionsGranted(int requestCode, List<String> perms, boolean isAllGranted); /** * @param isAllDenied 是否全部拒绝 */ void onPermissionsDenied(int requestCode, List<String> perms, boolean isAllDenied); } }
主要功能代码实现
public class MainActivity extends AppCompatActivity{ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); } @Override protected void initData() { requestCameraPermisson();//动态权限申请 GetPresenter getPresenter = new GetPresenter(); getPresenter.getData(VersionUpdateBean.class, VERSION_UPDATE); getPresenter.attachView(new DataView<VersionUpdateBean>() { @Override public void callBackData(VersionUpdateBean value) { if (!APKVersionCodeUtils.getVerName(mContext).equals(value.getData().getVersion())) {//判断版本号 forceUpdate(mContext,"应用名称",DOWNLOAD_APK,value.getMsg()); } } @Override public void callBackDataError(Throwable throwable) { } }); } /** * 版本更新 * * @param context * @param appName * @param downUrl * @param updateinfo 更新内容 */ private void forceUpdate(final Context context, final String appName, final String downUrl, final String updateinfo) { mDialog = new AlertDialog.Builder(context); mDialog.setTitle(appName + "版本更新!"); mDialog.setMessage(updateinfo); requestCameraPermisson(); mDialog.setPositiveButton("立即更新", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { AppInnerDownLoder.downLoadApk(mContext, downUrl, appName); } }).setCancelable(false).create().show(); } /** * 权限获取 */ private final int REQUEST_CODE_CAMERA = 101; public void requestCameraPermisson() { PermissionUtil.requestPerssions(this, REQUEST_CODE_CAMERA, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_SMS, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE); PermissionUtil.getCameraPermissions(this, REQUEST_CODE_CAMERA); } /** * 权限获取 */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); PermissionUtil.onRequestPermissionsResult(requestCode, permissions, grantResults, new PermissionUtil.OnRequestPermissionsResultCallbacks() { @Override public void onPermissionsGranted(int requestCode, List<String> perms, boolean isAllGranted) { Log.e(Constant.TAG, "同意:" + perms.size() + "个权限,isAllGranted=" + isAllGranted); for (String perm : perms) { Log.e(Constant.TAG, "同意:" + perm); } } @Override public void onPermissionsDenied(int requestCode, List<String> perms, boolean isAllDenied) { Log.e(Constant.TAG, "拒绝:" + perms.size() + "个权限,isAllDenied=" + isAllDenied); for (String perm : perms) { Log.e(Constant.TAG, "拒绝:" + perm); } } }); } }我的动态权限里有的为获取位置信息,删掉即可