APK静默安装与更新

来源:互联网 发布:ims数据 编辑:程序博客网 时间:2024/06/16 04:29

在平时开发中,很常见的功能就是一个版本发布了,在进入主页面之前会对本地版本和服务器版本对比,发现新版本就会直接弹出对话框然用户选择是否要更新,实现逻辑代码如下,建立一个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();}}};@Overrideprotected 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);//发送消息给handlerhandler.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() {@Overridepublic 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() {@Overridepublic 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;}@Overridepublic void run() {try {//获取路径和文件和dialogFile 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 {@Overrideprotected 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