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

Android okhttp image upload causes java.lang.OutofMemoryError

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

所以,我创建了一个不断尝试上传数据的服务 (我知道这不是使它全部工作的最佳结构,但这是我能想到的唯一解决方案,我在这整个编程方面还是个初学者) 当第一个请求失败时。这就是问题所在,它适用于数据,但不适用于图像,在我保存图像几秒钟后应用程序崩溃,给我留下 java.lang.OutOfMemoryError.

我正在使用 okhttp 2.5 进行连接,并使用 sqlite 空间来存储数据。

这里是感兴趣的代码:

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();
                     }

               }
           });
       }

    }

上传服务

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 调度程序的内存不足错误的任何建议?任何帮助表示赞赏。

您不需要每次都尝试发送数据。但是您可以创建一个 Broadcast Receiver 来收听 phone 上所需的事件并采取相应的行动。例如跟踪 phone 的连通性,仅当 phone 已连接时才发送数据。

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

Receiver element

BroadcastReceiver

来自 Whosebug 的这个 post 讨论了监听 NETWORK CHANGE

Broadcast receiver for checking internet connection in android app

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