简体   繁体   English

Android-下载文件+状态栏通知放慢手机速度

[英]Android- download file + status bar notification slowing down phone

I currently have an asynctask which downloads a mp3 from a server. 我目前有一个asynctask从服务器下载MP3。 When the user starts to download it, a status bar notification is created. 当用户开始下载时,会创建状态栏通知。 This displays the progress of the download in real time. 这将实时显示下载进度。 My only concern is that the phone slows down almost too much. 我唯一担心的是手机放慢了几乎。 Is there any way to delay the progress displayed or a way to make my code faster? 有没有办法延迟显示的进度或使我的代码更快的方法? Thanks. 谢谢。

Code below: 代码如下:

public class DownloadFile extends AsyncTask<String, String, String> {
    CharSequence contentText;
    Context context;
    CharSequence contentTitle;
    PendingIntent contentIntent;
    int HELLO_ID = 1;
    long time;
    int icon;
    CharSequence tickerText;
    File file;

    public void downloadNotification() {
        String ns = Context.NOTIFICATION_SERVICE;
        notificationManager = (NotificationManager) getSystemService(ns);

        icon = R.drawable.sdricontest;
        //the text that appears first on the status bar
        tickerText = "Downloading...";
        time = System.currentTimeMillis();

        notification = new Notification(icon, tickerText, time);

        context = getApplicationContext();
        //the bold font
        contentTitle = "Your download is in progress";
        //the text that needs to change
        contentText = "0% complete";
        Intent notificationIntent = new Intent(Intent.ACTION_VIEW);
        notificationIntent.setType("audio/*");
        contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);

        notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
        notificationManager.notify(HELLO_ID, notification);
    }

    @Override
    protected void onPreExecute() {
        //execute the status bar notification
        downloadNotification();
        super.onPreExecute();
    }

    @Override
    protected String doInBackground(String... url) {
        int count;
        try {
            URL url2 = new URL(sdrUrl);
            HttpURLConnection connection = (HttpURLConnection) url2.openConnection();
            connection.setRequestMethod("GET");
            connection.setDoOutput(true);
            connection.connect();

            int lengthOfFile = connection.getContentLength();

            //make the stop drop rave folder
            File sdrFolder = new File(Environment.getExternalStorageDirectory() + "/StopDropRave");
            boolean success = false;

            if (!sdrFolder.exists()) {
                success = sdrFolder.mkdir();
            }
            if (!success) {
                String PATH = Environment.getExternalStorageDirectory()
                        + "/StopDropRave/";
                file = new File(PATH);
                file.mkdirs();
            } else {
                String PATH = Environment.getExternalStorageDirectory()
                        + "/StopDropRave/";
                file = new File(PATH);
                file.mkdirs();
            }

            String[] path = url2.getPath().split("/");
            String mp3 = path[path.length - 1];
            String mp31 = mp3.replace("%20", " ");
            String sdrMp3 = mp31.replace("%28", "(");
            String sdrMp31 = sdrMp3.replace("%29", ")");
            String sdrMp32 = sdrMp31.replace("%27", "'");

            File outputFile = new File(file, sdrMp32);
            FileOutputStream fos = new FileOutputStream(outputFile);

            InputStream input = connection.getInputStream();

            byte[] data = new byte[1024];
            long total = 0;
            while ((count = input.read(data)) != -1) {
                total += count;
                publishProgress("" + (int) (total * 100 / lengthOfFile));
                fos.write(data, 0, count);
            }
            fos.close();
            input.close();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void onProgressUpdate(String... progress) {
        contentText = Integer.parseInt(progress[0]) + "% complete";
        notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
        notificationManager.notify(HELLO_ID, notification);
        super.onProgressUpdate(progress);
    }
}

I had similar issue once, I solved it using CountDownTimer . 我有过类似的问题,我使用CountDownTimer解决了它。

Similar to how @superfell suggested, you can call progress update of AsyncTask regularly while downloading file. 与@superfell建议的类似,您可以在下载文件时定期调用AsyncTask的进度更新。 And call the Notification Manager only at specific interval. 并且只在特定时间间隔调用通知管理器。

After calling start() of CountDownTimer, it will call onTick() function after every fixed interval of time, and will call onFinish() either when timer is timed out or when called explicitly. 在调用CountDownTimer的start()之后,它将在每个固定的时间间隔之后调用onTick()函数,并在计时器超时或显式调用时调用onFinish() cancel() function will only cancel the timer and will not call onFinish() method. cancel()函数只会取消定时器而不会调用onFinish()方法。

class DownloadMaterial extends AsyncTask<String, String, String> {

    CountDownTimer cdt;
    int id = i;
    NotificationManager mNotifyManager;
    NotificationCompat.Builder mBuilder;

    @Override
    protected void onPreExecute() {
        /**
         * Create custom Count Down Timer
         */
        cdt = new CountDownTimer(100 * 60 * 1000, 500) {
            public void onTick(long millisUntilFinished) {
                mNotifyManager.notify(id, mBuilder.build());
            }

            public void onFinish() {
                mNotifyManager.notify(id, mBuilder.build());
            }
        };
    }

    @Override
    protected String doInBackground(String... strings) {
        /**
         * Start timer to update Notification
         * Set Progress to 20 after connection
         * Build Notification
         * Increment Progress
         * Download and Save file
         */
        try {
            mNotifyManager =
                    (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            mBuilder = new NotificationCompat.Builder(context);
            mBuilder.setContentTitle("Downloading File")
                    .setContentText(file_name)
                    .setProgress(0, 100, false)
                    .setOngoing(true)
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setPriority(Notification.PRIORITY_LOW);

            // Initialize Objects here
            publishProgress("5");
            mNotifyManager.notify(id, mBuilder.build());
            cdt.start();

            // Create connection here
            publishProgress("20");

            // Download file here
            while ((count = input.read(data)) != -1) {
                total += count;
                publishProgress("" + (int) (20 + (total * 80 / fileLength)));
                output.write(data, 0, count);
            }
        } catch (Exception e) {
            return "Failed";
        }
        return "Success";
    }

    @Override
    protected void onProgressUpdate(String... values) {
        /**
         * Update Download Progress
         */
        mBuilder.setContentInfo(values[0] + "%")
                .setProgress(100, Integer.parseInt(values[0]), false);
    }

    @Override
    protected void onPostExecute(String s) {

        String title;
        if (s.equals("Success")) {
            title = "Downloaded";
        } else {
            title = "Error Occurred";
        }
        mBuilder.setContentTitle(title)
                .setContentInfo("")
                .setOngoing(false)
                .setProgress(0, 0, false);
        cdt.onFinish();
        cdt.cancel();
    }
}

It is a good practice to call onFinish() first and then call cancel() . 首先调用onFinish()然后调用cancel()是一个好习惯。

I saw similar results, you need to not push the update the notification so often, i changed mine to update only update a few times a second. 我看到类似的结果,你需要不经常推送更新通知,我改变我的更新只更新几次一秒钟。 (eg in onProgressUpdate keep track of the last time you called notify, and only call notify if you're past 100ms of the previous call, or if you're at the max value. (例如,在onProgressUpdate中跟踪您最后一次调用notify,并且只有在您超过前一次调用的100ms时,或者如果您处于最大值时才调用notify。

I too experienced this problem. 我也遇到过这个问题。 I was updating the progress bar WAY too often (even when the progress didn't change), here's how I fixed that: 我经常更新进度条WAY (即使进度没有改变),这是我如何修复:

        // While loop from generic download method.
        int previousProgress = 0;
        while ((count = inputStream.read(buff)) != -1) {
            outputStream.write(buff, 0, count);
            totalBytesDownloaded += count;
            int prog = (int) (totalBytesDownloaded * 100 / contentLength);
            if (prog > previousProgress) {
                // Only post progress event if we've made progress.
                previousProgress = prog;
                myPostProgressMethod(prog);

            }
        }

Now the app runs great and the user still receives a progress notification. 现在应用程序运行良好,用户仍然收到进度通知。

I had the same issue that I was not able to update the progress bar notification even with an interval of 3 seconds so after hours of digging I came to realize the fact that whenever we update the notification the RemoteView object must be re-instantiated and re-initialized to the Notification object's contentView. 我有同样的问题,即使间隔3秒我也无法更新进度条通知,所以经过几个小时的挖掘后我才意识到每当我们更新通知时,必须重新实例化RemoteView对象并重新启动 - 初始化为Notification对象的contentView。 After doing this I was able to update the Notification progress bar with an interval of 100ms-500ms for a very long period without facing any UI blocking. 执行此操作后,我能够在很长一段时间内以100ms-500ms的间隔更新通知进度条,而不会遇到任何UI阻塞。

Note: If you don't agree you can verify this answer by running this snippet after commenting out the marked line and see the difference. 注意:如果您不同意,可以在注释掉标记的行并查看差异后运行此代码段来验证此答案。 It may take about 5mins to start the severe UI blockage which will heat up your device and may stop functioning. 可能需要大约5分钟才能启动严重的UI阻塞,这会阻塞您的设备并可能停止运行。 I tried with an S3 mini with Android 4.2.2 and the updateNotification(....) method was called from a worker thread inside a service. 我尝试使用Android 4.2.2的S3 mini,并且从服务中的工作线程调用updateNotification(....)方法。 and Moreover I already double checked it and don't know what happens when Notification.Builder is used for the same purpose. 而且我已经仔细检查了它,并且不知道当Notification.Builder用于同一目的时会发生什么。

Note: The reason to write this answer after 3 years of the question is because I wonder that I did not find even a single stackoverflow answer or other blog post handling this serious issue with this very simple solution. 注意:在问题3年之后写这个答案的原因是因为我想我甚至找不到一个stackoverflow答案或其他博客文章处理这个非常简单的解决方案的严重问题。

I hope this answer will be helpful for other newbies like me. 我希望这个答案对我这样的其他新手有所帮助。 Enjoy. 请享用。

Here is my copy pasted code that you can use directly.... I use the same code for updating a notification layout which contains two ProgressBars and four TextViews with a frequency of 500ms-100ms. 这是我可以直接使用的复制粘贴代码....我使用相同的代码更新通知布局,其中包含两个ProgressBars和四个TextViews,频率为500ms-100ms。

//long mMaxtTimeoutNanos = 1000000000 // 1000ms.
long mMinTimeNanos     = 100000000;//100ms minimum update limit. For fast downloads.
long mMaxtTimeoutNanos = 500000000;//500ms maximum update limit. For Slow downloads
long mLastTimeNanos = 0;
private void updateNotification(.....){
    // Max Limit
    if (mUpdateNotification || ((System.nanoTime()-mLastTimeNanos) > mMaxtTimeoutNanos)) {
        // Min Limit
        if (((System.nanoTime() - mLastTimeNanos) > mMinTimeNanos)) {
            mLastTimeNanos = System.nanoTime();
            // instantiate new RemoteViews object.
            // (comment out this line and instantiate somewhere
            // to verify that the above told answer is true)
            mRemoteView = new RemoteViews(getPackageName(),
                    R.layout.downloader_notification_layout);
            // Upate mRemoteView with changed data
            ...
            ...
            // Initialize the already existing Notification contentView
            // object with newly instatiated mRemoteView.
            mNotification.contentView = mRemoteView;
            mNotificationManager.notify(mNotificatoinId, mNotification);
            mUpdateNotification = false;
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM