Android 检查更新 下载 安装

本文详细介绍了如何在Android应用中实现自动更新功能,包括版本信息管理、本地版本与服务器版本对比、服务端请求与响应处理、下载更新文件、创建通知栏及更新应用流程等关键步骤。

android 自动更新什么的网上一堆,废话不说,直接上代码

首先,用全局数据类保存更新URL(获取服务端版本、apk文件下载),以及文件路径等

public class Global {

	/*
	 * =================== 版本信息 ================
	 */

	/**
	 * 本地安装版本
	 */
	public static int localVersion = 0;

	/**
	 * 服务器版本
	 */
	public static int serverVersion = 0;

	/*
	 * =================== 常量数据 =================
	 */

	/** 服务器url */
	public static final String REQUEST_HOST = "http://xxxxxxxx";

	/*
	 * 其他数据涉及公司项目,就不写出来了
	 */

	/** 数据存储目录 */
	public static final String DATA_PATH = "xxxxx";

	/** 升级存储目录 */
	public static final String DOWNLOAD_DIR = DATA_PATH + "/download";

	/** 更新应用url */
	public static final String UPDATE_URL = REQUEST_HOST + "xxxxxx.apk";

	/** 检查更新(服务器xml文件链接url) */
	public static final String SERVER_VERSION_URL = REQUEST_HOST + "xxxxxx"; // 这里我们用xml保存版本信息

}
然后呢,既然想要检测版本,那么就要有本地安装版本code和服务器端的版本code

在AndroidManifest.xml文件中,versionCode用来识别应用版本code(用来比较大小的,integer),versionName则是给用户看的(string)

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="xxx.xxx.xxx"
    android:versionCode="1"
    android:versionName="1.0" >
那么在MyApplication中提供两个方法,getLocalVersion(获取本地版本code)和 startUpdateService(启动升级服务入口方法)

public class MApplication extends Application {
	
	@Override
	public void onCreate() {
		super.onCreate();
		// ......
		
		getLocalVersion(); // 读取版本code
	}
	
	/**
	 * 获取应用版本信息
	 */
	public void getLocalVersion() {
		try {
			PackageInfo packageInfo = getApplicationContext().getPackageManager().getPackageInfo(getPackageName(), 0);
			Global.localVersion = packageInfo.versionCode;
		} catch (NameNotFoundException e) {
			e.printStackTrace();
		}
	}
	
}
现在就是重点的UpdateService了

public class UpdateService extends Service {  
      
    private static final String TAG = "UpdateService";  
    private static final int TIMEOUT = 5 * 1000;    // 超时  
    private static final int DOWN_OK = 1;  
    private static final int DOWN_ERROR = 0;  
  
    private String appName;  
  
    private NotificationManager notificationManager; // 通知管理器  
    private Notification notification; // 通知  
  
    private Intent updateIntent;  
    private PendingIntent pendingIntent;  
  
    private int notification_id = 1;  
  
    RemoteViews contentView;  
  
    @Override  
    public IBinder onBind(Intent arg0) {  
        return null;  
    }  
  
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        appName = intent.getStringExtra("appName");  
        // 创建文件  
        FileUtil.createFile(getString(R.string.app_name)); // 这里就是根据应用名在存储目录创建一个appname.apk文件  
        createNotification();  
        startUpdate();  
          
