Android APK版本更新下载

        今天做了下APK版本更新的功能。其实关于APK版本更新相关的文章网上挺多的,功能实现上也并不难。基本思路就是讲当前APK的版本号和服务器上保存的最新APK包的版本号进行对比,如果当前版本小于最新版本,则进行下载更新。流程图如下:

Android APK版本更新下载

      

        引用的第三方jar包有okhttp,okio(为了支持okhttp)和gson:

compile 'com.squareup.okio:okio:1.5.0'
compile 'com.squareup.okhttp:okhttp:2.4.0'
compile 'com.google.code.gson:gson:2.3'
        PC端里APK版本列表的数据结构如下:

        APK版本列APK_VERSION

PK

字段名称

字段别名

字段类型

字段长度

是否允许为空

True

id

标识码

NUMBER

(10)

False

False

apk_name

apk名称

NVARCHAR2

500)

False

False

apk_version

apk版本

NVARCHAR2

100)

False

False

apk_size

apk文件大小(M)

NVARCHAR2

(50)

False

False

apk_path

apk打包文件存放路径

NVARCHAR2

2000)

False

False

app_time

apk数据记录时间

NVARCHAR2

20

False

False

app_desc

apk版本更新说明

NVARCHAR2

(500)

True

False

remark

备注

NVARCHAR2

(500)

True


        对应的Android端的数据抽象类为:

public class APKVersionBean {
    private String apkversion;
    private String apkname;
    private String apksize;
    private String apkpath;
    private String apptime;
    private String apkdesr;
    private String remark;

    public String getApkversion() {
        return apkversion;
    }

    public void setApkversion(String apkversion) {
        this.apkversion = apkversion;
    }

    ...
}

        
        首先是进行版本验证功能,每个APP版本设置是在AndroidManifest文件里,如下所示:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.diit.okhttptest"
    android:versionCode="1"
    android:versionName="测试版本">
        versionCode就是版本号,要获取它可以采用getPackageManager的方法,代码为:
public int getVerCode(Context context){
    int verCode = -1;
    try {
        verCode = context.getPackageManager().getPackageInfo("com.diit.okhttptest", 0).versionCode;
    } catch (PackageManager.NameNotFoundException e) {
        // TODO Auto-generated catch block
        Log.e("版本号获取异常", e.getMessage());
    }
    return verCode;
}
      其中com.diit.okhttptest是系统的包名。

      当当前版本号小于最新版本号时候,弹出对话框,提示用户更新代码:
AlertDialog.Builder builder = new AlertDialog.Builder(
        activity);
builder.setTitle("版本更新");//对话框标题
builder.setMessage(apkDescribe);//对话框详细内容
builder.setPositiveButton(R.string.str_button_ok,
        new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                _downloadAsyn(downUrl);//点击确定时候进行下载
        }
        });
builder.setNegativeButton(R.string.str_button_cancel, null);
builder.setCancelable(false); //设置为false时候点击返回键获取屏幕边缘,对话框也无法取消
builder.show(); // 显示这个消息框
        显示的效果为:
Android APK版本更新下载
       
        当点击现在更新按钮时候,进入下载的方法。在下载里首先也是弹出一个进度对话框,用来跟踪下载进度:
final ProgressDialog mProgress=new ProgressDialog(activity);//进度对话框
final Message message = updateHandler.obtainMessage();
mProgress.setIcon(R.drawable.ic_launcher_lc);//小图标
mProgress.setTitle("APK文件下载中,请稍候...");//进度对话框标题
mProgress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//进度对话框设置为水平进度条
mProgress.setCancelable(false); // 设置允许取消
mProgress.show();
        效果为:
Android APK版本更新下载
        
        这是进度对话框初始状态,然后可以根据下载情况进行进度赋值,代码为:
