参考了其他一些大神的文章,最后自己也写了一下作为一个笔记吧,因为是菜鸟,希望有发现错误的地方能够帮忙指出,本文最后也提出几个我发现尚未被我解决的问题,希望大家能帮忙看看。
demo的逻辑过程:
1.进入程序
2.检查是否有版本更新,如果有则询问用户是否更新,否则维持原状
3.检测当前网络状态并且询问用户是否进行版本更新,如果是则进行更新,否则维持原状
4.切换网络,当当前网络为wifi时,检查版本更新,重复2、3.
结构:
CommonAsyncTask:执行网络请求操作
ConnectionUrl:记录要请求的IP地址
NetworkHelp:网络辅助类
upDateAppUtil:更新版本类
MainActivity:UI及执行界面
客户端:
MainActivity:
<span style="font-size:14px;">public class MainActivity extends Activity { //接收网络请求返回回调 private ListenerImpl mListenerImpl; private ProgressDialog m_progressDlg; private static final String TAG = "MainActivity"; private Dialog dialogs; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); m_progressDlg = new ProgressDialog(this); m_progressDlg.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); // 设置ProgressDialog 的进度条是否不明确 false 就是不设置为不明确 m_progressDlg.setIndeterminate(false); Log.d(TAG,"ONCREATE"); //注册广播接收器 registerReceiver(); //绑定网络数据回调接收器 initListener(); // //获取服务器版本 // updateAppUtil.getServerVersion(this); } protected void onStart(){ super.onStart(); Log.d(TAG, "ONSTART"); } /** * 网络数据回调 */ public void initListener() { mListenerImpl = null; mListenerImpl = ListenerImpl.getInstance(); mListenerImpl.setOnListener(new Listener() { @Override public <T> void receiveData(T data) { Log.d(TAG, data.toString()); dealAfterResponse((String) data); } }); } /** * 解析忘了数据 * @param s */ private void dealAfterResponse(String s) { try { JSONObject object; object = new JSONObject(s); if (object.getInt("Success")==200) { //版本需要更新操作 if (object.getInt("appVersion")!= updateAppUtil.getAppVersion(this)){ Log.d(TAG, "not same"); if (NetworkHelp.isWifi(this)){ if (dialogs==null) showDialog("有版本更新,是否更新版本"); } else { if (dialogs==null) showDialog("有版本更新,当前不在wifi状态,是否更新版本"); } } //版本不需要更新操作 else{ Log.d(TAG, "same"); } } } catch (JSONException e) { e.printStackTrace(); } } /** * 接收网络状态广播消息 */ public class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { ConnectivityManager connectivityManager=(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo mobNetInfo=connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); NetworkInfo wifiNetInfo=connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); if (!mobNetInfo.isConnected() && !wifiNetInfo.isConnected()) { Toast.makeText(context, "网络状态不可用", Toast.LENGTH_SHORT).show(); }else { dialogs=null; //获取服务器版本 Log.d(TAG,"MyReceiver"); updateAppUtil.getServerVersion(context); } } //如果无网络连接activeInfo为null } /** * 提示框 * @param str */ public void showDialog(String str){ dialogs = new AlertDialog.Builder(this).setTitle("软件更新").setMessage(str) // 设置内容 .setPositiveButton("更新",// 设置确定按钮 new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { m_progressDlg.setTitle("正在下载"); m_progressDlg.setMessage("请稍候..."); updateAppUtil.downNewApp(ConnectionUrl.GET_SERVER_IP, m_progressDlg); updateAppUtil.getAllFiles(new File("/sdcard/newApp")); } }) .setNegativeButton("暂不更新", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { dialogs.dismiss(); } }).create();// 创建 // 显示对话框 dialogs.show(); } /** * 注册广播接收器 */ private void registerReceiver(){ IntentFilter filter=new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); MyReceiver myReceiver=new MyReceiver(); this.registerReceiver(myReceiver, filter); } protected void onDestroy(){ super.onDestroy(); Log.d(TAG,"ONDESTORY"); } protected void onPause(){ super.onPause(); Log.d(TAG,"ONPAUSE"); if (isFinishing()){ Log.d(TAG,"ONONON"); } } } </span>
该类主要工作是注册了一个网络状态改变的广播接收器,当网络状态改变的时候就会执行不同的操作,但是经过这个demo发现他并非改变时才会发送广播,进入app后也会发送广播:
<span style="font-size:14px;">if (!mobNetInfo.isConnected() && !wifiNetInfo.isConnected()) { Toast.makeText(context, "网络状态不可用", Toast.LENGTH_SHORT).show(); }else { dialogs=null; //获取服务器版本 Log.d(TAG,"MyReceiver"); updateAppUtil.getServerVersion(context); }</span>
mobNetInfo是指手机卡网络,wifiNetInfo是指无线网络。当两者任意一个存在时就会执行以下代码获取服务器上的版本号:
updateAppUtil.getServerVersion(context);
该类还有一个工作是注册了一个回调,接收服务器返回的版本号并且调用dealAfterResponse方法解析:
<span style="font-size:14px;">public void initListener() { mListenerImpl = null; mListenerImpl = ListenerImpl.getInstance(); mListenerImpl.setOnListener(new Listener() { @Override public <T> void receiveData(T data) { Log.d(TAG, data.toString()); dealAfterResponse((String) data); } }); }</span>
调用getAppVersion能够获取当前app的版本号,版本号不同就会询问是否更新,判断不同的网络状态,弹出不同内容的提示框——showDialog():
<span style="font-size:14px;">if (object.getInt("appVersion")!= updateAppUtil.getAppVersion(this)){ Log.d(TAG, "not same"); if (NetworkHelp.isWifi(this)){ if (dialogs==null) showDialog("有版本更新,是否更新版本"); } else { if (dialogs==null) showDialog("有版本更新,当前不在wifi状态,是否更新版本"); } } //版本不需要更新操作 else{ Log.d(TAG, "same"); }</span>
点确定后调用以下方法下载并且安装新版本app:
<span style="font-size:14px;">updateAppUtil.downNewApp(ConnectionUrl.GET_SERVER_IP, m_progressDlg);</span>
updateAppUtil
该类封装了一些更新app版本要用到的一些方法。
<span style="font-size:14px;">public class updateAppUtil { private static Context mContext; private static ProgressDialog progressDialog; private static final String DIRECTORY_NAME = "/newApp"; private static final String File_NAME = "NewVersion.apk"; private static final String TAG = "updateAppUtil"; /** * 获取本app版本号 * @param context * @return */ public static int getAppVersion(Context context) { mContext =context; int verCode = -1; try { //对应AndroidManifest.xml里的package部分 verCode = context.getPackageManager().getPackageInfo( "com.test.tangjiarao.versionupdate", 0).versionCode; } catch (PackageManager.NameNotFoundException e) { Log.e("msg", e.getMessage()); } return verCode; } /** * 获取服务器的版本号 * @param context */ public static void getServerVersion(Context context){ Log.d(TAG,"getServerVersion"); new CommonAsyncTask(context).execute("get", ConnectionUrl.GET_SERVER_IP); } /** * 创建文件路径 */ public static File getDirectory(){ File file = new File(Environment.getExternalStorageDirectory() + DIRECTORY_NAME); //如果该路径不存在,则创建文件夹 if (!file.exists()) { file.mkdir(); } return file; } /** * 获取目标路径下的文件 * @param root */ public static void getAllFiles(File root){ File files[] = root.listFiles(); if(files != null) for(File f:files){ if(f.isDirectory()){ getAllFiles(f); } else{ Log.d(TAG, f.getName()); } } } /** * 下载app * @param path * @param mProgressDialog */ public static void downNewApp(String path,ProgressDialog mProgressDialog) { progressDialog =mProgressDialog; progressDialog.show(); new Thread() { public void run() { URL url = null; FileOutputStream fos = null; BufferedInputStream bis = null; HttpURLConnection connection = null; try { url = new URL(ConnectionUrl.DOWN_NEW_APP); connection = (HttpURLConnection) url.openConnection(); //不能获取服务器响应 if (HttpURLConnection.HTTP_OK != connection.getResponseCode()) { Message message = Message.obtain(); message.what = 1; handler.sendMessage(message); } //不存在sd卡 else if (Environment.getExternalStorageState() .equals(Environment.MEDIA_UNMOUNTED)){ Message message=Message.obtain(); message.what=2; handler.sendMessage(message); } //满足上两个条件 else{ //获取网络输入流 bis = new BufferedInputStream(connection.getInputStream()); //文件大小 int length = connection.getContentLength(); progressDialog.setMax((int)length); //缓冲区大小 byte[] buf = new byte[10]; int size =0; //获取存储文件的路径,在该路径下新建一个文件为写入流作准备 File cfile = new File(getDirectory().getPath(), File_NAME); //如果不存在则新建文件 if (!cfile.exists()) { cfile.createNewFile(); } //将流与文件绑定 fos = new FileOutputStream(cfile); //记录进度条 int count=0; //保存文件 while ((size = bis.read(buf)) != -1) { fos.write(buf, 0, size); count += size; if (length > 0) { progressDialog.setProgress(count); } } Log.d("JSON",count+""); Log.d("JSON","HAHA"+cfile.getAbsolutePath()+cfile.getName()); Bundle bundle=new Bundle(); Message message=Message.obtain(); message.what=3; bundle.putString("msg", cfile.getAbsolutePath()); message.setData(bundle); handler.sendMessage(message); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { try { if (fos!= null) { fos.close(); } if (bis != null) { bis.close(); } if (connection!= null) { connection.disconnect(); } } catch (Exception e) { e.printStackTrace(); } } } }.start(); } private static Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case 1: Toast.makeText(mContext, "网络状态不可用", Toast.LENGTH_SHORT).show(); Log.d(TAG, "网络不通"); break; case 2: Toast.makeText(mContext, "请插入SD卡", Toast.LENGTH_SHORT).show(); Log.d(TAG, "没有sd卡"); break; case 3: Bundle bundle = msg.getData(); String fileName = bundle.getString("msg"); installAPK(fileName,mContext); Log.d(TAG, "已经下载"); break; default: break; } }; }; /** * 安装app * @param fileName * @param mContext */ private static void installAPK(String fileName,Context mContext){ File file =new File(fileName); if(!file.exists()){ return; } Intent intent=new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction(Intent.ACTION_VIEW); Log.d(TAG,"AA"+"file://"+file.toString()); //"file://"+file.toString()下载的app的路径 intent.setDataAndType(Uri.parse("file://"+file.toString()), "application/vnd.android.package-archive"); mContext.startActivity(intent); } }</span><span style="font-size:18px;"> </span>
CommonAsyncTask
public class CommonAsyncTask extends AsyncTask<String,Integer,String>{ //显示UI的组件 private Context mContext; //回调 private ListenerImpl listener; //调用标识 private String flag; //访问url private String url; private String httpFuntion; //post传参 private Map<String, String> parameters; private final String TAG="CommonAsyncTask"; public CommonAsyncTask(Context mContext){ this.mContext = mContext; } //onPreExecute方法用于在执行后台任务前做一些操作 protected void onPreExecute() { super.onPreExecute(); Log.i(TAG, "onPreExecute() called"); if (!(NetworkHelp.isConnected(mContext))) { Toast.makeText(mContext, "网络状态不可用", Toast.LENGTH_SHORT).show(); return; } } //doInBackground方法内部执行后台任务,不可在此方法内修改UI @Override protected String doInBackground(String... params) { //get方法或者post方法的标识 httpFuntion= params[0]; url = params[1]; if(httpFuntion.equals("post")){ // flag =params[2]; // parameters = new HashMap<>(); // switch (flag) { // case "text" : // // parameters.put("account", params[3]); // break; // } return NetworkHelp.sendDataByPost(parameters, "utf-8", url); } else{ return NetworkHelp.getDataByGet("utf-8", url); } } //onProgressUpdate方法用于更新进度信息 @Override protected void onProgressUpdate(Integer... progresses) { Log.i(TAG, "onProgressUpdate(Progress... progresses) called"); } //onPostExecute方法用于在执行完后台任务后更新UI,显示结果 @Override protected void onPostExecute(String result) { Log.i(TAG, "onPostExecute(Result result) called"); super.onPostExecute(result); //获取返回数据后给MainActivity listener = null; listener = ListenerImpl.getInstance(); listener.transferData(result); clear(); } @Override protected void onCancelled() { Log.i(TAG, "onCancelled() called"); } protected void clear(){ parameters = null; flag = null; url = null; httpFuntion = null; } }<span style="font-size:18px;"> </span>NetWorkHelp<span style="font-size:14px;">public class NetworkHelp { private static final String TAG ="NetworkHelp"; private static final int TIMEOUT_MILLIONS = 8000; /** * 判断网络是否连接 * * @param context * @return * */ public static boolean isConnected(Context context) { ConnectivityManager connectivity = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); if (null != connectivity) { NetworkInfo info = connectivity.getActiveNetworkInfo(); if (null != info && info.isConnected()) { if (info.getState() == NetworkInfo.State.CONNECTED) { return true; } } } return false; } /** * 判断是否是wifi连接 */ public static boolean isWifi(Context context) { ConnectivityManager connectivity = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); if (connectivity == null) return false; return connectivity.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI; } /** * Get funtion * @param encode * @param path * @return */ public static String getDataByGet(String encode, String path){ URL url =null; HttpURLConnection connection =null; InputStream inptStream =null; int responseCode; try { url = new URL(path); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setReadTimeout(TIMEOUT_MILLIONS); connection.setConnectTimeout(TIMEOUT_MILLIONS); connection.setDoInput(true); connection.setUseCaches(false); responseCode = connection.getResponseCode(); if(responseCode == HttpURLConnection.HTTP_OK) { inptStream = connection.getInputStream(); Log.d(TAG,"GET FUNCTION OK"); return dealResponseResult(inptStream,encode); } } catch (IOException e) { return "err: " + e.getMessage().toString(); } finally { try { if (connection != null) { connection.disconnect(); } if (inptStream != null) { inptStream.close(); } } catch (IOException e) { e.printStackTrace(); } } return ""; } public static String sendDataByPost(Map<String, String> params, String encode, String path) { URL url=null; HttpURLConnection connection = null; OutputStream outputStream = null; InputStream inputStream = null; int responseCode; byte [] data = getRequestData(params, encode).toString().getBytes(); try { url = new URL(path); connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("POST"); connection.setConnectTimeout(TIMEOUT_MILLIONS); connection.setReadTimeout(TIMEOUT_MILLIONS); connection.setDoInput(true); connection.setDoOutput(true); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection.setRequestProperty("Content-Length", String.valueOf(data.length)); outputStream = connection.getOutputStream(); outputStream.write(data, 0, data.length); responseCode = connection.getResponseCode(); if (responseCode == 200) { Log.d(TAG,"POST FUNCTION OK"); inputStream = connection.getInputStream(); return dealResponseResult(inputStream, encode); } } catch (Exception e) { } finally { try { if (outputStream != null) { outputStream.close(); } if (inputStream != null) { inputStream.close(); } if (connection != null) { connection.disconnect(); } } catch (Exception e) { e.printStackTrace(); } } return ""; } public static StringBuffer getRequestData(Map<String, String> params, String encode) { StringBuffer buffer = new StringBuffer(); try { for (Map.Entry<String, String> entry : params.entrySet()) { buffer.append(entry.getKey()) .append("=") .append(URLEncoder.encode(entry.getValue(), encode)) .append("&"); } buffer.deleteCharAt(buffer.length() - 1); } catch (Exception e) { e.printStackTrace(); } return buffer; } public static String dealResponseResult(InputStream inputStream, String encode) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte [] data = new byte[1024]; int lenngth = 0; try { while ((lenngth = inputStream.read(data)) != -1) { byteArrayOutputStream.write(data, 0, lenngth); } return new String(byteArrayOutputStream.toByteArray(), encode); } catch (Exception e) { e.printStackTrace(); } return ""; } }</span>ConnectionUrl
<span style="font-size:14px;">public class ConnectionUrl { //获取版本号IP public static String GET_SERVER_IP = "http://192.168.0.62:3000/getVersion"; //下载app IP public static String DOWN_NEW_APP = "http://192.168.0.62:3000/updateApp"; }</span>服务器端(nodejs):
<span style="font-size:14px;">var express = require('express'); var router = express.Router(); /* GET home page. */ router.get('/updateApp', function(req, res, next) { ///Users/tangjiarao/version2.apk是该版本2apk在你计算机中的路径 res.download("/Users/tangjiarao/version2.apk","version2"); }); router.get('/getVersion', function(req, res, next) { //返回版本号 res.json({"Success":200,"appVersion":2}); }); module.exports = router;</span>演示:
进入程序界面&不更新&更新
安装&更新完成
问题:进入版本1app调用一次getServerVersion()调用一次,而更新版本2后,进入app捕抓不了mainActivy生命周期动作,并且调用两次getServerVersion()方法。
进入版本1app:
下载版本2后:
猜测:是否是因为广播接收器没有注销?
博客:
http://blog.csdn.net/jdsjlzx/article/details/46356013
http://blog.csdn.net/harvic880925/article/details/25191159
http://royzhou1985.iteye.com/blog/421961
http://outofmemory.cn/code-snippet/4663/network-xiazai-apk-zidong-install-xiao-example
源码:
http://download.csdn.net/detail/tangjiarao/9544361