Android okhttp图片上传导致java.lang.OutofMemoryError

时间:2018-10-19 08:03:24

标签: android android-sqlite out-of-memory image-uploading okhttp

我正在开发一个Android应用程序,用户可以在其中报告问题(作为json数据上传)并将图像文件(与问题相关的文件)上传到服务器。如果该应用程序具有在线连接,则可以正常运行。不幸的是,这对我来说还不够,它必须具有保存这些图像并在手机重新联机后稍后上传的功能。

因此,我创建了一项服务,该服务不断尝试上传数据 (我知道这不是使其全部工作的最佳构造,但这是我能想到的唯一解决方案,我是整个编程领域的初学者) 当第一个请求失败时。这就是问题所在,它涉及到数据,但是对于图像却不起作用,在我保存图像几秒钟后,应用崩溃了,给了我java.lang.OutOfMemoryError

我正在使用okhttp 2.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 :(得分:1)

您不需要每次都尝试发送数据。但是,您可以创建一个广播接收器,以在电话上收听所需的事件并采取相应的措施。例如,跟踪电话的连接并仅在电话已连接时发送数据。

下面的链接可以帮助您实施该解决方案:

Receiver element

BroadcastReceiver

StackOverFlow的这篇文章谈到了收听网络更改

Broadcast receiver for checking internet connection in android app

我认为这将对您的项目有所帮助。不要犹豫,要求在评论中进行澄清,但请尝试。良好的编码