Retrofit+Service+自定义Task+GreenDao实现批量上传本地图片功能

7 篇文章 0 订阅
5 篇文章 0 订阅

最近要完成一个批量上传本地图片的功能

1.上传单张的话,只要用简单的Retrofit上传,即可,但是产品要求做到批量上传功能,特地看了一下QQ空间的上传图片功能,并且分析了一下;

2.QQ空间上传的时候,有一个进度条在页面展示,点击进去是一个多线程上传过程,这就要数据库保存上传的图片数据,并且多线程上传,在压缩的时候,感觉容易出现OOM;

3.而且要保持数据在后台上传,就必须要有一个Service在后台跑,而且还要回调给某些界面显示,可用广播或者是RxBus等数据总线完成;

完成上面的分析,直接上代码吧

4.最开始是数据库表的设计,是用GreenDao,实体类如下

@Entity
public class UploadImageEntity implements android.os.Parcelable {

  @Id
  private Long id;
  @Property(nameInDb = "DIR_ID")
  private String dirId; //目录id
  @Property(nameInDb = "TASK_ID")
  private String taskId;
  @Property(nameInDb = "CUST_ID")
  private String custId; //用户id
  @Property(nameInDb = "SAVE_BIZ_TYPE")
  private boolean saveBizType;//是否关联业务
  @Property(nameInDb = "STATE")
  private int state;   //0排队中,1上传成功,2上传中,3上传失败
  @Property(nameInDb = "PATH")
  private String path; //图片路径
  @Property(nameInDb = "IMAGE_NAME")
  private String imageName;
  @Property(nameInDb = "PROGRESS")
  private long progress;
  @Property(nameInDb = "DIR_NAME")
  private String dirName;
  @Property(nameInDb = "IMAGE_SIZE")
  private long imageSize;
  @Property(nameInDb = "UPLOAD_ID")
  private String uploadId;
  @Property(nameInDb = "SELECT_DIR_ID")
  private String selectDirId;
  @Property(nameInDb = "CUST_NAME")
  private String custName; //对应客户的名字
  @Property(nameInDb = "CUST_TYPE")
  private String custType; //客户类型
  @Property(nameInDb = "USER_ID")
  private String userId; //登录用户的id
  @Property(nameInDb = "UPLOAD_COUNT")
  private String uploadCount; //图片上传成功的个数
  @Property(nameInDb = "IMAGE_COUNT")
  private String imageCount; //该用户总共上传的个数
  @Property(nameInDb = "FAILURE_COUNT")
  private String failCount; //图片上传失败的个数

对应的DBHelper

public class DBHelper {

	private static final String DB_NAME = "upload_task.db";//数据库名称
	private static DBHelper instance;
	private static DBManager<UploadTaskEntity, Long> uploadTask;
	private static DBManager<UploadImageEntity, Long> uploadImage;
	private DaoMaster.DevOpenHelper mHelper;
	private DaoMaster mDaoMaster;
	private DaoSession mDaoSession;

	private DBHelper() {

	}

	public static DBHelper getInstance() {
		if (instance == null) {
			synchronized (DBHelper.class) {
				if (instance == null) {
					instance = new DBHelper();
				}
			}
		}
		return instance;
	}

	public void init(Context context) {
		mHelper = new DaoMaster.DevOpenHelper(context, DB_NAME, null);
		mDaoMaster = new DaoMaster(mHelper.getWritableDatabase());
		mDaoSession = mDaoMaster.newSession();
		QueryBuilder.LOG_SQL = true;
		QueryBuilder.LOG_VALUES = true;

	}

	public void init(Context context, String dbName) {
		mHelper = new DaoMaster.DevOpenHelper(context, dbName, null);
		mDaoMaster = new DaoMaster(mHelper.getWritableDatabase());
		mDaoSession = mDaoMaster.newSession();
	}

	public DBManager<UploadImageEntity, Long> uploadImage() {
		if (uploadImage == null) {
			uploadImage = new DBManager<UploadImageEntity, Long>(){

				@Override
				public AbstractDao<UploadImageEntity, Long> getAbstractDao() {
					return mDaoSession.getUploadImageEntityDao();
				}
			};
		}
		return uploadImage;
	}


