请原谅我糟糕的英语,我希望我的解释是可以理解的。
我正在开发应用程序,它与我的服务器通信。它有不同的任务。其中之一是将我的照片从智能手机上传到我的服务器。我希望我的应用程序在后台执行此操作,尽量减少内存和带宽的使用。
基本上它可以工作,但是当我有很多新图片时,同时有很多 AsyncTask 并会明显减少内存和带宽。
首先我使用广播接收器,它每 30 分钟启动一次我的图片扫描仪。 扫描仪首先检查外部存储是否可读、WIFI 是否打开以及是否有互联网连接。
如果是这样,它会从数据库中查询已上传的图片列表。
然后它从 MediaStore 库请求所有图片,检查一些最小值(大小、高度、宽度)以及图片是否未上传。当一切正常后,它会启动一个 AsyncTask 来调整图像大小并上传它:
public class Scanner {
private Context context;
private PicDAO picDAO;
private Helper helper;
public Scanner(Context context) {
this.context = context;
picDAO = new PicDAO(context);
helper = new Helper(context);
}
public void startScan(){
if(helper.isExternalStorageReadable()
&& helper.isWifiOn()
&& helper.checkInternet()){
HashMap<Integer, String> pics = picDAO.picsHashMap();
Cursor mCursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,null,null,null,MediaStore.Images.Media.DEFAULT_SORT_ORDER);
if(mCursor != null){
mCursor.moveToFirst();
while(!mCursor.isAfterLast()) {
PicClass pic = new PicClass(
mCursor.getInt(mCursor.getColumnIndex(MediaStore.Images.Media._ID)),
mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)),
mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DATA)),
mCursor.getInt(mCursor.getColumnIndex(MediaStore.Images.Media.WIDTH)),
mCursor.getInt(mCursor.getColumnIndex(MediaStore.Images.Media.HEIGHT)),
mCursor.getInt(mCursor.getColumnIndex(MediaStore.Images.Media.SIZE)),
context
);
if(pic.getSize() > 25000
&& pic.getHeight() > 200
&& pic.getWidth() > 200
&& ( pics.get(pic.getIdent()) == null || !pics.get(pic.getIdent()).equals(pic.getDisplay_name()))
){
CreateThumb createThumb = new CreateThumb(context);
createThumb.execute(new PicClass[]{pic});
}
mCursor.moveToNext();
}
mCursor.close();
}
}
}
}
creatThumb 看起来会调整图像大小并开始上传(使用 volley 库):
public class CreateThumb extends AsyncTask<PicClass, Void, PicClass> {
private Context context;
public CreateThumb(Context context) {
this.context = context;
}
@Override
protected PicClass doInBackground(PicClass... pics) {
Helper helper = new Helper(context);
String encodedString = "";
if(helper.isWifiOn() && helper.checkInternet()){
double dWidth = 1000;
double dHeight = 1000;
if(pics[0].getWidth() < (int) dWidth && pics[0].getHeight() < (int) dHeight){
dWidth = pics[0].getWidth();
dHeight = pics[0].getHeight();
}else{
if (pics[0].getWidth() > pics[0].getHeight()){
double div = pics[0].getWidth() / dWidth;
dHeight = pics[0].getHeight() / div;
}else{
double div = pics[0].getHeight() / dHeight;
dWidth = pics[0].getWidth() / div;
}
}
int width = (int) dWidth;
int height = (int) dHeight;
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeFile(pics[0].getPath(),bmOptions);
Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap,width,height,0);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
thumbnail.compress(Bitmap.CompressFormat.JPEG, 90, stream);
byte[] byte_arr = stream.toByteArray();
encodedString = Base64.encodeToString(byte_arr, 0);
}
pics[0].setThumb_file(encodedString);
return pics[0];
}
@Override
protected void onPostExecute(final PicClass pic) {
if(!pic.getThumb_file().equals("")){
RequestQueue queue = Volley.newRequestQueue(context);
String url ="http://example.de/upload.php";
StringRequest postRequest = new StringRequest(Request.Method.POST, url, new Response.Listener<String>(){
@Override
public void onResponse(String response) {
if(response.equals("OK")){
PicDAO picDAO = new PicDAO(context);
picDAO.savePic(pic);
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {}
}
){
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("img",pic.getThumb_file());
params.put("filename",pic.getDisplay_name() + ".jpg");
return params;
}
};
queue.add(postRequest);
}
}
}
我服务器上的脚本:
<?php
$base = $_POST['img'];
$filename = $_POST['filename'];
$binary = base64_decode($base);
$file = fopen('uploadedimages/'.$filename, 'wb');
fwrite($file, $binary);
fclose($file);
echo "OK";
问题是,当我有太多新图片时,它会减慢设备和互联网连接的速度,并且我会收到以下错误:
W/art:挂起所有线程花费的时间:278.260ms D/Volley:[2790] BasicNetwork.logSlowRequests:请求的 HTTP 响应=<[ ] http://example.de/upload.php 0x8a9f5792 NORMAL 1> [lifetime=3447]、[size=2]、[rc=200] ,[重试次数=1]
如何优化我的代码或防止同时进行多次上传。
编辑
我尝试重建扫描仪部分并仅使用一个队列来添加请求。但好像并没有什么作用。当只有一张图片时它可以工作,但是当脚本添加超过请求的图片时,它没有得到响应并且服务器上只有第一张图片。
public class Scanner {
private Context context;
private PicDAO picDAO;
private Helper helper;
public Scanner(Context context) {
this.context = context;
picDAO = new PicDAO(context);
helper = new Helper(context);
}
public void startScan(){
if(helper.isDeviceReady()){
Cursor mCursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,null,null,null,MediaStore.Images.Media.DEFAULT_SORT_ORDER);
if(mCursor != null){
final RequestQueue mRequestQueue = Volley.newRequestQueue(context);
mCursor.moveToFirst();
while(!mCursor.isAfterLast()) {
final PicClass pic = new PicClass(mCursor, context);
if(pic.checkSize() && !picDAO.picExist(pic)){
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeFile(pic.getPath(),bmOptions);
Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap,pic.getNewSize()[0],pic.getNewSize()[1],0);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
thumbnail.compress(Bitmap.CompressFormat.JPEG, 90, stream);
byte[] byte_arr = stream.toByteArray();
pic.setThumb_file(Base64.encodeToString(byte_arr, 0));
StringRequest postRequest = new StringRequest(Request.Method.POST, "http://example.de/upload.php", new Response.Listener<String>(){
@Override
public void onResponse(String response) {
Log.d("DEBUG",response);
if(response.equals("OK")){
PicDAO picDAO = new PicDAO(context);
picDAO.savePic(pic);
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
VolleyLog.e("Error: ", error.getMessage());
}
}
){
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("img",pic.getThumb_file());
params.put("filename",pic.getDisplay_name() + ".jpg");
return params;
}
};
mRequestQueue.add(postRequest);
}
mCursor.moveToNext();
}
mCursor.close();
}
}
}
}
如何优化我的代码或防止同时进行多次上传
我会放弃 AsyncTask,转而使用 IntentService,它一次执行一项作业,然后对所有其他作业进行排队。当然,整个过程并不像看起来那么简单,所以也许使用像 Android Priority Job Queue 这样的专用库会更好。
首先,不要为每次上传创建一个新的请求队列。 队列的想法是,向其中添加一堆请求,然后运行队列,让 Volley 慢慢地一次处理一小部分请求。 您正在创建数十个队列,但我什至没有看到运行队列的调用。
我还会使用线程而不是 AsyncTask。 AsyncTasks 应该是一次性的。 事实上,通过使用这么多任务,您可能会耗尽所有其他可能需要任务的东西,因为它们共享一个公共线程。