2009-03-25 15 views
36

Tôi đã tạo ra một dịch vụ ren theo cách sau:Nơi dừng/hủy các chủ đề trong lớp Dịch vụ Android?

public class TCPClientService extends Service{ 
... 

@Override 
public void onCreate() { 
    ... 
    Measurements = new LinkedList<String>(); 
    enableDataSending();  
} 

@Override 
public IBinder onBind(Intent intent) { 
    //TODO: Replace with service binding implementation 
    return null; 
} 

@Override 
public void onLowMemory() { 
    Measurements.clear(); 
    super.onLowMemory(); 
} 

@Override 
public void onDestroy() { 
    Measurements.clear(); 
    super.onDestroy(); 
    try { 
     SendDataThread.stop(); 
    } catch(Exception e){ 
     ...  
    } 

} 

private Runnable backgrounSendData = new Runnable() { 

    public void run() { 
     doSendData(); 
    } 
}; 

private void enableDataSending() { 
    SendDataThread = new Thread(null, backgrounSendData, "send_data"); 
    SendDataThread.start(); 
} 

private void addMeasurementToQueue() { 
    if(Measurements.size() <= 100) { 
     String measurement = packData(); 
     Measurements.add(measurement); 
    } 
} 

private void doSendData() { 
    while(true) { 
     try {  
      if(Measurements.isEmpty()) { 
       Thread.sleep(1000); 
       continue; 
      } 
      //Log.d("TCP", "C: Connecting..."); 
      Socket socket = new Socket(); 
      socket.setTcpNoDelay(true); 
      socket.connect(new InetSocketAddress(serverAddress, portNumber), 3000); 
      //socket.connect(new InetSocketAddress(serverAddress, portNumber)); 
      if(!socket.isConnected()) { 
       throw new Exception("Server Unavailable!"); 
      } 
      try { 
       //Log.d("TCP", "C: Sending: '" + message + "'"); 
       PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true); 
       String message = Measurements.remove(); 
       out.println(message); 
       Thread.sleep(200); 
       Log.d("TCP", "C: Sent."); 
       Log.d("TCP", "C: Done."); 
       connectionAvailable = true;    
      } catch(Exception e) { 
       Log.e("TCP", "S: Error", e); 
       connectionAvailable = false; 
      } finally { 
       socket.close(); 
       announceNetworkAvailability(connectionAvailable); 
      } 
     } catch (Exception e) { 
      Log.e("TCP", "C: Error", e); 
      connectionAvailable = false; 
      announceNetworkAvailability(connectionAvailable); 
     } 
    } 
} 

... 
} 

Sau khi tôi đóng ứng dụng các tác phẩm điện thoại rất chậm và tôi đoán đó là do sợi thất bại chấm dứt.

Có ai biết cách tốt nhất để chấm dứt tất cả các chuỗi trước khi chấm dứt ứng dụng không?

Trả lời

89

Phụ lục: Khung công tác Android cung cấp nhiều người trợ giúp cho công việc một lần, công việc nền, v.v., có thể thích hợp hơn khi cố gắng cuộn chuỗi của riêng bạn trong nhiều trường hợp. Như đã đề cập trong bài đăng dưới đây, AsyncTask là một điểm khởi đầu tốt để xem xét. Tôi khuyến khích người đọc xem xét các điều khoản khuôn khổ trước khi bắt đầu suy nghĩ về việc thực hiện luồng riêng của họ.

Có một số vấn đề trong các mẫu mã bạn được đăng, tôi sẽ giải quyết theo thứ tự:

1) Thread.stop() đã được chấp thuận cho một số thời gian hiện nay, vì nó có thể để lại các biến phụ thuộc ở các bang không phù hợp trong một số trường hợp. Xem this Sun answer page để biết thêm chi tiết (Chỉnh sửa: liên kết đó hiện đã chết, xem this page for why not to use Thread.stop()). Một phương pháp ưa thích dừng lại và bắt đầu một chủ đề như sau (giả chủ đề của bạn sẽ chạy hơi vô thời hạn):

private volatile Thread runner; 

public synchronized void startThread(){ 
    if(runner == null){ 
    runner = new Thread(this); 
    runner.start(); 
    } 
} 

public synchronized void stopThread(){ 
    if(runner != null){ 
    Thread moribund = runner; 
    runner = null; 
    moribund.interrupt(); 
    } 
} 

public void run(){ 
    while(Thread.currentThread() == runner){ 
    //do stuff which can be interrupted if necessary 
    } 
} 