	public void deleteImageUploadByUploadId(String uploadId){
		uploadImage()
				.queryBuilder().where(Properties.UploadId.eq(uploadId))
				.buildDelete().executeDeleteWithoutDetachingEntities();
	}


	public void updateImageUploadState(String uploadId,int state){
		StringUtils.checkUserId();
		UploadImageEntity entity = uploadImage().queryBuilder()
				.where(Properties.UploadId.eq(uploadId)
						,Properties.UserId.eq(Net.USER_ID)).build().unique();
		if (entity != null) {
			entity.setState(state);
			uploadImage().update(entity);
		}
	}

关于greenDao的使用,请自行查找

关键的看对应的service和task任务

public class UploadService extends Service {
	
	public static final String ACTION_START = "ACTION_START";  //开始
	public static final String ACTION_CONTINUE = "ACTION_CONTINUE"; //继续
	public static final String ACTION_STOP = "ACTION_STOP";
	public static final String ACTION_FAILURE = "ACTION_FAILURE";
	public static final String ACTION_UPDATE = "ACTION_UPDATE";
	public static final String ACTION_FINISH = "ACTION_FINISH";
	public static final String ACTION_DELETE = "ACTION_DELETE";
	
	public static final int DOWN_INIT = 0;
	private UploadImageTaskEntity imageTaskEntity;

	private String actionType;
	// 下载任务集合
	private static HashMap<String,UploadTask> mTasks = new HashMap<>();
	public Handler mHandler = new Handler(){
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case DOWN_INIT:
				//启动下载任务
				UploadTask mTask = new UploadTask(UploadService.this, imageTaskEntity);
				if(actionType.equals(ACTION_START)) {
					mTask.upload();
				}else if(actionType.equals(ACTION_CONTINUE)) {
					mTask.continueDownload(uploadImageEntity);
				}
				// 把下载任务添加到集合
				mTasks.put(imageTaskEntity.getTaskId(), mTask);
				break;
			default:
				break;
			}
		}
		
	};
	private UploadImageEntity uploadImageEntity;

	@Override
	public IBinder onBind(Intent arg0) {
		return null;
	}

	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		if(intent == null) {
			return super.onStartCommand(intent, flags, startId);
		}
		//开始下载传过来的参
		imageTaskEntity =intent.getParcelableExtra("uploadImageEntity");
		actionType = intent.getAction();
		if(ACTION_START.equals(actionType)){
			//启动线程下载
			mHandler.obtainMessage(DOWN_INIT, null).sendToTarget();
		}else if(ACTION_CONTINUE.equals(actionType)){
			uploadImageEntity = imageTaskEntity.getImageList().get(0);
			mHandler.obtainMessage(DOWN_INIT, null).sendToTarget();
		}else if(ACTION_STOP.equals(actionType)){
			//停止
			stopUpload();
		}else if(ACTION_DELETE.equals(actionType)){
			//停止
			deleteUpload();
		}
		return super.onStartCommand(intent, flags, startId);
	}

	private void deleteUpload() {
		UploadTask uploadTask = mTasks.get(imageTaskEntity.getTaskId());
		String uploadId = imageTaskEntity.getImageList().get(0).getUploadId();
		if(uploadTask != null) {
			uploadTask.deleteUploadById(uploadId);
		}else {
			DBHelper.getInstance().deleteImageUploadByUploadId(uploadId);
			Intent intent = new Intent(UploadService.ACTION_DELETE);
			UploadService.this.sendBroadcast(intent);
		}
	}

	public void stopUpload(){
		// 暂停下载
		UploadTask uploadTask = mTasks.get(imageTaskEntity.getTaskId());
		if(uploadTask != null) {
			uploadTask.isPause = true;
		}
		mTasks.remove(imageTaskEntity.getTaskId());
	}

	
	@Override
	public void onDestroy() {
		super.onDestroy();
		destroy();
	}
	
	public static void destroy(){
		LogUtils.e("destroy-------");
		Iterator iter = mTasks.entrySet().iterator();
		while (iter.hasNext()){
			Map.Entry entry = (Map.Entry) iter.next();
			String key = (String) entry.getKey();
			UploadTask uploadTask = (UploadTask) entry.getValue();
			if(uploadTask != null){
				uploadTask.isPause = true;
				uploadTask.onPause();
			}
			mTasks.remove(key);
		}
	}
}

