登陆界面软件自动更新功能的实现

来源:互联网 发布:科学计算器软件下载 编辑:程序博客网 时间:2024/05/22 01:57

在软件的开发和维护过程中,软件是需要不断更新的,不可能一开始就有好的软件,尤其是精品软件的形成。那么如何让用户第一时间获取最新的应用安装包呢?那么就要求我们从第一个版本就要实现升级模块这一功能。

自动更新功能的实现原理,就是我们事先和后台协商好一个接口,我们在应用的主Activity里,去访问这个接口,如果需要更新,后台会返回一些数据(比如,提示语;最新版本的url等)。然后我们给出提示框,用户点击开始下载,下载完成开始覆盖安装程序,这样用户的应用就会保持最新。

为了使大家更加容易理解,我把项目中的更新事例给大家展示下。我们把软件更新放在用户登陆的界面。

第一步:新建一个用户登陆的布局文件login.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    android:background="@drawable/main_bg"    android:orientation="vertical" >    <ScrollView        android:id="@+id/Main_LinearLayout"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:layout_above="@+id/bottommenu"        android:layout_alignParentTop="true" >        <LinearLayout            android:id="@+id/Main_LinearLayout"            android:layout_width="fill_parent"            android:layout_height="fill_parent"            android:orientation="vertical" >            <ImageView                android:id="@+id/TitleBG"                android:layout_width="fill_parent"                android:layout_height="wrap_content"                android:background="@drawable/title_bg" >            </ImageView>            <TextView                android:id="@+id/system_name"                android:layout_width="fill_parent"                android:layout_height="wrap_content"                android:gravity="center"                android:paddingTop="20dip"                android:text="@string/login_name"                android:textColor="@drawable/black"                android:textSize="30sp" >            </TextView>            <TableLayout                android:id="@+id/TableLayout01"                android:layout_width="fill_parent"                android:layout_height="wrap_content"                android:paddingLeft="20dip"                android:paddingRight="20dip"                android:paddingTop="20dip"                android:shrinkColumns="1"                android:stretchColumns="1" >                <TableRow android:paddingTop="15dip" >                    <TextView                        android:id="@+id/TextView01"                        android:padding="10dip"                        android:text="@string/user_code_text"                        android:textColor="@drawable/black"                        android:textSize="18sp" >                    </TextView>                    <EditText                        android:id="@+id/user_code"                        android:hint="@string/username_hint"                        android:padding="10dip"                        android:textSize="16sp" />                </TableRow>                <TableRow android:paddingTop="15dip" >                    <TextView                        android:id="@+id/TextView02"                        android:padding="10dip"                        android:text="@string/password_text"                        android:textColor="@drawable/black"                        android:textSize="18sp" >                    </TextView>                    <EditText                        android:id="@+id/user_pwd"                        android:hint="@string/password_hint"                        android:padding="10dip"                        android:password="true"                        android:textSize="16sp" />                </TableRow>                <TableRow                    android:gravity="center"                    android:paddingTop="10dip" >                    <CheckBox                        android:id="@+id/remember_pwd"                        android:layout_width="wrap_content"                        android:layout_height="wrap_content"                        android:checked="true"                        android:text="@string/rememberpwd_text"                        android:textColor="@drawable/black" />                </TableRow>            </TableLayout>        </LinearLayout>    </ScrollView>    <LinearLayout        android:id="@id/bottommenu"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:orientation="horizontal"        android:padding="10dip" >        <Button            android:id="@+id/login"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_weight="1"            android:text="@string/login_text" >        </Button>        <Button            android:id="@+id/exit"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_weight="1"            android:text="@string/exit_text" >        </Button>    </LinearLayout></RelativeLayout>

第二步:建立业务类文件LoginActions.java,这个类主要用来登陆