        return super.onStartCommand(intent, flags, startId);  
    }  
      
    /*** 
     * 创建通知栏 
     */  
    public void createNotification() {  
        /* 
         * 自定义Notification视图 
         */  
        contentView = new RemoteViews(getPackageName(), R.layout.notification_item);  
        contentView.setTextViewText(R.id.notificationTitle, appName + "—" +getString(R.string.soft_update_progress));  
        contentView.setTextViewText(R.id.notificationPercent, "0%");  
        contentView.setProgressBar(R.id.notificationProgress, 100, 0, false);  
          
        updateIntent = new Intent(this, MainActivity.class);  
        updateIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);  
        pendingIntent = PendingIntent.getActivity(this, 0, updateIntent, 0);  
          
        // 初始化通知  
        notification = new Notification(R.drawable.ic_logo,  
                getString(R.string.soft_down_start) + " " + appName, System.currentTimeMillis());  
        notification.flags |= Notification.FLAG_NO_CLEAR;  
        notification.defaults |= Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE; // 设置通知铃声和振动提醒  
        notification.contentView = contentView;  
        notification.contentIntent = pendingIntent;  
        // 发送通知  
        notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);  
        notificationManager.notify(notification_id, notification);  
        // 清除通知铃声  
        notification.defaults = 0;  
    }  
      
    /*** 
     * 开启线程下载更新 
     */  
    public void startUpdate() {  
        final Handler handler = new Handler() {  
            @Override  
            public void handleMessage(Message msg) {  
                // 添加通知声音  
                notification.defaults |= Notification.DEFAULT_SOUND;  
                switch (msg.what) {  
                case DOWN_OK:  
                    // 下载完成,点击安装  
                    Uri uri = Uri.fromFile(FileUtil.updateFile);  
                    // 安装应用意图  
                    Intent intent = new Intent(Intent.ACTION_VIEW);  
                    intent.setDataAndType(uri, "application/vnd.android.package-archive");  
  
                    pendingIntent = PendingIntent.getActivity(UpdateService.this, 0, intent, 0);  
  
                    notification.tickerText = appName + " " + getString(R.string.soft_down_complete);  
                    notification.setLatestEventInfo(UpdateService.this,  
                            appName, getString(R.string.soft_down_ok), pendingIntent);  
                      
                    break;  
                case DOWN_ERROR:  
                    notification.tickerText = appName + " " + getString(R.string.soft_down_error);  
                    notification.setLatestEventInfo(UpdateService.this,  
                            appName, getString(R.string.soft_down_error), pendingIntent);  
                }  
                notification.flags = Notification.FLAG_AUTO_CANCEL;  
                notificationManager.notify(notification_id, notification);  
                stopService(updateIntent);  
                stopSelf();  
            }  
        };  
        // 启动线程下载更新  
        new Thread(new Runnable(){  
            @Override  
            public void run() {  
                try {  
                    downloadUpdateFile(Global.UPDATE_URL, FileUtil.updateFile.toString(), handler);  
                } catch (Exception e) {  
                    e.printStackTrace();  
                    handler.sendMessage(handler.obtainMessage(DOWN_ERROR));  
                }  
            }  
        }).start();  
    }  
      
    /*** 
     * 下载文件 
     *  
     * @return 
     * @throws MalformedURLException 
     */  
    public void downloadUpdateFile(String down_url, String file, Handler handler)  
            throws Exception {  
        int totalSize;  // 文件总大小  
        InputStream inputStream;  
        FileOutputStream outputStream;  
  
        URL url = new URL(down_url);  
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();  
        httpURLConnection.setConnectTimeout(TIMEOUT);  
        httpURLConnection.setReadTimeout(TIMEOUT);  
        // 获取下载文件的size  
        totalSize = httpURLConnection.getContentLength();  
        if (httpURLConnection.getResponseCode() == 404) {  
            throw new Exception("fail!");  
        }  
        inputStream = httpURLConnection.getInputStream();  
        outputStream = new FileOutputStream(file, false);  
          
        // 异步任务开始下载  
        new UpdateAsyncTask(inputStream, outputStream, handler).execute(totalSize);  
    }  
      
    private class UpdateAsyncTask extends AsyncTask<Integer, Integer, Boolean> {  
          
        private InputStream in;  
          
        private FileOutputStream fos;  
          
        private Handler handler;  
          
        public UpdateAsyncTask(InputStream inputStream, FileOutputStream outputStream, Handler handler) {  
            super();  
            in = inputStream;  
            fos = outputStream;  
            this.handler = handler;  
        }  
  
        @Override  
        protected Boolean doInBackground(Integer... params) {  
            int totalSize = params[0];  // 下载总大小  
            int downloadCount = 0;  // 已下载大小  
            int updateProgress = 0; // 更新进度  
            int updateStep = 5; // 更新进度步进  
              
            byte buffer[] = new byte[1024];  
            int readsize = 0;  
            try {  
                while ((readsize = in.read(buffer)) != -1) {  
                    fos.write(buffer, 0, readsize);  
                    // 计算已下载到的大小  
                    downloadCount += readsize;  
                    // 先计算已下载的百分比,然后跟上次比较是否有增加,有则更新通知进度  
                    int now = downloadCount * 100 / totalSize;  
                    if (updateProgress < now) {  
                        updateProgress = now;  
                        Log.d(TAG, "update: " + updateProgress + "%");  
                        publishProgress(updateProgress);  
                    }  
                }  
            } catch (Exception e) {  
                Log.d(TAG, "download err===>\n");  
                e.printStackTrace();  
                return false;  
            } finally {  
                try {  
                    fos.close();  
                    in.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
            return true;  
        }  
          
        @Override  
        protected void onProgressUpdate(Integer... values) {  
            int progress = values[0];  
            // 改变通知栏  
            contentView.setTextViewText(R.id.notificationPercent, progress + "%");  
            contentView.setProgressBar(R.id.notificationProgress, 100, progress, false);  
            // show_view  
            notificationManager.notify(notification_id, notification);  
        }  
  
        @Override  
        protected void onPostExecute(Boolean result) {  
            if (result) {  
                handler.sendMessage(handler.obtainMessage(DOWN_OK)); // 通知handler已经下载完成  
            } else {  
                handler.sendMessage(handler.obtainMessage(DOWN_ERROR)); // 通知handler下载出错  
            }  
            super.onPostExecute(result);  
        }  
  
    }  
      
    /** 
     * 检查版本信息 
     */  
    public static void checkVersion(final Context context, final Handler msgHandler) {  

        final Handler promptHandler = new Handler() {  
            @Override  
            public void handleMessage(Message msg) {  
                new AlertDialog.Builder(context).setTitle("应用升级")  
                        .setMessage("发现新版本,要立即更新吗?")  
                        .setPositiveButton("更新", new DialogInterface.OnClickListener() {  
                            @Override  
                              public void onClick(DialogInterface dialog, int which) {  
                                 startUpdateService(context);  
                             }  
                         })  
                         .setNegativeButton("以后再说", new DialogInterface.OnClickListener() {  
                             @Override  
                              public void onClick(DialogInterface dialog, int which) {  
                                  dialog.dismiss();  
                              }  
                         ).show();  
            }  
        };  
        new Thread() {  
            @Override  
            public void run() {  
                getServiceVersion();  
                if (msgHandler != null)  
                    msgHandler.sendMessage(msgHandler.obtainMessage(1));  
                if (Global.localVersion < Global.serverVersion) {  
                    // 提示服务器有新版本,需要更新  
                    promptHandler.sendMessage(promptHandler.obtainMessage(1));  
                }  
            }  
        }.start();  
    }  
    
    /** 
     * 启动升级服务下载升级 
     */  
    public static void startUpdateService(Context context) {  
        Intent intent = new Intent(context, UpdateService.class);  
        intent.putExtra("appName", context.getString(R.string.app_name));  
        context.startService(intent);  
    }  
    
    /*** 
     * 获取服务器端的serverVersion.
     */  
    public static void getServiceVersion() {  
        HttpClient client = new DefaultHttpClient();  
        HttpGet get = new HttpGet(Global.UPDATE_VERSION_URL);  
        HttpResponse response = null;  
        try {  
            response = client.execute(get);  
        } catch (Exception e) {  
            Log.e(TAG, "get Service Version err==>\n");  
            e.printStackTrace();  
            client.getConnectionManager().closeExpiredConnections();  
        }  
        if (response != null) {  
            if (response.getStatusLine().getStatusCode() == 200) {  
                HttpEntity entity = response.getEntity();  
                if (entity.isStreaming()) {  
                    try {  
                        InputStream in = entity.getContent();  
                        Map<String, String> map = parseXml(in);  
                        if (map != null) {  
                            Global.serverVersion = Integer.parseInt((String) map.get("version"));  
                        }  
                    } catch (Exception e) {  
                        e.printStackTrace();  
                    }  
                }  
            }  
        }  
    }  
      
    /** 
     * 解析xml数据 
     * @param in 
     */  
    public static Map<String, String> parseXml(InputStream in) {  
        // 这里是通过流读取xml文件,然后解析出一个Map出来,当然,这时候我们需要的是 version  
    }  
      
}  

以上用了4个static方法来完成更新下载功能的启动,或者抽出来写在别的地方也可以啦

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingBottom="5dp"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    android:paddingTop="5dp" >

    <ImageView
        android:id="@+id/notificationImage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:contentDescription="@null"
        android:src="@android:drawable/stat_sys_download" />

    <TextView
        android:id="@+id/notificationTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_marginLeft="3dp"
        android:layout_toRightOf="@id/notificationImage"
        android:gravity="left"
        android:paddingLeft="8dp"
        android:paddingRight="8dp"
        android:textColor="#FFFFFF" />

    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@id/notificationTitle"
        android:layout_alignParentRight="true"
        android:layout_below="@id/notificationTitle"
        android:layout_marginTop="3dp"
        android:paddingLeft="8dp"
        android:paddingRight="8dp" >

        <ProgressBar
            android:id="@+id/notificationProgress"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingLeft="3dp"
            android:paddingRight="3dp"
            android:paddingTop="2dp" />

        <TextView
            android:id="@+id/notificationPercent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:textColor="#FFFFFF" />
    </FrameLayout>

</RelativeLayout>

这个就是通知的布局了


对了,自定义Service一定要在清单文件里的Application节点下注册进去

<service android:name=".service.UpdateService"></service>

启动检查的例子:点击检查更新的组件

public class PreferencesActivity extends Activity {
	
	//..............
	
	/*
	 * 检查更新
	 */
	public void checkUpdate(View v) {
		progressUpdate.setVisibility(0); // 显示进度圈圈
		UpdateService.checkVersion(this, new Handler() {
			@Override
			public void handleMessage(Message msg) {
				progressUpdate.setVisibility(View.GONE); // 获取到serverCode后,隐藏进度
			}
		});
	}
	
}



效果如下


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值