以及Task

public class UploadTask {
	private Context mContext;
	private ArrayList<UploadImageEntity> mList;
	private UploadImageTaskEntity taskEntity;
	public boolean isPause = false;
	private UploadImageEntity entity;
	private File file;
	private File orginaFile;
	private String deleteId;
	private static ExecutorService executorService =  Executors.newSingleThreadExecutor();
	private String uploadUrl = new String(AppConfig.Net.HOST
			+AppConfig.Net.PREFIX+"gamma/cust/info/res/attach/upload");
	
	public UploadTask(Context mContext,UploadImageTaskEntity imageTaskEntity) {
		this.mContext = mContext;
		this.taskEntity = imageTaskEntity;
		this.mList = taskEntity.getImageList();
	}
	
	//开始上传
	public void upload(){
		Intent intent = new Intent(UploadService.ACTION_START);
		mContext.sendBroadcast(intent);
		// 启动初始化线程 获取文件大小
		for(int i = 0 ; i < mList.size() ;i++) {
			final UploadImageEntity imageEntity = mList.get(i);
			executorService.submit(new Runnable() {
				@Override
				public void run() {
						startUpLoad(imageEntity); //开始上传
						LogUtils.e("startUpLoad");
				}
			});
			ProgressManager.getInstance().addRequestListener(uploadUrl, getUploadListener());
		}
	}


	@NonNull
	private ProgressListener getUploadListener() {
		return new ProgressListener() {
			@Override
			public void onProgress(ProgressInfo progressInfo) {
				int progress = progressInfo.getPercent();
				if(null == entity){
					return;
				}
				entity.setProgress(progress);
				entity.setState(2);
				RxBus.get().post(RxBusKey.UPLOAD_STATUS,entity);
			}

			@Override
			public void onError(long id, Exception e) {
				LogUtils.w("onError"+e.getLocalizedMessage());
			}
		};
	}
	/** 单个图片上传*/
	private void startUpLoad(final UploadImageEntity imageEntity){
		this.entity = imageEntity;
		if (!StringUtils.isEmpty(deleteId) && imageEntity.getUploadId().equals(deleteId)){
			LogUtils.w("删除下载"+imageEntity.getCustName()+"*****imageName="+imageEntity.getImageName());
			deleteUploadById(deleteId);
		}else {
			Observable.create(new ObservableOnSubscribe<List<File>>() {
				@Override
				public void subscribe(@NonNull final ObservableEmitter<List<File>> e) throws Exception {
					try {
						orginaFile = new File(imageEntity.getPath());
						int maxWidth = BitmapUtil.getImageMaxWidth(imageEntity.getPath());
						int ignoreBy = StringUtils.getScale(maxWidth);
						LogUtils.w("ignoreBy="+ignoreBy);
						LogUtils.w("orginaFile.path==" + orginaFile.getPath() + "size=" + StringUtils.bytes2MB(
								orginaFile.length()));
						if(ignoreBy == 100){
							file = StringUtils.getSmallFile(orginaFile);
						}else{
							List<File> list	 = Luban.with(MyApplication.getContext())
									.ignoreBy(ignoreBy)
									.load(orginaFile).get();
							file = list.get(0);
						}
						LogUtils.w("path==" + file.getPath() + "size=" + StringUtils.bytes2MB(file.length()));
						try {
							BitmapUtil.saveExif(imageEntity.getPath(), file.getPath());
						} catch (Exception e1) {
							e1.printStackTrace();
						}
						List<File> files = new ArrayList<>();
						files.add(file);
						e.onNext(files);
						e.onComplete();
					} catch (Exception ex) {
						e.onError(ex);
					}
				}
			}).flatMap(new Function<List<File>, ObservableSource<String>>() {
				@Override
				public ObservableSource<String> apply(@NonNull List<File> files) throws Exception {
					ArrayList<File> arrayList = new ArrayList<>();
					arrayList.addAll(files);
					return UserApi.uploadAttchImage(imageEntity.getCustId(), imageEntity.getDirId(),imageEntity.isSaveBizType(), arrayList);
				}
			}).subscribe(
					new SuccessSubscriber<String>(MyApplication.getInstance().getRxErrorHandler(), "3") {
						@Override
						protected void onSuccess(String baseResponse) {
							LogUtils.e("onSuccess");
							DBHelper.getInstance().deleteImageUploadByUploadId(imageEntity.getUploadId());
							if (entity != null) {
								entity.setState(1);
							}
							RxBus.get().post(RxBusKey.UPLOAD_STATUS, entity);
							sendUpdateBroadcast();
							file = null;
							orginaFile = null;
						}

						@Override
						public void onFailure(String msg) {
							LogUtils.e("onFailure");
							if (entity != null) {
								entity.setState(3);
								LogUtils.e("onFailure" + entity.getDirName());
							}
							sendUpdateBroadcast();
							RxBus.get().post(RxBusKey.UPLOAD_STATUS, entity);
							file = null;
							orginaFile = null;
							DBHelper.getInstance().updateImageUploadState(imageEntity.getUploadId(), 3);
						}
					});
		}
	}



