2012-04-26 32 views
36

Tôi đang sử dụng một IntentService để xử lý các liên lạc mạng với một máy chủ thông qua JSON. Phần JSON/máy chủ đang hoạt động tốt, nhưng tôi gặp khó khăn khi nhận kết quả về nơi cần thiết. Đoạn mã sau đây cho thấy cách tôi bắt đầu dịch vụ ý định từ bên trong onClick(), và sau đó có dịch vụ cập nhật một biến toàn cục để chuyển kết quả trở lại hoạt động chính.Làm thế nào để có được kết quả từ một IntentService trở lại một hoạt động?

public class GXActivity extends Activity { 

    private static final String TAG = "GXActivity"; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     // === called when the activity is first created 

     Log.i(TAG, "GXActivity Created"); 

     super.onCreate(savedInstanceState); 
     setContentView(R.layout.start); 

     View.OnClickListener handler = new View.OnClickListener() { 
      public void onClick(View v) { 

       // === set up an application context to hold a global variable 
       // === called user, to contain results from the intent service 
       GXApp appState = ((GXApp) getApplicationContext()); 
       User user = new User(-1); // initialize user id to -1 
       appState.setUser(user); 

       // === next start an intent service to get JSON from the server; 
       // === this service updates the user global variable with 
       // === results from the JSON transaction 
       Intent intent = new Intent(this, GXIntentService.class); 
       startService(intent); 

       // === check app context for results from intent service 
       appState = ((GXApp) getApplicationContext()); 
       if (appState.getUser().getId() != -1)... 

      } 
     } 
    } 
} 

Vấn đề tôi đang gặp là dịch vụ ý rằng phân tích cú pháp JSON không nhận được viện dẫn cho đến sau onCreate() hoàn tất, do đó, mã của tôi đó là tìm kiếm các kết quả là bị mắc kẹt nhìn vào kết quả chưa được khởi tạo.

Tôi nên làm gì khác biệt để dịch vụ ý định được gọi trước khi tôi kiểm tra kết quả? Nó sẽ hoạt động nếu tôi kéo trình nghe click vào hàm onCreate()? Có một/tốt hơn để cấu trúc mã này? Cảm ơn nhiều.

+1

thấy điều này có thể hữu ích http://stackoverflow.com/a/9089086/985143 –

Trả lời

78

Bạn nên xem xét việc tạo lớp con ResultReceiver riêng trong hoạt động của mình. ResultReceiver thực hiện Parcelable để có thể được chuyển từ số Activity của bạn sang số Service làm phụ phí trên Intent.

Bạn sẽ cần phải làm một cái gì đó như thế này:

  1. Thực hiện một lớp con của ResultReceiver trong lớp hoạt động của bạn. Phương pháp chính để triển khai là onReceiveResult(). Phương thức này cung cấp cho bạn dữ liệu kết quả có số Bundle có thể được sử dụng để chuyển bất kỳ thông tin nào bạn đang truy xuất trong Service. Chỉ cần giải nén dữ liệu bạn cần và sử dụng nó để cập nhật hoạt động của bạn.

  2. Trong hoạt động của bạn, tạo một phiên bản mới của tùy chỉnh ResultReceiver và thêm nó vào Intent bạn sử dụng để bắt đầu dịch vụ của mình.

  3. Trong onStartCommand() phương pháp Service của bạn, lấy ResultReceiver thông qua tại trên Intent và lưu nó trong một biến thành viên địa phương.

  4. Sau khi Service hoàn thành công việc, hãy gọi send() trên ResultReceiver chuyển bất kỳ dữ liệu nào bạn muốn gửi lại hoạt động trong Bundle.

Đây là một mẫu khá hiệu quả và có nghĩa là bạn không lưu trữ dữ liệu trong các biến tĩnh khó chịu.

+0

Cảm ơn bạn đã trả lời nhanh! Hai câu hỏi tiếp theo. Trước tiên, nếu tôi chuyển sang sử dụng tính năng bổ sung ý định như bạn đã đề xuất, có một ví dụ mã đơn giản, tốt về mẫu mà bạn phác họa không? Thứ hai, nếu tôi muốn gắn bó với phương pháp tiếp cận biến toàn cục (trong trường hợp này, khá vô hại, vì dữ liệu mà nó nắm giữ không thay đổi rất nhiều hoặc rất thường xuyên), tôi sẽ làm như thế nào? Có cách nào để kéo trình nghe nhấp chuột ra khỏi onCreate() hoặc đồng bộ hóa Hoạt động và Dịch vụ không? – gcl1

+0

Vấn đề cơ bản bạn có là Dịch vụ chạy hoàn toàn độc lập với Hoạt động và mất một khoảng thời gian không xác định để hoàn thành. Giải pháp "đúng" cho vấn đề này là sử dụng một số loại cơ chế gọi lại không đồng bộ (đó là những gì mà Trình phân tích kết quả làm cho bạn). Cách khác là liên tục thăm dò một số loại biến tĩnh 'isRunning' trong hoạt động của bạn để phát hiện khi dịch vụ được hoàn thành và sau đó đọc dữ liệu từ một biến tĩnh khác. Đó là khá kém hiệu quả mặc dù và có thể đòi hỏi một sợi nền chuyên dụng - không được khuyến khích. – tomtheguvnor

+0

Tôi đã thử, nhưng chỉ mới bắt đầu ở đây và dường như chưa có đủ điểm danh tiếng. Nhưng câu trả lời của bạn là tuyệt vời, và nứt hạt mà giữ tôi lên đến 3 giờ tối qua. Cảm ơn! – gcl1

7

Nếu tất cả những gì bạn cần làm là lấy JSON từ máy chủ mà không khóa chuỗi giao diện người dùng, có thể đơn giản hơn khi chỉ sử dụng AsyncTask.

+2

chu kỳ cuộc sống không đồng bộ-nhiệm vụ là ràng buộc với các chủ đề nó đang chạy vì vậy nếu tôi xoay màn hình nó sẽ được khởi chạy lại !!! – hadi

11

Có nhiều cách để thực hiện các nội dung trong nền lấy lại kết quả (AsyncTask, liên kết Hoạt động với Dịch vụ ...), Nhưng nếu bạn muốn để mantain mã của IntentService của bạn, bạn có thể chỉ đơn giản là:

1- gửi một ý định phát sóng (có chứa các trạng thái trong dữ liệu mở rộng của nó) ở phần cuối của tác phẩm ở IntentService bạn

2- thực hiện một LocalBroadcastReceiver trong hoạt động của bạn mà tiêu thụ dữ liệu từ tiếp cận mục đích

Đây cũng là cách khuyến khích trong các tài liệu chính thức (nếu bạn muốn để mantain IntentService của bạn):

https://developer.android.com/training/run-background-service/report-status.html

1

Bạn có thể sử dụng thư viện Otto. Otto là một dự án mã nguồn mở được thiết kế để cung cấp triển khai xe buýt sự kiện để các thành phần có thể xuất bản và đăng ký các sự kiện.

+0

Không chắc chắn nếu Otto hoặc GreenBus sẽ làm việc trong chuỗi của IntentService. – Genc

+0

Nó hoạt động. Tôi đã từng sử dụng. –

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