精华内容
下载资源
问答
  • Android软件自动更新升级(自动下载安装新版本)-附件资源
  • 1.Android 8.0以后无法下载到SDK中 问题原因:文件的存储权限原因导致的 动态申请sdk存储权限:Android 6.0以后都是需要动态申请权限的,注意即使动态申请了权限也要在AndroidManifest.xml申请一下,因为需要兼容...

    因为Android系统版本的不同踩了不少坑,在此记录。

    1.Android 8.0以后无法下载到SDK中

    问题原因: 文件的存储权限原因导致的

    1. 动态申请sdk存储权限:Android 6.0以后都是需要动态申请权限的,注意即使动态申请了权限也要在AndroidManifest.xml申请一下,因为需要兼容低版本,低版本中没有动态申请权限一说。
    2. Android 7.0以后又对存储权限加了限制应用私有目录被限制访问 
    •  私有文件的文件权限不应再由所有者放宽,使用MODE_WORLD_READABLE/MODE_WORLD_WRITEABLE将抛出异常 
    • 向应用外传递file://URI会出发FileUriExposedException

    解决方法:

       问题一:判断版本,动态申请权限

     //----------------------------------------动态申请权限-------------------------------
     private void sdkPermission() {
            if(Build.VERSION.SDK_INT>Build.VERSION_CODES.M) {//高于6.0版本,动态申请权限
                if (ContextCompat.checkSelfPermission(context, "android.permission.WRITE_EXTERNAL_STORAGE") != PackageManager.PERMISSION_GRANTED) {
                    ActivityCompat.requestPermissions(this, new String[]{"android.permission.WRITE_EXTERNAL_STORAGE"}, 111);
                } else {
                    downloadApk();
                }
            }
            else {
                downloadApk();//低于6.0版本,直接下载apk
            }
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
            switch (requestCode) {
                case 111:
                    if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        //权限已经都通过了,可以下载apk到SDk中了
                        downloadApk();
                    } else {
                       // 没有申请权限
                        showPermissionDialog();
                    }
                    break;
                default:
            }
        }

       问题二:7.0以后使用FileProvider访问sdk私有文件

    FileProvider:   

         当targetSdkVersion>=24时,会存在上述问题,可能涉及到的场景有:拍照,程序安装等。 
         同时,官方在v4包(api=22开始)中引入FileProvider类用于程序间私有文件的共享。该类继承自ContentProvider,使用时需  要在清单文件中注册

    • 第一步   注册:在清单文件中通过标签注册,参考代码对属性进行说明:

      注意:当自定义类继承FileProvider时,需要更改name属性值为该类的相对路径。

    <manifest>
        ...
        <application>
            ...
            <provider
                android:name="android.support.v4.content.FileProvider"
                android:authorities="com.mydomain.fileprovider"
                android:exported="false"
                android:grantUriPermissions="true">
                <meta-data
                  android:name="android.support.FILE_PROVIDER_PATHS"
                  android:resource="@xml/file_paths" />
            </provider>
            ...
        </application>
    </manifest>

       说明:meta_data中的

                     name:为固定值android.support.FILE_PROVIDER_PATHS 

                     resource:所对应的xml文件路径

    • 第二步  resource对应的xml文件编写:作用设置可访问的共享文件 

          FileProvide只能对在paths中声明了的文件夹下的文件生成uri。

         下例子就是声明私有文件目录下images/下的文件可以临时访问(文件在res/xml/目录下),下面时一个简单的样式:

        1.创建xml:res/xml/file_paths

    2.file_paths.xml中添加代码:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <paths xmlns:android="http://schemas.android.com/apk/res/android">
           <files-path name="my_images" path="images/"/>
           <files-path name="my_docs" path="docs/"/>
      </paths>
    </resources>

    因为的子标签可以有多种,这里对所有进行说明:

    子标签中属性说明:

    • 第三步  FileProvider的使用:

    1、通过路径生成要分享的File对象。

    2、使用FileProvider生成临时访问uri (FileProvide.getUriForFile()).

    3、客户端可以使用uri通过ContentResolver.openFileDescriptor获取到一个ParcelFileDescriptor 

    案例:

    File imagePath = new File(Context.getFilesDir(), "images");
    File newFile = new File(imagePath, "default_image.jpg");
    Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
    
    //改代码生成的uri为:content://com.mydomain.fileprovider/my_images/default_image.jpg

    临时权限的授予方式 

    1. 使用Context.grantUriPermission(package,Uri,mode_flags)方法,使用想要的模式。这个方法通过mode_flags方法授予客户  端package的临时权限,有两个取值,FLAG_GRANT_URI_PERMISSION和FLAG_GRANT_WRITE_URI_PERMISSOIN。该方式允许后,可通过revokeUriPermission终止,或者手机重启后 
    2. 通过Intent 
    • 通过Intent的setData()方法将该uri放入Intent中
    • 可以为Intent设置flag,设置一个或两个, FLAG_GRANT_URI_PERMISSION和FLAG_GRANT_WRITE_URI_PERMISSOIN
    • 将Intent发送给其他app,大部分情况,通过setResult()来做 这种方法获取的权限,当接收的Activity在栈中一直活跃时都会保留,当activity栈finish时,权限会自动移除。被允许的activity所在的app的其他组件也会被允许该权限。

    案例代码:

    • 步骤一:在AndroidManifest.xml中的application注册FileProvider的清单文件
     //临时访问文件的注册
            <provider
                android:authorities="${applicationId}.android7.fileprovider"
                android:name="android.support.v4.content.FileProvider"
                android:exported="false"
                android:grantUriPermissions="true">
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/file_paths" />
                ></provider>
    • 步骤二:res中创建xml包,添加file_paths.xml,在file_paths.xml中添加如下文件
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <paths>
            <external-path path="Android/data/com.example.socket.phonesafe/"        name="files_root" />
            <external-path path="." name="external_storage_root" />
        </paths>
    </resources>
    • 步骤三:代码中使用
    private void installApk(File file) {
            Uri uri=null;
            try {
                Intent intent=new Intent(Intent.ACTION_VIEW);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//为intent 设置特殊的标志,会覆盖 intent 已经设置的所有标志。
                if(Build.VERSION.SDK_INT>=24){//7.0 以上版本利用FileProvider进行访问私有文件
                    uri=FileProvider.getUriForFile(content,content.getPackageName() + ".android7.fileprovider",file);
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//为intent 添加特殊的标志,不会覆盖,只会追加。
                }
                else {
                    //直接访问文件
                    uri=Uri.fromFile(file);
                    intent.setAction(Intent.ACTION_VIEW);
                }
                intent.setDataAndType(uri, "application/vnd.android.package-archive");
                startActivity(intent);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

     

    2.下载成功了没有跳出应用安装界面

    原因:

    Android 8.0的系统中,“未知来源应用权限”的开关被移除掉了,取而代之的是未知来源应用的管理列表,如果你想要安装某个被自己所信任的开发者的app,则需要在每一次都手动授权“安装未知应用”的许可。设置页面如下图:(在华为Android 8.0中,打开该设置页面:设置列表—>安全与隐私—>更多安全设置—>安装未知应用)

    如图所示*,若某个应用选择的是“不允许”,那么假设app手动升级的时候,就无法成功跳转到安装页面进行正常的App升级流程了,此时需要手动去授权才行,但是很多用户并不知道需要这么设置。

    解决方法:

       安装软件前,先监测是否许可了此软件的“安装未知应用”,如果没有允许就跳转到设置页面,然后用户手动允许一下,如果允许了,就可以直接安装应用了。

     

    步骤一:在AndroidManifest.xml文件中,添加REQUEST_INSTALL_PACKAGES权限

    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

    步骤二:判断版本号、是否允许安装此未知应用

               if(Build.VERSION.SDK_INT>Build.VERSION_CODES.O){
                        installAllowed=content.getPackageManager().canRequestPackageInstalls();//是否允许安装包
                        if(installAllowed){
                            installApk(file);//允许,安装
                        }else {
                            //跳转到设置页面,设置成允许安装
                            Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:" + content.getPackageName()));
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            content.startActivity(intent);
                            installApk(file);
                            return;
                        }
    
                    }
                    //版本低于8.0
                    else {
                        installApk(file);
                    }
    //安装apk
        private void installApk(File file) {
            Uri uri=null;
            try {
                Intent intent=new Intent(Intent.ACTION_VIEW);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//为intent 设置特殊的标志,会覆盖 intent 已经设置的所有标志。
                if(Build.VERSION.SDK_INT>=24){//7.0 以上版本利用FileProvider进行访问私有文件
                    uri=FileProvider.getUriForFile(content,content.getPackageName() + ".android7.fileprovider",file);
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//为intent 添加特殊的标志,不会覆盖,只会追加。
                }
                else {
                    //直接访问文件
                    uri=Uri.fromFile(file);
                    intent.setAction(Intent.ACTION_VIEW);
                }
                intent.setDataAndType(uri, "application/vnd.android.package-archive");
                startActivity(intent);
            } catch (Exception e) {
                e.printStackTrace();
            }

    百度网盘源码:https://pan.baidu.com/s/1gKly1Syobmj1MkainAwOkw

     密码:9lj5

    注解:版本信息和要升级的apk存放在自己tomcat服务器中的webapps—ROOT中,然后开启tomcat,代码中就可以直接访问下载了。

    参考:https://blog.csdn.net/chen_white/article/details/72819814

    https://www.jianshu.com/p/e05f35fbb569

     

    注:如果你的gradle版本和我的不一样导致下载的代码不能运行,可看我另一篇代码可以解决这个问题https://blog.csdn.net/na2609613672/article/details/89086952

    展开全文
  • Android服务器——使用TomCat实现软件版本检测,升级,以及下载更新进度! 算下来,TomCat服务器已经写了很长一段时间了,一直说拿他来搞点事情,也一直没做,今天刚好有空,交流群还有人请教,就寻思着把一些...

    Android服务器——使用TomCat实现软件的版本检测,升级,以及下载更新进度!


    算下来,TomCat服务器已经写了很长一段时间了,一直说拿他来搞点事 情,也一直没做,今天刚好有空,交流群还有人请教,就寻思着把一些相关性的原理和基础操作写下来,虽然我网络这一块还是不怎么扎实,嘿嘿,还记得我们怎么搭建的服务器吗?

    地址:Android服务器——TomCat服务器的搭建

    我们新建一个项目TomCatVersion

    这边先来说一下原理,我们做的小例子也是十分的简单,一个首页,我们用Handler实现,然后同步检测当前版本号和系统的版本号对比,如果有升级则弹出提示框提示升级,点击确定开始下载apk,同时显示进度,等下载完成之后启动新下载的APK进行安装,如果点击取消,进入主页,如果没有升级,则直接进入主页,思路应该很清晰吧!那好,我们开始!

    一,准备工作

    • 1.搭建TomCat服务器

      我们没有服务器,所以使用TomCat服务器模拟,不会搭建的请看Android服务器——TomCat服务器的搭建

    • 2.新建一个IndexActivity类,并且在AndroidManifest.xml里注册并且设置为主入口

     <activity android:name="com.lgl.tomcatversion.IndexActivity" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    • 3.准备一张首页的图片

      可有可无,这里作为演示就去网上下载了一张

    • 4.版本更新接口

      也就是服务器的地址,我们这里也就直接自己写一段简单的json了

    {
        "versionName": "2.0",
        "versionCode": 1,
        "content": "修复多项bug!",
        "url": "http://localhost:192.168.1.101/lgl/TomCatVersion.apk"
    }

    我们把他放在服务器里面】

    这里写图片描述

    乱码请无视,浏览器的锅

    • 5.网络权限
     <uses-permission android:name="android.permission.INTERNET"/>

    二.layout_index.xml

    布局就没什么内容了,一个进度,一个文本

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/index" 
        android:gravity="center_horizontal">
    
        <TextView
            android:gravity="center"
            android:layout_alignParentBottom="true"
            android:id="@+id/tv_version"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:textColor="@android:color/white"
            android:textSize="20sp" />
    
        <ProgressBar
            android:layout_centerInParent="true"
            android:layout_alignParentBottom="true"
            android:id="@+id/progressBar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="60dp" />
    
    </RelativeLayout>

    三.逻辑分析

    1.获取版本号

    首先你的首页的文本上需要获取当前应用的版本号吧

        /**
         * 获取APP版本号
         * 
         * @return
         */
        private String getAppVersion() {
            try {
                //PackageManager管理器
                PackageManager pm = getPackageManager();
                //获取相关信息
                packageInfo = pm.getPackageInfo(getPackageName(), 0);
                //版本名称
                String name = packageInfo.versionName;
                //版本号
                int version = packageInfo.versionCode;
    
                Log.i("版本信息", "版本名称:"+name+"版本号"+version);
    
                return name;
            } catch (NameNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //如果出现异常抛出null
            return null;
        }

    我们打印出来的Log

    这里写图片描述

    我们需要的就是这个name(版本名称)

    2.解析JSON

    这段json还是十分的简单的,我们直接就用原生的方式解析了,本来想用Volley的,但是演示的话,希望各位自己根据需求使用

    
        /**
         * 解析JSON
         */
        private void getJSON() {
            // 子线程访问,耗时操作
            new Thread() {
                public void run() {
                    try {
                        // JSON地址
                        HttpURLConnection conn = (HttpURLConnection) new URL(
                                //模拟器一般有一个预留IP:10.0.2.2
                                "http://192.168.1.103:8080/lgl/update.json")
                                .openConnection();
                        //请求方式GRT
                        conn.setRequestMethod("GET");
                        //连接超时
                        conn.setConnectTimeout(5000);
                        //响应超时
                        conn.setReadTimeout(3000);
                        //连接
                        conn.connect();
                        //获取请求码
                        int responseCode = conn.getResponseCode();
                        //等于200说明请求成功
                        if(responseCode == 200){
                            //拿到他的输入流
                            InputStream in = conn.getInputStream();
                            String stream = Utils.toStream(in);
    
                            Log.i("JSON", stream);
                        }
    
                    } catch (MalformedURLException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }.start();
        }

    这里我们写了一个Utils来转换流

    package com.lgl.tomcatversion;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * 流转换Stream的工具栏
     * 
     * @author LGL
     *
     */
    public class Utils {
    
        // 对外发放
        public static String toStream(InputStream in) throws IOException {
    
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            // 长度
            int length = 0;
            byte[] buffer = new byte[1024];
            // -1代表读完了
            while ((length = in.read(buffer)) != -1) {
                out.write(buffer, 0, length);
            }
            // 读完关闭
            in.close();
            out.close();
            // 我们把返回的数据转换成String
            return out.toString();
        }
    }
    

    这样我们就可以把Json打印出来了

    这里写图片描述

    既然获取到了,那我们就开始解析JSON吧

    在Log后面继续写代码

    // 解析
    JSONObject jsonObject = new JSONObject(stream);
    // 获取版本名
    String versionName = jsonObject.getString("versionName");
    // 获取版本号
    int versionCode = jsonObject.getInt("versionCode");
    // 获取更新内容
    String content = jsonObject.getString("content");
    // 获取下载地址
    String url = jsonObject.getString("url");

    这样我们就解析完成了

    3.版本比较以及提示更新

    这里我们可以根据name或者code的比较来判断是否有更新,有更新的话,弹出提示框,点确定再更新,我这里就比较Code了,先写以恶搞获取code的方法

    
        /**
         * 获取versionCode
         */
        private int getCode() {
            // PackageManager管理器
            PackageManager pm = getPackageManager();
            // 获取相关信息
            try {
                packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 版本号
                int version = packageInfo.versionCode;
                return version;
            } catch (NameNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return 0;
    
        }

    然后我们接着刚才解析完JSON的地方比对,我们先把服务器的JSON数据改了

    {
        "versionName": "2.0",
        "versionCode": 2,
        "content": "修复多项bug!",
        "url": "http://192.168.1.103:8080/lgl/TomCatVersion.apk"
    }

    这样就会提示更新了,我们检修

    // 版本判断
    if (versionCode > getCode()) {
        // 提示更新
        msg.what = UPDATE_YES;
    } else {
        // 不更新,跳转到主页
        msg.what = UPDATE_NO;
    }

    子线程中我们是不能弹框的,所以我们用Handler,当我们发送UPDATE_YES的时候就弹框,也就是执行我们弹框的方法

    
        /**
         * 升级弹框
         */
        private void showUpdateDialog() {
            updateDialog = new CustomDialog(this, 0, 0, R.layout.dialog_update,
                    R.style.Theme_dialog, Gravity.CENTER, 0);
            // 更新内容
            dialog_update_content = (TextView) updateDialog
                    .findViewById(R.id.dialog_update_content);
            dialog_update_content.setText(content);
            // 确定更新
            dialog_confrim = (TextView) updateDialog
                    .findViewById(R.id.dialog_confrim);
            dialog_confrim.setOnClickListener(this);
            // 取消更新
            dialog_cancel = (TextView) updateDialog
                    .findViewById(R.id.dialog_cancel);
            dialog_cancel.setOnClickListener(this);
            updateDialog.show();
        }

    这里我用了一个自定义的Dialog’

    CustomDialog

    package com.lgl.tomcatversion;
    
    import android.app.Dialog;
    import android.content.Context;
    import android.view.Gravity;
    import android.view.Window;
    import android.view.WindowManager;
    
    public class CustomDialog extends Dialog {
    
        public CustomDialog(Context context, int layout, int style) {
            this(context, WindowManager.LayoutParams.MATCH_PARENT,
                    WindowManager.LayoutParams.WRAP_CONTENT, layout, style,
                    Gravity.CENTER);
        }
    
        public CustomDialog(Context context, int width, int height, int layout,
                int style, int gravity, int anim) {
            super(context, style);
    
            setContentView(layout);
            // set window params
            Window window = getWindow();
            WindowManager.LayoutParams params = window.getAttributes();
            // set width,height by density and gravity
            // float density = getDensity(context);
            params.width = WindowManager.LayoutParams.MATCH_PARENT;
    
            params.height = WindowManager.LayoutParams.WRAP_CONTENT;
    
            params.gravity = gravity;
            window.setAttributes(params);
            window.setWindowAnimations(anim);
        }
    
        public CustomDialog(Context context, int width, int height, int layout,
                int style, int gravity) {
            this(context, width, height, layout, style, gravity,
                    R.style.pop_anim_style);
        }
    
    }

    他需要用到一些资源

    dialog_update.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/main_layout"
        android:layout_width="match_parent"
        android:layout_height="150dip"
        android:layout_marginEnd="20dp"
        android:layout_marginStart="20dp"
        android:background="@drawable/dialog_bg"
        android:orientation="vertical" >
    
        <LinearLayout
            android:id="@+id/dialog_notifly_bottom"
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:layout_alignParentBottom="true"
            android:layout_gravity="bottom"
            android:orientation="horizontal" >
    
            <TextView
                android:id="@+id/dialog_cancel"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:singleLine="true"
                android:text="暂不更新"
                android:textSize="18sp" />
    
            <View
                android:layout_width="0.2dp"
                android:layout_height="match_parent"
                android:background="#AAAAAA" />
    
            <TextView
                android:id="@+id/dialog_confrim"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:singleLine="true"
                android:text="立即下载"
                android:textSize="18sp" />
        </LinearLayout>
    
        <View
            android:id="@+id/dialog_notifly_line"
            android:layout_width="match_parent"
            android:layout_height="0.2dp"
            android:layout_above="@id/dialog_notifly_bottom"
            android:background="#AAAAAA" />
    
        <TextView
            android:id="@+id/dialog_update_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_above="@+id/dialog_notifly_line"
            android:layout_below="@+id/TextView01"
            android:layout_marginBottom="10dip"
            android:layout_marginLeft="10dip"
            android:layout_marginRight="10dip"
            android:gravity="center"
            android:singleLine="true"
            android:textSize="18sp" />
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:layout_marginTop="20dp"
            android:gravity="center"
            android:singleLine="true"
            android:text="检测到有新版本,是否下载?"
            android:textSize="18sp" />
    
    </RelativeLayout>

    styles.xml

     <style name="pop_anim_style">
            <item name="android:windowEnterAnimation">@anim/pop_in</item>
            <item name="android:windowExitAnimation">@anim/pop_out</item>
        </style>
    
        <style name="Theme_dialog" parent="@android:style/Theme.Dialog">
            <item name="android:windowBackground">@android:color/transparent</item>
            <item name="android:windowNoTitle">true</item>
        </style>

    以及用到的动画

    pop_in.xml

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android" >
    
        <translate
            android:duration="@android:integer/config_shortAnimTime"
            android:fromXDelta="0"
            android:fromYDelta="100%"
            android:toXDelta="0"
            android:toYDelta="0" />
    
    </set>

    pop_out.xml

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android" >
    
        <translate
            android:duration="@android:integer/config_shortAnimTime"
            android:fromXDelta="0"
            android:fromYDelta="0"
            android:toXDelta="0"
            android:toYDelta="100%" />
    
    </set>

    接着我们再执行一下

    这里写图片描述

    我们现在再来处理一下没有更新的逻辑,没有更新的话直接跳主页面,我们写一个方法

    /**
         * 跳转主页面
         */
        private void goHome() {
            startActivity(new Intent(this, MainActivity.class));
            finish();
        }

    但是这样还是有个问题,他没有更新一下子就跳过去了,所以我们在发消息的时候先让线程睡一会儿

    try {
        //停留三秒钟
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        }
    // 全部走完发消息
    handler.sendMessage(msg);

    但是这里又出现了一个新的问题,毕竟是网络问题,他是耗时的,这样的话,万一等太久了用户体验也上不去啊,所以我们这里要做一个优化

    我们在开始网络请求的时间记录一个时间

    // 开始访问网络的时间
    long startTime = System.currentTimeMillis();
    

    然后再网络请求结束的时候去计算时间并且计算一共用了多少时间

    // 网络访问结束的时间
    long endTime = System.currentTimeMillis();
    // 计算网络用了多少时间
    long time = endTime - startTime;
    try {
        if (time < 3000) {
        // 停留三秒钟
        Thread.sleep(3000 - time);
    }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
                e.printStackTrace();
    }

    4.下载更新以及下载进度

    涉及到下载,这里你可以使用很多的开源框架,我这里使用的是xutils
    地址:https://github.com/wyouflf/xUtils
    我们下载之后拷贝在libs里就可以用了,用起来也很简单,使用之前先加个权限

       <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    然后开始下载

    /**
         * 下载更新
         */
        private void downloadAPK() {
            tv_pro.setVisibility(View.VISIBLE);
            // 判断是否有SD卡
            if (Environment.getExternalStorageState().equals(
                    Environment.MEDIA_MOUNTED)) {
                // 获取手机根目录
                String path = Environment.getExternalStorageDirectory()
                        .getAbsolutePath() + "/TomCatVersion.apk";
                HttpUtils httpUtils = new HttpUtils();
                /**
                 * 1.网络地址 2.存放地址 3.回调
                 */
                httpUtils.download(url, path, new RequestCallBack<File>() {
    
                    // 下载进度
                    @Override
                    public void onLoading(long total, long current,
                            boolean isUploading) {
                        // TODO Auto-generated method stub
                        super.onLoading(total, current, isUploading);
    
                        // 显示进度
                        tv_pro.setText(100 * current / total + "%");
    
                    }
    
                    // 成功
                    @Override
                    public void onSuccess(ResponseInfo<File> responseInfo) {
                    }
    
                    // 失败
                    @Override
                    public void onFailure(HttpException error, String msg) {
                        Log.i("error", msg);
                    }
                });
            } else {
                Toast.makeText(getApplicationContext(), "未找到SD卡", Toast.LENGTH_LONG)
                        .show();
            }
    
        }

    这里的代码逻辑也是十分的简单的,多吧,我们来看一下效果‘

    这里写图片描述

    更新的包体积有点小,所以一下子就百分之百了,我们去SD卡更目录看一下

    这里写图片描述

    确定是下载完成了,但是下完完之后啥也没发生啊,这就要我们再次优化了

    下载完之后软起动

    下载完之后自动进入安装界面,这才是真正的体验,我们在onSuccess()方法中

    // 跳转系统安装页面
    Intent i = new Intent();
    i.setAction(Intent.ACTION_VIEW);
    i.addCategory(Intent.CATEGORY_DEFAULT);
    i.setDataAndType(Uri.fromFile(new File(path)),"application/vnd.android.package-archive");
    startActivity(i);

    我们来看看效果

    这里写图片描述

    大致的一个逻辑思路就是这样了

    完整代码

    IndexActivity

    package com.lgl.tomcatversion;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import android.app.Activity;
    import android.content.DialogInterface;
    import android.content.DialogInterface.OnCancelListener;
    import android.content.Intent;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.content.pm.PackageManager.NameNotFoundException;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.view.Gravity;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.lidroid.xutils.HttpUtils;
    import com.lidroid.xutils.exception.HttpException;
    import com.lidroid.xutils.http.ResponseInfo;
    import com.lidroid.xutils.http.callback.RequestCallBack;
    
    /**
     * 首页
     * 
     * @author LGL
     *
     */
    public class IndexActivity extends Activity implements OnClickListener {
    
        // 更新
        private static final int UPDATE_YES = 1;
        // 不更新
        private static final int UPDATE_NO = 2;
        // URL错误
        private static final int URL_ERROR = 3;
        // 没有网络
        private static final int IO_ERROR = 4;
        // 数据异常
        private static final int JSON_ERROR = 5;
    
        private TextView tv_version;
        private PackageInfo packageInfo;
    
        private JSONObject jsonObject;
        private String versionName;
        private int versionCode;
        private String content;
        private String url;
    
        private TextView tv_pro;
    
        // 升级提示框
        private CustomDialog updateDialog;
        private TextView dialog_update_content;
        private TextView dialog_confrim;
        private TextView dialog_cancel;
    
        private Handler handler = new Handler() {
            public void handleMessage(android.os.Message msg) {
                switch (msg.what) {
                case UPDATE_YES:
                    showUpdateDialog();
                    break;
                case UPDATE_NO:
                    goHome();
                    break;
                case URL_ERROR:
                    Toast.makeText(getApplicationContext(), "地址错误",
                            Toast.LENGTH_LONG).show();
                    goHome();
                    break;
                case IO_ERROR:
                    Toast.makeText(getApplicationContext(), "请检查网络",
                            Toast.LENGTH_LONG).show();
                    goHome();
                    break;
                case JSON_ERROR:
                    Toast.makeText(getApplicationContext(), "Json解析错误",
                            Toast.LENGTH_LONG).show();
                    goHome();
                    break;
                // 就算你报任何错,爸比是爱你的,依然让你进主页面
                }
            }
        };
        private String path;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_index);
    
            initView();
            getJSON();
        }
    
        /**
         * 初始化控件
         * 
         * @author LGL
         */
        private void initView() {
    
            tv_pro = (TextView) findViewById(R.id.tv_pro);
    
            tv_version = (TextView) findViewById(R.id.tv_version);
            // 设置版本号
            tv_version.setText("版本号:" + getAppVersion());
        }
    
        /**
         * 获取APP版本号
         * 
         * @return
         */
        private String getAppVersion() {
            try {
                // PackageManager管理器
                PackageManager pm = getPackageManager();
                // 获取相关信息
                packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 版本名称
                String name = packageInfo.versionName;
                // 版本号
                int version = packageInfo.versionCode;
    
                Log.i("版本信息", "版本名称:" + name + "版本号" + version);
    
                return name;
            } catch (NameNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            // 如果出现异常抛出
            return "无法获取";
        }
    
        /**
         * 解析JSON
         */
        private void getJSON() {
            // 子线程访问,耗时操作
            new Thread() {
                public void run() {
    
                    Message msg = Message.obtain();
    
                    // 开始访问网络的时间
                    long startTime = System.currentTimeMillis();
    
                    try {
                        // JSON地址
                        HttpURLConnection conn = (HttpURLConnection) new URL(
                        // 模拟器一般有一个预留IP:10.0.2.2
                                "http://192.168.1.103:8080/lgl/update.json")
                                .openConnection();
                        // 请求方式GRT
                        conn.setRequestMethod("GET");
                        // 连接超时
                        conn.setConnectTimeout(5000);
                        // 响应超时
                        conn.setReadTimeout(3000);
                        // 连接
                        conn.connect();
                        // 获取请求码
                        int responseCode = conn.getResponseCode();
                        // 等于200说明请求成功
                        if (responseCode == 200) {
                            // 拿到他的输入流
                            InputStream in = conn.getInputStream();
                            String stream = Utils.toStream(in);
    
                            Log.i("JSON", stream);
                            jsonObject = new JSONObject(stream);
                            versionName = jsonObject.getString("versionName");
                            versionCode = jsonObject.getInt("versionCode");
                            content = jsonObject.getString("content");
                            url = jsonObject.getString("url");
    
                            // 版本判断
                            if (versionCode > getCode()) {
                                // 提示更新
                                msg.what = UPDATE_YES;
                            } else {
                                // 不更新,跳转到主页
                                msg.what = UPDATE_NO;
                            }
                        }
    
                    } catch (MalformedURLException e) {
                        // URL错误
                        e.printStackTrace();
                        msg.what = URL_ERROR;
                    } catch (IOException e) {
                        // 没有网络
                        e.printStackTrace();
                        msg.what = IO_ERROR;
                    } catch (JSONException e) {
                        // 数据错误
                        e.printStackTrace();
                        msg.what = JSON_ERROR;
                    } finally {
    
                        // 网络访问结束的时间
                        long endTime = System.currentTimeMillis();
                        // 计算网络用了多少时间
                        long time = endTime - startTime;
    
                        try {
                            if (time < 3000) {
                                // 停留三秒钟
                                Thread.sleep(3000 - time);
                            }
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        // 全部走完发消息
                        handler.sendMessage(msg);
                    }
                }
            }.start();
        }
    
        /**
         * 获取versionCode
         */
        private int getCode() {
            // PackageManager管理器
            PackageManager pm = getPackageManager();
            // 获取相关信息
            try {
                packageInfo = pm.getPackageInfo(getPackageName(), 0);
                // 版本号
                int version = packageInfo.versionCode;
                return version;
            } catch (NameNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return 0;
    
        }
    
        /**
         * 升级弹框
         */
        private void showUpdateDialog() {
            updateDialog = new CustomDialog(this, 0, 0, R.layout.dialog_update,
                    R.style.Theme_dialog, Gravity.CENTER, 0);
            //如果他点击其他地方,不安装,我们就直接去
            updateDialog.setOnCancelListener(new OnCancelListener() {
    
                @Override
                public void onCancel(DialogInterface dialog) {
                    goHome();
                }
            });
            // 更新内容
            dialog_update_content = (TextView) updateDialog
                    .findViewById(R.id.dialog_update_content);
            dialog_update_content.setText(content);
            // 确定更新
            dialog_confrim = (TextView) updateDialog
                    .findViewById(R.id.dialog_confrim);
            dialog_confrim.setOnClickListener(this);
            // 取消更新
            dialog_cancel = (TextView) updateDialog
                    .findViewById(R.id.dialog_cancel);
            dialog_cancel.setOnClickListener(this);
            updateDialog.show();
        }
    
        /**
         * 点击事件
         */
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.dialog_confrim:
                updateDialog.dismiss();
                downloadAPK();
    
                break;
            case R.id.dialog_cancel:
                // 跳主页面
                goHome();
                break;
            }
        }
    
        /**
         * 跳转主页面
         */
        private void goHome() {
            startActivity(new Intent(this, MainActivity.class));
            finish();
        }
    
        /**
         * 下载更新
         */
        private void downloadAPK() {
            tv_pro.setVisibility(View.VISIBLE);
            // 判断是否有SD卡
            if (Environment.getExternalStorageState().equals(
                    Environment.MEDIA_MOUNTED)) {
                path = Environment.getExternalStorageDirectory().getAbsolutePath()
                        + "/TomCatVersion.apk";
                HttpUtils httpUtils = new HttpUtils();
                /**
                 * 1.网络地址 2.存放地址 3.回调
                 */
                httpUtils.download(url, path, new RequestCallBack<File>() {
    
                    // 下载进度
                    @Override
                    public void onLoading(long total, long current,
                            boolean isUploading) {
                        // TODO Auto-generated method stub
                        super.onLoading(total, current, isUploading);
    
                        // 显示进度
                        tv_pro.setText(100 * current / total + "%");
    
                    }
    
                    // 成功
                    @Override
                    public void onSuccess(ResponseInfo<File> responseInfo) {
    
                        // 跳转系统安装页面
                        Intent i = new Intent();
                        i.setAction(Intent.ACTION_VIEW);
                        i.addCategory(Intent.CATEGORY_DEFAULT);
                        i.setDataAndType(Uri.fromFile(new File(path)),
                                "application/vnd.android.package-archive");
                        startActivity(i);
                    }
    
                    // 失败
                    @Override
                    public void onFailure(HttpException error, String msg) {
                        Log.i("error", msg);
                    }
                });
            } else {
                Toast.makeText(getApplicationContext(), "未找到SD卡", Toast.LENGTH_LONG)
                        .show();
            }
    
        }
    }
    

    layout_index.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/index"
        android:gravity="center_horizontal" >
    
        <TextView
            android:id="@+id/tv_version"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="20dp"
            android:gravity="center"
            android:textColor="@android:color/white"
            android:textSize="20sp" />
    
        <ProgressBar
            android:id="@+id/progressBar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerInParent="true"
            android:layout_marginBottom="60dp" />
    
        <TextView
            android:id="@+id/textView2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_marginTop="207dp"
            android:gravity="center"
            android:text="欢迎"
            android:textColor="#fff"
            android:textSize="50sp" />
    
        <TextView
            android:textColor="#220000"
            android:visibility="invisible"
            android:id="@+id/tv_pro"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@+id/progressBar"
            android:layout_centerHorizontal="true"
            android:layout_marginBottom="95dp"
            android:textAppearance="?android:attr/textAppearanceLarge" />
    
    </RelativeLayout>

    Demo下载地址:http://download.csdn.net/detail/qq_26787115/9463814

    展开全文
  • 待续····· 文件参考: Android服务器——TomCat服务器的搭建 ...Android服务器——使用TomCat实现软件版本检测,升级,以及下载更新进度! https://blog.csdn.net/qq_26787115/article/details/5085...

    待续·····

    文件参考:

    Android服务器——TomCat服务器的搭建
    https://blog.csdn.net/qq_26787115/article/details/50575915

    Android服务器——使用TomCat实现软件的版本检测,升级,以及下载更新进度!
    https://blog.csdn.net/qq_26787115/article/details/50850837

    展开全文
  • C/S系统自动升级软件帮助开发者将桌面管理软件升级到最新版本,可以轻松实现多渠道的同步更新,是进行存量用户更新的有效手段。使用自动更新程序后(即客户端已经部署升级程序以及集成了自动更新接口),当用户启动...
  • 软件版本更新

    2011-09-21 20:59:38
    定期/定时自动连接远程服务器,检索服务器指定位置版本信息,获取服务器上存放的版本信息,与本地系统进行比对,如果服务器上存放的版本高于本地系统版本,则更新提示,若用户选择更新版本,则从服务器下载版本...
  • 目前,Keil MDK 升级至V5.33版本,感兴趣的小伙伴欢迎下载!(下载方式见文末) 相比于上个版本,MDK V5.33的新增功能有哪些? MDK版本5.33包括Arm Compiler 6.15,新增显示软件组件的编译器/汇编器字符串,允许您...

    前不久,米尔发布了官方更新的Keil MDK V5.32。目前,Keil MDK 升级至V5.33版本,感兴趣的小伙伴欢迎下载!(下载方式见文末)

    相比于上个版本,MDK V5.33的新增功能有哪些?

    MDK版本5.33包括Arm Compiler 6.15,新增显示软件组件的编译器/汇编器字符串,允许您配置Fast Models的超时设置,以及包含使Holtek版本和GPDSC支持Cypress PSoC器件的修补程序。

    1、编译器Arm Compiler 6.15

    Arm Compiler 6.15包括以下针对Arm Cortex-M目标的主要亮点:

    • 支持Armv8-M自定义数据路径扩展(CDE)程序集和ACLE内部函数。

    • 新增优化选项-Omin。

    与Arm Compiler 6.14相比,Arm Compiler 6.15版本带来了一些重大的优化改进,参考下图:

    图片

    2、软体包版本

    MDK V5.33附以下软件包:

    • ARM.CMSIS.5.7.0

    • ARM.CMSIS-Driver.2.6.1

    • Keil.MDK-Middleware.7.12.0

    • Keil.Compiler.1.6.3

    3、MDK中间件7.12.0进行了以下更改:

    ①  文件系统组件

    • fwrite和fdelete中的优化和修复(仅适用于EFS)。

    ②  网络组件

    • 错误修复和更正。

    • 添加了配置选项以防止发送ping响应(无回显应答)。

    • 添加了阻塞功能netARP_ProbeX和netNDP_ProbeX,易于使用。

    ③  USB组件

    • USB主机:如果CDC设备是复合设备的一部分,则更正了CDC设备的枚举。

    • USB设备:更正了RNDIS。

    ④  图形组件

    • 更新:图形库V6.10h(使用GUI_USE_ARGB = 0,GUI_SUPPORT_BIDI = 0构建的库)。

    4、µVision

    • 新增显示软件组件的编译器/汇编器控制字符串。

    • 添加了一个配置选项,以禁用高级模型的CADI进程间通信超时。

    • 修复:µVision可以使用GPDSC与Cypress的Modus Toolbox交换项目数据。

    • 固定:免费的 Keil MDK Holtek Cortex-M0 / M0 +版本不再需要许可证映射文件的补丁。

    5、Arm固定虚拟平台(FVP)11.12

    将固定虚拟平台模型更新为v11.12.38。

    Arm官方本次更新描述地址:

    http://www2.keil.com/mdk5/533

    在米尔获取Arm Keil MDK V5.33:点击这里点击这里

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,492
精华内容 596
关键字:

升级更新版本软件下载