Android apk版本更新

本文介绍了如何在Android中实现版本更新服务,当检测到新版本时,后台提示用户更新且不影响正常操作。主要步骤包括获取app当前版本号,与服务器版本比较,下载更新apk并自动升级。使用自定义对话框提升用户体验,同时需添加相应权限并注册Service。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说到android的版本更新,要写一个出来还是需要耗费一点时间的,这次我们来写一个版本更新服务,用户进入app后有新版本需要更新则提示用户确认更新,更新在后台运行,不影响用户的正常操作,用户体验相对较好。


Android版本更新的步骤:

1、获取当前app版本号,代码如下

public String getAppVersion() {
try {
PackageInfo packinfo = pm.getPackageInfo(context.getPackageName(),0);
return packinfo.versionName;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return "1.0";
}


2、从服务器获取版本号,并与当前版本进行对比,如果相同则不更新,如果服务器版本号大于当前版本号,弹出更新对话框,提示用户进行更新升级。弹出对话框今天就不在这里写了,这部分代码相对还是比较简单的,当然如果我们需要一个比较好看的对话框,系统提供的原生对话框是不能满足我们的需求的,需要自定义对话框,读者可以自己去研究这方面的代码。

3、用户确认进行版本更新后,将从服务器下载更新的apk并进行自动升级。此处就用到了我们今天说到的版本更新服务,具体代码如下,部分代码已经做了注释,就不详细说明了。

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreConnectionPNames;
import com.ygwaimai.manage.R;
import com.ygwaimai.manage.util.AppInfo;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.widget.RemoteViews;
import android.widget.Toast;

public class UpdateService extends Service {

	private NotificationManager nm;
	private Notification notification;
	private File tempFile = null;
	private boolean cancelUpdate = false;
	private MyHandler myHandler;
	private int download_precent = 0;
	private RemoteViews views;
	private int notificationId = 1234;

	private int appicon;

	public static final int TIME_OUT_MILL = 50000;// 超时毫秒数
	private static final String DOWNLOAD_APK_PATH = "APK_UPDATE";
	
	//更新进度标记
	private static final int UPDATE_DOWNLOAD_PRECENCE = 3;
	//下载完成标记
	private static final int DOWNLOAD_FINISH = 2;
	//下载APK失败标记
	private static final int DOWNLOAD_FAIL = 4;
	
	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
		notification = new Notification();
		notification.icon = android.R.drawable.stat_sys_download;
		// notification.icon=android.R.drawable.stat_sys_download_done;
		// notification.tickerText=getString(R.string.app_name)+"更新";
		notification.tickerText = "版本更新";
		notification.when = System.currentTimeMillis();
		notification.defaults = Notification.DEFAULT_LIGHTS;

		// 设置任务栏中下载进程显示的views
		views = new RemoteViews(getPackageName(), R.layout.update);
		notification.contentView = views;

		PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
				new Intent(), 0);
		notification.setLatestEventInfo(this, "", "", contentIntent);

		// 将下载任务添加到任务栏中
		nm.notify(notificationId, notification);

		myHandler = new MyHandler(Looper.myLooper(), this);

		// 初始化下载任务内容views,初始化进度为 0
		Message message = myHandler.obtainMessage(UPDATE_DOWNLOAD_PRECENCE, 0);
		myHandler.sendMessage(message);

		// 启动线程开始执行下载任务
		String url = intent.getStringExtra("DOWNLOAD_URL");
		downFile(url);
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public void onDestroy() {
		// 停止掉当前的服务
		stopSelf();
		super.onDestroy();
	}

	// 下载更新文件
	private void downFile(final String url) {
		new Thread() {
			public void run() {
				try {
					HttpClient client = new DefaultHttpClient();
					// params[0]代表连接的url
					System.out.println("下载url======" + url);
					HttpGet request = new HttpGet(url);
					HttpResponse response = client.execute(request);
					// 请求超时
					client.getParams().setParameter(
							CoreConnectionPNames.CONNECTION_TIMEOUT,
							TIME_OUT_MILL);
					// 读取超时
					client.getParams().setParameter(
							CoreConnectionPNames.SO_TIMEOUT, TIME_OUT_MILL);
					client.getParams().setIntParameter("http.socket.timeout",
							TIME_OUT_MILL);

					HttpEntity entity = response.getEntity();
					long length = entity.getContentLength();
					InputStream is = entity.getContent();
					if (is != null) {
						File rootFile = new File(
								Environment.getExternalStorageDirectory(),
								"/" + DOWNLOAD_APK_PATH);
						if (!rootFile.exists() && !rootFile.isDirectory())
							rootFile.mkdir();

						tempFile = new File(
								Environment.getExternalStorageDirectory(),

								"/"+DOWNLOAD_APK_PATH + "/"
										+ url.substring(url.lastIndexOf("/") + 1));
						if (tempFile.exists())
							tempFile.delete();
						tempFile.createNewFile();

						// 已读出流作为参数创建一个带有缓冲的输出流
						BufferedInputStream bis = new BufferedInputStream(is);

						// 创建一个新的写入流,讲读取到的图像数据写入到文件中
						FileOutputStream fos = new FileOutputStream(tempFile);
						// 已写入流作为参数创建一个带有缓冲的写入流
						BufferedOutputStream bos = new BufferedOutputStream(fos);

						int read;
						long count = 0;
						int precent = 0;
						byte[] buffer = new byte[1024];
						while ((read = bis.read(buffer)) != -1 && !cancelUpdate) {
							bos.write(buffer, 0, read);
							count += read;
							precent = (int) (((double) count / length) * 100);

							// 每下载完成5%就通知任务栏进行修改下载进度
							if (precent - download_precent >= 5) {
								download_precent = precent;
								Message message = myHandler.obtainMessage(UPDATE_DOWNLOAD_PRECENCE,precent);
								myHandler.sendMessage(message);
							}
						}
						bos.flush();
						bos.close();
						fos.flush();
						fos.close();
						is.close();
						bis.close();
					}

					if (!cancelUpdate) {
						Message message = myHandler.obtainMessage(DOWNLOAD_FINISH, tempFile);
						myHandler.sendMessage(message);
					} else {
						tempFile.delete();
					}
				} catch (ClientProtocolException e) {
					e.printStackTrace();
					Message message = myHandler.obtainMessage(DOWNLOAD_FAIL, "下载更新文件失败");
					myHandler.sendMessage(message);
				} catch (IOException e) {
					e.printStackTrace();
					Message message = myHandler.obtainMessage(DOWNLOAD_FAIL, "下载更新文件失败");
					myHandler.sendMessage(message);
				} catch (Exception e) {
					e.printStackTrace();
					Message message = myHandler.obtainMessage(DOWNLOAD_FAIL, "下载更新文件失败");
					myHandler.sendMessage(message);
				}
			}
		}.start();
	}

	/**
	 * 安装下载后的apk文件
	 * @param file
	 * @param context
	 */
	private void Instanll(File file, Context context) {
		Intent intent = new Intent(Intent.ACTION_VIEW);
		intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		intent.setAction(android.content.Intent.ACTION_VIEW);
		intent.setDataAndType(Uri.fromFile(file),
				"application/vnd.android.package-archive");
		context.startActivity(intent);
	}

	/* 事件处理类 */
	class MyHandler extends Handler {
		private Context context;

		public MyHandler(Looper looper, Context c) {
			super(looper);
			this.context = c;
			// 获取应用图标
			AppInfo appinfo = new AppInfo(c);
			appicon = appinfo.getAppIcon2();
		}

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			if (msg != null) {
				switch (msg.what) {
				
				case DOWNLOAD_FINISH:

					// 下载完成后清除所有下载信息,执行安装提示
					download_precent = 0;
					nm.cancel(notificationId);
					Instanll((File) msg.obj, context);

					// 停止掉当前的服务
					stopSelf();
					break;
				case UPDATE_DOWNLOAD_PRECENCE:

					// 更新状态栏上的下载进度信息
					views.setImageViewResource(R.id.ivLogo, appicon);
					views.setTextViewText(R.id.tvProcess, "已下载"
							+ download_precent + "%");
					views.setProgressBar(R.id.pbDownload, 100,
							download_precent, false);
					notification.contentView = views;
					nm.notify(notificationId, notification);
					break;
				case DOWNLOAD_FAIL:
					nm.cancel(notificationId);
					break;
				}
			}
		}
	}

}

