-1

映画をダウンロードして進行状況を通知するために作成Serviceしましたが、ダウンロードを開始してアプリを終了すると、前のサービスを破棄せずに新しいサービスを作成するときに問題が見つかりました。

ログ:

 CREATE  // I press Download button Service Created
 START  
 RUN
 CREATE  // I exit from app and it creates new, without DESTROY
 START
 RUN    
 START   // I press to stop downloading
 DESTROY

DownloadManager.java

public class DownloadManager extends Service{



private ExecutorService exec;

private int mb = 1024*1024;
private int Notifid;
private int progressPercent;

private String title;
private String url;

private boolean serviceWork = true;

private NotificationManager manager;
private NotificationCompat.Builder builder;

@Override
public void onCreate() {
    super.onCreate();   
    exec = Executors.newFixedThreadPool(1);
    builder = new NotificationCompat.Builder(getApplicationContext());
    manager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    url = intent.getStringExtra(C.SERVICE_URL);
    title = intent.getStringExtra(C.SERVICE_TITLE);

    if(url.equals("cancel")){
     stopSelf();
    }       
    else {          
        Run run = new Run(url, title);
        serviceWork = true;
        exec.execute(run);
    }

    Notifid = 0x45;

    return START_REDELIVER_INTENT;
}

@Override
public void onDestroy() {
    if(url.equals("cancel")){
        cancel();
    }           
    serviceWork = false;
    super.onDestroy();
}


void generateNotify(String msg1, String msg2){              
    builder.setAutoCancel(false);
    builder.setOngoing(true);
    builder.setContentTitle(msg1);
    builder.setContentText(msg2);
    builder.setSmallIcon(R.drawable.ic_launcher, 0);
    builder.setTicker(msg1);
    builder.setProgress(0, 0, true);

    Intent intent = new Intent(this, DownloadManager.class);
    intent.putExtra(C.SERVICE_URL, "cancel");
    PendingIntent pending = PendingIntent.getService(this, 0, intent, 0);
    builder.setContentIntent(pending);
    manager.notify(Notifid, builder.build());   

}

void progress(final int progress, final String msg){
    new Thread(
            new Runnable() {
                @Override
                public void run() {
                    builder.setProgress(100, progress, false);
                    builder.setContentText(msg);
                    manager.notify(Notifid, builder.build());   
                }
         }
    ).start();
}

void ticker(String msg1){
    builder.setTicker(msg1);
    builder.setContentText(msg1);
    manager.notify(Notifid, builder.build());   
}

void cancel(){
    manager.cancel(Notifid);
}

void cancable(){
    builder.setOngoing(false);

    Intent intent  = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(Uri.parse("sdcard/Mover/"+title+".mp4"), "video/*");

    PendingIntent pending = PendingIntent.getActivity(this, 0, intent, 0);
    builder.setContentIntent(pending);
    builder.setAutoCancel(true);

    manager.notify(Notifid, builder.build());
}

class Run implements Runnable{

    private String url;
    private String title;

    private int count;
    private int fileLength;

    public Run(String url, String title) {
        this.url = url;
        this.title = title;
    }

    @Override
    public void run() {

        try{

            // Создаем подключение к ссылке.

            URL openUrl = new URL(url);
            URLConnection connection = openUrl.openConnection();
            connection.connect();

            // Проверяем наличие папки если отсуствует создаем.

            File file = new File("sdcard/Mover/");
            file.mkdirs();

            // Размер файла

            fileLength = connection.getContentLength();

            // Загружаем конетнт

            InputStream ips = new BufferedInputStream(openUrl.openStream());
            OutputStream ops = new FileOutputStream("sdcard/Mover/"+title+".mp4");

            // Показываем уведомление
            DownloadManager.this.generateNotify(title, "Всего: " +format(fileLength));

            byte[] data = new byte[1024];
            int total = 0;
            int last = 0;
            int progress = 1;
            int lasttotal = 0;

            int speed = 0;

            long current = System.currentTimeMillis();
            // Читаем
            while ((count = ips.read(data)) != -1) {
                if(serviceWork){
                    ops.write(data, 0, count);                  
                    total += count;    

                    long now = System.currentTimeMillis();

                    // Определяем скорость загрзки. 
                    // Для этого делаем  проверку на каждую секунду
                    if(now > current + 1000){
                        current = now;

                        speed = (total - lasttotal)/1024;                       
                        lasttotal = total;
                    }


                    progressPercent = (total*100)/fileLength;
                    if(last != progressPercent){
                        last = progressPercent;
                        progress++;
                        DownloadManager.this.progress(progress, "Всего: " +format(fileLength) + " / " + format(total) + " / " + speed + "KB/s");                            
                    }
                }
            }

            ops.flush();
            // Закрываем 

            ops.close();
            ips.close();

        }
        catch(Exception e){
            e.printStackTrace();
        }

        finish();           
    }

