.

作者 :万境绝尘 

转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/18964835

.

软件的自动更新一般都与Splash界面绑定在一起, 由于需要维护的软件界面很复杂, 一个Activity中嵌入ViewPager, 并且逻辑比较复杂, 索性重新写一个Activity, 现在的软件都很流行使用Splash界面, 正好与自动更新配套在一起;

在这个自动更新Splash中, 使用到了 动画设置 ,SharedPerference ,pull解析 ,dialog对话框 ,http网络编程 ,handler 等.

注意一个错误已安装具有该名称和不同签名的数据包 , 早上测试人员报告突然出现这个问题, 在开发的时候我直接将eclipse上编译的版本放到了服务器上, 最后出现了这个问题, 开发的时候明明是好的啊, 怎么测试的时候出问题了呢.

编译环境不同, 产生的签名是不一样的, 在eclipse上编译生成 与 正式版本在linux下编译 所产生的 数字签名 是不一样的.

又发现一个BUG : 在弹出更新对话框, 点击确定下载完毕之后会弹出系统自带的替换应用程序对话框, 在这里点取消的话就会一直卡在Splash界面. 设置一个跳转机制解决这个问题.

解决方案 :利用触摸划屏事件, 向左侧划屏100px, 就自动跳转到主界面 , 最后的最终代码已经加上去了

 /***    设置触摸事件* 在手指按下时记录x坐标值 , 在手指抬起的时候记录x坐标值 , 如果两个值相差超过100*   那么跳转到主界面 * @see android.app.Activity#onTouchEvent(android.view.MotionEvent)*/@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN :touchPositionX0 = (int) event.getX();break;case MotionEvent.ACTION_UP :touchPositionX1 = (int) event.getX();if((touchPositionX0 - touchPositionX1) > 100)loadMainUI();touchPositionX0 = 0;touchPositionX1 = 0;break;}return true;}

一. 创建Activity

1. 创建Activity大概流程

a. 设置全屏显示.

b. 设置布局, 并在布局中显示当前版本号, 为Splash界面添加动画.

c. 获取当前时间.

d. 获取SharedPerence配置文件.

e. 开启检查版本号线程, 后续的操作都在这个线程中执行.

2. 设置窗口样式

(1) 设置全屏显示

a. 代码实现 : 由于是Splash界面, 这里需要设置成无标题, 并且全屏显示, 注意下面的两行代码需要在setContentView()方法之前调用;

        //隐藏标题栏requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏状态栏getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

b. 配置实现 :

AndroidManifest.xml
<activity android:name="myAcitivty"  android:theme="@android:style/Theme.NoTitleBar.Fullscreen" />

(2) 关于窗口的其它设置

//①设置窗体始终点亮
getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
//②设置窗体始终点亮
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

设置窗体始终点亮的配置文件实现

//③AndroidManifest.xml添加权限
<uses-permission android:name="android.permission.WAKE_LOCK" />
//设置窗体背景模糊
getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,WindowManager.LayoutParams.FLAG_BLUR_BEHIND);

(3) 屏幕方向设置

a. 配置文件实现

//设置横屏
<activity android:name="myAcitivty"  android:screenOrientation="landscape" />   //设置竖屏
<activity android:name="myAcitivty"  android:screenOrientation="portrait" /> 

b. 代码实现

//设置横屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//设置竖屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

c. 获取屏幕方向

//获取横屏方向
int orientation = this.getResources().getConfiguration().orientation;

其中的orientation方向可以使 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE 或者 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE .

3. 设置动画

为了更好的用户体验, 这里给Splash界面添加一个动画, 这个动画加给整个界面.

(1) 创建动画

AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f);  //创建动画
animation.setDuration(2000);    //设置渐变
splash_rl.setAnimation(animation);  //设置动画载体

创建动画吧: 创建的这个动画是透明度渐变动画, 传入浮点型参数, 0代表完全透明, 1代表不透明, 传入参数代表透明度从完全透明到不透明.

设置时间 : 设置的duration是动画渐变过程所消耗的时间.

设置动画 : 最后使用setAnimation()方法将穿件的动画设置给Splash界面.

(2) 动画常用方法

a. 普通设置

        alphaAnimation.setRepeatCount(5);//设置重复次数alphaAnimation.setFillAfter(true);//动画执行完是否停留在执行完的状态alphaAnimation.setStartOffset(1000);//动画执行前等待的时间, 单位是毫秒alphaAnimation.start();//开始动画

b. 设置监听器

