APK静默安装与更新

在平时开发中,很常见的功能就是一个版本发布了,在进入主页面之前会对本地版本和服务器版本对比,发现新版本就会直接弹出对话框然用户选择是否要更新,实现逻辑代码如下,建立一个splash.xml,这个页面主要是作用是检测版本异同

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical|center_horizontal"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv_splash_version"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="280dip"
        android:text="版本号"
        android:textColor="#FF01b6f8"
        android:textSize="20sp" />

    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dip" />

</LinearLayout>

建立一个SplashActivity,这个类的主要作用是检测版本更新,请求服务器并解析返回结果,把版本抽取成一个对象

public class Info {
<span style="white-space:pre">	</span>//省去set/get和构造
	private String version;
	private String desc;
	private String apkurl;
}

public class SplashActivity extends Activity {
	private LinearLayout ll_main;
	private TextView tv_splash_version;
	private ProgressDialog pd;
	private String versiontext;
	private Info info;
	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			if (isNeedUpdate(versiontext)) {
				showUpdataDialog();
			}
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.splash);
		ll_main = (LinearLayout) findViewById(R.id.ll_main);
		tv_splash_version = (TextView) findViewById(R.id.tv_splash_version);
		pd = new ProgressDialog(this);

		pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		pd.setMessage("正在下载...");

		versiontext = getVersion();
		new Thread() {
			public void run() {
				//延迟2秒
				SystemClock.sleep(2000);
				//发送消息给handler
				handler.sendEmptyMessage(0);
			};
		}.start();

		tv_splash_version.setText(versiontext);
		AlphaAnimation aa = new AlphaAnimation(0.0f, 1.0f);
		aa.setDuration(2000);
		ll_main.startAnimation(aa);
		// 全屏显示
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
				WindowManager.LayoutParams.FLAG_FULLSCREEN);
	}

	// 更新提示框
	private void showUpdataDialog() {
		AlertDialog.Builder builder = new Builder(this);
		builder.setTitle("升级提示");
		builder.setMessage(info.getDesc());
		builder.setCancelable(false);
		builder.setPositiveButton("下载", new OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// 判断SD卡是否可用
				if (Environment.getExternalStorageState().equals(
						Environment.MEDIA_MOUNTED)) {
					DownloadFileThreadTask task = new DownloadFileThreadTask(
							info.getApkurl(), Environment
									.getExternalStorageDirectory() + "/new.apk");
					pd.show();
					//开启下载APK线程
					new Thread(task).start();

				} else {
					Toast.makeText(getApplicationContext(), "sd卡不可用", 1).show();
					loadMainUI();
				}
			}
		});
		builder.setNegativeButton("取消", new OnClickListener() {

			@Override
			public void onClick(DialogInterface dialog, int which) {
				//点击取消,也进入主页面
				loadMainUI();
			}
		});
		builder.create().show();
	}

	// 下载文件
	private class DownloadFileThreadTask implements Runnable {
		private String path;
		private String filePath;

		public DownloadFileThreadTask(String path, String filePath) {
			this.path = path;
			this.filePath = filePath;
		}

		@Override
		public void run() {
			try {
				//获取路径和文件和dialog
				File file = DownloadFileTask.getFile(path, filePath, pd);
				pd.dismiss();
				//下载完成就安装
				installAPK(file);
			} catch (Exception e) {
				pd.dismiss();
				e.printStackTrace();
				Toast.makeText(getApplicationContext(), "文件下载失败", 0).show();
				loadMainUI();
			}
		}
	}

	// 检测本地版本和服务器是否一致
	private boolean isNeedUpdate(String textversion) {
		HttpTools httpTools = new HttpTools(this);
		// 从服务器获取最新版本信息
		try {
			info = httpTools.getServerVersion(R.string.apkupdateurl);
			String version = info.getVersion();
			if (textversion.equals(version)) {
				loadMainUI();
				return false;
			} else {
				return true;
			}
		} catch (Exception e) {
			e.printStackTrace();
			Toast.makeText(this, "获取版本异常", 0).show();
			loadMainUI();
			return false;
		}
	};

	// 加载主页面
	private void loadMainUI() {
		startActivity(new Intent(SplashActivity.this, MainActivity.class));
		finish();
	}

	// 静默安装
	public void installAPK(File file) {
		Intent intent = new Intent();
		intent.setAction(Intent.ACTION_VIEW);
		intent.setDataAndType(Uri.fromFile(file),
				"application/vnd.android.package-archive");
		finish();
		startActivity(intent);
	}

	// 获取版本号
	public String getVersion() {
		PackageManager packageManager = getPackageManager();
		try {
			PackageInfo info = packageManager.getPackageInfo(getPackageName(),
					0);
			return info.versionName;
		} catch (NameNotFoundException e) {
			e.printStackTrace();
		}
		return "版本异常";
	}
}
在SplashActivity中,使用到了请求服务器数据并解析数据的HttpTools这个类,其中使用到了android内部提供的httpClient来解析服务器返回的json数据