package org.DigitalCM;import java.io.BufferedReader;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Properties;import org.Base.Activities.ContextActions;import org.Base.Activities.ContextActivity;import org.Base.Utils.PropertyUtil;import org.Base.Webservice.WSObjectMapUtil;import org.Base.Webservice.WSObjectUtil;import org.Base.Webservice.WSUtil;import org.Base.Webservice.WebServiceConfig;import org.DigitalCM.entities.UserInfo;import org.ksoap2.serialization.SoapObject;import android.content.Context;import android.content.Intent;import android.content.pm.PackageInfo;import android.content.pm.PackageManager;import android.content.pm.PackageManager.NameNotFoundException;import android.os.Environment;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.CheckBox;import android.widget.EditText;public class LoginActions extends ContextActions {public String SDPATH;public int totalSize = 0;public int downloadedSize = 0;public URL url = null;public final static int PROGRESS_DIALOG = 1;public final static int DIALOG_LOGIN_FAIELD = 2;public final static int DIALOG_CONNECT_ERROR = 3;public final static int DIALOG_USER_PWD_EMPTY = 4;public final static int DIALOG_EXIT_PROMPT = 5;public final static int DIALOG_VERSION_UPDATE = 6;boolean bNeedUpdate = false;private Button login = null;private Button exit = null;private EditText userCode = null;private EditText password = null;private CheckBox rememberpwd = null;// private CheckBox outlineLogin = null;public LoginActions(ContextActivity contextActivity) {// TODO Auto-generated constructor stubsuper(contextActivity);SDPATH = Environment.getExternalStorageDirectory() + "/";}public void setOnClickListener() {/* 为 Button 注册点击事件监听对象,采用了匿名内部类的方式。 */login.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {boolean flag = false;flag = login();if (flag) {loginaction();}}});/* 为 Button 注册点击事件监听对象,采用了匿名内部类的方式。 */exit.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {exitApp();}});}public void findViews() {userCode = (EditText) contextActivity.findViewById(R.id.user_code);password = (EditText) contextActivity.findViewById(R.id.user_pwd);rememberpwd = (CheckBox) contextActivity.findViewById(R.id.remember_pwd);login = (Button) contextActivity.findViewById(R.id.login);exit = (Button) contextActivity.findViewById(R.id.exit);// outlineLogin = (CheckBox)// contextActivity.findViewById(R.id.outline_login);}public String result = null;private void loginaction() {String version = null;PackageManager pm = contextActivity.getPackageManager();PackageInfo info = null;try {info = pm.getPackageInfo("org.DigitalCM",PackageManager.GET_ACTIVITIES);} catch (NameNotFoundException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}// PackageInfo info = pm.getPackageArchiveInfo(archiveFilePath,// PackageManager.GET_ACTIVITIES);if (info != null) {// ApplicationInfo appInfo = info.applicationInfo;// String appName = pm.getApplicationLabel(appInfo).toString();// String packageName = appInfo.packageName; // 得到安装包名称version = info.versionName; // 得到版本信息}HashMap<String, Object> params = new HashMap<String, Object>();params.put("version", version);Object loginResult = null;try {loginResult = WSUtil.getObjectByCallingWS(WebServiceConfig.NAMESPACE, "versionupdate", params,WebServiceConfig.wsdl);result = loginResult.toString();} catch (Exception e) {contextActivity.showDialog(DIALOG_CONNECT_ERROR);Log.e("Exception", e.getMessage());return;}if (result == null|| (result != null && result.equals("notneedupdate"))) {loginSuccess();} else {contextActivity.showDialog(DIALOG_VERSION_UPDATE);}}@SuppressWarnings("static-access")public boolean login() {if (!validate(userCode.getText().toString(), password.getText().toString()))return false;Map<String, Object> params = new HashMap<String, Object>();params.put("userName", userCode.getText().toString().trim().toLowerCase());params.put("userPwd", password.getText().toString());SoapObject result = null;try {result = WSUtil.getSoapObjectByCallingWS(WebServiceConfig.NAMESPACE, "login", params,WebServiceConfig.wsdl);} catch (Exception e) {contextActivity.showDialog(DIALOG_CONNECT_ERROR);Log.e("Exception", e.getMessage());return false;}if (result == null) {loginFailed();return false;}WSObjectUtil wsObjectUtil = new WSObjectUtil();SoapObject dataSet;try {dataSet = wsObjectUtil.getDataSetObject(result);} catch (Exception e) {loginFailed();return false;}if (dataSet == null) {loginFailed();return false;}List<Map<String, Object>> rowMapList = WSObjectMapUtil.getRowMapList(dataSet);UserInfo.setUserInfo(rowMapList.get(0));return true;}public void loginSuccess() {// TODO Auto-generated method stubIntent intent = null;if (UserInfo.getDeptID() != null && UserInfo.getDeptID().equals("12")) {// 社区领导(报送物业、上报上级、结案评价)intent = new Intent(contextActivity, MainCommunity.class);} else if (UserInfo.getDeptID() != null&& UserInfo.getDeptID().equals("17")) {// 网格责任人(问题上报、核查)intent = new Intent(contextActivity, Main.class);} else if ((UserInfo.getDeptID() != null && UserInfo.getDeptID().equals("41"))|| (UserInfo.getDeptID() != null && UserInfo.getDeptID().toString().equals("11"))|| (UserInfo.getDeptID() != null && UserInfo.getDeptID().toString().equals("411"))) {// 街道领导,区监督指挥中心(受理中心)intent = new Intent(contextActivity, MainAccept.class);} else if (UserInfo.getDeptID() != null&& UserInfo.getDeptID().equals("61")) {// 处置部门(街道-区级事务用户)intent = new Intent(contextActivity, MainDispose.class);}contextActivity.startActivity(intent);contextActivity.finish();return;}private Boolean validate(String user_code, String password) {if (user_code == null || user_code.trim().equals("")|| password == null || password.trim().equals("")) {contextActivity.showDialog(DIALOG_USER_PWD_EMPTY);return false;}return true;}public void loadConfig() {Properties properties = PropertyUtil.loadConfig(WebServiceConfig.CONFIG_PATH);String username = properties.getProperty("username");String pwd = properties.getProperty("password");properties = PropertyUtil.loadConfig(WebServiceConfig.CONFIG_PATH);username = properties.getProperty("username");pwd = properties.getProperty("password");if (pwd != null && !pwd.equals("")) {rememberpwd.setChecked(true);password.setText(pwd);} else {rememberpwd.setChecked(false);}userCode.setText(username);}private void loginFailed() {contextActivity.showDialog(DIALOG_LOGIN_FAIELD);}private void exitApp() {contextActivity.showDialog(DIALOG_EXIT_PROMPT);}/** * 在SD卡上创建文件 */public File creatSDFile(String fileName) throws IOException {File file = new File(SDPATH + fileName);file.createNewFile();return file;}/** * 在SD卡上删掉相应目录文件 */public Boolean deleteSDFile(String fileName) throws IOException {File file = new File(SDPATH + fileName);if (file.exists()) {File filelist[] = file.listFiles();for (int index = 0; index < filelist.length; index++) {filelist[index].delete();}} else {file.mkdir();}return true;}/** * 在SD卡上创建目录 */public File creatSDDir(String dirName) {File dir = new File(SDPATH + dirName);dir.mkdir();return dir;}/** * 判断SD卡上的文件夹是否存在 */public boolean isFileExist(String fileName) {File file = new File(SDPATH + fileName);return file.exists();}/** * 根据URL得到输入流 */public InputStream getInputStreamFromUrl(String urlStr)throws MalformedURLException, IOException {url = new URL(urlStr);// 创建一个URL对象HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();// 创建一个Http连接totalSize = urlConn.getContentLength();InputStream inputStream = urlConn.getInputStream();// 得到输入流return inputStream;}/** * 根据URL下载文件,前提是这个文件当中的内容是文本,函数的返回值就是文件当中的内容 1.创建一个URL对象 * 2.通过URL对象,创建一个HttpURLConnection对象 3.得到InputStram 4.从InputStream当中读取数据 */public String downStr(String urlStr)// 下载字符流的方法{/** * String和StringBuffer他们都可以存储和操作字符串,即包含多个字符的字符串数据。 * String类是字符串常量,是不可更改的常量。而StringBuffer是字符串变量,它的对象是可以扩充和修改的。 */StringBuffer sb = new StringBuffer();String line = null;BufferedReader buffer = null;// BufferedReader类用于从缓冲区中读取内容try {/** * 因为直接使用InputStream不好用,多以嵌套了BufferedReader,这个是读取字符流的固定格式。 */url = new URL(urlStr);// 创建一个URL对象HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();// 创建一个Http连接buffer = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));// 使用IO流读取数据while ((line = buffer.readLine()) != null) {sb.append(line);}} catch (Exception e) {e.printStackTrace();} finally {try {buffer.close();} catch (Exception e) {e.printStackTrace();}}return sb.toString();}}

第三步:新建一个Activity

package org.DigitalCM;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import org.Base.Activities.ContextActivity;import android.app.AlertDialog;import android.app.Dialog;import android.app.ProgressDialog;import android.app.SearchManager.OnCancelListener;import android.app.SearchManager.OnDismissListener;import android.content.DialogInterface;import android.content.Intent;import android.net.Uri;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;public class DigitalCity extends ContextActivity implements OnClickListener {private LoginActions loginActivities = new LoginActions(this);private ProgressDialog mProgressDialog = null;private ProcessThread mThread;/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.login);loginActivities.findViews();loginActivities.loadConfig();loginActivities.setOnClickListener();}@Overridepublic void onClick(View v) {// TODO Auto-generated method stub}@Overrideprotected Dialog onCreateDialog(int id) {// TODO Auto-generated method stubswitch (id) {case LoginActions.PROGRESS_DIALOG:mThread = new ProcessThread();mProgressDialog = new ProgressDialog(this);mProgressDialog.setTitle("提示"); // 设置标题mProgressDialog.setMessage("正在下载文件"+ loginActivities.result.replace("png", "apk") + ",请稍候..."); // 设置body信息mProgressDialog.setMax(100); // 进度条最大值是100mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); // 设置进度条样式是// mProgressDialog.setOnCancelListener(mThread);// mProgressDialog.setOnDismissListener(mThread);// mProgressDialog.setButton("取消", mThread);mProgressDialog.setCancelable(false);mThread.start();return mProgressDialog;case LoginActions.DIALOG_LOGIN_FAIELD:return new AlertDialog.Builder(this).setTitle(R.string.error_title).setMessage(R.string.login_failed).setPositiveButton(R.string.OK_text,new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog,int arg1) {// TODO Auto-generated method stubdialog.cancel();}}).show();case LoginActions.DIALOG_CONNECT_ERROR:return new AlertDialog.Builder(this).setTitle(R.string.message_title).setMessage(R.string.connection_error).setPositiveButton(R.string.OK_text,new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog,int which) {// TODO Auto-generated method stubdialog.cancel();}}).show();case LoginActions.DIALOG_USER_PWD_EMPTY:return new AlertDialog.Builder(this).setTitle(R.string.message_title).setMessage(R.string.usercode_or_password_empty_warning).setPositiveButton(R.string.OK_text,new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog,int which) {// TODO Auto-generated method stubdialog.cancel();}}).show();case LoginActions.DIALOG_EXIT_PROMPT:return new AlertDialog.Builder(this).setTitle(R.string.message_title).setMessage(R.string.exit_prompt).setPositiveButton(R.string.OK_text,new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog,int which) {finish();}}).setNegativeButton(R.string.cancel_text,new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog,int which) {dialog.cancel();}}).show();case LoginActions.DIALOG_VERSION_UPDATE:return new AlertDialog.Builder(this).setTitle(R.string.message_title).setMessage("此软件有新版本,需要更新,是否要下载新版本?").setPositiveButton(R.string.OK_text,new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog,int which) {// TODO Auto-generated method stubshowDialog(LoginActions.PROGRESS_DIALOG);}}).setNegativeButton(R.string.cancel_text,new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog,int which) {// TODO Auto-generated method stubloginActivities.loginSuccess();return;}}).show();}return super.onCreateDialog(id);}private class ProcessThread extends Thread implements OnCancelListener,OnDismissListener, OnClickListener {public void run() {try {loginActivities.deleteSDFile("voa/");} catch (IOException e1) {e1.printStackTrace();}// 下载文件路径,存储文件名,// 220.178.224.44String downfile = "http://192.168.0.2/webservices/apk/"+ loginActivities.result;downFile(downfile, "voa/", "DigitalCity.apk");}@Overridepublic void onClick(View v) {// TODO Auto-generated method stub}@Overridepublic void onDismiss() {// TODO Auto-generated method stub}@Overridepublic void onCancel() {// TODO Auto-generated method stub}}private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {// 定义一个Handler,用于处理下载线程与UI间通讯if (!Thread.currentThread().isInterrupted()) {switch (msg.what) {case 0:mProgressDialog.setMax(loginActivities.totalSize);case 1:mProgressDialog.setProgress(loginActivities.downloadedSize);break;case 2:loginActivities.downloadedSize = 0;mProgressDialog.dismiss();removeDialog(LoginActions.PROGRESS_DIALOG);AlertDialog.Builder dialog = new AlertDialog.Builder(DigitalCity.this);dialog.setTitle("提示").setMessage("下载完成,请安装!").setPositiveButton("确认",new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog,int which) {// TODO Auto-generated method stubUri data = Uri.parse("file://"+ loginActivities.SDPATH+ "voa/"+ "DigitalCity.apk");// 调用系统的安装方法Intent intent = new Intent(Intent.ACTION_VIEW);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.setDataAndType(data,"application/vnd.android.package-archive");DigitalCity.this.startActivity(intent);}});dialog.show();removeCallbacks(mThread);// android.os.Process.killProcess(android.os.Process.myPid());break;case -1:break;}}super.handleMessage(msg);}};/** * 将一个InputStream里面的数据写入到SD卡中 */public File write2SDFromInput(String path, String fileName,InputStream input) {File file = null;OutputStream output = null;try// InputStream里面的数据写入到SD卡中的固定方法{loginActivities.creatSDDir(path);file = loginActivities.creatSDFile(path + fileName);output = new FileOutputStream(file);byte buffer[] = new byte[4 * 1024];int bufferLength = 0;sendMsg(0);while ((bufferLength = input.read(buffer)) != -1) {output.write(buffer, 0, bufferLength);loginActivities.downloadedSize += bufferLength;sendMsg(1);// 更新进度条}sendMsg(2);// 通知下载完成output.flush();} catch (Exception e) {e.printStackTrace();} finally {try {output.close();} catch (Exception e) {e.printStackTrace();}}return file;}private void sendMsg(int flag) {Message msg = new Message();msg.what = flag;handler.sendMessage(msg);}/** * -1:代表下载文件出错 0:代表下载文件成功 1:代表文件已经存在 */public int downFile(String urlStr, String path, String fileName)// 下载文件的方法{InputStream inputStream = null;try {loginActivities.deleteSDFile(path);if (loginActivities.isFileExist(path + fileName)) {return 1;} else {inputStream = loginActivities.getInputStreamFromUrl(urlStr);File resultFile = write2SDFromInput(path, fileName, inputStream);if (resultFile == null) {return -1;}}} catch (Exception e) {e.printStackTrace();return -1;} finally {try {if (inputStream != null) {inputStream.close();}} catch (Exception e) {e.printStackTrace();return -1;}}return 0;}@Overrideprotected void onStop() {// TODO Auto-generated method stubsuper.onStop();}@Overrideprotected void setOnClickListener() {// TODO Auto-generated method stub}}

第四步:添加程序所用的权限:

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

注意:在登陆和下载的时候,与后台提供的接口都是通过webservice联系的。

原创粉丝点击