参考了其他一些大神的文章,最后自己也写了一下作为一个笔记吧,因为是菜鸟,希望有发现错误的地方能够帮忙指出,本文最后也提出几个我发现尚未被我解决的问题,希望大家能帮忙看看。
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