【边做项目边学Android】手机安全卫士03:获取更新的服务器配置,显示更新对话框
来源:互联网 发布:电力造价软件 编辑:程序博客网 时间:2024/06/06 00:45
配置应用程序在手机桌面显示的名称和图标-AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.liuhao.mobilesafe" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher"《在程序管理列表中显示的图标》 ① android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:icon="@drawable/icon5"《在桌面显示为自己配置的icon5图标》 ② android:name="com.liuhao.mobilesafe.ui.SplashActivity" android:label="@string/app_name" >《名称为自己配置的app_name》 ② <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>配置后,显示如图:
① ②
获取更新的服务器配置流程:
服务器配置:
以tomcat作为服务器,在TOMCAT_HOME/ROOT目录下新建update.xml文件,在其中添加新版本的相关信息;
<?xml version="1.0" encoding="utf-8"?><info> <version>2.0</version> <description>亲,最新的版本,速度来下载!</description> <apkurl>http://localhost:18081/newapk.apk</apkurl></info>在浏览器访问:http://localhost:18081/update.xml
xml配置文件的获取和解析
那么我们的应用程序启动时就要尝试到上述地址获取新版本的信息,同时对xml配置文件进行解析。
那么应用程序如何获取到上述的版本信息的地址呢?一般在资源文件中以配置文件的方式存储。
config.xml<?xml version="1.0" encoding="utf-8"?><resources> <string name="updateurl">http://192.168.1.123:18081/update.xml</string></resources>
下面要建立update.xml文件对应的实体类-UpdateInfo.java:
package com.liuhao.mobilesafe.domain;/*** @author liuhao* 升级信息*/public class UpdateInfo { String version; String description; String apkurl; public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getApkurl() { return apkurl; } public void setApkurl(String apkurl) { this.apkurl = apkurl; }}
如何获取这个config.xml里url对应的文件内容(即http://192.168.1.123:18081/update.xml)?
新建更新信息服务类:UpdateInfoService.java:
package com.liuhao.mobilesafe.engine;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.URL;import android.content.Context;import com.liuhao.mobilesafe.domain.UpdateInfo;public class UpdateInfoService { private Context context; // 应用程序环境的上下文信息 public UpdateInfoService(Context context) { this.context = context; } /** * @param urlId * 服务器资源路径对应的id * @return 更新信息 * @throws Exception */ public UpdateInfo getUpdateInfo(int urlId) throws Exception { String path = context.getResources().getString(urlId);// 根据urlId获取资源文件中对应的内容 URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(2000); conn.setRequestMethod("GET"); InputStream is = conn.getInputStream(); //得到url对应的文件流,应该是xml文件流,需要对其进行解析 return UpdateInfoParser.getUpdateInfo(is); }}
- 知识点:为什么在业务类中不对异常进行捕获,而是直接抛出了?
向外传播给更高层处理,以便异常的错误原因不丢失,便于排查错误或进行捕获处理。对于异常处理,应该从设计、需要、维护等多个角度综合考虑,有一个通用准则:千万别捕获了异常什么事情都不干,这样一旦出现异常了,你没法依据异常信息来排错。
见:J2EE系统异常的处理准则
解析xml文件:
获取到xml文件流后,要对其进行解析,使用XmlPullParser:
XmlPullParser将xml分解成不同的事件类型(EventType)
常用的有:
XmlPullParser.END_DOCUMENT:文档的结束
XmlPullParser.START_DOCUMENT:文档的开始
XmlPullParser.START_TAG:标签的开始
XmlPullParser.END_TAG:标签的结束
XmlPullParser.TEXT :内容
并且该类中的方法主要是用于获取EventType的内容,以及在EventType之间进行跳转。
创建解析更新信息的工具服务类UpdateInfoParser:
package com.liuhao.mobilesafe.engine;import java.io.InputStream;import org.xmlpull.v1.XmlPullParser;import android.util.Xml;import com.liuhao.mobilesafe.domain.UpdateInfo;public class UpdateInfoParser { /** * @param is xml格式的文件输入流 * @return 解析好的UpdateInfo */ public static UpdateInfo getUpdateInfo(InputStream is) throws Exception{ XmlPullParser parser = Xml.newPullParser(); UpdateInfo info = new UpdateInfo(); // 初始化parser解析器,设置准备对哪个输入流进行解析 // 这个方法会对parser进行重置,同时会将事件类型(event type)定位到文档初始位置(START_DOCUMENT) parser.setInput(is, "utf-8"); int type = parser.getEventType(); //获取当前的EventType while(type != XmlPullParser.END_DOCUMENT){ switch (type) { // 对其中的标签类型进行处理 case XmlPullParser.START_TAG: if("version".equals(parser.getName())){ String version = parser.nextText(); info.setVersion(version); } else if("description".equals(parser.getName())){ String description = parser.nextText(); info.setDescription(description); } else if("apkurl".equals(parser.getName())){ String apkurl = parser.nextText(); info.setApkurl(apkurl); } break; } type = parser.next(); } return info; } }
测试
不知道Android如何测试?
1、新建一个Android Test Project,将我们的项目放在测试项目中。
2、将test项目中AndroidManifest.xml的<uses-library android:name="android.test.runner" />内容和<instrumentation>节点下的内容拷贝到项目的AndroidManifest.xml中,注意节点的对应。
之后,test项目便可以暂时不用了。
3、创建测试类
package com.liuhao.mobilesafe.test;import junit.framework.Assert;import com.liuhao.mobilesafe.R;import com.liuhao.mobilesafe.domain.UpdateInfo;import com.liuhao.mobilesafe.engine.UpdateInfoService;import android.test.AndroidTestCase;public class TestGetUpdateInfo extends AndroidTestCase { public void testGetInfo() throws Exception{ UpdateInfoService service = new UpdateInfoService(getContext()); UpdateInfo info = service.getUpdateInfo(R.string.updateurl); Assert.assertEquals("2.0", info.getVersion()); } }
4、从服务器上获取更新信息的配置文件,需要程序有访问Internet的权限:
保存,即可。
5、运行测试代码:
出现异常!!!connect failed: ECONNREFUSED (Connection refused)
异常处理:java.net.ConnectException
android 从tomcat读取文件时出现以下异常:
08-10 14:53:09.118: W/System.err(12527): java.net.ConnectException: failed to connect to localhost/127.0.0.1 (port 8080): connect failed: ECONNREFUSED (Connection refused)
解决方法:
String url = "http://localhost:18081/update.xml"; 修改成 String url = "http://192.168.1.123:18081/update.xml";
主机ip不能使用localhost或者127.0.0.1,使用本机真实ip地址即可。使用ipconfig命令就可以查看到:
异常处理后,运行成功!
在activity使用业务
所有的业务代码已经完成,回到splash的activity使用业务!
package com.liuhao.mobilesafe.ui;import com.liuhao.mobilesafe.R;import com.liuhao.mobilesafe.domain.UpdateInfo;import com.liuhao.mobilesafe.engine.UpdateInfoService;import android.os.Bundle;import android.app.Activity;import android.app.AlertDialog;import android.app.AlertDialog.Builder;import android.content.DialogInterface;import android.content.DialogInterface.OnClickListener;import android.content.pm.PackageInfo;import android.content.pm.PackageManager;import android.util.Log;import android.view.Menu;import android.view.Window;import android.view.WindowManager;import android.view.animation.AlphaAnimation;import android.widget.LinearLayout;import android.widget.TextView;import android.widget.Toast;public class SplashActivity extends Activity {private static final String TAG = "SplashActivity";private TextView tv_splash_version;private LinearLayout ll_splash_main;private UpdateInfo info; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //取消标题栏 requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.splash); tv_splash_version = (TextView) this.findViewById(R.id.tv_splash_version); ll_splash_main = (LinearLayout) this.findViewById(R.id.ll_splash_main); String versiontext = getVersion(); tv_splash_version.setText(versiontext); if(isNeedUpdate(versiontext)){ Log.i(TAG, "弹出升级对话框"); showUpdateDialog(); } /* AlphaAnimation类:透明度变化动画类 * AlphaAnimation类是Android系统中的透明度变化动画类,用于控制View对象的透明度变化,该类继承于Animation类。 * AlphaAnimation类中的很多方法都与Animation类一致,该类中最常用的方法便是AlphaAnimation构造方法。 * * public AlphaAnimation (float fromAlpha, float toAlpha)参数说明fromAlpha:开始时刻的透明度,取值范围0~1。toAlpha:结束时刻的透明度,取值范围0~1。 */ AlphaAnimation aa = new AlphaAnimation(0.0f, 1.0f); aa.setDuration(2000); //Animation类的方法,设置持续时间 ll_splash_main.startAnimation(aa); //设置动画 //完成窗体的全屏显示 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } private void <span style="color:#FF6666;">showUpdateDialog</span>() { //弹出一个消息框 <span style="color:#FF0000;">AlertDialog.Builder builder = new Builder(this);</span> builder.setIcon(R.drawable.icon5); //设置消息框的标题图标 builder.setTitle("升级提醒"); //设置消息框的标题 builder.setMessage(info.getDescription()); //设置要显示的内容 builder.setCancelable(false); //让用户不能按后退键取消 builder.<span style="color:#FF6666;">setPositiveButton</span>("确定", new OnClickListener() { //设置用户选择确定时的按键操作@Overridepublic void onClick(DialogInterface dialog, int which) {Log.i(TAG, "下载pak文件:" + info.getApkurl());}}); builder.setNegativeButton("取消", new OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {Log.i(TAG, "用户取消升级,进入程序主界面");}}); builder.create().show(); }/** * * @param versiontext 当前客户端的版本信息 * @return 是否需要更新 */ private boolean isNeedUpdate(String versiontext) { UpdateInfoService service = new UpdateInfoService(this); try {info = service.getUpdateInfo(R.string.updateurl);String version = info.getVersion();if(versiontext.equals(version)){Log.i(TAG, "版本号相同,无需升级,进入到主界面");return false;}else{Log.i(TAG, "版本号不同,需要升级");return true;}} catch (Exception e) {e.printStackTrace();/** * Toast使用场景 * 1、需要提示用户,但又不需要用户点击“确定”或者“取消”按钮。 * 2、不影响现有Activity运行的简单提示。 */Toast.makeText(this, "获取更新信息异常", 2).show();//弹出文本,并保持2秒Log.i(TAG, "获取更新信息异常,进入到主界面");return false;} }@Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.splash, menu); return true; } /** * 获取当前程序的版本号 * @return */ private String getVersion(){ // 获取一个PackageManager的实例,从而可以获取全局包信息 PackageManager manager = getPackageManager(); try { // Retrieve overall information about an application package that is installed on the system.PackageInfo info = manager.getPackageInfo(getPackageName(), 0);// The version name of this package, as specified by the <manifest> tag's versionName attribute.return info.versionName;} catch (Exception e) {e.printStackTrace();return "版本号未知";} } }
- isNeedUpdate()方法:调用UpdateInfoService 的getUpdateInfo()方法,来获取更新信息。同时将服务器端的版本号和当前客户端的版本号进行对比,并做出是否让用户升级的操作。若发现两个版本号不一致,那么就要提醒用户进行升级:
- 这里调用了showUpdateDialog()方法,在这个方法中,设置界面弹出一个消息框,其中有两个按钮:“确定”“取消”,用户点击不同的按钮则对应不同的操作。
异常处理android.os.NetworkOnMainThreadException--多线程问题
一切搞定,以为高枕无忧了,结果还是有问题!
log开始报错了,获取更新信息异常!!!debug一下,发现Exception:android.os.NetworkOnMainThreadException
这个异常大概意思是在主线程访问网络时出的异常。 Android在4.0之前的版本 支持在主线程中访问网络,但是在4.0以后对这部分程序进行了优化,也就是说访问网络的代码不能写在主线程中了。
处理方法:http://blog.csdn.net/bruce_6/article/details/39640587
- 【边做项目边学Android】手机安全卫士03:获取更新的服务器配置,显示更新对话框
- 【边做项目边学Android】手机安全卫士06-手机防盗之自定义对话框
- 【边做项目边学Android】手机安全卫士08-一些布局和显示的细节:State List
- 手机安全卫士开发系列(3)——获取更新的服务器配置
- 【边做项目边学Android】手机安全卫士04_02:从服务器下载并安装新版本安装包
- 【边做项目边学Android】手机安全卫士07-手机防盗之进入限制
- 【边做项目边学Android】手机安全卫士09-手机防盗界面设置向导1
- 【边做项目边学Android】手机安全卫士04_01:界面(Activity)之间的切换,Activity和任务栈
- 【边做项目边学Android】手机安全卫士02:splash界面ui
- 【边做项目边学Android】手机安全卫士05_1:程序主界面
- 【边做项目边学Android】手机安全卫士10-设置向导之绑定SIM卡
- 【边做项目边学Android】手机安全卫士11-设置向导之设置安全号码
- 【边做项目边学Android】手机安全卫士05_2:程序主界面,为每个条目添加事件
- 手机安全卫士开发系列(4)——显示更新对话框
- 【边做项目边学Android】知识点:Android控件系列之对话框AlertDialog.Builder
- Android项目:手机安全卫士(15)—— 获取手机安装应用与存储空间
- android应用程序安全卫士——2、通过服务器xml文件,判断app是否需要更新
- Android小项目实战:手机安全卫士
- Tomcat中Context的配置
- Java Study@2014-09-28
- 调用相机拍照和图册中的图片并且对图片进行裁剪
- 【边做项目边学Android】知识点:Android控件系列之Toast
- Docker源码分析(一):Docker架构
- 【边做项目边学Android】手机安全卫士03:获取更新的服务器配置,显示更新对话框
- Android APK 反编译详解(附图)
- Windows 2003全面优化
- Hadoop ecosystem自己的理解
- vectour向量容器的基本使用
- 全自动软化水设备:全自动软化水设备工作原理分析
- 【iOS开发-21】UINavigationController导航控制器初始化,导航控制器栈的push和pop跳转理解
- 全自动软化水设备:全自动软化水设备自身优化能力简介
- 微信、陌陌的架构方案分析(LBS之二)