我正在开发一个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调度程序发生内存不足错误?任何帮助表示赞赏。
答案 0 :(得分:1)
您不需要每次都尝试发送数据。但是,您可以创建一个广播接收器,以在电话上收听所需的事件并采取相应的措施。例如,跟踪电话的连接并仅在电话已连接时发送数据。
下面的链接可以帮助您实施该解决方案:
StackOverFlow的这篇文章谈到了收听网络更改
Broadcast receiver for checking internet connection in android app
我认为这将对您的项目有所帮助。不要犹豫,要求在评论中进行澄清,但请尝试。良好的编码