AndroidStudio 实现APP版本自动更新(内部更新,不涉及第三方市场)

实现效果图

AndroidStudio 实现APP版本自动更新(内部更新,不涉及第三方市场)_第1张图片
AndroidStudio 实现APP版本自动更新(内部更新,不涉及第三方市场)_第2张图片

调用自动更新

  //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);
    }

你可能感兴趣的:(Java,java,android,开发语言)