Android主线程在多线程上崩溃 - >文件下载

时间:2015-09-16 01:13:29

标签: java android multithreading android-notifications

我已经创建了一个自定义类来从我们的服务器下载单个音频文件。单个下载工作没有任何问题,但现在我需要包括一次或连续下载多个音频文件的功能..

问题是应用程序因我的下载实施而崩溃:"应用程序没有响应。你想关闭吗? [等待] [退出]"

我不确定导致问题的原因,但我已经知道NotificationManager可能是问题所以我已经在一个教程上实现了它,该教程说它会修复它但是它没有。所以我认为它必须是线程的问题..

AudioDownload类:

public class AudioDownload implements Runnable {

private static Boolean bIAmBusy = false;
private Activity activity;

OnAudioDownloadFinishedListener mycallback;

private String Folder;
private String FileName;
private String FileUrl;

public AudioDownload(String COURSE_FLAG, String AUDIO, String language, Activity a){
    Folder = COURSE_FLAG;
    FileName = AUDIO;
    FileUrl = "http://SOMEURL/"+ language + "/" + COURSE_FLAG + "/" + FileName;
    activity = a;
}


@Override
public void run() {
    bIAmBusy = true;
    File localFile = new File(activity.getFilesDir() + "/" + Folder + "/" + FileName);

    if(localFile.exists()){
        //don't download
    }
    else{
        /*
        In older android versions, your Notification has to have a content Intent,
        so that when the user clicks your Notification, something happens. This means you must:
        Make an Intent object, pointing somewhere, such as your MainActivity.
        Make sure to add the Intent.FLAG_ACTIVITY_NEW_TASK flag.
        */

        Intent intent = new Intent(activity, CoursesActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(activity,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);


        NotificationManager notificationManager = (NotificationManager) activity.getSystemService(activity.getApplicationContext().NOTIFICATION_SERVICE);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(activity);
        builder.setContentTitle("Audio Download");
        builder.setContentText("Download in Progress");
        builder.setSmallIcon(R.drawable.btn_download);
        builder.setTicker("Audio Download");
        builder.setContentIntent(pendingIntent);

        int id = 1;

        BufferedInputStream in = null;
        FileOutputStream out = null;

        boolean saved = false;
        try {
            URL url = new URL(FileUrl);
            URLConnection conn = url.openConnection();
            int size = conn.getContentLength();

            in = new BufferedInputStream(new URL(FileUrl).openStream());

            final File dir = new File(activity.getFilesDir() + "/" + Folder);
            dir.mkdirs();
            final File file = new File(dir,FileName);

            out = new FileOutputStream(file);

            final byte data[] = new byte[1024];
            int count;
            int sumCount = 0;


            while ((count = in.read(data, 0, 1024)) != -1) {
                sumCount += count;
                builder.setProgress(size,sumCount,false);
                notificationManager.notify(id, builder.build());
                out.write(data, 0, count);

            }
            saved = true;
            builder.setContentText("Download Complete");
            builder.setProgress(0,0,false);
            notificationManager.notify(id, builder.build());

        }catch (IOException e){

        }
        finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null){
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        final boolean finalSaved = saved;
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mycallback.OnAudioDownloadThreadFinished(finalSaved);
            }
        });
    }

    bIAmBusy = false;
}

在我的活动按钮上单击我尝试排队线程。