Notification使用到的布局文件update.xml文件

<?xml version="1.0" encoding="utf-8"?>
   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"
       android:orientation="vertical"
       android:background="#f2f2f2" >
       <RelativeLayout
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:orientation="horizontal">
          <ImageView
              android:id="@+id/ivLogo"
              android:layout_width="45dip"
              android:layout_height="45dip"
              android:layout_margin="10dip"
              android:layout_alignParentLeft="true" 
              android:layout_centerVertical="true"/>
          <LinearLayout
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:orientation="vertical"
              android:layout_toRightOf="@+id/ivLogo"
              android:layout_marginRight="10dip"
              android:layout_alignParentRight="true" 
              android:layout_centerVertical="true">
              <TextView
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:textColor="@color/textgray"
                  android:text="版本更新"
                  android:textSize="18dip"
                  android:textStyle="bold"/>
              <ProgressBar
                  android:id="@+id/pbDownload"
                  android:layout_width="fill_parent"
                  android:layout_height="12dip"
                  android:progress="0"
                  android:max="100"
                  style="?android:attr/progressBarStyleHorizontal" />
              <TextView
                  android:id="@+id/tvProcess"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:textColor="#6a6a6a"
                  android:text="已下载0%"
                  android:textSize="12dip"
                 android:textStyle="bold" />
         </LinearLayout>
      </RelativeLayout>

    </LinearLayout>


