背景
最近由于项目需求,需要抓取上传app的日志,方便问题追踪,同时给其他公司其他项目进行依赖使用,因此需要打成aar库。
实现
使用Retrfit 进行网络部分的请求,Retrfit的依赖
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation "com.squareup.okhttp3:logging-interceptor:3.8.1"
采用后台服务的方式,进行日志上传,同时上传完成之后,自动销毁任务,因为采用IntentService
代码部分
public class UploadFileService extends IntentService {
private static final String TAG = "UploadFileService";
private SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("yyyyMMdd");
private UploadServiceApi mUploadServiceApi;
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public UploadFileService(String name) {
super(name);
}
public UploadFileService() {
this("NEW_UPLOAD");
}
@Override
public void onCreate() {
super.onCreate();
mUploadServiceApi = RetrfitApiUtil.getService(UploadServiceApi.class, "");
}
@Override
protected void onHandleIntent(Intent intent) {
List<File> files = getNeedUploadFiles();
File zipFile = ZipUtils.zipFiles(files, mSimpleDateFormat.format(new Date()) + ".zip");
Log.d(TAG, "zipFile " + zipFile.length() + zipFile.getAbsolutePath());
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), zipFile);
MultipartBody.Part body = MultipartBody.Part.createFormData("file", zipFile.getName(), requestFile);
Map<String, RequestBody> map = getUploadParamsMap();
Call<ResponseBody> call = mUploadServiceApi.uploadLogFile(UploadUtil.getUrl(), map, body);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d(TAG, "上传成功" + response.toString());
ZipUtils.deleteZipFile();
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e(TAG, t.toString());
}
});
}
private Map<String, RequestBody> getUploadParamsMap() {
Map<String, RequestBody> newMap = new HashMap<>();
Map<String, Object> map = UploadUtil.getmParamMap();
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
newMap.put(entry.getKey(), toRequestBody(entry.getValue()));
}
return newMap;
}
private List<File> getNeedUploadFiles() {
List<File> files = new ArrayList<>();
File fileDir = new File(UploadUtil.getLocation());
if (fileDir.isDirectory()) {
File[] fileArray = fileDir.listFiles();
files = new ArrayList<>(Arrays.asList(fileArray));
}
List<File> newFiles = new ArrayList<>();
for (File file : files) {
if (UploadUtil.getDatas() == null || UploadUtil.getDatas().isEmpty()) break;
for (String fileName : UploadUtil.getDatas()) {
if (file.getName().contains(fileName)) {
newFiles.add(file);
break;
}
}
}
return newFiles;
}
@Override
public void onDestroy() {
super.onDestroy();
}
private RequestBody toRequestBody(Object value) {
String newValue;
if (value instanceof String) {
newValue = (String) value;
} else if (value != null) {
newValue = value.toString();
} else {
newValue = "";
}
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain"), newValue);
return requestBody;
}
}
Retrfit接口定义
public interface UploadServiceApi {
/**
* 上传日志文件/到服务器
* @return
*/
@Multipart
@POST
Call<ResponseBody> uploadLogFile(@Url String url, @PartMap Map<String, RequestBody> files, @Part MultipartBody.Part file);
}
服务器地址由接口直接传入,因此host直接传null即可
public class RetrfitApiUtil {
/**
* @param tClass retrofitAPI 类
* @param host 服务器地址(必须以 / 结尾)
* @return
*/
public static <T> T getService(Class<T> tClass, String host) {
//网络日志
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY );
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.addInterceptor(interceptor);
OkHttpClient okHttpClient = builder.connectTimeout(15000L, TimeUnit.MILLISECONDS)
.readTimeout(15000L, TimeUnit.MILLISECONDS)
.build();
if (TextUtils.isEmpty(host)) {
host = "http://localhost/";
}
Retrofit retrofit = new Retrofit.Builder().baseUrl(host)
.client(okHttpClient)
.build();
return retrofit.create(tClass);
}
}
可能需要进行多个文件进行上传,因此需要打包,ZipUtil工具栏
public class ZipUtils {
public static File zipFiles(List<File> srcFiles, String zipFileName) {
// 判断压缩后的文件存在不,不存在则创建
File zipFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/T3出行/Log", zipFileName);
createOrExistsFile(zipFile);
// 创建 FileOutputStream 对象
FileOutputStream fileOutputStream = null;
// 创建 ZipOutputStream
ZipOutputStream zipOutputStream = null;
// 创建 FileInputStream 对象
FileInputStream fileInputStream = null;
try {
// 实例化 FileOutputStream 对象
fileOutputStream = new FileOutputStream(zipFile);
// 实例化 ZipOutputStream 对象
zipOutputStream = new ZipOutputStream(fileOutputStream);
// 创建 ZipEntry 对象
ZipEntry zipEntry = null;
// 遍历源文件数组
for (int i = 0; i < srcFiles.size(); i++) {
// 将源文件数组中的当前文件读入 FileInputStream 流中
fileInputStream = new FileInputStream(srcFiles.get(i));
// 实例化 ZipEntry 对象,源文件数组中的当前文件
zipEntry = new ZipEntry(srcFiles.get(i).getName());
zipOutputStream.putNextEntry(zipEntry);
// 该变量记录每次真正读的字节个数
int len;
// 定义每次读取的字节数组
byte[] buffer = new byte[1024];
while ((len = fileInputStream.read(buffer)) > 0) {
zipOutputStream.write(buffer, 0, len);
}
}
if (zipEntry != null) {
zipOutputStream.closeEntry();
}
zipOutputStream.close();
fileInputStream.close();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return zipFile;
}
public static void deleteZipFile() {
File zipFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/T3出行/Log");
deleteFile(zipFile);
}
/***
* 删除压缩后的文件
*/
/**
* 先根遍历序递归删除文件夹
*
* @param dirFile 要被删除的文件或者目录
* @return 删除成功返回true, 否则返回false
*/
public static boolean deleteFile(File dirFile) {
// 如果dir对应的文件不存在,则退出
if (!dirFile.exists()) {
return false;
}
if (dirFile.isFile()) {
return dirFile.delete();
} else {
for (File file : dirFile.listFiles()) {
deleteFile(file);
}
}
return dirFile.delete();
}
/**
* 判断文件是否存在,不存在则判断是否创建成功
*
* @param file 文件
* @return {@code true}: 存在或创建成功<br>{@code false}: 不存在或创建失败
*/
public static boolean createOrExistsFile(final File file) {
if (file == null) return false;
// 如果存在,是文件则返回 true,是目录则返回 false
if (file.exists()) return file.isFile();
if (!createOrExistsDir(file.getParentFile())) return false;
try {
return file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
/**
* 判断目录是否存在,不存在则判断是否创建成功
*
* @param file 文件
* @return {@code true}: 存在或创建成功<br>{@code false}: 不存在或创建失败
*/
public static boolean createOrExistsDir(final File file) {
// 如果存在,是目录则返回 true,是文件则返回 false,不存在则返回是否创建成功
return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
}
}
App或者其他库调用的入口UploadUtil,
url 为上传的服务器地址,location为需要上传文件的目录,datas为需要上传文件的文件名的集合,logKey为公司项目统一要求字段,其他库可以直接传null
public class UploadUtil {
private static boolean test = false;
/**
* 请求参数map
*/
private static HashMap<String, Object> mParamMap;
private static String url;
private static String location;
private static List<String> datas;
private static Context context;
public static void setLogKey(String logKey) {
if (mParamMap == null) {
mParamMap = new HashMap<>();
}
mParamMap.put("logKey", logKey);
}
public static String getUrl() {
return url;
}
public static void setUrl(String url) {
UploadUtil.url = url;
}
public static String getLocation() {
return location;
}
public static void setLocation(String location) {
UploadUtil.location = location;
}
public static List<String> getDatas() {
return datas;
}
public static void setDatas(List<String> datas) {
UploadUtil.datas = datas;
}
public static HashMap<String, Object> getmParamMap() {
return mParamMap;
}
public static void setmParamMap(HashMap<String, Object> mParamMap) {
UploadUtil.mParamMap = mParamMap;
}
public static void init(Context context, HashMap<String, Object> map) {
UploadUtil.context = context;
mParamMap = map;
}
public static void prepare(String url, String location) {
UploadUtil.url = url;
UploadUtil.location = location;
}
public static void uploadFile(String logKey, List<String> datas) throws Exception {
setLogKey(logKey);
UploadUtil.datas = datas;
if (test) {
UploadUtil.datas = new ArrayList<>(Arrays.asList("Screenshot_20190506-180709.jpg", "Screenshot_20190505-102016_3D.jpg"));
}
if (context != null && mParamMap != null) {
context.startService(new Intent(context, UploadFileService.class));
} else {
throw new Exception("please init context & paramMap first");
}
}
public static void uploadFile(String logKey, String fileName) throws Exception {
Log.d("UploadUtil",fileName+"===="+logKey);
setLogKey(logKey);
UploadUtil.datas = new ArrayList<>(Arrays.asList(fileName));
if (test) {
UploadUtil.datas = new ArrayList<>(Arrays.asList("Screenshot_20190506-180709.jpg", "Screenshot_20190505-102016_3D.jpg"));
}
if (context != null && mParamMap != null) {
context.startService(new Intent(context, UploadFileService.class));
} else {
throw new Exception("please init context & paramMap first");
}
}
}
使用方法
- 先调用UploadUtil.init()进行文件夹目录和基本上传参数的构建,比如token等
- 其次,调用UploadUtil.prepare()设置需要上传的服务器地址和文件目录
- 最后,UploadUtil.uploadFile()进行文件开始的上传,由于采用IntentService,多次调用,也会在队列里一个个进行上传,任务全部结束之后,service自动销毁
网友评论