public void onClick(View view) {
            Thread t = new Thread(){
                @Override
                public void run() {
                    Log.d("Audio", "Downloading all the audio for the chapter");

                    Toast.makeText(context, "Downloading all the audio for the chapter", Toast.LENGTH_SHORT).show();

                    List<AudioDownload> downloadList = new ArrayList<AudioDownload>();

                    for (int child = 0; child < chapters.get(groupPosition).getSubChapters().size(); child++) {

                        String Language = "en";
                        String FileName = subChapters.get(chapters.get(group)).get(child).getAudio();

                        audioDownload = new AudioDownload(Course, FileName, Language, activity);
                        audioDownload.setOnAudioDownloadFinishedListener(new AudioDownload.OnAudioDownloadFinishedListener() {
                            @Override
                            public void OnAudioDownloadThreadFinished(Boolean success) {
                                if (success) {
                                    Toast.makeText(context, "Download was successful", Toast.LENGTH_SHORT).show();

                                }
                            }
                        });
                        downloadList.add(audioDownload);

                    }

                    for (int i = 0; i < downloadList.size(); i++) {
                        while (downloadList.get(i).isBusy()) {
                            //wait
                        }
                        if (downloadList.get(i).isBusy() == false) {
                            new Thread(downloadList.get(i), "AudioDownload_" + i).start();

                        }
                    }
                }
            };
            t.start();

等待Thread完成的while循环应该有效,因为bIamBusy被声明为static - &gt;从我的观点来看,预期的行为是每个AudioDownload在另一个完成后启动/启动。

不幸的是,App崩溃了多个下载。如上所述,如果我将此类用于单个音频下载,则App / UIThread不会崩溃。有人知道为什么会这样吗?

更新 应用程序仅在我下拉NotificationManager时崩溃。

ThreadLog ... - &gt;似乎它不会等待启动它们

  17    30669   Native  21  6   AudioDownload_6 
  18    30666   Native  19  7   AudioDownload_4 
  19    30657   Native  20  7   AudioDownload_3 
  20    30673   Native  18  6   AudioDownload_10    
  21    30677   Native  20  8   AudioDownload_12    

堆栈跟踪:

09-16 01:43:57.235      405-440/? E/ActivityManager﹕ ANR in smapp.com.smapp (smapp.com.smapp/.Controller.Screen_4.ChapterOverviewActivity)
Reason: keyDispatchingTimedOut
Load: 1.12 / 0.76 / 0.51
CPU usage from 10166ms to 4102ms ago:
36% 30551/smapp.com.smapp: 35% user + 1.3% kernel / faults: 2463 minor
17% 481/com.android.systemui: 16% user + 0.8% kernel / faults: 5726 minor
9.5% 405/system_server: 8.2% user + 1.3% kernel / faults: 483 minor
1.8% 121/surfaceflinger: 0.6% user + 1.1% kernel
0.2% 124/mediaserver: 0% user + 0.2% kernel / faults: 3 minor
0.9% 114/local_opengl: 0% user + 0.9% kernel
0.3% 60/adbd: 0% user + 0.3% kernel / faults: 34 minor
0.3% 112/vinput: 0% user + 0.3% kernel
0% 28602/flush-8:16: 0% user + 0% kernel
0% 29589/logcat: 0% user + 0% kernel
70% TOTAL: 60% user + 8.8% kernel + 1.1% softirq
CPU usage from 257ms to 760ms later:
59% 30551/smapp.com.smapp: 59% user + 0% kernel
55% 30551/smapp.com.smapp: 55% user + 0% kernel
3.8% 31130/AudioDownload_5: 1.9% user + 1.9% kernel
1.9% 31121/AudioDownload_2: 1.9% user + 0% kernel
1.9% 31122/AudioDownload_3: 1.9% user + 0% kernel
24% 481/com.android.systemui: 20% user + 4% kernel / faults: 657 minor
16% 481/ndroid.systemui: 16% user + 0% kernel
4% 575/Binder_3: 2% user + 2% kernel
2% 519/GC: 2% user + 0% kernel
2% 526/Binder_2: 2% user + 0% kernel
10% 405/system_server: 8% user + 2% kernel
2% 405/system_server: 2% user + 0% kernel
2% 415/Binder_1: 0% user + 2% kernel
2% 416/Binder_2: 0% user + 2% kernel
2% 440/InputDispatcher: 0% user + 2% kernel
2% 662/Binder_5: 2% user + 0% kernel
1.7% 60/adbd: 0% user + 1.7% kernel
1.7% 121/surfaceflinger: 0% user + 1.7% kernel
1.7% 303/VSyncThread: 0% user + 1.7% kernel
100% TOTAL: 90% user + 10% kernel

2 个答案:

答案 0 :(得分:0)

如果你想让线程等到其他线程完成,那么'CountDownLatch'将是一个巨大的帮助。这是一个关于如何使用它的快速sudocode示例。

'CountDownLatch l = new CountDownLatch(n)' n是您需要等待的任务数

'l.await()' 在后台线程中调用它来等待所有n个任务完全阻止

'l.countdown()' 在每个任务完成后调用,减少此锁存器等待的任务数量一旦达到0,await方法将释放,之后的其余代码将在该点执行

答案 1 :(得分:0)

我现在已经解决了问题,感谢您的支持。 CountDownLatch是我的解决方案。

现在正在制定工作守则:

public class AudioDownload implements Runnable {

private static Boolean bIAmBusy = false;
private Activity activity;

OnAudioDownloadFinishedListener mycallback;

private final CountDownLatch latch;

private String Folder;
private String FileName;
private String FileUrl;

public AudioDownload(String COURSE_FLAG, String AUDIO, String language, Activity a, CountDownLatch latch){
    Folder = COURSE_FLAG;
    FileName = AUDIO;
    FileUrl = "http://xxx.xx/xxxxx/xxxx/"+ language + "/" + COURSE_FLAG + "/" + FileName;
    activity = a;
    this.latch = latch;
}


@Override
public void run() {
    bIAmBusy = true;
    File localFile = new File(activity.getFilesDir() + "/" + Folder + "/" + FileName);

    if(localFile.exists()){
        //don't download
    }
    else{
        /*
        In older android versions, your Notification has to have a content Intent,
        so that when the user clicks your Notification, something happens. This means you must:
        Make an Intent object, pointing somewhere, such as your MainActivity.
        Make sure to add the Intent.FLAG_ACTIVITY_NEW_TASK flag.
        */

        Intent intent = new Intent(activity, CoursesActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(activity,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);


        NotificationManager notificationManager = (NotificationManager) activity.getSystemService(activity.getApplicationContext().NOTIFICATION_SERVICE);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(activity);
        builder.setContentTitle("Audio Download");
        builder.setContentText("Download in Progress");
        builder.setSmallIcon(R.drawable.btn_download);
        builder.setTicker("Audio Download");
        builder.setContentIntent(pendingIntent);

        int id = 1;

        BufferedInputStream in = null;
        FileOutputStream out = null;

        boolean saved = false;
        try {
            URL url = new URL(FileUrl);
            URLConnection conn = url.openConnection();
            int size = conn.getContentLength();

            in = new BufferedInputStream(new URL(FileUrl).openStream());

            final File dir = new File(activity.getFilesDir() + "/" + Folder);
            dir.mkdirs();
            final File file = new File(dir,FileName);

            out = new FileOutputStream(file);

            final byte data[] = new byte[1024];
            int count;
            int sumCount = 0;


            while ((count = in.read(data, 0, 1024)) != -1) {
                sumCount += count;
                builder.setProgress(size,sumCount,false);
                notificationManager.notify(id, builder.build());
                out.write(data, 0, count);

            }
            saved = true;
            builder.setContentText("Download Complete");
            builder.setProgress(0,0,false);
            notificationManager.notify(id, builder.build());

        }catch (IOException e){

        }
        finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null){
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        final boolean finalSaved = saved;
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mycallback.OnAudioDownloadThreadFinished(finalSaved);
            }
        });
    }
    latch.countDown();
    bIAmBusy = false;
}

