├── .gitignore
├── README.md
├── build.gradle
├── proguard-rules.pro
└── src
├── androidTest
└── java
│ └── com
│ └── clock
│ └── utils
│ └── ApplicationTest.java
├── main
├── AndroidManifest.xml
├── java
│ └── com
│ │ └── clock
│ │ └── utils
│ │ ├── bitmap
│ │ └── BitmapUtils.java
│ │ ├── common
│ │ ├── RuleUtils.java
│ │ └── SystemUtils.java
│ │ ├── crash
│ │ └── CrashExceptionHandler.java
│ │ ├── file
│ │ └── FileUtils.java
│ │ └── text
│ │ └── StringUtils.java
└── res
│ └── values
│ └── strings.xml
└── test
└── java
└── com
└── clock
└── utils
└── ExampleUnitTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | *.iml
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AndroidUtils
2 |
3 | 本库主要整理汇总自己平常做Android开发经常会用到的代码。方便己迁移到各大项目的开发。
4 |
5 | 如果你对本库的代码感兴趣,欢迎拿走。如果有bug,欢迎在Github上给我提个Issue反馈一下,我会尽快修复。
6 |
7 | ## 当前已有的类
8 |
9 | - BitmapUtils:Bitmap常用操作处理,如获取旋转角度、计算inSampleSize值等
10 |
11 | - CrashExceptionHandler:捕获app奔溃异常,并将其奔溃日志信息生成到本地SD卡上,也可以回传到服务器
12 |
13 | - RuleUtils:尺寸大小转换工具类,如dp,sp转换成为对应设备上的px值
14 |
15 | - SystemUtils:系统实用工具类,如获取手机设备制造商,系统版本号,app版本号,以及设备id(IMEI)等
16 |
17 | - StringUtils:字符串处理类,目前只有将字符串进行MD5转换的功能
18 |
19 | - FileUtils:目前暂无任何功能
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.0"
6 |
7 | defaultConfig {
8 | minSdkVersion 8
9 | targetSdkVersion 23
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(dir: 'libs', include: ['*.jar'])
23 | testCompile 'junit:junit:4.12'
24 | compile 'com.android.support:appcompat-v7:23.1.1'
25 | }
26 |
--------------------------------------------------------------------------------
/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\Android-Studio\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/src/androidTest/java/com/clock/utils/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.clock.utils;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/main/java/com/clock/utils/bitmap/BitmapUtils.java:
--------------------------------------------------------------------------------
1 | package com.clock.utils.bitmap;
2 |
3 | import android.content.ContentResolver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.res.Resources;
7 | import android.graphics.Bitmap;
8 | import android.graphics.BitmapFactory;
9 | import android.graphics.Matrix;
10 | import android.media.ExifInterface;
11 | import android.net.Uri;
12 | import android.provider.MediaStore;
13 | import android.text.TextUtils;
14 | import android.util.Log;
15 |
16 | import java.io.BufferedOutputStream;
17 | import java.io.File;
18 | import java.io.FileDescriptor;
19 | import java.io.FileNotFoundException;
20 | import java.io.FileOutputStream;
21 | import java.io.IOException;
22 | import java.text.SimpleDateFormat;
23 | import java.util.Date;
24 |
25 | /**
26 | * Bitmap操作常用工具类
27 | * Created by Clock on 2015/12/31.
28 | */
29 | public class BitmapUtils {
30 |
31 | private final static String TAG = BitmapUtils.class.getCanonicalName();
32 | public final static String JPG_SUFFIX = ".jpg";
33 | private final static String TIME_FORMAT = "yyyyMMddHHmmss";
34 |
35 | /**
36 | * 显示图片到相册
37 | *
38 | * @param context
39 | * @param photoFile 要保存的图片文件
40 | */
41 | public static void displayToGallery(Context context, File photoFile) {
42 | if (photoFile == null || !photoFile.exists()) {
43 | return;
44 | }
45 | String photoPath = photoFile.getAbsolutePath();
46 | String photoName = photoFile.getName();
47 | // 其次把文件插入到系统图库
48 | try {
49 | ContentResolver contentResolver = context.getContentResolver();
50 | MediaStore.Images.Media.insertImage(contentResolver, photoPath, photoName, null);
51 | } catch (FileNotFoundException e) {
52 | e.printStackTrace();
53 | }
54 | // 最后通知图库更新
55 | context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + photoPath)));
56 | }
57 |
58 | /**
59 | * 将Bitmap保存到指定目录下
60 | *
61 | * @param bitmap
62 | * @param folder
63 | * @return 保存成功,返回其对应的File,保存失败则返回null
64 | */
65 | public static File saveToFile(Bitmap bitmap, File folder) {
66 | String fileName = new SimpleDateFormat(TIME_FORMAT).format(new Date());//直接以当前时间戳作为文件名
67 | return saveToFile(bitmap, folder, fileName);
68 | }
69 |
70 | /**
71 | * 将Bitmap保存到指定目录下,并且指定好文件名
72 | *
73 | * @param bitmap
74 | * @param folder
75 | * @param fileName 指定的文件名包含后缀
76 | * @return 保存成功,返回其对应的File,保存失败则返回null
77 | */
78 | public static File saveToFile(Bitmap bitmap, File folder, String fileName) {
79 | if (bitmap != null) {
80 | if (!folder.exists()) {
81 | folder.mkdir();
82 | }
83 | File file = new File(folder, fileName + JPG_SUFFIX);
84 | if (file.exists()) {
85 | file.delete();
86 | }
87 | try {
88 | file.createNewFile();
89 | BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
90 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
91 | bos.flush();
92 | bos.close();
93 | return file;
94 | } catch (IOException e) {
95 | e.printStackTrace();
96 | return null;
97 | }
98 | } else {
99 | return null;
100 | }
101 | }
102 |
103 | /**
104 | * 获取图片的旋转角度
105 | *
106 | * @param path 图片绝对路径
107 | * @return 图片的旋转角度
108 | */
109 | public static int getBitmapDegree(String path) {
110 | int degree = 0;
111 | try {
112 | // 从指定路径下读取图片,并获取其EXIF信息
113 | ExifInterface exifInterface = new ExifInterface(path);
114 | // 获取图片的旋转信息
115 | int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
116 | switch (orientation) {
117 | case ExifInterface.ORIENTATION_ROTATE_90:
118 | degree = 90;
119 | break;
120 | case ExifInterface.ORIENTATION_ROTATE_180:
121 | degree = 180;
122 | break;
123 | case ExifInterface.ORIENTATION_ROTATE_270:
124 | degree = 270;
125 | break;
126 | }
127 | } catch (IOException e) {
128 | e.printStackTrace();
129 | }
130 | return degree;
131 | }
132 |
133 | /**
134 | * 将图片按照指定的角度进行旋转
135 | *
136 | * @param bitmap 需要旋转的图片
137 | * @param degree 指定的旋转角度
138 | * @return 旋转后的图片
139 | */
140 | public static Bitmap rotateBitmapByDegree(Bitmap bitmap, int degree) {
141 | // 根据旋转角度,生成旋转矩阵
142 | Matrix matrix = new Matrix();
143 | matrix.postRotate(degree);
144 | // 将原始图片按照旋转矩阵进行旋转,并得到新的图片
145 | Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
146 | if (bitmap != null && !bitmap.isRecycled()) {
147 | bitmap.recycle();
148 | }
149 | return newBitmap;
150 | }
151 |
152 | /**
153 | * 压缩Bitmap的大小
154 | *
155 | * @param imageFile 图片文件
156 | * @param requestWidth 压缩到想要的宽度
157 | * @param requestHeight 压缩到想要的高度
158 | * @return
159 | */
160 | public static Bitmap decodeBitmapFromFile(File imageFile, int requestWidth, int requestHeight) {
161 | if (imageFile != null) {
162 | return decodeBitmapFromFile(imageFile.getAbsolutePath(), requestWidth, requestHeight);
163 | } else {
164 | return null;
165 | }
166 | }
167 |
168 | /**
169 | * 压缩Bitmap的大小
170 | *
171 | * @param imagePath 图片文件路径
172 | * @param requestWidth 压缩到想要的宽度
173 | * @param requestHeight 压缩到想要的高度
174 | * @return
175 | */
176 | public static Bitmap decodeBitmapFromFile(String imagePath, int requestWidth, int requestHeight) {
177 | if (!TextUtils.isEmpty(imagePath)) {
178 | Log.i(TAG, "requestWidth: " + requestWidth);
179 | Log.i(TAG, "requestHeight: " + requestHeight);
180 | if (requestWidth <= 0 || requestHeight <= 0) {
181 | Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
182 | return bitmap;
183 | }
184 | BitmapFactory.Options options = new BitmapFactory.Options();
185 | options.inJustDecodeBounds = true;//不加载图片到内存,仅获得图片宽高
186 | BitmapFactory.decodeFile(imagePath, options);
187 | Log.i(TAG, "original height: " + options.outHeight);
188 | Log.i(TAG, "original width: " + options.outWidth);
189 | if (options.outHeight == -1 || options.outWidth == -1) {
190 | try {
191 | ExifInterface exifInterface = new ExifInterface(imagePath);
192 | int height = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, ExifInterface.ORIENTATION_NORMAL);//获取图片的高度
193 | int width = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, ExifInterface.ORIENTATION_NORMAL);//获取图片的宽度
194 | Log.i(TAG, "exif height: " + height);
195 | Log.i(TAG, "exif width: " + width);
196 | options.outWidth = width;
197 | options.outHeight = height;
198 | } catch (IOException e) {
199 | e.printStackTrace();
200 | }
201 | }
202 | options.inSampleSize = calculateInSampleSize(options, requestWidth, requestHeight); //计算获取新的采样率
203 | Log.i(TAG, "inSampleSize: " + options.inSampleSize);
204 | options.inJustDecodeBounds = false;
205 | return BitmapFactory.decodeFile(imagePath, options);
206 |
207 | } else {
208 | return null;
209 | }
210 | }
211 |
212 | /**
213 | * Decode and sample down a bitmap from resources to the requested width and height.
214 | *
215 | * @param res The resources object containing the image data
216 | * @param resId The resource id of the image data
217 | * @param reqWidth The requested width of the resulting bitmap
218 | * @param reqHeight The requested height of the resulting bitmap
219 | * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
220 | * that are equal to or greater than the requested width and height
221 | */
222 | public static Bitmap decodeBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
223 |
224 | // BEGIN_INCLUDE (read_bitmap_dimensions)
225 | // First decode with inJustDecodeBounds=true to check dimensions
226 | final BitmapFactory.Options options = new BitmapFactory.Options();
227 | options.inJustDecodeBounds = true;
228 | BitmapFactory.decodeResource(res, resId, options);
229 |
230 | // Calculate inSampleSize
231 | options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
232 | // END_INCLUDE (read_bitmap_dimensions)
233 |
234 | // Decode bitmap with inSampleSize set
235 | options.inJustDecodeBounds = false;
236 | return BitmapFactory.decodeResource(res, resId, options);
237 | }
238 |
239 | /**
240 | * Decode and sample down a bitmap from a file input stream to the requested width and height.
241 | *
242 | * @param fileDescriptor The file descriptor to read from
243 | * @param reqWidth The requested width of the resulting bitmap
244 | * @param reqHeight The requested height of the resulting bitmap
245 | * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
246 | * that are equal to or greater than the requested width and height
247 | */
248 | public static Bitmap decodeBitmapFromDescriptor(FileDescriptor fileDescriptor, int reqWidth, int reqHeight) {
249 |
250 | // First decode with inJustDecodeBounds=true to check dimensions
251 | final BitmapFactory.Options options = new BitmapFactory.Options();
252 | options.inJustDecodeBounds = true;
253 | BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
254 |
255 | // Calculate inSampleSize
256 | options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
257 |
258 | // Decode bitmap with inSampleSize set
259 | options.inJustDecodeBounds = false;
260 |
261 | return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
262 | }
263 |
264 | /**
265 | * Google官方代码,计算合适的采样率
266 | * Calculate an inSampleSize for use in a {@link android.graphics.BitmapFactory.Options} object when decoding
267 | * bitmaps using the decode* methods from {@link android.graphics.BitmapFactory}. This implementation calculates
268 | * the closest inSampleSize that is a power of 2 and will result in the final decoded bitmap
269 | * having a width and height equal to or larger than the requested width and height.
270 | *
271 | * @param options An options object with out* params already populated (run through a decode*
272 | * method with inJustDecodeBounds==true
273 | * @param reqWidth The requested width of the resulting bitmap
274 | * @param reqHeight The requested height of the resulting bitmap
275 | * @return The value to be used for inSampleSize
276 | */
277 | public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
278 | // BEGIN_INCLUDE (calculate_sample_size)
279 | // Raw height and width of image
280 | final int height = options.outHeight;
281 | final int width = options.outWidth;
282 | int inSampleSize = 1;
283 |
284 | if (height > reqHeight || width > reqWidth) {
285 |
286 | final int halfHeight = height / 2;
287 | final int halfWidth = width / 2;
288 |
289 | // Calculate the largest inSampleSize value that is a power of 2 and keeps both
290 | // height and width larger than the requested height and width.
291 | while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
292 | inSampleSize *= 2;
293 | }
294 |
295 | // This offers some additional logic in case the image has a strange
296 | // aspect ratio. For example, a panorama may have a much larger
297 | // width than height. In these cases the total pixels might still
298 | // end up being too large to fit comfortably in memory, so we should
299 | // be more aggressive with sample down the image (=larger inSampleSize).
300 |
301 | long totalPixels = width * height / inSampleSize;
302 |
303 | // Anything more than 2x the requested pixels we'll sample down further
304 | final long totalReqPixelsCap = reqWidth * reqHeight * 2;
305 |
306 | while (totalPixels > totalReqPixelsCap) {
307 | inSampleSize *= 2;
308 | totalPixels /= 2;
309 | }
310 | }
311 | return inSampleSize;
312 | // END_INCLUDE (calculate_sample_size)
313 | }
314 | }
315 |
--------------------------------------------------------------------------------
/src/main/java/com/clock/utils/common/RuleUtils.java:
--------------------------------------------------------------------------------
1 | package com.clock.utils.common;
2 |
3 | import android.content.Context;
4 | import android.util.DisplayMetrics;
5 | import android.util.TypedValue;
6 |
7 | /**
8 | * 尺寸大小实用工具类
9 | *
10 | * Created by Clock on 2016/1/16.
11 | */
12 | public class RuleUtils {
13 |
14 | /**
15 | * 获取屏幕的宽度
16 | *
17 | * @param context
18 | * @return
19 | */
20 | public static int getScreenWidth(Context context) {
21 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
22 | return displayMetrics.widthPixels;
23 | }
24 |
25 | /**
26 | * 获取屏幕的高度
27 | *
28 | * @param context
29 | * @return
30 | */
31 | public static int getScreenHeight(Context context) {
32 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
33 | return displayMetrics.heightPixels;
34 | }
35 |
36 | /**
37 | * 将dp转换成对应的像素值
38 | *
39 | * @param context
40 | * @param dp
41 | * @return
42 | */
43 | public static float convertDp2Px(Context context, int dp) {
44 | DisplayMetrics metrics = context.getResources().getDisplayMetrics();
45 | return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics);
46 | }
47 |
48 | /**
49 | * 将sp转换成对应的像素值
50 | *
51 | * @param context
52 | * @param sp
53 | * @return
54 | */
55 | public static float convertSp2Px(Context context, int sp) {
56 | DisplayMetrics metrics = context.getResources().getDisplayMetrics();
57 | return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, metrics);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/clock/utils/common/SystemUtils.java:
--------------------------------------------------------------------------------
1 | package com.clock.utils.common;
2 |
3 |
4 | import android.content.Context;
5 | import android.content.pm.PackageInfo;
6 | import android.content.pm.PackageManager;
7 | import android.net.ConnectivityManager;
8 | import android.net.NetworkInfo;
9 | import android.os.Environment;
10 | import android.provider.Settings;
11 | import android.telephony.TelephonyManager;
12 | import android.text.TextUtils;
13 |
14 | /**
15 | * 系统实用工具类
16 | *
17 | * Created by Clock on 2016/1/24.
18 | */
19 | public class SystemUtils {
20 |
21 | /**
22 | * 获取设备的制造商
23 | *
24 | * @return 设备制造商
25 | */
26 | public static String getDeviceManufacture() {
27 | return android.os.Build.MANUFACTURER;
28 | }
29 |
30 | /**
31 | * 获取设备名称
32 | *
33 | * @return 设备名称
34 | */
35 | public static String getDeviceName() {
36 | return android.os.Build.MODEL;
37 | }
38 |
39 | /**
40 | * 获取系统版本号
41 | *
42 | * @return 系统版本号
43 | */
44 | public static String getSystemVersion() {
45 | return android.os.Build.VERSION.RELEASE;
46 | }
47 |
48 | /**
49 | * 获取设备号
50 | *
51 | * @param context
52 | * @return
53 | */
54 | public static String getDeviceIMEI(Context context) {
55 | TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
56 | if (telephonyManager == null || TextUtils.isEmpty(telephonyManager.getDeviceId())) {
57 | return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
58 | } else {
59 | return telephonyManager.getDeviceId();
60 | }
61 | }
62 |
63 | /**
64 | * 获取应用的版本号
65 | *
66 | * @param context
67 | * @return
68 | */
69 | public static String getAppVersion(Context context) {
70 | PackageManager packageManager = context.getPackageManager();
71 | PackageInfo packageInfo;
72 | try {
73 | packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
74 | return packageInfo.versionName;
75 | } catch (PackageManager.NameNotFoundException e) {
76 | e.printStackTrace();
77 | }
78 | return null;
79 | }
80 |
81 | /**
82 | * 判断当前有没有网络连接
83 | *
84 | * @param context
85 | * @return
86 | */
87 | public static boolean getNetworkState(Context context) {
88 | ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
89 | NetworkInfo networkinfo = manager.getActiveNetworkInfo();
90 | if (networkinfo == null || !networkinfo.isAvailable()) {
91 | return false;
92 | }
93 | return true;
94 | }
95 |
96 | /**
97 | * SD卡是否挂载
98 | *
99 | * @return
100 | */
101 | public static boolean mountedSdCard() {
102 | return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/com/clock/utils/crash/CrashExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.clock.utils.crash;
2 |
3 | import android.content.Context;
4 | import android.os.Looper;
5 | import android.util.Log;
6 | import android.widget.Toast;
7 |
8 | import com.clock.utils.common.SystemUtils;
9 |
10 | import java.io.File;
11 | import java.io.FileOutputStream;
12 | import java.io.IOException;
13 | import java.io.OutputStreamWriter;
14 | import java.io.PrintWriter;
15 | import java.io.RandomAccessFile;
16 | import java.text.SimpleDateFormat;
17 | import java.util.Date;
18 |
19 | /**
20 | * app奔溃异常处理器
21 | *
22 | * 使用此类需要在AndroidManifest.xml配置以下权限
23 | *
24 | * android.permission.READ_EXTERNAL_STORAGE
25 | *
26 | * android.permission.WRITE_EXTERNAL_STORAGE
27 | *
28 | * android.permission.READ_PHONE_STATE
29 | *
30 | * Created by Clock on 2016/1/24.
31 | */
32 | public class CrashExceptionHandler implements Thread.UncaughtExceptionHandler {
33 |
34 | private final static String TAG = CrashExceptionHandler.class.getSimpleName();
35 |
36 | private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmm");
37 |
38 | private Context mApplicationContext;
39 | /**
40 | * 保存闪退日志的文件目录
41 | */
42 | private File mCrashInfoFolder;
43 | /**
44 | * 向远程服务器发送错误信息
45 | */
46 | private CrashExceptionRemoteReport mCrashExceptionRemoteReport;
47 |
48 | /**
49 | * @param context
50 | * @param crashInfoFolder 保存闪退日志的文件夹目录
51 | */
52 | public CrashExceptionHandler(Context context, File crashInfoFolder) {
53 | this.mApplicationContext = context.getApplicationContext();
54 | this.mCrashInfoFolder = crashInfoFolder;
55 | }
56 |
57 | @Override
58 | public void uncaughtException(Thread thread, Throwable ex) {
59 | ex.printStackTrace();
60 | handleException(ex);
61 | try {
62 | Thread.sleep(3000);
63 | } catch (InterruptedException e) {
64 | e.printStackTrace();
65 | }
66 | //杀死进程
67 | android.os.Process.killProcess(android.os.Process.myPid());
68 | }
69 |
70 | /**
71 | * 配置远程传回log到服务器的设置
72 | *
73 | * @param crashExceptionRemoteReport
74 | */
75 | public void configRemoteReport(CrashExceptionRemoteReport crashExceptionRemoteReport) {
76 | this.mCrashExceptionRemoteReport = crashExceptionRemoteReport;
77 | }
78 |
79 | /**
80 | * 处理异常
81 | *
82 | * @param ex
83 | */
84 | private void handleException(Throwable ex) {
85 | if (ex == null) {
86 | return;
87 | } else {
88 | saveCrashInfoToFile(ex);
89 | sendCrashInfoToServer(ex);
90 |
91 | //使用Toast来显示异常信息
92 | new Thread() {
93 | @Override
94 | public void run() {
95 | Looper.prepare();
96 | try {
97 | Toast.makeText(mApplicationContext, "程序出现异常 , 即将退出....", Toast.LENGTH_LONG).show();
98 | } catch (Exception ex) {
99 | ex.printStackTrace();
100 | }
101 | Looper.loop();
102 | }
103 | }.start();
104 | }
105 | }
106 |
107 | /**
108 | * 保存闪退信息到本地文件中
109 | *
110 | * @param ex
111 | */
112 | private void saveCrashInfoToFile(Throwable ex) {
113 | try {
114 | if (mCrashInfoFolder == null) {
115 | return;
116 | }
117 |
118 | if (!mCrashInfoFolder.exists()) {//闪退日志目录不存在则先创建闪退日志目录
119 | mCrashInfoFolder.mkdirs();
120 | }
121 |
122 | if (mCrashInfoFolder.exists()) {
123 | String timeStampString = DATE_FORMAT.format(new Date());//当先的时间格式化
124 | String crashLogFileName = timeStampString + ".log";
125 | File crashLogFile = new File(mCrashInfoFolder, crashLogFileName);
126 | crashLogFile.createNewFile();
127 |
128 | //记录闪退环境的信息
129 | RandomAccessFile randomAccessFile = new RandomAccessFile(crashLogFile, "rw");
130 | randomAccessFile.writeChars("------------Crash Environment Info------------" + "\n");
131 | randomAccessFile.writeChars("------------Manufacture: " + SystemUtils.getDeviceManufacture() + "------------" + "\n");
132 | randomAccessFile.writeChars("------------DeviceName: " + SystemUtils.getDeviceName() + "------------" + "\n");
133 | randomAccessFile.writeChars("------------SystemVersion: " + SystemUtils.getSystemVersion() + "------------" + "\n");
134 | randomAccessFile.writeChars("------------DeviceIMEI: " + SystemUtils.getDeviceIMEI(mApplicationContext) + "------------" + "\n");
135 | randomAccessFile.writeChars("------------AppVersion: " + SystemUtils.getAppVersion(mApplicationContext) + "------------" + "\n");
136 | randomAccessFile.writeChars("------------Crash Environment Info------------" + "\n");
137 | randomAccessFile.writeChars("\n");
138 | randomAccessFile.close();
139 |
140 | PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(crashLogFile.getAbsolutePath(), true)), true);
141 | ex.printStackTrace(pw);//写入奔溃的日志信息
142 | pw.close();
143 |
144 | } else {
145 | Log.e(TAG, "crash info folder create failure!!!");
146 | }
147 |
148 | } catch (IOException e) {
149 | e.printStackTrace();
150 | } catch (Exception e) {
151 | e.printStackTrace();
152 | }
153 | }
154 |
155 | /**
156 | * 发送发送闪退信息到远程服务器
157 | *
158 | * @param ex
159 | */
160 | private void sendCrashInfoToServer(Throwable ex) {
161 | if (mCrashExceptionRemoteReport != null) {
162 | mCrashExceptionRemoteReport.onCrash(ex);
163 | }
164 | }
165 |
166 | /**
167 | * 闪退日志远程奔溃接口,主要考虑不同app下,把log回传给服务器的方式不一样,所以此处留一个对外开放的接口
168 | */
169 | public static interface CrashExceptionRemoteReport {
170 | /**
171 | * 当闪退发生时,回调此接口函数
172 | *
173 | * @param ex
174 | */
175 | public void onCrash(Throwable ex);
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/src/main/java/com/clock/utils/file/FileUtils.java:
--------------------------------------------------------------------------------
1 | package com.clock.utils.file;
2 |
3 | /**
4 | * 文件操作常用工具类
5 | * Created by Clock on 2015/12/31.
6 | */
7 | public class FileUtils {
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/clock/utils/text/StringUtils.java:
--------------------------------------------------------------------------------
1 | package com.clock.utils.text;
2 |
3 | import java.security.MessageDigest;
4 | import java.security.NoSuchAlgorithmException;
5 |
6 | /**
7 | * 字符串通用处理类
8 | * Created by Clock on 2016/1/17.
9 | */
10 | public class StringUtils {
11 |
12 | /**
13 | * 将字符串进行md5转换
14 | *
15 | * @param str
16 | * @return
17 | */
18 | public static String md5(String str) {
19 | String cacheKey;
20 | try {
21 | final MessageDigest mDigest = MessageDigest.getInstance("MD5");
22 | mDigest.update(str.getBytes());
23 | cacheKey = bytesToHexString(mDigest.digest());
24 | } catch (NoSuchAlgorithmException e) {
25 | cacheKey = String.valueOf(str.hashCode());
26 | }
27 | return cacheKey;
28 | }
29 |
30 | private static String bytesToHexString(byte[] bytes) {
31 | StringBuilder sb = new StringBuilder();
32 | for (int i = 0; i < bytes.length; i++) {
33 | String hex = Integer.toHexString(0xFF & bytes[i]);
34 | if (hex.length() == 1) {
35 | sb.append('0');
36 | }
37 | sb.append(hex);
38 | }
39 | return sb.toString();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidUtils
3 |
4 |
--------------------------------------------------------------------------------
/src/test/java/com/clock/utils/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.clock.utils;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------