public class HttpTools {
	private  Context context;

	public HttpTools(Context context) {
		this.context = context;
	}

	// 根据配置文件中的url获取json地址
	public Info getServerVersion(int uid) {
		Info info = null;
<span style="white-space:pre">		</span>//使用外部配置文件中的地址
		String path = context.getResources().getString(uid);
		HttpClient httpClient = new DefaultHttpClient();
		HttpGet httpGet = new HttpGet(path);
		HttpResponse httpResponse;
		JSONObject jsonObject;
		try {
			httpResponse = httpClient.execute(httpGet);
			if (httpResponse.getStatusLine().getStatusCode() == 200) {
				// {version:'2.0',desc:'fuck',apkurl:'http://xxx'}
				String json = EntityUtils.toString(httpResponse.getEntity(),
						"UTF-8");
				try {
					jsonObject = new JSONObject(json);
					for (int i = 0; i < jsonObject.length(); i++) {
						String version = jsonObject.getString("version");
						String apkurl = jsonObject.getString("apkurl");
						String desc = jsonObject.getString("desc");
						info = new Info(version, desc, apkurl);
					}
				} catch (JSONException e) {
					e.printStackTrace();
				}
			}
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}  
		return info;
	}
}
好了,基于上面的代码,大体功能已经完成一半了;继续ing....。完成上面代码之后,在SplashActivity中使用到了一个DownloadFileTask这个类,这个类主要是负责下载文件和更新进度条,写完代码之后才发现,冗余的代码很多,想用AsyncHtttpClient这个开源项目简化一下

public class DownloadFileTask {
	public static File getFile(String path, String filePath, ProgressDialog pd)
			throws Exception {
		URL url = new URL(path);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod("GET");
		conn.setConnectTimeout(5000);
		if (conn.getResponseCode() == 200) {
			int total = conn.getContentLength();
			pd.setMax(total);
			InputStream is = conn.getInputStream();
			File file = new File(filePath);
			FileOutputStream fos = new FileOutputStream(file);
			byte[] buffer = new byte[1024];
			int len = 0;
			int process = 0;
			while ((len = is.read(buffer)) != -1) {
				fos.write(buffer, 0, len);
				process += len;
				pd.setProgress(process);
				Thread.sleep(50);
			}
			fos.flush();
			fos.close();
			is.close();

			return file;
		}
		return null;
	}
}
到了这里,客户端的代码完成差不多了,最后别忘了一点,在values下见了一个config.xml文件,在这个文件里面动态的添加访问服务器的路径

<resources>
    <string name="apkupdateurl">http://192.168.4.112:8080/Server/Server</string>
</resources>
=====================我是分割线==============================

编写服务端的代码,在服务端用一个servlet来编写代码如下:

@WebServlet("/Server")
public class ServerServlet extends HttpServlet {
	@Override
	protected void service(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html;charset=utf-8");
		response.setCharacterEncoding("UTF-8");
		Info info = new Info();
		info.setVersion("2.0");
		info.setDesc("发现新版本,请更新哦(*^__^*) ");
		info.setApkurl("http://192.168.4.112:8080/Demo.apk");
		String json = JSONObject.toJSONString(info);
		response.getWriter().write(json);
	}
}

把需要添加的权限加入:

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

最后,记得把最新的版本放到服务器上,并且确保本地客户端的清单文件中的版本和服务器上的版本不同,才能弹出更新对话框和更新并且安装。








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值