2013-02-22 133 views
-1

我創建Service下載電影和證明進度,我發現一個問題,當我開始下載,然後退出應用程序我的服務創建新的而不破壞前一個。Android服務啓動

登錄:

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; 
} 

}

回答

1

的代碼檢查您的onStartCommand指定的返回標誌,我可以看到你指定START_REDELIVER_INTENT。這種類型的返回標誌的Javadoc狀態;

如果同時啓動( 從onStartCommand(意向,INT,INT)返回後)這個服務的過程中被殺死,那麼這將是 安排在重新啓動和最後遞送的意圖重新交付給 它再次通過onStartCommand(Intent,int,int)

從這個解釋可以明顯看出你的服務正在被殺死。其原因尚不清楚,但確保服務(及其子線程)保持'活躍'是我之前所處理的。在我們的應用程序中,我們發現電話睡眠特別具有破壞性。我們通過爲服務提供Wake-Lock和WiFi-Lock來確保(a)服務在睡眠時不被禁止,並且(b)系統保持wifi鎖定(即沒有放棄活動的無線連接),而我們有工作要做在我們的子線程。請在進行排序後查看您的服務在睡眠環境下的行爲。 儘管如此,這個標誌似乎解釋了您的服務明顯重新啓動和下載重新嘗試。

從簡要回顧一下你的代碼,我發現你正在從你的線程與你的服務進行通信。 Ranjith指出,在這種情況下,IntentService也許適用。但是,子線程無法與離開工作線程的服務通信。這是因爲服務的工作是在所有剩餘工作異步完成的情況下完成的,因此託管服務在離開工作線程後幾乎立即死亡。這可以解釋爲什麼這不適合你。

爲此,我會嘗試以下方法之一;

  1. 使用IntentService並將所有通知碼下移到線程中並切斷所有AsyncTask-Service通信。還請注意我對網絡通信的有關WakeLocks和WiFiLocks的評論。如果使用時不小心這些工具對電池顯得非常危險。
  2. 如果您希望您的服務只要您的子線程存在,那麼我會建議您從服務中返回START_STICKY標誌,並允許您的應用程序綁定到它(參考:Bound Service以查看它是如何進行的。