final Request request = new Request.Builder()
        .url(url)
        .build();
Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback()
{
    @Override
    public void onFailure(final Request request, final IOException e)
    {
        message.what = DOWNLOAD_FAIL;
        updateHandler.sendMessage(message);
        e.printStackTrace();
    }

    @Override
    public void onResponse(Response response)
    {
        InputStream is = null;
        byte[] buf = new byte[2048];
        int len = 0;
        FileOutputStream fos = null;
        try
        {
            long total=response.body().contentLength();//获取文件大小
            mProgress.setMax((int)total);//设置进度对话框总共数值大小
            long sum = 0;
            is = response.body().byteStream();
            updateFile = new File(updateDir, apkName);
            fos = new FileOutputStream(updateFile,false);
            while ((len = is.read(buf)) != -1)
            {
                sum+=len;
                int progress=(int)(sum*100/total);
                mProgress.setProgress((int)sum);//设置进度对话框当前完成数值大小
                fos.write(buf, 0, len);
            }
            fos.flush();
            mProgress.dismiss();
            if(sum>0){
                message.what = DOWNLOAD_COMPLETE;
                updateHandler.sendMessage(message);
            }
        }
      这里利用了OkHttp包进行文件下载,响应成功时候可以获取文件大小,再根据下载的进度实时更新下进度条和进度值,如图所示:
Android APK版本更新下载
      当下载成功后,进入安装界面
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");

activity.startActivityForResult(installIntent,requestcode);


      好了,大概功能就是这样,下面是我封装的包,代码为:
package com.diit.okhttptest;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AlertDialog;
import android.widget.Toast;

import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.CookieManager;
import java.net.CookiePolicy;

/**
 * Created by kaki on 2017/5/17.
 */
public class APKUpdateManager {
    private static APKUpdateManager mInstance;
    private final static int DOWNLOAD_COMPLETE = 0;
    private final static int DOWNLOAD_FAIL = 1;
    private final static int requestcode =100;
    private OkHttpClient mOkHttpClient;
    private String downUrl;
    private String apkDescribe;
    private String apkName;
    private File updateDir;
    private File updateFile;
    private Activity activity;


    public APKUpdateManager(APKVersionBean apkVersionBean, File updateDir, Activity activity){
        this.downUrl=apkVersionBean.getApkpath();
        this.apkDescribe=apkVersionBean.getApkdesr();
        this.apkName=apkVersionBean.getApkname();
        this.updateDir=updateDir;
        this.activity=activity;
    }

    public void APKUpdate(){
        mOkHttpClient = new OkHttpClient();
        //cookie enabled
        mOkHttpClient.setCookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER));
        AlertDialog.Builder builder = new AlertDialog.Builder(
                activity);
        builder.setTitle("版本更新");//对话框标题
        builder.setMessage(apkDescribe);//对话框详细内容
        builder.setPositiveButton(R.string.str_button_ok,
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        _downloadAsyn(downUrl);//点击确定时候进行下载
                    }
                });
        builder.setNegativeButton(R.string.str_button_cancel, null);
        builder.setCancelable(false); //设置为false时候点击返回键获取屏幕边缘,对话框也无法取消
        builder.show(); // 显示这个消息框
    }

    private void _downloadAsyn(final String url)
    {
        final ProgressDialog mProgress=new ProgressDialog(activity);//进度对话框
        final Message message = updateHandler.obtainMessage();
        mProgress.setIcon(R.drawable.ic_launcher_lc);//小图标
        mProgress.setTitle("APK文件下载中,请稍候...");//进度对话框标题
        mProgress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//进度对话框设置为水平进度条
        mProgress.setCancelable(false); // 设置允许取消
        mProgress.show();
        final Request request = new Request.Builder()
                .url(url)
                .build();
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback()
        {
            @Override
            public void onFailure(final Request request, final IOException e)
            {
                message.what = DOWNLOAD_FAIL;
                updateHandler.sendMessage(message);
                e.printStackTrace();
            }

            @Override
            public void onResponse(Response response)
            {
                InputStream is = null;
                byte[] buf = new byte[2048];
                int len = 0;
                FileOutputStream fos = null;
                try
                {
                    long total=response.body().contentLength();//获取文件大小
                    mProgress.setMax((int)total);//设置进度对话框总共数值大小
                    long sum = 0;
                    is = response.body().byteStream();
                    updateFile = new File(updateDir, apkName);
                    fos = new FileOutputStream(updateFile,false);
                    while ((len = is.read(buf)) != -1)
                    {
                        sum+=len;
                        int progress=(int)(sum*100/total);
                        mProgress.setProgress((int)sum);//设置进度对话框当前完成数值大小
                        fos.write(buf, 0, len);
                    }
                    fos.flush();
                    mProgress.dismiss();
                    if(sum>0){
                        message.what = DOWNLOAD_COMPLETE;
                        updateHandler.sendMessage(message);
                    }
                } catch (IOException e)
                {
                    message.what = DOWNLOAD_FAIL;
                    updateHandler.sendMessage(message);
                    e.printStackTrace();
                } finally
                {
                    try
                    {
                        if (is != null) {
                            is.close();
                        }
                    } catch (IOException e)
                    {
                    }
                    try
                    {
                        if (fos != null) {
                            fos.close();
                        }
                    } catch (IOException e)
                    {
                    }
                }

            }
        });
    }

    private Handler updateHandler = new Handler() {

        @SuppressWarnings("deprecation")
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {

                case DOWNLOAD_COMPLETE:

                    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");

                    activity.startActivityForResult(installIntent,requestcode);

                    break;
                case DOWNLOAD_FAIL:
                    Toast.makeText(activity,"APK文件下载失败,请稍候重试",Toast.LENGTH_LONG).show();
                    break;


            }
        }
    };

    private String getNameFromUrl(String url) {
        return url.substring(url.lastIndexOf("/") + 1);
    }
}
      
      在Activity里调用方法为:
int newversion=Integer.parseInt(apkVersionBean.getApkversion());
int version=getVerCode(this);
if(version<newversion) {
    APKUpdateManager apkUpdateManager = new APKUpdateManager(apkVersionBean, updateDir, this);
    apkUpdateManager.APKUpdate();
}
      
      安装后的回调函数为:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode){
        case requestcode:

            break;
    }
}