同时上传多个AsyncTask

问题描述 投票:0回答:2

请原谅我糟糕的英语,我希望我的解释是可以理解的。

我正在开发应用程序,它与我的服务器通信。它有不同的任务。其中之一是将我的照片从智能手机上传到我的服务器。我希望我的应用程序在后台执行此操作,尽量减少内存和带宽的使用。

基本上它可以工作,但是当我有很多新图片时,同时有很多 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();
            }
        }
    }
}
java android file-upload android-asynctask android-volley
2个回答
0
投票

如何优化我的代码或防止同时进行多次上传

我会放弃 AsyncTask,转而使用 IntentService,它一次执行一项作业,然后对所有其他作业进行排队。当然,整个过程并不像看起来那么简单,所以也许使用像 Android Priority Job Queue 这样的专用库会更好。


0
投票

首先,不要为每次上传创建一个新的请求队列。 队列的想法是,向其中添加一堆请求,然后运行队列,让 Volley 慢慢地一次处理一小部分请求。 您正在创建数十个队列,但我什至没有看到运行队列的调用。

我还会使用线程而不是 AsyncTask。 AsyncTasks 应该是一次性的。 事实上,通过使用这么多任务,您可能会耗尽所有其他可能需要任务的东西,因为它们共享一个公共线程。

© www.soinside.com 2019 - 2024. All rights reserved.