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