实现效果图
文章来源地址https://www.toymoban.com/news/detail-592624.html
调用自动更新
//Activity创建或者从被覆盖、后台重新回到前台时被调用
@Override
protected void onResume() {
super.onResume();
//查询APP版本
selectAPPVesion();
}
1、获取更新版本号
private int getVersion(final SysNotice sysNotice, int vision) {
if (sysNotice == null) {
return 0;
}
//网络请求获取当前版本号和下载链接
//实际操作是从服务器获取
String sdcardRoot = getExternalFilesDir(null) + File.separator + "test/apk";
String apkSavePath = sdcardRoot + "/1.apk";
String newversion = sysNotice.getVersion();//更新新的版本号
String content = sysNotice.getNoticeContent();//更新内容
String url = "127.0.0.1:8080" + Interface.APPapk;//安装包下载地址
double newversioncode = Double
.parseDouble(newversion);
int cc = (int) (newversioncode);
if (cc != vision) {
if (vision < cc) {
System.out.println(newversion + "v"
+ vision);
// 版本号不同
ShowDialog(vision, newversion, sysNotice, url);
}
return 1;
} else {
return 0;
}
}
2、弹出弹窗升级系统
private void ShowDialog(int vision, String newversion, SysNotice sysNotice,
final String url) {
new android.app.AlertDialog.Builder(this)
.setTitle(sysNotice.getNoticeTitle())
.setMessage(Html.fromHtml(sysNotice.getNoticeContent()).toString())
.setPositiveButton("更新", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
pBar = new CommonProgressDialog(MainActivity.this);
pBar.setCanceledOnTouchOutside(false);
pBar.setTitle("正在下载");
pBar.setCustomTitle(LayoutInflater.from(
MainActivity.this).inflate(
R.layout.title_dialog, null));
pBar.setMessage("正在下载");
pBar.setIndeterminate(true);
pBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pBar.setCancelable(true);
// downFile(URLData.DOWNLOAD_URL);
final DownloadTask downloadTask = new DownloadTask(
MainActivity.this);
downloadTask.execute(url);
pBar.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
downloadTask.cancel(true);
}
});
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.show();
}
3、下载应用
/**
* 下载应用
*
* @author Administrator
*/
class DownloadTask extends AsyncTask<String, Integer, String> {
private Context context;
private PowerManager.WakeLock mWakeLock;
public DownloadTask(Context context) {
this.context = context;
}
@Override
protected String doInBackground(String... sUrl) {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
File file = null;
try {
URL url = new URL(sUrl[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error
// report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "Server returned HTTP "
+ connection.getResponseCode() + " "
+ connection.getResponseMessage();
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
String sdcardRoot = getExternalFilesDir(null) + File.separator + "test/apk";
final String apkSavePath = sdcardRoot + "/1.apk";
System.err.println(apkSavePath);
file = new File(apkSavePath);
if (!file.exists()) {
// 判断父文件夹是否存在
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
}
input = connection.getInputStream();
output = new FileOutputStream(file);
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
if (isCancelled()) {
input.close();
return null;
}
total += count;
// publishing the progress....
if (fileLength > 0) // only if total length is known
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
} catch (Exception e) {
System.out.println(e.toString());
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return null;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
// take CPU lock to prevent CPU from going off if the user
// presses the power button during download
PowerManager pm = (PowerManager) context
.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
getClass().getName());
mWakeLock.acquire();
pBar.show();
}
@Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
// if we get here, length is known, now set indeterminate to false
pBar.setIndeterminate(false);
pBar.setMax(100);
pBar.setProgress(progress[0]);
}
@Override
protected void onPostExecute(String result) {
mWakeLock.release();
pBar.dismiss();
if (result != null) {
// 申请多个权限。
AndPermission.with(MainActivity.this)
.requestCode(REQUEST_CODE_PERMISSION_SD)
.permission(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
// rationale作用是:用户拒绝一次权限,再次申请时先征求用户同意,再打开授权对话框,避免用户勾选不再提示。
.rationale(rationaleListener
)
.send();
Toast.makeText(context, "您未打开SD卡权限" + result, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "File downloaded",
Toast.LENGTH_SHORT)
.show();
update();
}
// }
}
}
4、解决权限获取
private static final int REQUEST_CODE_PERMISSION_SD = 101;
private static final int REQUEST_CODE_SETTING = 300;
private RationaleListener rationaleListener = new RationaleListener() {
@Override
public void showRequestPermissionRationale(int requestCode, final Rationale rationale) {
// 这里使用自定义对话框,如果不想自定义,用AndPermission默认对话框:
// AndPermission.rationaleDialog(Context, Rationale).show();
// 自定义对话框。
com.yanzhenjie.alertdialog.AlertDialog.build(MainActivity.this)
.setTitle(R.string.title_dialog)
.setMessage(R.string.message_permission_rationale)
.setPositiveButton(R.string.btn_dialog_yes_permission, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
rationale.resume();
}
})
.setNegativeButton(R.string.btn_dialog_no_permission, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
rationale.cancel();
}
})
.show();
}
};
// ----------------------------------SD权限----------------------------------//
@PermissionYes(REQUEST_CODE_PERMISSION_SD)
private void getMultiYes(List<String> grantedPermissions) {
Toast.makeText(this, R.string.message_post_succeed, Toast.LENGTH_SHORT).show();
}
@PermissionNo(REQUEST_CODE_PERMISSION_SD)
private void getMultiNo(List<String> deniedPermissions) {
Toast.makeText(this, R.string.message_post_failed, Toast.LENGTH_SHORT).show();
// 用户否勾选了不再提示并且拒绝了权限,那么提示用户到设置中授权。
if (AndPermission.hasAlwaysDeniedPermission(this, deniedPermissions)) {
AndPermission.defaultSettingDialog(this, REQUEST_CODE_SETTING)
.setTitle(R.string.title_dialog)
.setMessage(R.string.message_permission_failed)
.setPositiveButton(R.string.btn_dialog_yes_permission)
.setNegativeButton(R.string.btn_dialog_no_permission, null)
.show();
// 更多自定dialog,请看上面。
}
}
//----------------------------------权限回调处理----------------------------------//
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[]
grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
/**
* 转给AndPermission分析结果。
*
* @param object 要接受结果的Activity、Fragment。
* @param requestCode 请求码。
* @param permissions 权限数组,一个或者多个。
* @param grantResults 请求结果。
*/
AndPermission.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CODE_SETTING: {
Toast.makeText(this, R.string.message_setting_back, Toast.LENGTH_LONG).show();
//设置成功,再次请求更新
getVersion(sysNotice, Tools.getVersion(MainActivity.this));
break;
}
}
}
private void update() {
//安装应用
//获取SD卡的根路径
String sdcardRoot = getExternalFilesDir(null) + File.separator + "test/apk";
final String apkSavePath = sdcardRoot + "/1.apk";
System.err.println(apkSavePath);
//安装应用
Intent intent = new Intent(Intent.ACTION_VIEW);
File apkFile = new File(apkSavePath);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri uri = FileProvider.getUriForFile(MainActivity.this, this.getPackageName() + ".fileprovider", apkFile);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
startActivity(intent);
} else {
// intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
}
}
5、调用后端接口,查询APP版本和后端发布公告版本
public void selectAPPVesion() {
try {
OkHttpTool.httpPost("127.0.0.1:8080" + Interface.selectAPPVesion, null, null, new OkHttpTool.ResponseCallback() {
@Override
public void onResponse(final boolean isSuccess, final int responseCode, final String response, Exception exception) {
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
if (isSuccess && responseCode == 200) {
if (!TextUtils.isEmpty(response)) {
//得到resultBean的数据
JSONObject jsonObject = JSON.parseObject(response);
Integer code = jsonObject.getInteger("code");
if (code.equals(200)) {
sysNotice = JSON.parseObject(jsonObject.getString("data"), SysNotice.class);
}
}
}
// 获取本版本号,是否更新
int vision = Tools.getVersion(MainActivity.this);
int version = getVersion(sysNotice, vision);
if (version == 0) {
initseifLogin();
}
}
});
}
});
} catch (Exception e) {
Intent intent = new Intent(MainActivity.this, Configure_Activity.class);
intent.putExtra("isLogin", "false");
startActivity(intent);
Toast.makeText(MainActivity.this, "Invalid URL port", Toast.LENGTH_SHORT).show();
}
}
6、工具封装Tools–获取APP版本号
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
/**
*
*/
public class Tools {
/**
* 2 * 获取版本号 3 * @return 当前应用的版本号 4
*/
public static int getVersion(Context context) {
try {
PackageManager manager = context.getPackageManager();
PackageInfo info = manager.getPackageInfo(context.getPackageName(),
0);
String version = info.versionName;
int versioncode = info.versionCode;
return versioncode;
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}
7、网络请求封装–OkHttpTool
//网络请求
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
import android.util.Log;
import androidx.annotation.NonNull;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class OkHttpTool {
private static String TAG = "OkHttpTool";
private static final OkHttpClient myOkHttpClient = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
//添加cookie处理
.cookieJar(new CookieJar() {//这里是设置cookie的,但是并没有做持久化处理;只是把cookie保存在内存中
private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
@Override
public void saveFromResponse(@NonNull HttpUrl url, @NonNull List<Cookie> cookies) {
cookieStore.put(url.host(), cookies);
}
@Override
public List<Cookie> loadForRequest(@NonNull HttpUrl url) {
List<Cookie> cookies = cookieStore.get(url.host());
return cookies != null ? cookies : new ArrayList<Cookie>();
}
})
//添加日志处理拦截器
.addInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(@NonNull Chain chain) throws IOException {
Request request = chain.request();
long startTime = System.currentTimeMillis();
okhttp3.Response response = chain.proceed(chain.request());
long endTime = System.currentTimeMillis();
long duration=endTime-startTime;
okhttp3.MediaType mediaType = response.body().contentType();
String content = response.body().string();
Log.d(TAG,"\n");
Log.d(TAG,"----------Start----------------");
Log.d(TAG, "| "+request.toString());
String method=request.method();
if("POST".equals(method)){
StringBuilder sb = new StringBuilder();
if (request.body() instanceof FormBody) {
FormBody body = (FormBody) request.body();
for (int i = 0; i < body.size(); i++) {
sb.append(body.encodedName(i) + "=" + body.encodedValue(i) + ",");
}
if (sb.length()>0){
//在参数不为空的情况下处理最后的 “,”
sb.delete(sb.length() - 1, sb.length());
}
Log.d(TAG, "| RequestParams:{"+sb.toString()+"}");
}
}
Log.d(TAG, "| Response:" + content);
Log.d(TAG,"----------End:"+duration+"毫秒----------");
return response.newBuilder()
.body(okhttp3.ResponseBody.create(mediaType, content))
.build();
}
})
.build();
public static void httpGet(String url, Map<String, Object> parameters, Map<String, Object> headers, ResponseCallback responseCallback){
Request request=createGetRequest(url,parameters,headers);
doRequest(request,responseCallback);
}
public static void httpPost(String url, Map<String, Object> parameters, Map<String, Object> headers, ResponseCallback responseCallback){
Request request=createPostRequest(url,parameters,headers);
doRequest(request,responseCallback);
}
public static void httpPostJson(String url, String json, Map<String, Object> headers, ResponseCallback responseCallback){
Request request=createPostRequestJson(url,json,headers);
doRequest(request,responseCallback);
}
public static void httpPostWithFile(String url, Map<String, Object> parameters, Map<String, File> files, Map<String, Object> headers, ResponseCallback responseCallback) {
Request request=createPostRequestWithFile(url,parameters,files,headers);
doRequest(request,responseCallback);
}
public static Request createGetRequest(String url, Map<String, Object> parameters, Map<String, Object> headers) {
StringBuilder urlBuilder = new StringBuilder();
urlBuilder.append(url);
if (url.indexOf('?') <= -1) {
//未拼接参数
urlBuilder.append("?");
}
if (parameters != null) {
for (Map.Entry<String, Object> entry : parameters.entrySet()) {
urlBuilder.append("&");
urlBuilder.append(entry.getKey());
urlBuilder.append("=");
urlBuilder.append(entry.getValue().toString());
}
}
return getBaseRequest(headers).url(urlBuilder.toString()).build();
}
public static Request createPostRequest(String url, Map<String, Object> parameters, Map<String, Object> headers) {
FormBody.Builder builder = new FormBody.Builder(Charset.forName("UTF-8"));
if (parameters != null) {
for (Map.Entry<String, Object> entry : parameters.entrySet()) {
builder.add(entry.getKey(), entry.getValue().toString());
}
}
FormBody formBody = builder.build();
return getBaseRequest(headers).url(url).post(formBody).build();
}
public static Request createPostRequestJson(String url, String json, Map<String, Object> headers) {
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
RequestBody body = RequestBody.create(JSON, json);
return getBaseRequest(headers).url(url).post(body).build();
}
public static Request createPostRequestWithFile(String url, Map<String, Object> parameters, Map<String, File> files, Map<String, Object> headers) {
// form 表单形式上传
MultipartBody.Builder requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM);
if (files != null) {
for (Map.Entry<String, File> fileEntry : files.entrySet()) {
File file = fileEntry.getValue();
if (file != null) {
// MediaType.parse() 里面是上传的文件类型。
RequestBody body = RequestBody.create(MediaType.parse("application/octet-stream"), file);
String filename = file.getName();
// 参数分别为, 请求key ,文件名称 , RequestBody
requestBody.addFormDataPart(fileEntry.getKey(), filename, body);
}
}
}
if (parameters != null) {
// map 里面是请求中所需要的 key 和 value
for (Map.Entry<String, Object> entry : parameters.entrySet()) {
String key = entry.getKey();
String value = entry.getValue().toString();
requestBody.addFormDataPart(key, value);
}
}
return getBaseRequest(headers).url(url).post(requestBody.build()).build();
}
private static void doRequest(final Request request, final ResponseCallback responseCallback) {
//使用okhttp3的异步请求
myOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
//回调
Log.e(TAG,e.getMessage());
responseCallback.onResponse(false, -1, null, e);
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
int responseCode = response.code();//获取响应码
ResponseBody responseBody = response.body();//获取 ResponseBody
if (response.isSuccessful() && responseBody != null) {
String strResponse = responseBody.string();
//回调
responseCallback.onResponse(true, responseCode, strResponse, null);
} else {
//回调
responseCallback.onResponse(false, responseCode, null, null);
}
}
});
}
private static Request.Builder getBaseRequest(Map<String, Object> headers) {
Request.Builder builder = new Request.Builder();
builder.addHeader("client", "Android");
if (headers != null) {
// map 里面是请求中所需要的 key 和 value
for (Map.Entry<String, Object> entry : headers.entrySet()) {
String key = entry.getKey();
String value = entry.getValue().toString();
builder.addHeader(key, value);
}
}
return builder;
}
public interface ResponseCallback {
void onResponse(boolean isSuccess, int responseCode, String response, Exception exception);
}
}
8、后端下载接口
/**
* 本地资源通用下载
*/
@GetMapping("/api/download/APPapk")
public void APPapk(HttpServletRequest request, HttpServletResponse response)
throws Exception
{
File dir = new File("E:\\appPath\\");
String[] children = dir.list();
if (children == null) {
System.out.println("该目录不存在");
throw new Exception(StringUtils.format("该更新APK不存在"));
}
else {
for (int i = 0; i < children.length; i++) {
String filename = children[i];
if(filename.contains(".apk")){
try
{
// 数据库资源地址
String downloadPath = dir.getAbsolutePath()+File.separator+filename;
// 下载名称
response.setContentLengthLong((int) new File(dir.getAbsolutePath()+File.separator+filename).length());
response.setContentLength((int) new File(dir.getAbsolutePath()+File.separator+filename).length());
response.setHeader("Content-Length", String.valueOf(new File(dir.getAbsolutePath()+File.separator+filename).length()));
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, filename);
FileUtils.writeBytes(downloadPath, response.getOutputStream());
}
catch (Exception e)
{
log.error("下载文件失败", e);
}
return;
}
}
}
}
9、查询后端发布公告是否更新APP
/**
* 查找app版本信息 select * from sys_notice order by notice_id desc Limit 1
* 查询最后一条数据
*/
@PostMapping("selectAPPVesion")
public AjaxResult selectAPPVesion() {
SysNotice sysNoticePa=new SysNotice();
SysNotice sysNotice = noticeMapper.selectAPPVesion(sysNoticePa);
return AjaxResult.success(sysNotice);
}
文章来源:https://www.toymoban.com/news/detail-592624.html
到了这里,关于AndroidStudio 实现APP版本自动更新(内部更新,不涉及第三方市场)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!