我们在进行项目开发的时候可能会遇到一些获取屏幕宽度,dp px的相互转换等问题,我们当然不能每用一次就复制粘贴一次。这时候就需要一个利器-工具类。
这个工具类包含了我们一些公用的方法,只需要一句话我们就可以拿到想要的结果,既精简了我们的代码又省下了我们宝贵的时间。同时,一个好的软件工程师,万能的工具类便是他的护身法宝。
和上一篇文章一样 我只拿出几个特殊比较难理解的方法去讲解其余的我会贴在git上,如果有不懂得可以和我交流,我会把讲解贴到这篇文章里。
Github:https://github.com/EspressoToast/BaseTest
以下工具类包括:请求权限帮助类,校验帮助类,通用工具类,拼接文字,图片帮助类,MD5加密帮助类,String格式化帮助类,软键盘帮助类等
校验
大多数的app都会带有登陆注册功能,所以手机号和密码的校验是我们无法绕过的。
手机号码校验
我们先去建立一个枚举类对应几种校验的结果.
public enum PhoneNumberCheck{
/**校验通过**/
CHECK_SUCCESS,
/**号码为空**/
NUMBER_IS_NULL,
/**长度不足11位**/
NUMBER_TOO_SHORT,
/**电话号码首位不为1**/
NUMBER_START_NOT_ONE,
/**电话号码不全是数字组成**/
NUMBER_IS_NOT_NUMBER
}
接下来是方法.
/**
* 手机号码有效性校验
*/
public static PhoneNumberCheck checkPhoneNumber(String phoneNumber){
if(null == phoneNumber || phoneNumber.length() == 0){//号码为空
return PhoneNumberCheck.NUMBER_IS_NULL;
}else if(phoneNumber.length() != 11){//长度不足11位
return PhoneNumberCheck.NUMBER_TOO_SHORT;
}else if(!phoneNumber.startsWith("1")){//首位不为1
return PhoneNumberCheck.NUMBER_START_NOT_ONE;
}else if(!checkIsNumber(phoneNumber)){//不是全数字组成
return PhoneNumberCheck.NUMBER_IS_NOT_NUMBER;
}
return PhoneNumberCheck.CHECK_SUCCESS;
}
然后我们就可以通过去调用这个方法根据他的返回值来输出我们的结果了.
密码校验
只提供一个正则便可以达到我们想要的效果。
//密码正则 必须包含字母,数字,特殊字符三种中的两种组合,并且长度在6-16位
private static final String PWD_REGEX = "(?!^\\d+$)(?!^[a-zA-Z]+$)(?!^[~!@#$%^&*()_+-=]+$).{6,16}";
/**
* 密码正则校验
*
*/
public static boolean checkPwdRegex(String str){
return null != str && str.matches(PWD_REGEX);
}
log工具类
一个合格的项目缺少不了日志管理工具,有了日志管理工具我们可以更加方便的去调试程序。
public class Logger {
/**
* Wrapper API for logging
*/
protected final static String TAG = "YourProjectName"; // /< LOG TAG
protected final static int LOG_OUTPUT_MAX_LENGTH = 300;
public static void output(String msg) {
wrapperMsg(msg);
}
/**
* Wrapper the message
*
* @param msg : The message to wrapper
*/
protected static void wrapperMsg(String msg) {
if (null == msg || !isDebugMode()) {
return;
}
while (msg.length() > LOG_OUTPUT_MAX_LENGTH) {
final String tempMsg = msg.substring(0, LOG_OUTPUT_MAX_LENGTH);
msg = msg.substring(LOG_OUTPUT_MAX_LENGTH, msg.length());
Log.d(TAG, tempMsg);
}
StackTraceElement elem = new Throwable().fillInStackTrace()
.getStackTrace()[2];
Log.d(TAG, msg + "(" + elem.getFileName() + " : " + elem.getLineNumber() + ")");
}
public static void i(String tag, String msg) {
Log.i(tag, msg);
}
public static void e(String tag, String msg) {
Log.e(tag, msg);
}
public static void d(String tag, String msg) {
Log.e(tag, msg);
}
private static boolean isDebugMode() {
return false;
}
}
加密
项目里的密码或者其他数据为了保证安全就需要我们把它们加密。以下是常用的几种加密工具类:
/**
*
* MD5加密工具类
*/
public class MD5 {
// MD5加密
private static final char HEX_DIGITS[] = {'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
public static String md5(String s) {
try {
// Create MD5 Hash
MessageDigest digest = MessageDigest
.getInstance("MD5");
digest.update(s.getBytes());
byte[] messageDigest = digest.digest();
return toHexString(messageDigest).toLowerCase();
} catch (NoSuchAlgorithmException e) {
Logger.output("MD5 Exception:" + e.getMessage());
return "";
}
}
private static String toHexString(byte[] b) { // String to byte
StringBuilder sb = new StringBuilder(b.length * 2);
for (byte aB : b) {
sb.append(HEX_DIGITS[(aB & 0xf0) >>> 4]);
sb.append(HEX_DIGITS[aB & 0x0f]);
}
return sb.toString();
}
}
public class Base64 {
/** prevents anyone from instantiating this class */
private Base64() {
}
/**
* This character array provides the alphabet map from RFC1521.
*/
private final static char ALPHABET[] = {
// 0 1 2 3 4 5 6 7
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
'4', '5', '6', '7', '8', '9', '+', '/' // 7
};
/**
* Decodes a 7 bit Base64 character into its binary value.
*/
private static int valueDecoding[] = new int[128];
/**
* initializes the value decoding array from the
* character map
*/
static {
for (int i = 0; i < valueDecoding.length; i++) {
valueDecoding[i] = -1;
}
for (int i = 0; i < ALPHABET.length; i++) {
valueDecoding[ALPHABET[i]] = i;
}
}
/**
*
* @return base64 string
*/
public static String encodeFile(String filePath) throws IOException{
if(null != filePath){
File file = new File(filePath);
if(file.exists()){
FileInputStream inputFile = new FileInputStream(file);
byte[] buffer = new byte[(int)file.length()];
inputFile.read(buffer);
inputFile.close();
return encode(buffer, 0, buffer.length);
}
}
return null;
}
/**
* Converts a byte array into a Base64 encoded string.
* @param data bytes to encode
* @param offset which byte to start at
* @param length how many bytes to encode; padding will be added if needed
* @return base64 encoding of data; 4 chars for every 3 bytes
*/
public static String encode(byte[] data, int offset, int length) {
int i;
int encodedLen;
char[] encoded;
// 4 chars for 3 bytes, run input up to a multiple of 3
encodedLen = (length + 2) / 3 * 4;
encoded = new char [encodedLen];
for (i = 0, encodedLen = 0; encodedLen < encoded.length;
i += 3, encodedLen += 4) {
encodeQuantum(data, offset + i, length - i, encoded, encodedLen);
}
return new String(encoded);
}
/**
* Encodes 1, 2, or 3 bytes of data as 4 Base64 chars.
*
* @param in buffer of bytes to encode
* @param inOffset where the first byte to encode is
* @param len how many bytes to encode
* @param out buffer to put the output in
* @param outOffset where in the output buffer to put the chars
*/
private static void encodeQuantum(byte in[], int inOffset, int len,
char out[], int outOffset) {
byte a = 0, b = 0, c = 0;
a = in[inOffset];
out[outOffset] = ALPHABET[(a >>> 2) & 0x3F];
if (len > 2) {
b = in[inOffset + 1];
c = in[inOffset + 2];
out[outOffset + 1] = ALPHABET[((a << 4) & 0x30) +
((b >>> 4) & 0xf)];
out[outOffset + 2] = ALPHABET[((b << 2) & 0x3c) +
((c >>> 6) & 0x3)];
out[outOffset + 3] = ALPHABET[c & 0x3F];
} else if (len > 1) {
b = in[inOffset + 1];
out[outOffset + 1] = ALPHABET[((a << 4) & 0x30) +
((b >>> 4) & 0xf)];
out[outOffset + 2] = ALPHABET[((b << 2) & 0x3c) +
((c >>> 6) & 0x3)];
out[outOffset + 3] = '=';
} else {
out[outOffset + 1] = ALPHABET[((a << 4) & 0x30) +
((b >>> 4) & 0xf)];
out[outOffset + 2] = '=';
out[outOffset + 3] = '=';
}
}
/**
* Converts a Base64 encoded string to a byte array.
* @param encoded Base64 encoded data
* @return decode binary data; 3 bytes for every 4 chars - minus padding
* @exception IOException is thrown, if an I/O error occurs reading the data
*/
public static byte[] decode(String encoded)
throws IOException {
return decode(encoded, 0, encoded.length());
}
/**
* Converts an embedded Base64 encoded string to a byte array.
* @param encoded a String with Base64 data embedded in it
* @param offset which char of the String to start at
* @param length how many chars to decode; must be a multiple of 4
* @return decode binary data; 3 bytes for every 4 chars - minus padding
* @exception IOException is thrown, if an I/O error occurs reading the data
*/
public static byte[] decode(String encoded, int offset, int length)
throws IOException {
int i;
int decodedLen;
byte[] decoded;
// the input must be a multiple of 4
if (length % 4 != 0) {
throw new IOException(
"Base64 string length is not multiple of 4");
}
// 4 chars for 3 bytes, but there may have been pad bytes
decodedLen = length / 4 * 3;
if (encoded.charAt(offset + length - 1) == '=') {
decodedLen--;
if (encoded.charAt(offset + length - 2) == '=') {
decodedLen--;
}
}
decoded = new byte [decodedLen];
for (i = 0, decodedLen = 0; i < length; i += 4, decodedLen += 3) {
decodeQuantum(encoded.charAt(offset + i),
encoded.charAt(offset + i + 1),
encoded.charAt(offset + i + 2),
encoded.charAt(offset + i + 3),
decoded, decodedLen);
}
return decoded;
}
/**
* Decode 4 Base64 chars as 1, 2, or 3 bytes of data.
*
* @param in1 first char of quantum to decode
* @param in2 second char of quantum to decode
* @param in3 third char of quantum to decode
* @param in4 forth char of quantum to decode
* @param out buffer to put the output in
* @param outOffset where in the output buffer to put the bytes
*/
private static void decodeQuantum(char in1, char in2, char in3, char in4,
byte[] out, int outOffset)
throws IOException {
int a = 0, b = 0, c = 0, d = 0;
int pad = 0;
a = valueDecoding[in1 & 127];
b = valueDecoding[in2 & 127];
if (in4 == '=') {
pad++;
if (in3 == '=') {
pad++;
} else {
c = valueDecoding[in3 & 127];
}
} else {
c = valueDecoding[in3 & 127];
d = valueDecoding[in4 & 127];
}
if (a < 0 || b < 0 || c < 0 || d < 0) {
throw new IOException("Invalid character in Base64 string");
}
// the first byte is the 6 bits of a and 2 bits of b
out[outOffset] = (byte)(((a << 2) & 0xfc) | ((b >>> 4) & 3));
if (pad < 2) {
// the second byte is 4 bits of b and 4 bits of c
out[outOffset + 1] = (byte)(((b << 4) & 0xf0) | ((c >>> 2) & 0xf));
if (pad < 1) {
// the third byte is 2 bits of c and 4 bits of d
out[outOffset + 2] =
(byte)(((c << 6) & 0xc0) | (d & 0x3f));
}
}
}
}
下载apk并自动更新
为什么要单独提出这个方法来呢?因为正好前两天遇到了一个坑导致这个方法出问题了,于是查了查资料发现是android7.0系统适配的问题。
/**
* 安装APK
* 我是以前的安装apk方法
* @return
*/
public static void installApk(Context context, File file) {
Intent intent = new Intent();
//执行动作
intent.setAction(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//执行的数据类型
intent.setDataAndType(FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + BuildConfig.APPLICATION_ID + ".provider", file)
, "application/vnd.android.package-archive"); //此处Android应为android,否则造成安装不了
context.startActivity(intent);
}
/**
* 安装APK
* 我是现在的apk安装方法
* @return
*/
public static void installApk(Context context, File file) {
Intent intent = new Intent();
//执行动作
intent.setAction(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", file);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
if (context.getPackageManager().queryIntentActivities(intent, 0).size() > 0) {
context.startActivity(intent);
}
}
如果按照我以前的逻辑在6.0以上会报这个异常:
[android.os.FileUriExposedException:
原因是如果你的sdk是24或者更高的话你必须使用fileprovider来访问特定的文件。 具体操作方案点击这里
https://stackoverflow.com/questions/38200282/android-os-fileuriexposedexception-file-storage-emulated-0-test-txt-exposed