Android——app的版本更新(强制更新/非强制更新)
1.App版本检测:
要实现App的更新下载,我们上面介绍了,前提是服务器要保存一个App的版本号(通常的方式是保存versionCode,当然你要对比versionName也没关系)。当用户去手动检测版本,或者进入首页自动检测时,第一步是需要请求服务器的版本号,拿到版本号之后与当前App版本号(当前版本号可通过PackageInfo获取)进行对比。服务器返回的版本号大于当前App版本号,证明App已经有更新,那么进入第2步。
2.Apk下载
Apk文件是保存在服务器的。我们可以通过Http流将其下载到本地手机,然后更新安装。Android中下载的方式很多种:HttpUrlConnection,Retrofit,okHttp,以及android原生的下载工具类DownLoadManager 等等。我们采用的方式是Google推荐的下载工具类DownLoadManager。关于DownLoadManager的使用其实很简单,简单概括如下:
(1)通过getSystemService获取DownLoadManager。
(2)初始化DownLoadManager的Request,构建下载请求。
(3)调用DownLoadManager的enqueue异步发起请求,该方法返回值为标识当前下载任务的id,即downloadId。
(4)当下载完成后,系统会发出条件为android.intent.action.DOWNLOAD_COMPLETE的广播,我们可以自定义广播接受器,然后在onReceive中处理下载完成的逻辑即可。
我们使用OkHttp实现版本更新下载:
添加依赖:
compile 'com.google.code.gson:gson:2.8.2'
compile 'com.squareup.okhttp3:okhttp:3.9.1'
封装OKHttpUtils
public class OKHttpUtils {
private static volatile OKHttpUtils instance;
private final OkHttpClient client;
private Handler handler = new Handler();
private OKHttpUtils(){
client = new OkHttpClient();
}
public static OKHttpUtils getInstance(){
if (null == instance){
synchronized (OKHttpUtils.class){
if (instance==null){
instance = new OKHttpUtils();
}
}
}
return instance;
}
public void get(String url, Map<String, String> map, final NetCallBack callBack, final Class clazz) {
// http://www.wuxirui.com/api/checkversion.php?version=2.0.2&from=android // 1.http://www.wuxirui.com/api/checkversion.php // 2.http://www.wuxirui.com/api/checkversion.php?version=2.0.2 & // 3.http://www.wuxirui.com/api/checkversion.php? if (TextUtils.isEmpty(url)) {
return;
}
StringBuffer sb = new StringBuffer(url);
// 是否含有? (2,3) if (url.contains("?")) {
// 问号是否是最后一个字符,是3 否2 if (url.endsWith("?")) {
for (Map.Entry entry : map.entrySet()) {
sb.append(entry.getKey())
.append("=")
.append(entry.getValue())
.append("&");
}
} else { // 问号不是最后一个字符2 for (Map.Entry entry : map.entrySet()) {
sb.append("&")
.append(entry.getKey())
.append("=")
.append(entry.getValue());
}
sb.append("&");
}
} else { // 不包含? if (!map.isEmpty()) {
sb.append("?");
for (Map.Entry entry : map.entrySet()) {
sb.append(entry.getKey())
.append("=")
.append(entry.getValue())
.append("&");
}
}
}
sb.deleteCharAt(sb.lastIndexOf("&"));
Log.i("zzz", "get url is: " + sb);
Request request = new Request.Builder()
.url(sb.toString())
.get()
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override public void onFailure(Call call, final IOException e) {
Log.e("zzz", "onFailure: " + e.getMessage());
handler.post(new Runnable() {
@Override public void run() {
callBack.onFailed(e);
}
});
}
@Override public void onResponse(Call call, Response response) throws IOException {
String result = response.body().string();
Log.i("zzz", "onResponse: " + result);
if (!TextUtils.isEmpty(result)) {
Gson gson = GsonUtils.getInstance();
final Object o = gson.fromJson(result, clazz);
handler.post(new Runnable() {
@Override public void run() {
callBack.onSuccess(o);
}
});
}
}
});
}
public void download(String url, final File file, final DownLoadListener listener) {
// /sdcard/download/aaa.apk // 父目录是否存在 File parent = file.getParentFile();
if (!parent.exists()) {
parent.mkdir();
}
// 文件是否存在 if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
Request request = new Request.Builder()
.url(url)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override public void onFailure(Call call, final IOException e) {
handler.post(new Runnable() {
@Override public void run() {
listener.failed(e);
}
});
}
@Override public void onResponse(Call call, Response response) throws IOException {
ResponseBody body = response.body();
// 获取文件总长度 long totalLength = body.contentLength();
try {
InputStream is = body.byteStream();
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[2048];
int length = 0;
int sum = 0;
while ((length = is.read(buf, 0, buf.length)) != -1) {
sum += length;
long progress = sum * 100 / totalLength;
listener.progress(progress);
fos.write(buf, 0, length);
}
fos.flush();
is.close();
fos.close();
listener.success(file.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
定义接口:
NetCallBack
public interface NetCallBack {
void onSuccess(Object o);
void onFailed(Exception e);
}
DownLoadListener
public interface DownLoadListener {
void success(String path);
void failed(Exception e);
void progress(long progress);
}
gson单例模式:
GsonUtils
public class GsonUtils {
private static Gson instance;
private GsonUtils(){
}
public static Gson getInstance(){
if (instance==null){
instance = new Gson();
}
return instance;
}
}
VersionUtils
public class VersionUtils {
public static String getVersionName(Context context) {
String versionName = "";
PackageManager packageManager = context.getPackageManager();
String packageName = context.getPackageName();
try {
PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
versionName = packageInfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return versionName;
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private String updateUrl = "http://www.wuxirui.com/api/checkversion.php";
//http://www.wuxirui.com/download/jinritoutiao.apk private String path = "/download/";
private Button btn;
private ProgressBar progressBar;
private TextView tv_pro;
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
int i = msg.what;
if (i==100){
tv_pro.setVisibility(View.GONE);
progressBar.setVisibility(View.GONE);
}else {
tv_pro.setText(i + "%");
progressBar.setProgress(i);
}
}
};
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = findViewById(R.id.btn);
progressBar = findViewById(R.id.progressBar);
tv_pro = findViewById(R.id.tv_pro);
String versionName = VersionUtils.getVersionName(this);
Log.i("zzz", "versionName: " + versionName);
Map<String, String> map = new HashMap<>();
map.put("version", versionName);
// if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File externalStorageDirectory = Environment.getExternalStorageDirectory();
// /sdcard/download/ String absolutePath = externalStorageDirectory.getAbsolutePath();
path = absolutePath + path;
}
OKHttpUtils.getInstance().get(updateUrl, map, new NetCallBack() {
@Override public void onSuccess(Object o) {
if (o instanceof MessageBean) {
MessageBean info = (MessageBean) o;
if (info != null) {
if (info.isSuccess()) {
MessageBean.ResultBean result1 = info.getResult();
final MessageBean.ResultBean result = info.getResult();
// 有更新 if (result.isHasnewversion()) {
boolean isMust = result.isMustupdate();
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("更新提示")
.setMessage(isMust ? "需要立即更新" : "是否需要更新")
.setPositiveButton("更新", new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialogInterface, int i) {
tv_pro.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.VISIBLE);
String url = result.getUrl();
// http://www.wuxirui.com/download/jinritoutiao.apk if (url.contains(".")) {
String typeName = url.substring(url.lastIndexOf(".") + 1);
if (url.contains("/")) {
String filename = url.substring(url.lastIndexOf("/") + 1, url.lastIndexOf("."));
path = path + filename + "." + typeName;
}
}
// 下载 download(result.getUrl(), new File(path), new DownLoadListener() {
@Override public void success(String path) {
Log.i(TAG, "success: " + path);
// 启动自动安装 installApk(new File(path));
}
@Override public void failed(Exception e) {
Log.e(TAG, "failed: " + e.getMessage());
}
@Override public void progress(long progress) {
Log.i(TAG, "progress: " + progress);
handler.sendEmptyMessageDelayed((int) progress, 1000);
}
});
}
});
if (!isMust) {
builder.setNegativeButton("稍候", new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialogInterface, int i) {
}
});
}
builder.create().show();
}
}
}
}
}
@Override public void onFailed(Exception e) {
}
}, MessageBean.class);
}
private void download(String url, File file, DownLoadListener listener) {
OKHttpUtils.getInstance().download(url, file, listener);
}
/** * 安装apk * * @param file */ private void installApk(File file) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.addCategory("android.intent.category.DEFAULT");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
startActivity(intent);
android.os.Process.killProcess(android.os.Process.myPid());
}
}
布局文件:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.bwie.com.myapplication.MainActivity">
<Button android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="检测版本更新"
/>
<ProgressBar android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/btn"
android:layout_marginTop="45dp"
android:max="100"
android:visibility="gone"
android:progress="0"
/>
<TextView android:id="@+id/tv_pro"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0%"
android:layout_below="@+id/progressBar"
android:layout_centerHorizontal="true"
android:visibility="gone"
/>
</RelativeLayout>
还没有评论,来说两句吧...