alphaAnimation.setAnimationListener(new AnimationListener() {//动画开始时回调@Overridepublic void onAnimationStart(Animation animation) {}//动画重复执行时回调@Overridepublic void onAnimationRepeat(Animation animation) {}//动画执行结束时回调@Overridepublic void onAnimationEnd(Animation animation) {}});

4. SharedPerference使用

        //获取SharedPerferenceSharedPreferences sharedPreferences = getSharedPreferences("sp", Context.MODE_PRIVATE);Editor editor = sharedPreferences.edit(); //获取Editor对象editor.putBoolean("isUpdate", true);      //向sp中写入数据editor.commit();                          //提交sharedPreferences.getBoolean("isUpdate", true);//获取sp中的变量

5. onCreate()方法代码

/*** 创建Activity时调用* * ① 设置全屏显示, 由于是Splash界面, 因此不能有标题* ② 设置布局, 版本号, 执行动画 * ③ 设置当前时间* ④ 获取SharedPerference配置文件* ⑤ 开启检查版本号线程, 后续操作都在改线程中操作* */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//隐藏标题栏requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏状态栏getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);//设置布局setContentView(R.layout.splash);/**  显示当前软件的版本号*     获取布局中的TextView控件, 将版本号设置到这个TextView控件中*/tv_version = (TextView) findViewById(R.id.tv_version);version =getString(R.string.current_version) + " " + getVersion();tv_version.setText(version);/**   在界面设置一个动画, 用来表明正在运行*    a. 获取布局*    b. 创建一个动画对象*    c. 将动画设置到布局中*/splash_rl = (RelativeLayout) findViewById(R.id.splash_rl);AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f);animation.setDuration(2000);splash_rl.setAnimation(animation);/** 这个时间值是用来控制Splash界面显示时间的* 记录下这个值, 然后执行到下面, 如果时间差在3秒以内, * 就执行下面的操作, 如果时间差不足3秒, 就Thread.sleep时间差* 等够3秒在执行下面的操作*/time = System.currentTimeMillis();//从SharedPreference中获取一些配置sp = getSharedPreferences("config", Context.MODE_PRIVATE);//开启检查版本号线程new Thread(new CheckVersionTask()).start();}

二. 检查版本号

1. 检查版本号线程

流程 :

a. 保持Splash持续时间 : 获取当前时间与time进行比较, 如果不足3秒, 人为使Splash保持3秒时间;

b. 查看更新设置 : 从sp中获取更新设置, 如果sp中自动更新为true, 那么就执行下面的更新流程, 如果sp中自动更新为false, 那么直接进入主界面.

c. 获取信息 : 从网络中获取更新信息, 根据是否成功获取信息执行不同的操作.

源码 :

 private final class CheckVersionTask implements Runnable{public void run() {try {/** 获取当前时间, 与onCreate方法中获取的时间进行比较* 如果不足3秒, 在等待够3秒之后在执行下面的操作*/long temp = System.currentTimeMillis();if(temp - time < 3000){SystemClock.sleep(temp - time);}/** 检查配置文件中的设置, 是否设置了自动更新; * 如果设置了自动更新, 就执行下面的操作,* 如果没有设置自动更新, 就直接进入主界面*/boolean is_auto_update = sp.getBoolean("is_auto_update", true);if(!is_auto_update){loadMainUI();return;}/** 获取更新信息* 如果信息不为null, 向handler发信息SUCESS_GET_UPDATEINOF, 执行后续操作* 如果信息为null, 向handler发信息ERROR_GET_UPDATEINOF, 执行后续操作* 如果出现异常, 向handler发信息ERROR_GET_UPDATEINOF, 执行后续操作*/updateInfo = getUpdateInfo(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + XML_FILE_DIRECTORY);if(updateInfo != null){Message msg = new Message();msg.what = SUCESS_GET_UPDATEINOF;mHandler.sendMessage(msg);}else{Message msg = new Message();msg.what = ERROR_GET_UPDATEINOF;mHandler.sendMessage(msg);}} catch (Exception e) {e.printStackTrace();Message msg = new Message();msg.what = ERROR_GET_UPDATEINOF;mHandler.sendMessage(msg);}}}

2. 获取版本号方法

流程 :

a. 创URL建对象;

b. 创建HttpURLConnection对象;

c. 设置超时时间;

d. 设置获取方式;

e. 查看链接是否成功;

f. 解析输入流信息;

源码 :

 /*** 获取更新信息*        ① 根据字符串地址创建URL对象*       ② 根据URL对象创建HttpURLConnection链接对象*       ③ 设置链接对象5秒超时*       ④ 设置链接对象获取的方式为get方式*        ⑤ 如果成功连接, conn.getRequestCode值就是200, 此时就可以获取输入流*        ⑥ 解析输入流获取更新信息*      */private UpdateInfo getUpdateInfo(String path){try {URL url = new URL(path);  //创建URL对象//创建连接对象HttpURLConnection conn = (HttpURLConnection) url.openConnection();//设置链接超时conn.setConnectTimeout(5000);//设置获取方式conn.setRequestMethod("GET");//如果连接成功, 获取输入流if(conn.getResponseCode() == 200){InputStream is = conn.getInputStream();//解析输入流中的数据, 返回更新信息return parserUpdateInfo(is);}} catch (MalformedURLException e) {e.printStackTrace();} catch (ProtocolException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}

3. 更新信息对象

将从网上获取的更新信息 包括 版本号, apk文件地址, 软件描述等信息封装在一个类中.

 public class UpdateInfo {private String version;    //当前软件版本号private String url;        //获取到的软件地址private String description;   //软件描述public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}@Overridepublic String toString() {return "UpdateInfo [version=" + version + ", url=" + url+ ", description=" + description + "]";}}

4. pull解析输入流

(1) pull解析流程

a. 获取pull解析器 : XmlPullParser parser = Xml.newPullParser();

b. 为pull解析器设置编码 : parser.setInput(inputStream, "UTF-8");

c. 获取pull解析器事件 : int eventType = parser.getEventType(), 之后的解析都要根据这个解析事件进行, 例如开始解析标签的事件时 XmlPullParser.START_TAG, 文档结束的事件时 XmlPullParser.END_DOCUMENT.

d. 解析流程控制 : 解析的时候, 如果没有解析到文档最后就一直解析, 这里使用while循环, eventType != XmlPullParser.END_DOCUMENT 就一直循环, 循环玩一个元素之后, 调用parser.next()遍历下一个元素.

e. 获取标签名 : 在事件解析标签的时候 ( eventType == XmlPullParser.START_TAG ) , 调用parser.getName()可以获取这个标签的标签名, 如果我们想要获取这个标签下的文本元素, 可以使用parser.nextText()来获取.

(2) 更新xml文件

<?xml version="1.0" encoding="UTF-8"?>
<updateInfo><version>3.2</version><url>http://127.0.0.1:8080/web/mobilesafe.apk</url><description>客户端更新</description>
</updateInfo>

(3) 源码

 /*** 获取更新信息*        ① 创建pull解析器*        ② 为解析器设置编码格式*       ③ 获取解析事件*       ④ 遍历整个xml文件节点, 获取标签元素内容*/private UpdateInfo parserUpdateInfo(InputStream is){try {UpdateInfo updateInfo = null;//1. 创建pull解析解析器XmlPullParser parser = Xml.newPullParser();//2. 设置解析编码parser.setInput(is, "UTF-8");//3. 获取解析器解事件, 如解析到文档开始 , 结尾, 标签等int eventType = parser.getEventType();//4. 在文档结束前一直解析while (eventType != XmlPullParser.END_DOCUMENT) {switch (eventType) {//只解析标签case XmlPullParser.START_TAG:if ("updateInfo".equals(parser.getName())) {//当解析到updateInfo标签的时候, 跟标签开始, 创建一个UpdateInfo对象updateInfo = new UpdateInfo();} else if ("version".equals(parser.getName())) {//解析版本号标签updateInfo.setVersion(parser.nextText());} else if ("url".equals(parser.getName())) {//解析url标签updateInfo.setUrl(parser.nextText());} else if ("description".equals(parser.getName())) {//解析描述标签updateInfo.setDescription(parser.nextText());}break;default:break;}//每解析完一个元素, 就将解析标志位下移eventType = parser.next();}is.close();return updateInfo;} catch (XmlPullParserException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}

三. Handler对象

Handler对象用来控制整个更新过程的进行;

 private Handler mHandler = new Handler(){public void handleMessage(android.os.Message msg) {switch (msg.what) {/** 获取更新信息错误 , 在断网或者获取信息出现异常执行* 提示一下, 之后进入主界面*/case ERROR_GET_UPDATEINOF:ToastHint.getInstance().showHint(R.string.fail_to_get_updateinfo);loadMainUI();break;/** 成功获取更新信息, 一般在成功从网上获取xml文件并解析出来* 如果版本号相同, 说明不用更新, 直接进入主界面* 如果版本号不同, 需要弹出更新对话框*/case SUCESS_GET_UPDATEINOF:if(updateInfo.getVersion().equals(version)){loadMainUI();}else{showUpdateDialog();}break;/** 下载apk文件出现错误, 中途断网 出现异常等情况* 提示后进入主界面*/case ERROR_DOWNLOAD_APK:mPb.dismiss();ToastHint.getInstance().showHint(R.string.fail_to_get_apk);loadMainUI();break;/** 成功下载apk文件之后执行的操作* 取消进度条对话框, 之后安装apk文件*/case SUCCESS_DOWNLOAD_APK:mPb.dismiss();installApk();break;default:break;}};};

.

作者 :万境绝尘 

转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/18964835

.

四. 下载安装apk文件

1. 更新对话框

(1) 更新流程

先弹出更新对话框提示, 点击确定就弹出进度条对话框, 下载apk文件 . 如果点击取消, 直接进入主界面

更新对话框 : 这是一个AlertDialog , 先创建builder, 然后设置标题, 显示内容, 设置积极消极按钮, 创建对话框 之后显示对话框;

进度条对话框 : 这是一个ProgressDialog, 直接使用new创建, 设置信息与显示样式, 最后显示对话框.

(2) 创建对话框流程

创建一个对话框的流程 :

a. 创建builder对象 : Builder builder = new Builder(context);

b. 设置标题 : builder.setTittle("");

c. 设置显示信息 : builder.setMessage("");

d. 设置按钮 : builder.setPositiveButton("", onClickListener);

e. 创建对话框 : Dialog dialog = builder.create();

f. 显示对话框 : dialog.show();

创建进度条对话框流程 :

a. 创建进度条对话框 : ProgressDialog progressDialog = new ProgressDialog(context);

b. 设置进度条对话框样式 : progressDialog.setProgressStyle();

c. 设置显示信息 : progressDialog.setMessage();

d. 显示对话框 : progressDialog.show();

(3) 源码

 /*** 弹出更新对话框* * a. 创建builder对象* b. 设置标题* c. 设置对话框显示信息* d. 设置该对话框不可回退, 如果回退的话就会卡在本界面* e. 设置确定按钮* f. 设置取消按钮* g. 创建对话框* h. 显示对话框* * 确定按钮按下显示进度条对话框* a. 创建一个进度条对话框* b. 设置该对话框不能回退* c. 设置进度条样式* d. 设置进度条的信息* e. 显示进度条对话框* f. 开启一个线程, 下载apk文件*/protected void showUpdateDialog() {//创建builder对象AlertDialog.Builder builder = new AlertDialog.Builder(this);//设置标题builder.setTitle(getString(R.string.update_dialog_tittle));//设置对话框信息builder.setMessage(updateInfo.getDescription());//设置不可回退builder.setCancelable(false);//设置确定按钮builder.setPositiveButton(getString(R.string.confirm), new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {//创建进度条对话框mPb = new ProgressDialog(SplashActivity.this);//设置进度条对话框不可回退mPb.setCancelable(false);//设置进度条对话框样式mPb.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//设置进度条对话框的信息mPb.setMessage(getString(R.string.update_dialog_messsage));//显示进度条对话框mPb.show();//开启显示进度条对话框线程new Thread(new DownloadApkTask()).start();}});builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {loadMainUI();}});//创建更新信息提示对话框mUpdateInfoDialog = builder.create();//显示更新信息提示对话框mUpdateInfoDialog.show();}

2. 下载apk线程

 /*** 在这个线程中主要执行downloadApk方法, 这个方法传入apk路径和进度条对话框* 注意 : 下载的前提是sd卡的状态是挂载的*/private final class DownloadApkTask implements Runnable{public void run() {if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){try {SystemClock.sleep(2000);apkFile = downloadApk(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + updateInfo.url,mPb);Message msg = new Message();msg.what = SUCCESS_DOWNLOAD_APK;mHandler.sendMessage(msg);} catch (Exception e) {e.printStackTrace();Message msg = new Message();msg.what = ERROR_DOWNLOAD_APK;mHandler.sendMessage(msg);}}}}

3. 下载apk核心方法

从网络下载文件流程 :

a. 创建URL对象 : 这个对象一般根据字符串地址创建, URL url = new URL(path);

b. 创建HttpURLConnection对象 : 这个对象根据URL对象创建, HttpURLConnection conn = (HttpURLConnection)url.openConnection();

c. 设置超时时间 : 单位是毫秒, conn.setConnectionTimeout(5000);

d. 设置请求方式 : conn.setRequestMethod("GET");

e. 成功连接 : 如果成功连接, 那么conn.getResponseCode()的值为200;

进度条对话框设置 :

a. 设置进度条最大值 : mProgressDialog.setMax(int max);

b. 设置进度条当前值 : mProgressDialog.setProgress(int curr);

 /*** 下载apk更新文件*     * a. 根据SD卡路径创建文件对象, 这个文件用来保存下载的文件* b. 创建URL对象* c. 创建HttpUrlConnection对象* d. 设置链接对象超时时间* e. 设置请求方式 get* f. 如果请求成功执行下面的操作* * g. 通过链接对象获取网络资源的大小* h. 将文件大小设置给进度条对话框* i. 获取输入流, 并且读取输入流信息* j. 根据读取到的字节数, 将已经读取的数据设置给进度条对话框*/public File downloadApk(String path,ProgressDialog pb) throws Exception{//创建本地文件对象File file = new File(Environment.getExternalStorageDirectory(), getFileName(path));//创建HttpURL连接URL url = new URL(path);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(5000);conn.setRequestMethod("GET");if(conn.getResponseCode() == 200){int max = conn.getContentLength();//设置进度条对话框的最大值pb.setMax(max);int count = 0;InputStream is = conn.getInputStream();FileOutputStream fos = new FileOutputStream(file);byte[] buffer = new byte[1024];int len = 0;while((len = is.read(buffer)) != -1){fos.write(buffer, 0, len);//设置进度条对话框进度count = count + len;pb.setProgress(count);}is.close();fos.close();}return file;}

4. 安装apk文件

 /*** 安装apk文件流程* * a. 设置Action : Intent.ACTION_VIEW.* b. 设置数据和类型 : 设置apk文件的uri 和 MIME类型* c. 开启安装文件的Activity.*/protected void installApk() {Intent intent = new Intent();intent.setAction(Intent.ACTION_VIEW);intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");startActivity(intent);}

五. 相关的源码

(1) 布局文件

splash.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/ivt_splash" android:id="@+id/splash_rl"><ProgressBar android:id="@+id/pb"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:layout_alignParentBottom="true"android:layout_marginBottom="30dip"/><TextView android:id="@+id/tv_version"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:layout_above="@id/pb"android:layout_marginBottom="60dip"android:textSize="30sp"android:textColor="#17A6E8"android:text="version"/>
</RelativeLayout>

(2) Activity页面切换动画

main_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><translateandroid:fromXDelta="100%p"android:toXDelta="0"android:fromYDelta="0"android:toYDelta="0" android:duration="200"/>
</set>

splash_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><translateandroid:fromXDelta="0"android:toXDelta="-100%p"android:fromYDelta="0"android:toYDelta="0" android:duration="200"/>
</set>

(3) SplashActivity源码

SplashActivity.java

public class SplashActivity extends Activity {private static final String TAG = "SplashActivity";public static final int ERROR_GET_UPDATEINOF = 0;public static final int SUCESS_GET_UPDATEINOF = 1;public static final int ERROR_DOWNLOAD_APK = 2;public static final int SUCCESS_DOWNLOAD_APK = 3;private static final String XML_FILE_DIRECTORY = "updateinfo.xml";private static final String UPDATE_FOLDER_DIRECTORY = "/webupdate/";private TextView tv_version;private PackageManager pm;private String version;private UpdateInfo updateInfo;private Dialog mUpdateInfoDialog;private ProgressDialog mPb;private File apkFile;private RelativeLayout splash_rl;private long time;private SharedPreferences sp;private int touchPositionX0;private int touchPositionX1;private Handler mHandler = new Handler(){public void handleMessage(android.os.Message msg) {switch (msg.what) {/** 获取更新信息错误 , 在断网或者获取信息出现异常执行* 提示一下, 之后进入主界面*/case ERROR_GET_UPDATEINOF:ToastHint.getInstance().showHint(R.string.fail_to_get_updateinfo);loadMainUI();break;/** 成功获取更新信息, 一般在成功从网上获取xml文件并解析出来* 如果版本号相同, 说明不用更新, 直接进入主界面* 如果版本号不同, 需要弹出更新对话框*/case SUCESS_GET_UPDATEINOF:if(updateInfo.getVersion().equals(version)){loadMainUI();}else{showUpdateDialog();}break;/** 下载apk文件出现错误, 中途断网 出现异常等情况* 提示后进入主界面*/case ERROR_DOWNLOAD_APK:mPb.dismiss();ToastHint.getInstance().showHint(R.string.fail_to_get_apk);loadMainUI();break;/** 成功下载apk文件之后执行的操作* 取消进度条对话框, 之后安装apk文件*/case SUCCESS_DOWNLOAD_APK:mPb.dismiss();installApk();break;default:break;}};};/*** 创建Activity时调用* * ① 设置全屏显示, 由于是Splash界面, 因此不能有标题* ② 设置布局, 版本号, 执行动画 * ③ 设置当前时间* ④ 获取SharedPerference配置文件* ⑤ 开启检查版本号线程, 后续操作都在改线程中操作* */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//隐藏标题栏requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏状态栏getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);//设置布局setContentView(R.layout.splash);/**     显示当前软件的版本号*     获取布局中的TextView控件, 将版本号设置到这个TextView控件中*/tv_version = (TextView) findViewById(R.id.tv_version);version =getString(R.string.current_version) + " " + getVersion();tv_version.setText(version);/**   在界面设置一个动画, 用来表明正在运行*    a. 获取布局*    b. 创建一个动画对象*    c. 将动画设置到布局中*/splash_rl = (RelativeLayout) findViewById(R.id.splash_rl);AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);alphaAnimation.setDuration(2000);splash_rl.setAnimation(alphaAnimation);/** 这个时间值是用来控制Splash界面显示时间的* 记录下这个值, 然后执行到下面, 如果时间差在3秒以内, * 就执行下面的操作, 如果时间差不足3秒, 就Thread.sleep时间差* 等够3秒在执行下面的操作*/time = System.currentTimeMillis();//从SharedPreference中获取一些配置sp = getSharedPreferences("config", Context.MODE_PRIVATE);//开启检查版本号线程new Thread(new CheckVersionTask()).start();}private final class CheckVersionTask implements Runnable{public void run() {try {/** 获取当前时间, 与onCreate方法中获取的时间进行比较* 如果不足3秒, 在等待够3秒之后在执行下面的操作*/long temp = System.currentTimeMillis();if(temp - time < 3000){SystemClock.sleep(temp - time);}/** 检查配置文件中的设置, 是否设置了自动更新; * 如果设置了自动更新, 就执行下面的操作,* 如果没有设置自动更新, 就直接进入主界面*/boolean is_auto_update = sp.getBoolean("is_auto_update", true);if(!is_auto_update){loadMainUI();return;}/** 获取更新信息* 如果信息不为null, 向handler发信息SUCESS_GET_UPDATEINOF, 执行后续操作* 如果信息为null, 向handler发信息ERROR_GET_UPDATEINOF, 执行后续操作* 如果出现异常, 向handler发信息ERROR_GET_UPDATEINOF, 执行后续操作*/updateInfo = getUpdateInfo(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + XML_FILE_DIRECTORY);if(updateInfo != null){Message msg = new Message();msg.what = SUCESS_GET_UPDATEINOF;mHandler.sendMessage(msg);}else{Message msg = new Message();msg.what = ERROR_GET_UPDATEINOF;mHandler.sendMessage(msg);}} catch (Exception e) {e.printStackTrace();Message msg = new Message();msg.what = ERROR_GET_UPDATEINOF;mHandler.sendMessage(msg);}}}/*** 安装apk文件流程* * a. 设置Action : Intent.ACTION_VIEW.* b. 设置数据和类型 : 设置apk文件的uri 和 MIME类型* c. 开启安装文件的Activity.*/protected void installApk() {Intent intent = new Intent();intent.setAction(Intent.ACTION_VIEW);intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");startActivity(intent);}/*** 弹出更新对话框* * a. 创建builder对象* b. 设置标题* c. 设置对话框显示信息* d. 设置该对话框不可回退, 如果回退的话就会卡在本界面* e. 设置确定按钮* f. 设置取消按钮* g. 创建对话框* h. 显示对话框* * 确定按钮按下显示进度条对话框* a. 创建一个进度条对话框* b. 设置该对话框不能回退* c. 设置进度条样式* d. 设置进度条的信息* e. 显示进度条对话框* f. 开启一个线程, 下载apk文件*/protected void showUpdateDialog() {//创建builder对象AlertDialog.Builder builder = new AlertDialog.Builder(this);//设置标题builder.setTitle(getString(R.string.update_dialog_tittle));//设置对话框信息builder.setMessage(updateInfo.getDescription());//设置不可回退builder.setCancelable(false);//设置确定按钮builder.setPositiveButton(getString(R.string.confirm), new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {//创建进度条对话框mPb = new ProgressDialog(SplashActivity.this);//设置进度条对话框不可回退mPb.setCancelable(false);//设置进度条对话框样式mPb.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//设置进度条对话框的信息mPb.setMessage(getString(R.string.update_dialog_messsage));//显示进度条对话框mPb.show();//开启显示进度条对话框线程new Thread(new DownloadApkTask()).start();}});builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {loadMainUI();}});//创建更新信息提示对话框mUpdateInfoDialog = builder.create();//显示更新信息提示对话框mUpdateInfoDialog.show();}/*** 在这个线程中主要执行downloadApk方法, 这个方法传入apk路径和进度条对话框* 注意 : 下载的前提是sd卡的状态是挂载的*/private final class DownloadApkTask implements Runnable{public void run() {if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){try {SystemClock.sleep(2000);apkFile = downloadApk(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + updateInfo.url,mPb);Message msg = new Message();msg.what = SUCCESS_DOWNLOAD_APK;mHandler.sendMessage(msg);} catch (Exception e) {e.printStackTrace();Message msg = new Message();msg.what = ERROR_DOWNLOAD_APK;mHandler.sendMessage(msg);}}}}/*** 下载apk更新文件*    * a. 根据SD卡路径创建文件对象, 这个文件用来保存下载的文件* b. 创建URL对象* c. 创建HttpUrlConnection对象* d. 设置链接对象超时时间* e. 设置请求方式 get* f. 如果请求成功执行下面的操作* * g. 通过链接对象获取网络资源的大小* h. 将文件大小设置给进度条对话框* i. 获取输入流, 并且读取输入流信息* j. 根据读取到的字节数, 将已经读取的数据设置给进度条对话框*/public File downloadApk(String path,ProgressDialog pb) throws Exception{//创建本地文件对象File file = new File(Environment.getExternalStorageDirectory(), getFileName(path));//创建HttpURL连接URL url = new URL(path);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(5000);conn.setRequestMethod("GET");if(conn.getResponseCode() == 200){int max = conn.getContentLength();//设置进度条对话框的最大值pb.setMax(max);int count = 0;InputStream is = conn.getInputStream();FileOutputStream fos = new FileOutputStream(file);byte[] buffer = new byte[1024];int len = 0;while((len = is.read(buffer)) != -1){fos.write(buffer, 0, len);//设置进度条对话框进度count = count + len;pb.setProgress(count);}is.close();fos.close();}return file;}private String getFileName(String path){return path.substring(path.lastIndexOf("/") + 1);}private String getVersion() {try {pm = this.getPackageManager();PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);return packageInfo.versionName;} catch (Exception e) {e.printStackTrace();}return null;}/*** 获取更新信息*       ① 根据字符串地址创建URL对象*       ② 根据URL对象创建HttpURLConnection链接对象*       ③ 设置链接对象5秒超时*       ④ 设置链接对象获取的方式为get方式*        ⑤ 如果成功连接, conn.getRequestCode值就是200, 此时就可以获取输入流*        ⑥ 解析输入流获取更新信息*      */private UpdateInfo getUpdateInfo(String path){try {URL url = new URL(path);  //创建URL对象//创建连接对象HttpURLConnection conn = (HttpURLConnection) url.openConnection();//设置链接超时conn.setConnectTimeout(5000);//设置获取方式conn.setRequestMethod("GET");//如果连接成功, 获取输入流if(conn.getResponseCode() == 200){InputStream is = conn.getInputStream();//解析输入流中的数据, 返回更新信息return parserUpdateInfo(is);}} catch (MalformedURLException e) {e.printStackTrace();} catch (ProtocolException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}/*** 获取更新信息*        ① 创建pull解析器*        ② 为解析器设置编码格式*       ③ 获取解析事件*       ④ 遍历整个xml文件节点, 获取标签元素内容*/private UpdateInfo parserUpdateInfo(InputStream is){try {UpdateInfo updateInfo = null;//1. 创建pull解析解析器XmlPullParser parser = Xml.newPullParser();//2. 设置解析编码parser.setInput(is, "UTF-8");//3. 获取解析器解事件, 如解析到文档开始 , 结尾, 标签等int eventType = parser.getEventType();//4. 在文档结束前一直解析while (eventType != XmlPullParser.END_DOCUMENT) {switch (eventType) {//只解析标签case XmlPullParser.START_TAG:if ("updateInfo".equals(parser.getName())) {//当解析到updateInfo标签的时候, 跟标签开始, 创建一个UpdateInfo对象updateInfo = new UpdateInfo();} else if ("version".equals(parser.getName())) {//解析版本号标签updateInfo.setVersion(parser.nextText());} else if ("url".equals(parser.getName())) {//解析url标签updateInfo.setUrl(parser.nextText());} else if ("description".equals(parser.getName())) {//解析描述标签updateInfo.setDescription(parser.nextText());}break;default:break;}//每解析完一个元素, 就将解析标志位下移eventType = parser.next();}is.close();return updateInfo;} catch (XmlPullParserException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}private void loadMainUI(){Intent intent = new Intent(this,HomeActivity.class);startActivity(intent);finish();overridePendingTransition(R.anim.main_in, R.anim.splash_out);}public class UpdateInfo {private String version;    //当前软件版本号private String url;        //获取到的软件地址private String description;   //软件描述public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}@Overridepublic String toString() {return "UpdateInfo [version=" + version + ", url=" + url+ ", description=" + description + "]";}}/*** 设置触摸事件* 在手指按下时记录x坐标值 , 在手指抬起的时候记录x坐标值 , 如果两个值相差超过100*   那么跳转到主界面 * @see android.app.Activity#onTouchEvent(android.view.MotionEvent)*/@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN :touchPositionX0 = (int) event.getX();break;case MotionEvent.ACTION_UP :touchPositionX1 = (int) event.getX();if((touchPositionX0 - touchPositionX1) > 100)loadMainUI();touchPositionX0 = 0;touchPositionX1 = 0;break;}return true;}
}

.

作者 :万境绝尘 

转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/18964835

.

【Android 应用开发】Android应用的自动更新模块相关推荐

  1. android程序突然崩溃如何调试,Android应用开发android 调试崩溃Unable to instantiate application的解决方法...

    本文将带你了解Android应用开发android 调试崩溃Unable to instantiate application的解决方法,希望本文对大家学Android有所帮助. 更新后Android ...

  2. android studio真机测试不生成apk,Android应用开发Android Studio编译出来的APK无法安装如何解决?...

    本文将带你了解Android应用开发Android Studio编译出来的APK无法安装如何解决?,希望本文对大家学Android有所帮助. Android Studio编译出来的APK无法安装如何解 ...

  3. android ble 设备扫描程序,Android应用开发Android 7.0 BLE scan 问题:程序无错但扫描不到BLE设备...

    本文将带你了解Android应用开发Android 7.0  BLE scan 问题:程序无错但扫描不到BLE设备,希望本文对大家学Android有所帮助. < 最近在做毕设,需要几周内从头学起 ...

  4. android studio socket 失败,Android应用开发Android Studio建立Socket连接失败解决方法

    本文将带你了解Android应用开发Android Studio建立Socket连接失败解决方法,希望本文对大家学Android有所帮助. < Android Studio建立Socket连接失 ...

  5. android落下动画,Android应用开发android 购物车小球掉落动画

    本文将带你了解Android应用开发android 购物车小球掉落动画,希望本文对大家学Android有所帮助. 先贴效果图 对自定义View小红球的绘制 public class BallView ...

  6. android baseactivity,Android应用开发Android通过BaseActivity获取到当前启动的Activity名称...

    本文将带你了解Android应用开发Android通过BaseActivity获取到当前启动的Activity名称,希望本文对大家学Android有所帮助. < 在BaseActivity的on ...

  7. android view 点击变暗,Android应用开发Android ImageView点击变暗效果

    本文将带你了解Android应用开发Android ImageView点击变暗效果,希望本文对大家学Android有所帮助. < 自定义ImageView: 在ImageView中setPres ...

  8. android 防止重复启动app,Android应用开发Android 防止启动页面(SplashActivity)被多次启动...

    本文将带你了解Android应用开发Android 防止启动页面(SplashActivity)被多次启动,希望本文对大家学Android有所帮助. 话不多说直接上代码: @Override prot ...

  9. android studio获取数字签名,Android应用开发Android Studio数字签名打包apk图文步骤教程...

    本文将带你了解Android应用开发Android Studio数字签名打包apk图文步骤教程,希望本文对大家学Android有所帮助. Android Studio数字签名打包release版apk ...

  10. android修改用户名和密码错误,Android应用开发Android Studio 修改用户名、密码、URL等操作教程...

    本文将带你了解Android应用开发Android Studio 修改用户名.密码.URL等操作教程,希望本文对大家学Android有所帮助. 修改用户名.密码: 在AndroidStudio的ter ...

最新文章

  1. java 排序算法 讲解_java实现排序算法之冒泡排序法详细讲解
  2. SPI的原理以及Verilog HDL实现
  3. jQuery中find()方法和filter()方法的区别
  4. 利用PyMySQL模块操作数据库
  5. VHDL移位寄存器的设计与实现
  6. React开发(172):React引入背景图片
  7. NOI2011 智能车比赛
  8. Matlab面向对象编程
  9. 通过wifi使用nfs把ubuntu挂载到android
  10. 机器人潘森护盾_新版潘森无伤抗塔 E技能护盾机制全解析
  11. 新手学编程?选python吧!
  12. XShell下载安装并连接阿里云
  13. c语言md5函数 linux,【转】MD5校验C语言实现源代码
  14. 解决matlab错误使用 mex 未找到支持的编译器或 SDK一系列问题
  15. java用swing日历标记节日,java基于swing实现日历
  16. linux grep 和 find 命令
  17. 《关爱码农成长计划》第一期报告
  18. 无情链表的创建,,插入,,删除第一个位
  19. Windows游戏编程之从零开始d
  20. 合作伙伴眼中的鸿蒙,专访海雀科技研发总监李尹

热门文章

  1. 简单的ALV显示例子
  2. 乱码问题引申 python 中string和unicode
  3. 总结sqlserver数据库性能优化相关的注意事项
  4. (转)软件开发人员如何提高自己的软件专业技术方面的具体建议
  5. 趣谈Linux操作系统学习笔记:用户态内存映射:如何找到正确的会议室?(第25讲)...
  6. Android点击事件(click button)的四种写法
  7. NSS_06 extjs弹出窗口上的文本框默认获得焦点
  8. Linux下时间戳格式和标准时间格式的转换
  9. 也谈Hashtable
  10. 从oracle10g 10.2.0.1 升级到10.2.0.4碰到的问题及解决