最近开发有版本更新的需求,然后就研究了下,找到了某位大神的代码 开发中应用版本更新功能 (链接在这里),并在基础上改进了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里权限配置
请求封装
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 onNext, Consumer onError, String url) {
ApiServer api = retrofit.create(ApiServer.class);
Observable 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 getTest(@Url String url);
}
返回数据并进行封装
public class GetPresenter extends BasePresenter {
public void getData(final Class cla, String url) {
HttpUtil.get(new Consumer() {
@Override
public void accept(String s) throws Exception {
Log.e(TAG, "get:"+s);
T t = Constant.GsonToBean(s, cla);
getView().callBackData(t);
}
}, new Consumer() {
@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 GsonToBean(String gsonString, Class cls)
{
T t = null;
if (!TextUtils.isEmpty(gsonString))
{
t = gson.fromJson(gsonString, cls);
}
return t;
}
}
封装的BasePresenter
public abstract class BasePresenter {
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 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 getDeniedPermissions(@NonNull Activity activity, @NonNull String... permissions) {
if (!needCheckPermission()) {
return null;
}
List 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 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详细设置界面
*
* 在 onActivityResult() 中没有必要对 resultCode 进行判断,因为用户只能通过返回键才能回到我们的 App 中,
* 所以 resultCode 总是为 RESULT_CANCEL,所以不能根据返回码进行判断。
* 在 onActivityResult() 中还需要对权限进行判断,因为用户有可能没有授权就返回了!
*/
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);
}
/**
* 申请权限
* 使用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 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 granted = new ArrayList<>();
List 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 perms, boolean isAllGranted);
/**
* @param isAllDenied 是否全部拒绝
*/
void onPermissionsDenied(int requestCode, List 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() {
@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 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 perms, boolean isAllDenied) {
Log.e(Constant.TAG, "拒绝:" + perms.size() + "个权限,isAllDenied=" + isAllDenied);
for (String perm : perms) {
Log.e(Constant.TAG, "拒绝:" + perm);
}
}
});
}
}
我的动态权限里有的为获取位置信息,删掉即可