检测应用是否安装和检测版本更新,反之提供下载地址并安装应用(V1.2)

之前写过类似的博客,参见判断某一款应用(通过包名)是否安装,如果安装直接打开,否则弹出下载对话框(V1.0)_骑猪追大象的博客-CSDN博客,现在这个版本添加些功能并优化代码结构。

 

首先我们理一下思路:(参见下图)

思路脑图

思路图

1.新建一个工具类用于交互和一些固定的方法 BaseHelper

package com.lzy.demo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.json.JSONObject;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;

/**
 * @author luzhenyu
 * @describe <p>
 *           工具类
 *           <dd>
 *           </p>
 */
public class BaseHelper {

	/**
	 * 流转字符串方法
	 * 
	 * @param is
	 * @return
	 */
	public static String convertStreamToString(InputStream is) {
		BufferedReader reader = new BufferedReader(new InputStreamReader(is));
		StringBuilder sb = new StringBuilder();
		String line = null;
		try {
			while ((line = reader.readLine()) != null) {
				sb.append(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return sb.toString();
	}

	/**
	 * @describe <p>
	 *           显示dialog
	 *           <dd>
	 *           </p>
	 * 
	 * @param context
	 *            环境
	 * @param strTitle
	 *            标题
	 * @param strText
	 *            内容
	 * @param icon
	 *            图标
	 */
	public static void showDialog(Activity context, String strTitle,
			String strText, int icon) {
		AlertDialog.Builder tDialog = new AlertDialog.Builder(context);
		tDialog.setIcon(icon);
		tDialog.setTitle(strTitle);
		tDialog.setMessage(strText);
		tDialog.setPositiveButton("取消", null);
		tDialog.show();
	}

	/**
	 * @describe <p>
	 *           获取权限
	 *           <dd>
	 *           </p>
	 * 
	 * @param permission
	 *            权限
	 * @param path
	 *            路径
	 */
	public static void chmod(String permission, String path) {
		try {
			String command = "chmod " + permission + " " + path;
			Runtime runtime = Runtime.getRuntime();
			runtime.exec(command);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	//
	// show the progress bar.
	/**
	 * @describe <p>
	 *           显示进度条
	 *           <dd>
	 *           </p>
	 * 
	 * @param context
	 *            环境
	 * @param title
	 *            标题
	 * @param message
	 *            信息
	 * @param indeterminate
	 *            确定性
	 * @param cancelable
	 *            可撤销
	 * @return
	 */
	public static ProgressDialog showProgress(Context context,
			CharSequence title, CharSequence message, boolean indeterminate,
			boolean cancelable) {
		ProgressDialog dialog = new ProgressDialog(context);
		dialog.setTitle(title);
		dialog.setMessage(message);
		dialog.setIndeterminate(indeterminate);
		dialog.setCancelable(false);
		dialog.setOnCancelListener(new OnCancelListener() {

			@Override
			public void onCancel(DialogInterface dialog) {

			}
		});

		dialog.show();
		return dialog;
	}

	/**
	 * @describe <p>
	 *           字符串转json对象
	 *           <dd>
	 *           </p>
	 * @param str
	 * @param split
	 * @return
	 */
	public static JSONObject string2JSON(String str, String split) {
		JSONObject json = new JSONObject();
		try {
			String[] arrStr = str.split(split);
			for (int i = 0; i < arrStr.length; i++) {
				String[] arrKeyValue = arrStr[i].split("=");
				json.put(arrKeyValue[0],
						arrStr[i].substring(arrKeyValue[0].length() + 1));
			}
		}

		catch (Exception e) {
			e.printStackTrace();
		}

		return json;
	}
}


2.网络交互类 NetworkManager

package com.lzy.demo;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.util.ArrayList;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;

import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.message.BasicNameValuePair;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

/**
 * @author luzhenyu
 * @describe <p>
 *           网络连接工具类
 *           <dd>
 *           </p>
 */
public class NetworkManager {
	static final String TAG = "NetworkManager";

	private int connectTimeout = 30 * 1000;
	private int readTimeout = 30 * 1000;
	Proxy mProxy = null;
	Context mContext;

	public NetworkManager(Context context) {
		this.mContext = context;
		setDefaultHostnameVerifier();
	}

	/**
	 * @describe <p>
	 *           检查代理,是否cnwap接入
	 *           <dd>
	 *           </p>
	 */
	private void detectProxy() {
		ConnectivityManager cm = (ConnectivityManager) mContext
				.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo ni = cm.getActiveNetworkInfo();
		if (ni != null && ni.isAvailable()
				&& ni.getType() == ConnectivityManager.TYPE_MOBILE) {
			@SuppressWarnings("deprecation")
			String proxyHost = android.net.Proxy.getDefaultHost();
			@SuppressWarnings("deprecation")
			int port = android.net.Proxy.getDefaultPort();
			if (proxyHost != null) {
				final InetSocketAddress sa = new InetSocketAddress(proxyHost,
						port);
				mProxy = new Proxy(Proxy.Type.HTTP, sa);
			}
		}
	}

	private void setDefaultHostnameVerifier() {
		HostnameVerifier hv = new HostnameVerifier() {
			public boolean verify(String hostname, SSLSession session) {
				return true;
			}
		};

		HttpsURLConnection.setDefaultHostnameVerifier(hv);
	}

	/**
	 * @describe <p>
	 *           发送和接收数据
	 *           <dd>
	 *           </p>
	 * 
	 * @param strReqData
	 *            请求数据
	 * @param strUrl
	 *            请求地址
	 * @return
	 */
	public String SendAndWaitResponse(String strReqData, String strUrl) {
		detectProxy();

		String strResponse = null;
		ArrayList<BasicNameValuePair> pairs = new ArrayList<BasicNameValuePair>();
		pairs.add(new BasicNameValuePair("requestData", strReqData));

		HttpURLConnection httpConnect = null;
		UrlEncodedFormEntity p_entity;
		try {
			p_entity = new UrlEncodedFormEntity(pairs, "utf-8");
			URL url = new URL(strUrl);

			if (mProxy != null) {
				httpConnect = (HttpURLConnection) url.openConnection(mProxy);
			} else {
				httpConnect = (HttpURLConnection) url.openConnection();
			}
			httpConnect.setConnectTimeout(connectTimeout);
			httpConnect.setReadTimeout(readTimeout);
			httpConnect.setDoOutput(true);
			httpConnect.addRequestProperty("Content-type",
					"application/x-www-form-urlencoded;charset=utf-8");

			httpConnect.connect();

			OutputStream os = httpConnect.getOutputStream();
			p_entity.writeTo(os);
			os.flush();

			InputStream content = httpConnect.getInputStream();
			strResponse = BaseHelper.convertStreamToString(content);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			httpConnect.disconnect();
		}

		return strResponse;
	}

	/**
	 * @describe <p>
	 *           下载文件
	 *           <dd>
	 *           </p>
	 * 
	 * @param context
	 *            上下文环境
	 * @param strurl
	 *            下载地址
	 * @param path
	 *            下载路径
	 * @return
	 */
	public boolean urlDownloadToFile(Context context, String strurl, String path) {
		boolean bRet = false;

		detectProxy();

		try {
			URL url = new URL(strurl);
			HttpURLConnection conn = null;
			if (mProxy != null) {
				conn = (HttpURLConnection) url.openConnection(mProxy);
			} else {
				conn = (HttpURLConnection) url.openConnection();
			}
			conn.setConnectTimeout(connectTimeout);
			conn.setReadTimeout(readTimeout);
			conn.setDoInput(true);

			conn.connect();
			InputStream is = conn.getInputStream();

			File file = new File(path);
			file.createNewFile();
			FileOutputStream fos = new FileOutputStream(file);

			byte[] temp = new byte[1024];
			int i = 0;
			while ((i = is.read(temp)) > 0) {
				fos.write(temp, 0, i);
			}

			fos.close();
			is.close();

			bRet = true;

		} catch (IOException e) {
			e.printStackTrace();
		}

		return bRet;
	}
}

3.对外调用方法汇合类  里面都设置了public访问权限,可以单独调用也可以使用混合调用,里面有些方法还是挺实用的

package com.lzy.demo;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;

/**
 * @author luzhenyu
 * @version 1.0
 * @describe <p>
 *           检测工具类
 *           <dd>
 *           </p>
 * */
public class DetectHelper {
	@SuppressWarnings("unused")
	private static final String TAG = "DetectHelper";

	private ProgressDialog mProgress = null;
	private Context mContext = null;

	public DetectHelper() {

	}

	public DetectHelper(Context mContext) {
		this.mContext = mContext;
	}

	/**
	 * @describe <p>
	 *           检测手机是否安装,如果没有则先从assets文件下安装/网络上下载安装,并检测是否需要更新,汇总调用方法
	 *           <dd>
	 *           </p>
	 * */
	public void detectMobile() {
		if (detectIsInstall(Constant.PACKAGENAME_STRING)) {// "com.lzy.appdemo"
															// 采用文件或者一个静态类来管理
			// 根据需要进行处理
			startApplicationByPackageName(Constant.PACKAGENAME_STRING);
		} else {
			inStallAppFromAssets(Constant.APKNAME_STRING);
			// 检测版本更新
			// 对话框
			mProgress = BaseHelper
					.showProgress(mContext, null, "正在检测更新", false, true);
			// 子线程进行检查更新耗时操作
			new Thread(new Runnable() {

				@Override
				public void run() {
					checkUpdate();
				}

			}).start();
		}

	}

	public void checkUpdate() {
		// 获取系统缓存目录
		File cacheDir = mContext.getCacheDir();
		final String cachePath = cacheDir.getAbsolutePath() + "/temp.apk";
		// 检测是否有新的版本。
		PackageInfo apkInfo = getApkInfo(mContext, cachePath);
		String newApkdlUrl = checkNewUpdate(apkInfo);

		//
		// 动态下载
		if (newApkdlUrl != null)
			retrieveApkFromNet(mContext, newApkdlUrl, cachePath);

		// 发送结果
		Message msg = new Message();
		msg.what = Constant.INSTALL_CHECK;
		msg.obj = cachePath;
		mHandler.sendMessage(msg);

	}

	/**
	 * @describe <p>
	 *           从assets目录捆绑安装APP
	 *           <dd>
	 *           </p>
	 * @param apkName
	 *            - String
	 * @return true if assets file exist you need apk,or false
	 * */
	public boolean inStallAppFromAssets(String apkName) {
		boolean exist = false;
		// 获取系统缓存目录
		File cacheDir = mContext.getCacheDir();
		final String cachePath = cacheDir.getAbsolutePath() + "/temp.apk";
		try {
			InputStream inputStream = mContext.getAssets().open(apkName);
			File file = new File(cachePath);
			file.createNewFile();
			FileOutputStream fileOutputStream = new FileOutputStream(file);

			byte[] temp = new byte[1024];
			int i = 0;
			while ((i = inputStream.read(temp)) > 0) {
				fileOutputStream.write(temp, 0, i);
			}

			fileOutputStream.close();
			inputStream.close();

			exist = true;
		} catch (Exception e) {
			exist = false;
		}
		return exist;
	}

	/**
	 * @describe <p>
	 *           根据报名启动应用程序
	 *           <dd>
	 *           </p>
	 * @param packageName
	 *            - String
	 * */
	public void startApplicationByPackageName(String packageName) {
		PackageManager packageManager = mContext.getPackageManager();
		Intent intent = packageManager.getLaunchIntentForPackage(packageName);
		mContext.startActivity(intent);
	}

	/**
	 * @describe <p>
	 *           检测手机是否安装xxxxAPP
	 *           <dd>
	 *           </p>
	 * @param packageName
	 *            - String
	 * @return ture if install,or false
	 * */
	public boolean detectIsInstall(String packageName) {
		boolean isInstall = false;
		PackageManager mPackageManager = mContext.getPackageManager();
		List<PackageInfo> packageInfos = mPackageManager
				.getInstalledPackages(0);
		for (int i = 0, len = packageInfos.size(); i < len; i++) {
			PackageInfo info = packageInfos.get(i);
			if (info.packageName.equalsIgnoreCase(packageName)) {
				isInstall = true;
			} else {
				isInstall = false;
			}
		}
		return isInstall;
	}
	
	/**
	 * @describe <p> 获取未安装的APK信息<dd></p>
	 * @param context
	 * @param archiveFilePath
	 *            APK文件的路径。如:/sdcard/download/XX.apk
	 */
	public static PackageInfo getApkInfo(Context context, String archiveFilePath) {
		PackageManager pm = context.getPackageManager();
		PackageInfo apkInfo = pm.getPackageArchiveInfo(archiveFilePath,
				PackageManager.GET_META_DATA);
		return apkInfo;
	}

	/**
	 * @describe <p>检查是否有新版本,如果有,返回apk下载地址<dd></p>
	 * @param packageInfo
	 *            {@link PackageInfo}
	 * @return
	 */
	public String checkNewUpdate(PackageInfo packageInfo) {
		String url = null;

		try {
			JSONObject resp = sendCheckNewUpdate(packageInfo.versionName);
			if (resp.getString("needUpdate").equalsIgnoreCase("true")) {
				url = resp.getString("updateUrl");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return url;
	}

	/**
	 * @describe <p>发送当前版本信息,返回是否需要升级 如果需要升级返回更新apk地址<dd></p>
	 * @param versionName
	 *            当前版本号
	 * @return
	 */
	public JSONObject sendCheckNewUpdate(String versionName) {
		JSONObject objResp = null;
		try {
			JSONObject req = new JSONObject();
			req.put(Constant.ACTION, Constant.ACTIONUPDATE);

			JSONObject data = new JSONObject();
			data.put(Constant.PLATFORM, "android");
			data.put(Constant.VERSION, versionName);
			data.put(Constant.PARTENER, "");
			
			req.put(Constant.DATA, data);

			objResp = sendRequest(req.toString());
		} catch (JSONException e) {
			e.printStackTrace();
		}

		return objResp;
	}

	/**
	 * @describe <p>发送json数据<dd></p>
	 * @param content
	 * @return
	 */
	public JSONObject sendRequest(final String content) {
		NetworkManager nM = new NetworkManager(this.mContext);

		JSONObject jsonResponse = null;
		try {
			String response = null;

			synchronized (nM) {
				response = nM.SendAndWaitResponse(content, Constant.SERVER_STRING);
			}

			jsonResponse = new JSONObject(response);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return jsonResponse;
	}

	/**
	 * @describe <p>从网络上动态下载apk<dd></p>
	 * @param context
	 *            上下文环境
	 * @param strurl
	 *            下载地址
	 * @param filename
	 *            文件名称
	 * @return
	 */
	public boolean retrieveApkFromNet(Context context, String strurl,
			String filename) {
		boolean bRet = false;

		try {
			NetworkManager nM = new NetworkManager(this.mContext);
			bRet = nM.urlDownloadToFile(context, strurl, filename);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return bRet;
	}

	//
	// close the progress bar
	public void closeProgress() {
		try {
			if (mProgress != null) {
				mProgress.dismiss();
				mProgress = null;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@SuppressLint("HandlerLeak")
	private Handler mHandler = new Handler() {
		public void handleMessage(Message msg) {
			try {
				switch (msg.what) {
				case Constant.INSTALL_CHECK: {
					closeProgress();
					String cachePath = (String) msg.obj;
					showInstallConfirmDialog(mContext, cachePath);
				}
					break;
				}

				super.handleMessage(msg);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	};

	/**
	 * @describe <p>显示确认安装的提示<dd></p>
	 * @param context
	 *            上下文环境
	 * @param cachePath
	 *            安装文件路径
	 */
	public void showInstallConfirmDialog(final Context context,
			final String cachePath) {
		AlertDialog.Builder tDialog = new AlertDialog.Builder(context);
		tDialog.setIcon(R.drawable.ic_launcher);
		tDialog.setTitle("安装提示");
		tDialog.setMessage("您还没有安装XXXX。\n\n点击确定,立即安装。");

		tDialog.setPositiveButton("确定",
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
						//
						// 修改apk权限
						BaseHelper.chmod("777", cachePath);

						//
						// install the apk.
						Intent intent = new Intent(Intent.ACTION_VIEW);
						intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
						intent.setDataAndType(Uri.parse("file://" + cachePath),
								"application/vnd.android.package-archive");
						context.startActivity(intent);
					}
				});

		tDialog.setNegativeButton("取消",
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
					}
				});

		tDialog.show();
	}

}


4.为了便于管理一些常用的静态字段和上传服务器参数字段,采用一个静态类Constant 实现,对于以后的更新维护更方便。

package com.lzy.demo;

/**
 * @author luzhenyu
 * @describe <p>静态字段<dd></p>
 * */
public class Constant
{
	public final static int INSTALL_CHECK = 0x123;
	
	//参数根据实际开发的需求定义和传递,这里采用的是阿里巴巴的
	public final static String ACTION = "action";
	public final static String ACTIONUPDATE =  "update";
	public final static String DATA = "data";
	public final static String PLATFORM	= "platform";
	public final static String VERSION = "version";
	public final static String PARTENER = "partner";
	
	public final static String APKNAME_STRING = "TransActionBarDemo.apk";//assets文件夹下apk名称
	public final static String PACKAGENAME_STRING = "com.lzy.appdemo";//要安装的APP的包名
	public final static String SERVER_STRING = "https://msp.alipay.com/x.htm";//获取更新服务器地址
}

5.最后别忘了在AndroidManifest.xml中添加权限

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

        至此整个可以复用的功能完成了,当然,在具体的开发中可能会有不同,比如网络访问这块可能采用的第三方框架,基本思路就是这样,在开发中改动一下接口的访问方式和参数就可以转化为你的功能,这里使用的是阿里巴巴的接口,非常感谢阿里巴巴。

看到了一篇采用Xml方式更新的博客,在此也贴出飞机票:Android自动检测版本及自动升级_傅荣康的博客-CSDN博客_android 自动升级

再次说明,实际开发中请根据需要,改动这几个文件的某些实现方法。

方便大家也是方便自己,分享让世界更美好。

【欢迎上码】

【微信公众号搜索 h2o2s2】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值