	//	//下载一条后执行的任务
	private void sendUpdateBroadcast() {
		//删除线程中的任务
		List<UploadImageEntity> uploadImageEntities = DBHelper.getInstance().uploadImage().loadAll();
		int size = taskEntity.getImageList().size() -  uploadImageEntities.size();
		Intent intent = new Intent(UploadService.ACTION_UPDATE);
		intent.putExtra("taskId", taskEntity.getTaskId());
		intent.putExtra("size", size);
		mContext.sendBroadcast(intent);
		//如果数据库剩余下载数为0 则下载完成
		if(size == 0) {
			setThreadsFinished();
		}
	}

	/**
	 * 全部线程下载完成执行的方法
	 */
	private synchronized void setThreadsFinished() {
		//更新下载数据
		DBHelper.getInstance().updateImageUploadState(entity.getUploadId(),1);
		// 发送广播通知下载任务结束
		Intent intent = new Intent(UploadService.ACTION_FINISH);
		intent.putExtra("taskId", taskEntity.getTaskId());
		mContext.sendBroadcast(intent);
	}

	/**
	 * 重新下载
	 * @param uploadImageEntity
   */
	public void continueDownload(final UploadImageEntity uploadImageEntity) {
		UploadTask.executorService.submit(new Runnable() {
			@Override
			public void run() {
				//开始下载
				startUpLoad(uploadImageEntity);
			}
		});
		ProgressManager.getInstance().addRequestListener(uploadUrl, getUploadListener());
	}


	public void onPause(){
		if(null != entity){
			//当前图片上传失败
			DBHelper.getInstance().updateImageUploadState(entity.getUploadId(),3);
		}
		Intent intent = new Intent(mContext,UploadService.class);
		intent.setAction(UploadService.ACTION_STOP); //停止下载
		mContext.startService(intent);
		uploadUrl = null;
	}
	
	public void deleteUploadById(String uploadId) {
		this.deleteId = uploadId;
		DBHelper.getInstance().deleteImageUploadByUploadId(uploadId);
		Intent intent = new Intent(UploadService.ACTION_DELETE);
		mContext.sendBroadcast(intent);
	}
}

对应的监听使用的是ProgressManager

难点总结:

1.熟悉GreenDao,自己设计数据库表;

2.okhttp上传进度的监听;

3.task任务的安排,图片压缩,oom的控制,最后我只使用了单线程还上传图片,多线程能力有限还没实现,多线程容易出现oom,目前还没解决

4.已经后台上传的状态控制,数据库操作等;

看后续能不能完成多线程上传


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值