Загрузка изображений из Android okhttp вызывает java.lang.OutofMemoryError - PullRequest
0 голосов
/ 19 октября 2018

Я занимаюсь разработкой приложения для Android, в котором пользователи могут сообщать о проблемах (загружать в виде данных json) и загружать файлы изображений (файлы, связанные с проблемой) на сервер.Приложение работает нормально, если оно подключено к Интернету.К сожалению, этого недостаточно для меня, у него должна быть возможность сохранять эти изображения и загружать их позже, когда мобильный телефон снова будет в сети.

Итак, я создал сервис, который постоянно пытается загружать данные (я знаю,это не лучшая конструкция, чтобы заставить все это работать, но это единственное решение, которое я мог придумать, я довольно новичок во всем этом программировании), когда первый запрос не удался.И вот проблема, это работает, когда дело доходит до данных, но не в случае изображений, через несколько секунд после сохранения изображения приложение вылетает, оставляя мне java.lang.OutOfMemoryError.

Я использую okhttp2.5 для подключения и пространство через sqlite для хранения данных.

Вот код интереса:

Класс ErrorActivity:

    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.");
                       }
                   }
               });

}

Есть какие-нибудь предложения, как избежать ошибки памяти okhttp диспетчера?Любая помощь приветствуется.

1 Ответ

0 голосов
/ 19 октября 2018

Вам не нужно пытаться отправлять данные каждый раз.Но вы можете создать Broadcast Receiver , чтобы прослушивать нужные события на телефоне и действовать соответственно.Например, отслеживать подключение телефона и отправлять данные только в том случае, если телефон подключен.

Ссылки ниже могут помочь вам при реализации решения:

Элемент приемника

BroadcastReceiver

ИВ этом сообщении от StackOverFlow рассказывается о прослушивании NETWORK CHANGE

Вещательный приемник для проверки интернет-соединения в приложении для Android

Думаю, это поможет вам в вашем проекте.Не стесняйтесь просить разъяснений в комментариях, но попробуйте.Хорошее кодирование

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...