import android.content.Context; import android.content.SharedPreferences; import android.util.Base64; import java.io.UnsupportedEncodingException; import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * 加密Preference数据的类 */ public class SecurePreferences { public static class SecurePreferencesException extends RuntimeException { public SecurePreferencesException(Throwable e) { super(e); } } private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding"; private static final String KEY_TRANSFORMATION = "AES/ECB/PKCS5Padding"; private static final String SECRET_KEY_HASH_TRANSFORMATION = "SHA-256"; private static final String CHARSET = "UTF-8"; private final boolean encryptKeys; private final Cipher writer; private final Cipher reader; private final Cipher keyWriter; private final SharedPreferences preferences; /** * This will initialize an instance of the SecurePreferences class * * @param context * your current context. * @param preferenceName * name of preferences file (preferenceName.xml) * @param secureKey * the key used for encryption, finding a good key scheme is * hard. Hardcoding your key in the application is bad, but * better than plaintext preferences. Having the user enter the * key upon application launch is a safe(r) alternative, but * annoying to the user. * @param encryptKeys * settings this to false will only encrypt the values, true will * encrypt both values and keys. Keys can contain a lot of * information about the plaintext value of the value which can * be used to decipher the value. */ public SecurePreferences(Context context, String preferenceName, String secureKey, boolean encryptKeys) throws SecurePreferencesException { try { this.writer = Cipher.getInstance(TRANSFORMATION); this.reader = Cipher.getInstance(TRANSFORMATION); this.keyWriter = Cipher.getInstance(KEY_TRANSFORMATION); initCiphers(secureKey); this.preferences = context.getSharedPreferences(preferenceName, Context.MODE_PRIVATE); this.encryptKeys = encryptKeys; } catch (GeneralSecurityException e) { throw new SecurePreferencesException(e); } catch (UnsupportedEncodingException e) { throw new SecurePreferencesException(e); } } protected void initCiphers(String secureKey) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException { IvParameterSpec ivSpec = getIv(); SecretKeySpec secretKey = getSecretKey(secureKey); writer.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec); reader.init(Cipher.DECRYPT_MODE, secretKey, ivSpec); keyWriter.init(Cipher.ENCRYPT_MODE, secretKey); } protected IvParameterSpec getIv() { byte[] iv = new byte[writer.getBlockSize()]; System.arraycopy("fldsjfodasjifudslfjdsaofshaufihadsf".getBytes(), 0, iv, 0, writer.getBlockSize()); return new IvParameterSpec(iv); } protected SecretKeySpec getSecretKey(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException { byte[] keyBytes = createKeyBytes(key); return new SecretKeySpec(keyBytes, TRANSFORMATION); } protected byte[] createKeyBytes(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance(SECRET_KEY_HASH_TRANSFORMATION); md.reset(); byte[] keyBytes = md.digest(key.getBytes(CHARSET)); return keyBytes; } public void put(String key, String value) { if (value == null) { preferences.edit().remove(toKey(key)).commit(); } else { putValue(toKey(key), value); } } public boolean containsKey(String key) { return preferences.contains(toKey(key)); } public void removeValue(String key) { preferences.edit().remove(toKey(key)).commit(); } public String getString(String key) throws SecurePreferencesException { if (preferences.contains(toKey(key))) { String securedEncodedValue = preferences.getString(toKey(key), ""); return decrypt(securedEncodedValue); } return null; } public void clear() { preferences.edit().clear().commit(); } private String toKey(String key) { if (encryptKeys) return encrypt(key, keyWriter); else return key; } private void putValue(String key, String value) throws SecurePreferencesException { String secureValueEncoded = encrypt(value, writer); preferences.edit().putString(key, secureValueEncoded).commit(); } protected String encrypt(String value, Cipher writer) throws SecurePreferencesException { byte[] secureValue; try { secureValue = convert(writer, value.getBytes(CHARSET)); } catch (UnsupportedEncodingException e) { throw new SecurePreferencesException(e); } String secureValueEncoded = Base64.encodeToString(secureValue, Base64.NO_WRAP); return secureValueEncoded; } protected String decrypt(String securedEncodedValue) { byte[] securedValue = Base64.decode(securedEncodedValue, Base64.NO_WRAP); byte[] value = convert(reader, securedValue); try { return new String(value, CHARSET); } catch (UnsupportedEncodingException e) { throw new SecurePreferencesException(e); } } private static byte[] convert(Cipher cipher, byte[] bs) throws SecurePreferencesException { try { return cipher.doFinal(bs); } catch (Exception e) { throw new SecurePreferencesException(e); } } }
月份:2017年12月
URL处理:解析
import android.net.Uri; import java.net.URLDecoder; import java.util.regex.Pattern; public class Parser { protected static final Pattern PAT_QUERY = Pattern.compile("\\?(([a-z0-9-._~]|%[0-9a-f]{2}|[!$&'()*+,;=]|[:@]|[/?]|[/#])*)", Pattern.CASE_INSENSITIVE); public Parser() { } public static String getQueryParameter(String url, String key){ try { int qsi = url.indexOf('?', url.indexOf(':')); String query = url.substring(qsi + 1); final String encodedKey = Uri.encode(key, null); final int length = query.length(); int start = 0; do { int nextAmpersand = query.indexOf('&', start); int end = nextAmpersand != -1 ? nextAmpersand : length; int separator = query.indexOf('=', start); if (separator > end || separator == -1) { separator = end; } if (separator - start == encodedKey.length() && query.regionMatches(start, encodedKey, 0, encodedKey.length())) { if (separator == end) { return ""; } else { String encodedValue = query.substring(separator + 1, end); return URLDecoder.decode(encodedValue, "utf-8"); } } // Move start to end of name. if (nextAmpersand != -1) { start = nextAmpersand + 1; } else { break; } } while (true); }catch (Exception e){ } return null; } public class Components { public String scheme_ = null; public String userinfo_ = null; public String host_ = null; public HostType hostType_ = null; public Integer port_ = null; public String[] path_ = null; public Boolean pathAbsolute_ = null; public String query_ = null; public String fragment_ = null; } public enum HostType { IPV4, IPV6, IPVFUTURE, REG_NAME } }
位操作:高、中、低8位处理
public class BitwiseOperateUtil { /** * 计算结果 * @param top 高16位 * @param middle 中8位 * @param mantissa 尾8位 * @return */ public static int calSourceFromMask(int top,int middle,int mantissa){ return (top << 16) | (middle << 8) | mantissa; } /** * 获取指定位 * @param n 原值 * @param m 位数 * @return */ int getBit(int n, int m){ return (n >> (m-1)) & 1; } /** * 获取高16位 * @param value * @return */ private int getTopSixteen(int value){ return value >> 16; } /** * 获取中8位 * @param value * @return */ private int getMidEight(int value){ return getMattissaEight(value >> 8); } /** * 获取低8位 * @param value * @return */ private int getMattissaEight(int value){ return (value & 255); } }
MD5:字符串和文件
import java.io.FileInputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class Md5Utils { //十六进制 private static final char HEX_DIGITS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * byte数组转换为转换为16进制 * @param b 数据 * @return 返回字符串 */ private static String toHexString(byte[] b) { StringBuilder sb = new StringBuilder(b.length * 2); for (int i = 0; i < b.length; i++) { sb.append(HEX_DIGITS[(b[i] & 0xf0) >>> 4]); sb.append(HEX_DIGITS[b[i] & 0x0f]); } return sb.toString(); } /** * MD5对字符串校验 * @param string * @return */ public static String md5(String string) { byte[] bytes; try { bytes = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8")); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("MD5 should be supported?", e); } catch (UnsupportedEncodingException e) { throw new RuntimeException("UTF-8 should be supported?", e); } return toHexString(bytes); } /** * 取文件MD5值 * @param filename 文件路径 * @return MD5值 */ public static String fileMD5(String filename) { InputStream fis; byte[] buffer = new byte[1024]; int numRead = 0; MessageDigest md5; try { fis = new FileInputStream(filename); md5 = MessageDigest.getInstance("MD5"); while ((numRead = fis.read(buffer)) > 0) { md5.update(buffer, 0, numRead); } fis.close(); return toHexString(md5.digest()); } catch (Exception e) { System.out.println("error"); return null; } } }
Zip使用:压缩和解压缩“文件”
import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Enumeration; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; public class ZipFileUtil { private static final int BUFF_SIZE = 1024; private static ExecutorService mExecutor = Executors.newCachedThreadPool(); public static void unzipFile(File file, String folderPath, UnZipProgress progress){ mExecutor.execute(new UnZipTask(file, folderPath, progress)); } public interface UnZipProgress{ int STATUS_COMPLETE = 0; int STATUS_FAILURE = 1; void progress(int status, int progress); } private static class UnZipTask implements Runnable { private File mZipFile; private String mFolderPath; private UnZipProgress mUnZipProgress; UnZipTask(File zipFile, String folderPath, UnZipProgress progress) { mZipFile = zipFile; mFolderPath = folderPath; mUnZipProgress = progress; } public void unzipFile(File zipFile, String folderPath) throws ZipException, IOException { File desDir = new File(folderPath); if (!desDir.exists()) { desDir.mkdirs(); } ZipFile zf = new ZipFile(zipFile); for (Enumeration<?> entries = zf.entries(); entries.hasMoreElements();) { ZipEntry entry = ((ZipEntry)entries.nextElement()); if (entry.isDirectory()) { continue; } InputStream in = zf.getInputStream(entry); String str = folderPath + File.separator + entry.getName(); str = new String(str.getBytes(), "utf-8"); File desFile = new File(str); if (!desFile.exists()) { File fileParentDir = desFile.getParentFile(); if (!fileParentDir.exists()) { fileParentDir.mkdirs(); } desFile.createNewFile(); } OutputStream out = new FileOutputStream(desFile); byte buffer[] = new byte[BUFF_SIZE]; int realLength; while ((realLength = in.read(buffer)) > 0) { out.write(buffer, 0, realLength); } in.close(); out.close(); } zf.close(); if(mUnZipProgress != null){ mUnZipProgress.progress(UnZipProgress.STATUS_COMPLETE, 0); } } @Override public void run() { try { unzipFile(mZipFile, mFolderPath); }catch (Exception e) { if(mUnZipProgress != null){ mUnZipProgress.progress(UnZipProgress.STATUS_FAILURE, 0); } e.printStackTrace(); } } } }
Zip使用:压缩和解压缩“字符串”
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; public class ZipUtils { private static final String LOG_TAG = "ZipUtils"; /* * 用标准的Zip包压缩数据 */ public static String compress(String str){ try{ if (str == null || str.length() == 0) { return str; } ByteArrayOutputStream out = new ByteArrayOutputStream(); GZIPOutputStream gzip = new GZIPOutputStream(out); gzip.write(str.getBytes()); gzip.close(); return out.toString("ISO-8859-1"); } catch (Exception e) { Logger.i(LOG_TAG, "IOException,Failed to compress1 message."+ e.toString()); } return null; } // 解压缩 public static String uncompress(String str){ try{ if (str == null || str.length() == 0) { return str; } ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes("ISO-8859-1")); GZIPInputStream gunzip = new GZIPInputStream(in); byte[] buffer = new byte[256]; int n; while((n = gunzip.read(buffer))>=0) { out.write(buffer,0,n); } return out.toString(); } catch(Exception e) { Logger.i(LOG_TAG, "IOException,Failed to uncompress1 message."+ e.toString()); } return null; } }
解决 Could not find com.android.tools.build:gradle:3.0.0-alpha1 in circle ci
解决 Could not find com.android.tools.build:gradle:3.0.0-alpha1 in circle ci
如下:
buildscript {
repositories {
jcenter()
maven {
url 'https://maven.google.com'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0-alpha1'
classpath 'com.tencent.bugly:symtabfileuploader:latest.release'
}
}
修改成
buildscript {
repositories {
jcenter()
maven {
url 'https://dl.google.com/dl/android/maven2'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0-alpha1'
classpath 'com.tencent.bugly:symtabfileuploader:latest.release'
}
}
SimpleDateFormat使用详解
SimpleDateFormat
是一个以国别敏感的方式格式化和分析数据的具体类。 它允许格式化 (date -> text)、语法分析 (text -> date)和标准化。
SimpleDateFormat函数语法:
G 年代标志符
y 年
M 月
d 日
h 时 在上午或下午 (1~12)
H 时 在一天中 (0~23)
m 分
s 秒
S 毫秒
E 星期
D 一年中的第几天
F 一月中第几个星期几
w 一年中第几个星期
W 一月中第几个星期
a 上午 / 下午 标记符
k 时 在一天中 (1~24)
K 时 在上午或下午 (0~11)
z 时区
public static void main(String[] args) {
SimpleDateFormat myFmt=new SimpleDateFormat(“yyyy年MM月dd日 HH时mm分ss秒”);
SimpleDateFormat myFmt1=new SimpleDateFormat(“yy/MM/dd HH:mm”);
SimpleDateFormat myFmt2=new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);//等价于now.toLocaleString()
SimpleDateFormat myFmt3=new SimpleDateFormat(“yyyy年MM月dd日 HH时mm分ss秒 E “);
SimpleDateFormat myFmt4=new SimpleDateFormat(
“一年中的第 D 天 一年中第w个星期 一月中第W个星期 在一天中k时 z时区”);
Date now=new Date();
System.out.println(myFmt.format(now));
System.out.println(myFmt1.format(now));
System.out.println(myFmt2.format(now));
System.out.println(myFmt3.format(now));
System.out.println(myFmt4.format(now));
System.out.println(now.toGMTString());
System.out.println(now.toLocaleString());
System.out.println(now.toString());
}
2004年12月16日 17时24分27秒
04/12/16 17:24
2004-12-16 17:24:27
2004年12月16日 17时24分27秒 星期四
一年中的第 351 天 一年中第51个星期 一月中第3个星期 在一天中17时 CST时区
16 Dec 2004 09:24:27 GMT
2004-12-16 17:24:27
Thu Dec 16 17:24:27 CST 2004
AndroidManifest 合并出错 tools:replace=”android:allowBackup”
出现的问题: Manifest merger failed : Attribute application@allowBackup value=(false) from AndroidManifest.xml:116:9-36
is also present at [manager:common:unspecified:release] AndroidManifest.xml:12:9-35 value=(true).
Suggestion: add ‘tools:replace=”android:allowBackup”‘ to <application> element at AndroidManifest.xml:114:5-1136:19 to override.
解决方法:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="包名">
在application节点添加
<application tools:replace="android:allowBackup" ......
同样的还有
tools:replace=”android:icon, android:theme,android:allowBackup”
缩写
dst
idx
src
pre
ref:Reference
cnt:Count
offset
field
wrapper
invoker