用到的工具类AppInfo.java

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;

public class AppInfo {
	private Context context;
	private PackageManager pm;

	public AppInfo(Context context) {
		this.context = context;
		pm = context.getPackageManager();
	}

	/*
	 * 获取程序 图标
	 */
	public Drawable getAppIcon() {
		Drawable dr = null;
		try {
			ApplicationInfo info = pm.getApplicationInfo(
					context.getPackageName(), 0);
			dr = info.loadIcon(pm);
		} catch (NameNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();

		}
		return dr;
	}

	/*
	 * 获取程序 图标
	 */
	public int getAppIcon2() {
		int icon = 0;
		try {
			ApplicationInfo info = pm.getApplicationInfo(
					context.getPackageName(), 0);
			icon = info.icon;
		} catch (NameNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();

		}
		return icon;
	}

	/*
	 * 获取程序的版本号
	 */
	public String getAppVersion() {

		try {
			PackageInfo packinfo = pm.getPackageInfo(context.getPackageName(),
					0);
			return packinfo.versionName;
		} catch (NameNotFoundException e) {
			e.printStackTrace();

		}
		return "1.0";
	}

	/*
	 * 获取程序的名字
	 */
	public String getAppName(String packname) {
		try {
			ApplicationInfo info = pm.getApplicationInfo(packname, 0);
			return info.loadLabel(pm).toString();
		} catch (NameNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();

		}
		return null;
	}

	/*
	 * 获取程序的权限
	 */
	public String[] getAppPremission(String packname) {
		try {
			PackageInfo packinfo = pm.getPackageInfo(packname,
					PackageManager.GET_PERMISSIONS);
			// 获取到所有的权限
			return packinfo.requestedPermissions;

		} catch (NameNotFoundException e) {
			e.printStackTrace();

		}
		return null;
	}

	/*
	 * 获取程序的签名
	 */
	public String getAppSignature(String packname) {
		try {
			PackageInfo packinfo = pm.getPackageInfo(packname,
					PackageManager.GET_SIGNATURES);
			// 获取到所有的权限
			return packinfo.signatures[0].toCharsString();

		} catch (NameNotFoundException e) {
			e.printStackTrace();

		}
		return null;
	}

	public String getAppPackageName() {
		PackageInfo info;
		try {
			info = context.getPackageManager().getPackageInfo(
					context.getPackageName(), 0);
			// 当前应用的版本名称
			String versionName = info.versionName;
			// 当前版本的版本号
			int versionCode = info.versionCode;
			// 当前版本的包名
			String packageNames = info.packageName;
			return packageNames;
		} catch (NameNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}

}


在Activity中使用,我们只要传入版本更新的url就可以了,相关的下载和安装操作都在UpdateService 里面实现了,
用起来是不是很简单。

Intent intent = new Intent(ctx,UpdateService.class);
intent.putExtra("DOWNLOAD_URL", version_url);
startService(intent);

最后不要忘了在配置文件在AndroidManifest.mxl中中添加网络和SD卡读写权限,还有注册Service

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

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

<service android:name ="com.ygwaimai.manage.service.UpdateService" />

这样一个简单的版本更新服务就写好了,当然这只是比较简单的实现,具体做起来我们还要考虑很多问题,这只能看具体需求了,比如我们还可以加一些逻辑,判断sd卡里面有没有apk安装包,并和当前版本比较,看是否已经下载有新版本了,我们没有安装,这时就不必要再去下载一次了,浪费时间还浪费流量。还可以在下载的部分添加增量更新功能,这是现在比较流行的一种更新方法,这部分内容比较多,今天就不在这里介绍了,有兴趣的朋友可以自己去研究,一个版本更新的功能要做得比较好还是需要蛮多东西要考虑的,这篇文章就写到这里,夜了,晚安。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值