安卓app自动更新功能

来源:互联网 发布:tflearn windows 编辑:程序博客网 时间:2024/05/01 01:21

app是我们在开发时候经常碰到的事情,一般解决一些bug,添加了需求,实现了新的功能,以让用户体验新版本的功能,这些都是项目中用到的,今天就来总结一下

第一种:引用jjdxmashljjdxm_update

GitHub地址:jjdxmashl/jjdxm_update

这是大神jjdxmashl的开源项目,下载地址见上方。有版本更新、手动更新、静默更新、自动更新4种情况。应用内更新,实现类是友盟自动更新sdk的模式,用户使用前只需要配置自己的服务器更新检查接口即可(必须接口),也可以扩展加入一个接口作为在线参数配置来实现,可以实现下面的4种2更新方式和是否强制更新组合使用,支持get、post方式请求网络,默认是get请求。

4种更新检查类型

  1. 手动更新:手动检测更新(所有网络类型环境检测并提示主要用于点击检测使用)
  2. 自动更新:自动检测更新(所有网络类型环境检测并提示)
  3. 仅WiFi自动检测更新(只有WiFi网络类型环境检测并提示)
  4. 静默更新:仅WiFi自动检测下载(只有WiFi网络类型环境检测、下载完才提示)

2种强制更新方式

  1. 在更新检查返回后,直接设置update.setForce(true)
  2. 配合在线参数使用,通过在线参数返回的数据设置UpdateHelper.getInstance().setForced(true)

上述4中更新检查 结合 2种强制更新,适用于:上一个app版本有重大漏洞,修改在线 参数统一控制所有的app用户,不更新就不可以使用app。 

主要原理:服务器上修改参数值,app端获取后进行判断,如果是强制更新,则在打开应用时就提示有新版本的app,更新完成后才可以使用该app;更新为完成,则提示框不消失,点击back键则退出应用。



第二种: 判断VersionCode,xUtils实现下载