public interface OnAudioDownloadFinishedListener{
    void OnAudioDownloadThreadFinished(Boolean success);
}

public void setOnAudioDownloadFinishedListener(OnAudioDownloadFinishedListener listener){
    mycallback = listener;
}

public Boolean isBusy() {
    return bIAmBusy;
}
}

在我的onClick方法中:

public void onClick(View view) {
    Thread t = new Thread(){
                @Override
                public void run() {
                    Log.d("Audio", "Downloading all the audio for the chapter");

                    activity.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(context, "Downloading all the audio for the chapter", Toast.LENGTH_SHORT).show();
                        }
                    });

                    final CountDownLatch latch = new CountDownLatch(chapters.get(groupPosition).getSubChapters().size());
                    Executor ex;

                    for (int child = 0; child < chapters.get(groupPosition).getSubChapters().size(); child++) {

                        String Language = "en";
                        String FileName = subChapters.get(chapters.get(group)).get(child).getAudio();

                        audioDownload = new AudioDownload(Course, FileName, Language, activity, latch);
                        audioDownload.setOnAudioDownloadFinishedListener(new AudioDownload.OnAudioDownloadFinishedListener() {
                            @Override
                            public void OnAudioDownloadThreadFinished(Boolean success) {
                                if (success) {
                                    activity.runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            Toast.makeText(context, "Download was successful", Toast.LENGTH_SHORT).show();
                                        }
                                    });
                                }
                            }
                        });

                        ex = new Executor() {
                            @Override
                            public void execute(Runnable command) {
                                command.run();
                            }
                        };
                        ex.execute(audioDownload);
                    }

                    try{
                        latch.await();
                        Log.d("multipleAudioDownload", "all Threads are finished");
                        Toast.makeText(context, "Downloading was successful", Toast.LENGTH_SHORT).show();

                    }catch (InterruptedException e){
                        Log.e("multipleAudioDownload",e.getMessage());
                    }
                }
            };
            t.start();

        }