Đây chỉ là một ví dụ về cách ngăn chặn một chủ đề, nhưng takeaway là bạn có trách nhiệm thoát khỏi một chuỗi giống như bất kỳ phương thức nào khác. Duy trì phương pháp cắt ngang luồng (trong trường hợp này biến biến động, cũng có thể là thông qua một mutex, vv) và trong logic luồng của bạn, hãy sử dụng phương thức giao tiếp đó để kiểm tra xem bạn có nên thoát sớm, dọn dẹp, v.v.

2) Danh sách phép đo của bạn được truy cập bởi nhiều luồng (chuỗi sự kiện và chuỗi người dùng của bạn) cùng một lúc mà không cần đồng bộ hóa. Có vẻ như bạn không phải cuộn đồng bộ hóa của riêng mình, bạn có thể sử dụng BlockingQueue.

3) Bạn đang tạo một Ổ cắm mới mỗi lần lặp của Chủ đề gửi. Đây là một hoạt động khá nặng, và chỉ thực sự có ý nghĩa nếu bạn mong đợi các phép đo cực kỳ không thường xuyên (nói một giờ hoặc ít hơn). Hoặc bạn muốn một ổ cắm liên tục không được tạo lại mỗi vòng lặp của luồng, hoặc bạn muốn chạy một lần, bạn có thể 'kích hoạt và quên' tạo ra một socket, gửi tất cả dữ liệu có liên quan và kết thúc. (Lưu ý nhanh về việc sử dụng Socket, phương thức socket liên tục chặn, chẳng hạn như đọc, không thể bị gián đoạn bởi Thread.interrupt(), và vì vậy khi bạn muốn dừng thread, bạn phải đóng socket cũng như gọi ngắt)

4) Có rất ít điểm trong việc ném ngoại lệ của riêng bạn từ bên trong Chủ đề trừ khi bạn muốn bắt ở một nơi khác. Một giải pháp tốt hơn là để đăng nhập lỗi và nếu nó là không thể phục hồi, dừng thread. Một thread có thể ngăn chặn bản thân với mã tương tự (trong bối cảnh tương tự như trên):

public void run(){ 
    while(Thread.currentThread() == runner){ 
     //do stuff which can be interrupted if necessary 

     if(/*fatal error*/){ 
     stopThread(); 
     return; //optional in this case since the loop will exit anyways 
     } 
    } 
    } 

Cuối cùng, nếu bạn muốn chắc chắn một lối chủ đề với phần còn lại của ứng dụng của bạn, không có vấn đề gì, một kỹ thuật tốt là để gọi Thread.setDaemon (true) sau khi tạo và trước khi bạn bắt đầu luồng. Điều này cờ các chủ đề như một chủ đề daemon, có nghĩa là VM sẽ đảm bảo rằng nó sẽ tự động bị phá hủy nếu không có chủ đề không daemon chạy (chẳng hạn như nếu ứng dụng của bạn thoát).

Tuân thực hành tốt nhất liên quan đến Chủ đề phải đảm bảo rằng ứng dụng của bạn không treo hoặc làm chậm điện thoại, mặc dù họ có thể khá phức tạp :)

+0

Tất cả việc này là thoát khỏi vòng lặp while khi đã hoàn tất. Nó không dừng luồng giữa luồng mà bạn mong đợi sẽ làm gián đoạn. Về cơ bản bạn có thể đạt được điều tương tự bằng cách thiết lập một cờ boolean. 'continueRunning = false;' with 'while (continueRunning);' – Doomsknight

+0

Hi làm thế nào để bạn thực hiện điều này trong một chủ đề tùy chỉnh mở rộng từ Chủ đề? bạn có thể hiển thị một số ví dụ không. – bman

+0

Ai đó có thể giải thích mục đích của so sánh này Thread.currentThread() == runner? –

6

Trên thực tế, bạn không cần phải là "á quân" biến như được mô tả ở trên, một cái gì đó như:

while (!interrupted()) { 
    try { 
     Thread.sleep(1000); 
    } catch (InterruptedException ex) { 
     break; 
    } 
} 

Nhưng nói chung, ngồi trong vòng Thread.sleep() thực sự là một ý tưởng tồi.

Xem API AsyncTask trong API 1.5 mới. Nó có thể sẽ giải quyết vấn đề của bạn thanh lịch hơn so với sử dụng một dịch vụ. Điện thoại của bạn đang bị chậm vì dịch vụ không bao giờ tắt - không có gì khiến dịch vụ tự tử.

Các vấn đề liên quan