第一步 服务器端:
  • 服务端提供一个借口,或者网址,我这里就用的服务器是tomcat,这里提供一个网址如下
    //也就是一个json数据接口public static final String UPDATE_URL = "http://192.168.1.103:8080/update.json";
    • 第二步 客户端需要实现:

      首先我们要去解析服务端给的json,那么我们就要来创建一个model类了(代码过多,这里只有字段,getter和setter方法自己创建):
    //app名字    private String appname;    //服务器版本    private String serverVersion;    //服务器标志    private String serverFlag;    //强制升级    private String lastForce;    //app最新版本地址    private String updateurl;    //升级信息    private String upgradeinfo;//在这里使用了一个辅助类,基本和model字段差不多:public class UpdateInformation {    public static String appname = MyApplication.getInstance()            .getResources().getString(R.string.app_name);    public static int localVersion = 1;// 本地版本    public static String versionName = ""; // 本地版本名    public static int serverVersion = 1;// 服务器版本    public static int serverFlag = 0;// 服务器标志    public static int lastForce = 0;// 之前强制升级版本    public static String updateurl = "";// 升级包获取地址    public static String upgradeinfo = "";// 升级信息    public static String downloadDir = "wuyinlei";// 下载目录}
    我们知道,我们在进入app的时候,这个时候如果检测到服务器端有了新的版本,就回弹出提示框,提示我们更新。这个我们在MainActivity里面处理逻辑(onCreate()方法里面):
    OkhttpManager.getAsync(Config.UPDATE_URL, new OkhttpManager.DataCallBack() {            @Override            public void requestFailure(Request request, Exception e) {            }            @Override            public void requestSuccess(String result) {                try {                    Log.d("wuyiunlei",result);                    JSONObject object = new JSONObject(result);                    UpdateInfoModel model = new UpdateInfoModel();                    model.setAppname(object.getString("appname"));                    model.setLastForce(object.getString("lastForce"));                    model.setServerFlag(object.getString("serverFlag"));                    model.setServerVersion(object.getString("serverVersion"));                    model.setUpdateurl(object.getString("updateurl"));                    model.setUpgradeinfo(object.getString("upgradeinfo"));                    tmpMap.put(DeliverConsts.KEY_APP_UPDATE, model);                } catch (JSONException e) {                    e.printStackTrace();                }                //发送广播                sendBroadcast(new Intent(UpdateReceiver.UPDATE_ACTION));            }        });

    当然了,我们也要注册和结束广播:

     /**     * 广播注册     */    private void registerBroadcast() {        mUpdateReceiver = new UpdateReceiver(false);        mIntentFilter = new IntentFilter(UpdateReceiver.UPDATE_ACTION);        this.registerReceiver(mUpdateReceiver, mIntentFilter);    } /**     * 广播卸载     */    private void unRegisterBroadcast() {        try {            this.unregisterReceiver(mUpdateReceiver);        } catch (Exception e) {            e.printStackTrace();        }    }

    好了,接下来我们看下我们自定义的广播接收者UpdateReceiver .java:

    /** * 版本更新升级 广播接受者 * */public class UpdateReceiver extends BroadcastReceiver {    private AlertDialog.Builder mDialog;    public static final String UPDATE_ACTION = "wuyinlei_aixinwen";    private SharedPreferencesHelper mSharedPreferencesHelper;    private boolean isShowDialog;    public UpdateReceiver() {    }    public UpdateReceiver(boolean isShowDialog) {        super();        this.isShowDialog = isShowDialog;    }    @Override    public void onReceive(Context context, Intent intent) {        mSharedPreferencesHelper = mSharedPreferencesHelper                .getInstance(MyApplication.getInstance());        //当然了,这里也可以直接new处hashmap        HashMap<String, Object> tempMap = MyApplication.getInstance()                .getTempMap();        UpdateInfoModel model = (UpdateInfoModel) tempMap                //就是一个标志                .get(DeliverConsts.KEY_APP_UPDATE);        try {            /**             * 获取到当前的本地版本             */            UpdateInformation.localVersion = MyApplication                    .getInstance()                    //包管理独享                    .getPackageManager()                    //包信息                    .getPackageInfo(                            MyApplication.getInstance()                                    .getPackageName(), 0).versionCode;            /**             * 获取到当前的版本名字             */            UpdateInformation.versionName = MyApplication                    .getInstance()                    .getPackageManager()                    .getPackageInfo(                            MyApplication.getInstance()                                    .getPackageName(), 0).versionName;        } catch (Exception e) {            e.printStackTrace();        }        //app名字        UpdateInformation.appname = MyApplication.getInstance()                .getResources().getString(R.string.app_name);        //服务器版本        UpdateInformation.serverVersion = Integer.parseInt(model                .getServerVersion());        //服务器标志        UpdateInformation.serverFlag = Integer.parseInt(model.getServerFlag());        //强制升级        UpdateInformation.lastForce = Integer.parseInt(model.getLastForce());        //升级地址        UpdateInformation.updateurl = model.getUpdateurl();        //升级信息        UpdateInformation.upgradeinfo = model.getUpgradeinfo();        //检查版本        checkVersion(context);    }    /**     * 检查版本更新     *      * @param context     */    public void checkVersion(Context context) {        if (UpdateInformation.localVersion < UpdateInformation.serverVersion) {            // 需要进行更新            mSharedPreferencesHelper.putIntValue(                    //有新版本                    SharedPreferencesTag.IS_HAVE_NEW_VERSION, 1);            //更新            update(context);        } else {            mSharedPreferencesHelper.putIntValue(                    SharedPreferencesTag.IS_HAVE_NEW_VERSION, 0);            if (isShowDialog) {                //没有最新版本,不用升级                noNewVersion(context);            }            clearUpateFile(context);        }    }    /**     * 进行升级     *      * @param context     */    private void update(Context context) {        if (UpdateInformation.serverFlag == 1) {            // 官方推荐升级            if (UpdateInformation.localVersion < UpdateInformation.lastForce) {                //强制升级                forceUpdate(context);            } else {                //正常升级                normalUpdate(context);            }        } else if (UpdateInformation.serverFlag == 2) {            // 官方强制升级            forceUpdate(context);        }    }    /**     * 没有新版本     * @param context     */    private void noNewVersion(final Context context) {        mDialog = new AlertDialog.Builder(context);        mDialog.setTitle("版本更新");        mDialog.setMessage("当前为最新版本");        mDialog.setNegativeButton("确定", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                dialog.dismiss();            }        }).create().show();    }    /**     * 强制升级 ,如果不点击确定升级,直接退出应用     *      * @param context     */    private void forceUpdate(final Context context) {        mDialog = new AlertDialog.Builder(context);        mDialog.setTitle("版本更新");        mDialog.setMessage(UpdateInformation.upgradeinfo);        mDialog.setPositiveButton("确定", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                Intent mIntent = new Intent(context, UpdateService.class);                mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                mIntent.putExtra("appname", UpdateInformation.appname);                mIntent.putExtra("appurl", UpdateInformation.updateurl);                //启动服务                context.startService(mIntent);            }        }).setNegativeButton("退出", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                // 直接退出应用                //ManagerActivity.getInstance().finishActivity();                System.exit(0);            }        }).setCancelable(false).create().show();    }    /**     * 正常升级,用户可以选择是否取消升级     *      * @param context     */    private void normalUpdate(final Context context) {        mDialog = new AlertDialog.Builder(context);        mDialog.setTitle("版本更新");        mDialog.setMessage(UpdateInformation.upgradeinfo);        mDialog.setPositiveButton("确定", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                Intent mIntent = new Intent(context, UpdateService.class);                mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                //传递数据                mIntent.putExtra("appname", UpdateInformation.appname);                mIntent.putExtra("appurl", UpdateInformation.updateurl);                context.startService(mIntent);            }        }).setNegativeButton("取消", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                dialog.dismiss();            }        }).create().show();    }    /**     * 清理升级文件     *      * @param context     */    private void clearUpateFile(final Context context) {        File updateDir;        File updateFile;        if (Environment.MEDIA_MOUNTED.equals(Environment                .getExternalStorageState())) {            updateDir = new File(Environment.getExternalStorageDirectory(),                    UpdateInformation.downloadDir);        } else {            updateDir = context.getFilesDir();        }        updateFile = new File(updateDir.getPath(), context.getResources()                .getString(R.string.app_name) + ".apk");        if (updateFile.exists()) {            Log.d("update", "升级包存在,删除升级包");            updateFile.delete();        } else {            Log.d("update", "升级包不存在,不用删除升级包");        }    }}

    接下最后我们来看下服务吧UpdateService .java:

    /** * 不要忘记注册,在mainfest文件中*/public class UpdateService extends Service {    // BT字节参考量    private static final float SIZE_BT = 1024L;    // KB字节参考量    private static final float SIZE_KB = SIZE_BT * 1024.0f;    // MB字节参考量    private static final float SIZE_MB = SIZE_KB * 1024.0f;    private final static int DOWNLOAD_COMPLETE = 1;// 完成    private final static int DOWNLOAD_NOMEMORY = -1;// 内存异常    private final static int DOWNLOAD_FAIL = -2;// 失败    private String appName = null;// 应用名字    private String appUrl = null;// 应用升级地址    private File updateDir = null;// 文件目录    private File updateFile = null;// 升级文件    // 通知栏    private NotificationManager updateNotificationManager = null;    private Notification updateNotification = null;    private Intent updateIntent = null;// 下载完成    private PendingIntent updatePendingIntent = null;// 在下载的时候    @Override    public IBinder onBind(Intent arg0) {        return null;    }    @Override    public void onStart(Intent intent, int startId) {        super.onStart(intent, startId);        appName = intent.getStringExtra("appname");        appUrl = intent.getStringExtra("appurl");        updateNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);        updateNotification = new Notification();        //通知图标        updateNotification.icon = R.mipmap.head;        //通知信息描述        updateNotification.tickerText = "正在下载 " + appName;        updateNotification.when = System.currentTimeMillis();        updateIntent = new Intent(this, MyApplication.class);        updatePendingIntent = PendingIntent.getActivity(this, 0, updateIntent,                0);        updateNotification.contentIntent = updatePendingIntent;        updateNotification.contentIntent.cancel();        updateNotification.contentView = new RemoteViews(getPackageName(),                //这个布局很简单,就是一个图片和两个textview,分别是正在下载和下载进度                R.layout.download_notification);        updateNotification.contentView.setTextViewText(                R.id.download_notice_name_tv, appName + " 正在下载");        updateNotification.contentView.setTextViewText(                R.id.download_notice_speed_tv, "0MB (0%)");        updateNotificationManager.notify(0, updateNotification);        new UpdateThread().execute();    }    /**     * 在这里使用了asynctask异步任务来下载     */    class UpdateThread extends AsyncTask<Void, Void, Integer> {        @Override        protected Integer doInBackground(Void... params) {            return downloadUpdateFile(appUrl);        }        @Override        protected void onPostExecute(Integer result) {            super.onPostExecute(result);            if (result == DOWNLOAD_COMPLETE) {                Log.d("update", "下载成功");                String cmd = "chmod 777 " + updateFile.getPath();                try {                    Runtime.getRuntime().exec(cmd);                } catch (IOException e) {                    e.printStackTrace();                }                Uri uri = Uri.fromFile(updateFile);                //安装程序                Intent installIntent = new Intent(Intent.ACTION_VIEW);                installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                installIntent.setDataAndType(uri,                        "application/vnd.android.package-archive");                updatePendingIntent = PendingIntent.getActivity(                        UpdateService.this, 0, installIntent, 0);                updateNotification.contentIntent = updatePendingIntent;                updateNotification.contentView.setTextViewText(                        R.id.download_notice_speed_tv,                        getString(R.string.update_notice_finish));                updateNotification.tickerText = appName + "下载完成";                updateNotification.when = System.currentTimeMillis();                updateNotification.defaults = Notification.DEFAULT_SOUND;                updateNotification.flags |= Notification.FLAG_AUTO_CANCEL;                updateNotificationManager.notify(0, updateNotification);                //启动安装程序                UpdateService.this.startActivity(installIntent);                stopSelf();            } else if (result == DOWNLOAD_NOMEMORY) {                //如果内存有问题                updateNotification.tickerText = appName + "下载失败";                updateNotification.when = System.currentTimeMillis();                updateNotification.contentView.setTextViewText(                        R.id.download_notice_speed_tv,                        getString(R.string.update_notice_nomemory));                updateNotification.flags |= Notification.FLAG_AUTO_CANCEL;                updateNotification.defaults = Notification.DEFAULT_SOUND;                updateNotificationManager.notify(0, updateNotification);                stopSelf();            } else if (result == DOWNLOAD_FAIL) {                //下载失败                updateNotification.tickerText = appName + "下载失败";                updateNotification.when = System.currentTimeMillis();                updateNotification.contentView.setTextViewText(                        R.id.download_notice_speed_tv,                        getString(R.string.update_notice_error));                updateNotification.flags |= Notification.FLAG_AUTO_CANCEL;                updateNotification.defaults = Notification.DEFAULT_SOUND;                updateNotificationManager.notify(0, updateNotification);                stopSelf();            }        }    }    /**     * 下载更新程序文件     * @param downloadUrl   下载地址     * @return     */    private int downloadUpdateFile(String downloadUrl) {        int count = 0;        long totalSize = 0;   //总大小        long downloadSize = 0;   //下载的大小        URI uri = null;        //这个已经舍弃了,要用的话,就要加上org.apache.http.legacy.jar这个jar包        HttpGet httpGet = null;        try {            uri = new URI(downloadUrl);            httpGet = new HttpGet(uri);        } catch (URISyntaxException e) {            String encodedUrl = downloadUrl.replace(' ', '+');            httpGet = new HttpGet(encodedUrl);            e.printStackTrace();        }        HttpClient httpClient = new DefaultHttpClient();        HttpResponse httpResponse = null;        FileOutputStream fos = null;        InputStream is = null;        try {            httpResponse = httpClient.execute(httpGet);            if (httpResponse != null) {                int stateCode = httpResponse.getStatusLine().getStatusCode();                if (stateCode == HttpStatus.SC_OK) {                    HttpEntity entity = httpResponse.getEntity();                    if (entity != null) {                        totalSize = entity.getContentLength();                        //如果内存可用                        if (MemoryAvailable(totalSize)) {                            is = entity.getContent();                            if (is != null) {                                fos = new FileOutputStream(updateFile, false);                                byte buffer[] = new byte[4096];                                int readsize = 0;                                while ((readsize = is.read(buffer)) > 0) {                                    fos.write(buffer, 0, readsize);                                    downloadSize += readsize;                                    if ((count == 0)                                            || (int) (downloadSize * 100 / totalSize) >= count) {                                        count += 5;                                        updateNotification.contentView                                                .setTextViewText(                                                        R.id.download_notice_speed_tv,                                                        getMsgSpeed(downloadSize,totalSize));                                        updateNotificationManager.notify(0,                                                updateNotification);                                    }                                }                                fos.flush();                                if (totalSize >= downloadSize) {                                    return DOWNLOAD_COMPLETE;                                } else {                                    return DOWNLOAD_FAIL;                                }                            }                        } else {                            if (httpGet != null) {                                httpGet.abort();                            }                            return DOWNLOAD_NOMEMORY;                        }                    }                }            }        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                if (fos != null) {                    fos.close();                }                if (is != null) {                    is.close();                }            } catch (IOException e) {                e.printStackTrace();            }            if (httpClient != null) {                httpClient.getConnectionManager().shutdown();            }        }        return DOWNLOAD_FAIL;    }    /**     * 可用内存大小     * @param fileSize     * @return     */    private boolean MemoryAvailable(long fileSize) {        fileSize += (1024 << 10);        if (MemoryStatus.externalMemoryAvailable()) {            if ((MemoryStatus.getAvailableExternalMemorySize() <= fileSize)) {                if ((MemoryStatus.getAvailableInternalMemorySize() > fileSize)) {                    createFile(false);                    return true;                } else {                    return false;                }            } else {                createFile(true);                return true;            }        } else {            if (MemoryStatus.getAvailableInternalMemorySize() <= fileSize) {                return false;            } else {                createFile(false);                return true;            }        }    }    /**     * 获取下载进度     * @param downSize     * @param allSize     * @return     */    public static String getMsgSpeed(long downSize, long allSize) {        StringBuffer sBuf = new StringBuffer();        sBuf.append(getSize(downSize));        sBuf.append("/");        sBuf.append(getSize(allSize));        sBuf.append(" ");        sBuf.append(getPercentSize(downSize, allSize));        return sBuf.toString();    }    /**     * 获取大小     * @param size     * @return     */    public static String getSize(long size) {        if (size >= 0 && size < SIZE_BT) {            return (double) (Math.round(size * 10) / 10.0) + "B";        } else if (size >= SIZE_BT && size < SIZE_KB) {            return (double) (Math.round((size / SIZE_BT) * 10) / 10.0) + "KB";        } else if (size >= SIZE_KB && size < SIZE_MB) {            return (double) (Math.round((size / SIZE_KB) * 10) / 10.0) + "MB";        }        return "";    }    /**     * 获取到当前的下载百分比     * @param downSize   下载大小     * @param allSize    总共大小     * @return     */    public static String getPercentSize(long downSize, long allSize) {        String percent = (allSize == 0 ? "0.0" : new DecimalFormat("0.0")                .format((double) downSize / (double) allSize * 100));        return "(" + percent + "%)";    }    /**     * 创建file文件     * @param sd_available    sdcard是否可用     */    private void createFile(boolean sd_available) {        if (sd_available) {            updateDir = new File(Environment.getExternalStorageDirectory(),                    UpdateInformation.downloadDir);        } else {            updateDir = getFilesDir();        }        updateFile = new File(updateDir.getPath(), appName + ".apk");        if (!updateDir.exists()) {            updateDir.mkdirs();        }        if (!updateFile.exists()) {            try {                updateFile.createNewFile();            } catch (IOException e) {                e.printStackTrace();            }        } else {            updateFile.delete();            try {                updateFile.createNewFile();            } catch (IOException e) {                e.printStackTrace();            }        }    }}

    这个时候,可能看到服务怎么这么多代码啊,我头都大了,不要着急,我们一步一步说明一下,这里逻辑很简单,就是在通知栏中,用到了通知,这个时候我们有三种情况,造成了我们好多代码的重复,(你也可以不必考虑那么多情况),还有,里面有了几个工具类,没有提取出来,分别是获取sdcard大小是否可用(创建文件夹),获取当前下载进度,获取应用大小,下载文件,这里也可以使用第三方框架来下载。
    当然了哈,这里我写的还是有点问题的,每次进入都会提示,如果有必要,也可以实现是否要自动更新,用服务,也就是点击是否自动更新,如果不是自动更新,就不会去触发服务端接口信息,如果是自动更新,就去触发,来获取最新的app版本。



1 0
原创粉丝点击