I am developing an Android application, where users can report problems (upload as json data) and upload image files(those files connected to a problem) to a server. The application works fine if it has online connection. Unfortunately, that is not enough for me, It must have the capability of saving those images and upload them later when the mobile is back online.
So, I created a service which continuously tries to upload datas
(I know this is not the best construction to make it all work, but this is the only solution I could came up with, I am quite a beginner in this whole programming stuff)
when the first request fails. And here is the problem, it works when it comes to datas, but not in case of images, a few seconds after I save the image the app crashes, leaving me a java.lang.OutOfMemoryError
.
I am using okhttp 2.5 for connection and room over sqlite to store data.
Here is the code of interest:
ErrorActivity class:
public class ErrorActivity extends AppCompatActivity {
private static final int REQUEST_PICK_IMAGE = 3;
UploadService mUploadservice;
boolean mBound = false;
//... Giving permissions, other variables...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_error);
Button btnReport = findViewById(R.id.btnReport);
btnReport.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
reportdefiency();
} catch (JSONException e) {
e.printStackTrace();
}
}
});
//...
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(ErrorActivity.this,
UploadService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(mConnection);
mBound = false;
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder
service) {
UploadService.LocalBinder binder =
(UploadService.LocalBinder) service;
mUploadservice = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBound=false;
}
};
public void reportdefiency(){
//call static inner class AsyncTask, to avoid memory leak
//and avoid UI unresponsiveness
new ImgTask(this).execute();
//...
}
private static class ImgTask extends AsyncTask<Void, Void, Void>{
private WeakReference<ErrorActivity> activityWeakReference;
ImgTask(ErrorActivity context){
activityWeakReference = new WeakReference<>(context);
}
@Override
protected Void doInBackground(Void...voids) {
ErrorActivity activity = activityWeakReference.get();
if(activity == null || activity.isFinishing()) return null;
activity.uploadmultipleimages();
return null;
}
}
public void uploadmultipleimages(){
// check if any images are picked to upload and so on...
// create multipart body part ...
// .....................
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setConnectTimeout(30, TimeUnit.SECONDS);
okHttpClient.setReadTimeout(30, TimeUnit.SECONDS);
//create the requestbody
RequestBody rb =multipartBuilder
.type(MultipartBuilder.FORM)
.build();
Request request = new Request.Builder()
.url("http://blabla/bla/api"
+suburlblabla).post(rb).build();
}
//new variables, because inner
// class needs final fields
final List<byte[]> byteslist = fileinbytes;
//if fails -> save it and start service
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
//a list contains the image to save
final List<OffImage> offImageList = new
ArrayList<>();
File[] files = new File[pictureUriArray.size()];
for(int i=0; i<pictureUriArray.size();i++){
//convert and store in Offimage class
//which is also an @Entity in SQLite
//adding files to list, checked->works...
}
// DB = SQLite database, singleton pattern, insert
// images to table
DB.getInstance(getApplicationContext())
.daoAccess().insertoffimages(offImageList);
//if activity bounded to service, upload it...
if(mBound){
mUploadservice.uploaddefimagedata();
}
}
});
}
}
UploadService
public class UploadService extends Service {
// Users can save images multiple times on Erroractivity
// this int will register the number of calls from activity.
// Each call will be a request in an infinite loop to upload
// the images. But a change in this counter will indicate
// okhttp call to end the loop and start another, because more
// images added to the queue list, hence must query sqlite
// again and create another request with the refreshed list
// images in byte[] format
private static int defimagejobcounter = 0;
public void uploaddefimagedata(){
List<OffImage> offImageList =
DB.getInstance(getApplicationContext())
.daoAccess()
.selectalloffimages();
if(offImageList!=null){
if(offImageList.size()>0){
// increase the counter to indicate to a previous call
// to abandon its operation (See later)
defimagejobcounter++;
createmultipartrequests(offImageList);
}
}
}
private void createmultipartrequests(List<Offimage> listoflists){
// for cycle to create each request
for(int i = 0; listoflists.size(); i++){
//creating requestbody, multipart, etc....
uploadeqimage(requestBody, listoflists.get(0).getEqID(),
defimagejobcounter);
}
}
private void uploadeqimage(final RequestBody requestBody, final int
eqid, final int oldcounter){
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setConnectTimeout(20, TimeUnit.SECONDS);
okHttpClient.setReadTimeout(20, TimeUnit.SECONDS);
Request request = new
Request.Builder().url("http://blabla/bla/api"
+suburlblabla).post(requestBody).build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
// retry if request fails, AND no other calls yet
// from Activity
if(defiencyimagejobcounter==oldcounter){
uploadeqimage(requestBody, eqid, oldcounter);
}
}
@Override
public void onResponse(Response response){
if(response.isSuccessful()){
DB.getInstance(getApplicationContext())
.daoAccess().deletealleqimages();
defimagejobcounter = 0;
Log.d(TAG, "Image(s) uploaded.");
}
}
});
}
Any suggestions how to avoid outofmemoryerror from okhttp dispatcher? Any help appreciated.