    void finish(){
        DownloadManager.this.ticker("Загрузка успешно завершена.");
        DownloadManager.this.stopSelf();
        DownloadManager.this.cancable();
    }

    void stop(){
        DownloadManager.this.stopSelf();
        DownloadManager.this.cancel();
    }

}


public String format(int m){

    String size = m%mb+"";
    size = size.substring(0, Math.min(size.length(), 2));

    return m/mb + "." + size + "мб";
}

@Override
public IBinder onBind(Intent intent) {
    return null;
}

}

4

2 に答える 2

2

IntentService の使用をお勧めします。Android がワーカー スレッド ロジックを処理する場合、移植作業はほとんど必要ありません。以下のコードを見つけることができます

http://mobile.tutsplus.com/tutorials/android/android-fundamentals-intentservice-basics

于 2013-02-22T11:05:51.600 に答える
1

で指定された return フラグonStartCommandを調べると、 が指定されていることがわかりますSTART_REDELIVER_INTENT。このタイプのリターン フラグの Javadoc には次のように記載されています。

このサービスのプロセスが開始中に (onStartCommand(Intent, int, int) から戻った後) 強制終了された場合、再起動がスケジュールされ、最後に配信されたインテントが onStartCommand(Intent, int,整数)

この説明から、サービスが強制終了されていることが明らかです。この理由は不明ですが、サービス (およびその子スレッド) が「生きている」ことを保証することは、私が以前に取り組んだことです。私たちのアプリケーションでは、電話のスリープが特に破壊的であることがわかりました。サービスにウェイクロックとWiFiロックを与えることで、これらのニュアンスを「ハッキング」(?)して、(a)サービスがスリープ時に停止しないようにし、(b)システムがwifiロックを維持するようにしました(つまり、アクティブな Wi-Fi 接続をドロップします) 一方で、子スレッドで行う作業がありました。これを整理した後、スリープ環境でのサービスの動作を調べてください。それにもかかわらず、このフラグは、サービスの明らかな再起動とダウンロードの再試行を説明しているようです.

コードを簡単に確認すると、スレッドからサービスと通信していることがわかります。IntentServiceRanjith 氏が指摘しているように、このシナリオではおそらく適用可能です。ただし、子スレッドは、ワーカー スレッドをスピンオフした Service と通信できません。この理由は、サービスのジョブが完了し、残りのすべての作業が非同期で完了するため、ワーカー スレッドをスピンオフした後、ホスティング サービスがほぼ即座に強制終了されるためです。これにより、これがうまくいかなかった理由を説明できます。

これを進めるには、次のいずれかを試みます。

  1. を使用してIntentService、すべての通知コードをスレッドに移動し、すべての AsyncTask-Service 通信を切断します。ネットワーク通信用の WakeLocks と WiFiLocks に関して私が行ったコメントにも注意してください。注意せずに使用すると、これらのツールはバッテリーにとって非常に危険なようです。
  2. 子スレッドが続く限りサービスを存続させたい場合は、サービスからSTART_STICKYフラグを返し、アプリがそれにバインドできるようにすることをお勧めします (ref: Bound Serviceを参照して、どのように動作しているかを確認してください。
于 2013-02-22T11:59:03.290 に答える