2014-02-17 24 views
0

我有一個來自「Android實踐」書籍的示例應用程序,允許用戶檢查股價。應用程序創建URL字符串並執行HTTP請求。當應用程序啓動它通過使用AIDL「連接」到服務時,該請求在獨立進程中處於服務狀態。接口被「封鎖」直到數據被提取

我不知道爲什麼當我啓動它時,屏幕會變黑,直到應用程序從服務中獲取數據。當我讀bindService()是異步完成它的工作,所以它不應該是問題。

更新 - 清單中的服務聲明。

<service 
     android:name=".PortfolioManagerService" 
     android:icon="@drawable/icon" 
     android:label="@string/service_name" 
     android:process=":stocks_background" /> 

這裏是代碼的主要活動:

public class ViewStocks extends ListActivity { 

    private static final String LOGGING_TAG = "ViewStocks"; 

    // The list of stocks shown to the user 
    private ArrayList<Stock> stocks; 
    // Service used to persist and retrieve stocks 
    private IStockService stockService; 
    // Is the service bound currently? 
    private boolean bound = false; 

    // Connection to the stock service, handles lifecycle events 
    private ServiceConnection connection = new ServiceConnection(){ 

     public void onServiceConnected(ComponentName className, 
       IBinder service) { 
      stockService = IStockService.Stub.asInterface(service); 
      Log.d(LOGGING_TAG,"Connected to service"); 
      try { 
       stocks = (ArrayList<Stock>) stockService.getPortfolio(); 
       if (stocks == null){ 
        stocks = new ArrayList<Stock>(0); 
        Log.d(LOGGING_TAG, "No stocks returned from service"); 
       } else { 
        Log.d(LOGGING_TAG, "Got "+ stocks.size() +" stocks from service"); 
       } 
       refresh(); 
      } catch (RemoteException e) { 
       Log.e(LOGGING_TAG, "Exception retrieving portfolio from service",e); 
      } 
     } 

     public void onServiceDisconnected(ComponentName className) { 
      stockService = null; 
      Log.d(LOGGING_TAG,"Disconnected from service"); 
     } 

    }; 

    @Override 
    public void onStart(){ 
     super.onStart(); 
     // create initial list 
     if (!bound){ 
      bound = bindService(new Intent(ViewStocks.this, PortfolioManagerService.class), 
           connection, Context.BIND_AUTO_CREATE); 
      Log.d(LOGGING_TAG, "Bound to service: " + bound); 
     } 
     if (!bound){ 
      Log.e(LOGGING_TAG, "Failed to bind to service"); 
      throw new RuntimeException("Failed to find to service"); 
     } 
     setListAdapter(new BaseAdapter(){ 

      public int getCount() { 
       if (stocks == null){ 
        return 0; 
       } 
       return stocks.size(); 
      } 

      public Object getItem(int position) { 
       if (stocks == null){ 
        return null; 
       } 
       return stocks.get(position); 
      } 

      public long getItemId(int position) { 
       if (stocks == null){ 
        return 0L; 
       } 
       return stocks.get(position).getId(); 
      } 

      public View getView(int position, View convertView, 
        ViewGroup parent) { 
       if (convertView == null){ 
        LayoutInflater inflater = getLayoutInflater(); 
        convertView = 
         inflater.inflate(R.layout.stock, parent, false); 
       } 
       TextView rowTxt = 
        (TextView) convertView.findViewById(R.id.rowTxt); 
       rowTxt.setText(stocks.get(position).toString()); 
       return convertView; 
      } 

      @Override 
      public boolean hasStableIds() { 
       return true; 
      } 

     }); 
    } 


    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     // Create UI elements, data loaded by <code>onStart</code> 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main);  

     // add widgets 
     final EditText symbolIn = (EditText) findViewById(R.id.inputSymbol); 
     final EditText maxIn = (EditText) findViewById(R.id.inputMax); 
     final EditText minIn = (EditText) findViewById(R.id.inputMin); 
     final EditText priceIn = (EditText) findViewById(R.id.inputPrice); 
     final EditText quantIn = (EditText) findViewById(R.id.inputQuant); 

     // Add event handler to button 
     Button button = (Button) findViewById(R.id.btn); 
     button.setOnClickListener(new OnClickListener(){ 
      public void onClick(View v) {    
       String symbol = symbolIn.getText().toString(); 
       symbolIn.setText(""); 

       double max = Double.parseDouble(maxIn.getText().toString()); 
       maxIn.setText(""); 

       double min = Double.parseDouble(minIn.getText().toString()); 
       minIn.setText(""); 

       double pricePaid = Double.parseDouble(priceIn.getText().toString()); 
       priceIn.setText(""); 

       int quantity = Integer.parseInt(quantIn.getText().toString()); 
       quantIn.setText(""); 

       Stock stock = new Stock(symbol, pricePaid, quantity); 
       stock.setMaxPrice(max); 
       stock.setMinPrice(min); 

       // Add stock to portfolio using service in the background 
       new AsyncTask<Stock,Void,Stock>(){ 
        @Override 
        protected Stock doInBackground(Stock... newStocks) { 
         // There can be only one! 
         try { 
          return stockService.addToPortfolio(newStocks[0]); 
         } catch (RemoteException e) { 
          Log.e(LOGGING_TAG, "Exception adding stock " + 
            "to portfolio", e); 
         } 
         return null; 
        } 
        @Override 
        protected void onPostExecute(Stock s){ 
         Log.d(LOGGING_TAG, "Stock returned from service: " + s); 
         if (s == null){ 
          Log.w(LOGGING_TAG, "Stock returned from Service " + 
            "was null or invalid"); 
          Toast.makeText(ViewStocks.this, "Stock not found", 
            Toast.LENGTH_SHORT).show(); 
         } else { 
          refreshStockData(); 
         } 
        } 
       }.execute(stock); 
      } 
     }); 
    } 

    @Override 
    public void onPause(){ 
     super.onPause(); 
     if (bound){ 
      bound = false; 
      unbindService(connection); 
     } 
    } 

    @Override 
    protected void onDestroy() { 
     super.onDestroy(); 
     // disconnect from the stock service 
     if (bound) { 
      bound = false; 
      unbindService(connection); 
     } 
    } 

    // Update stock data from the service and refresh the UI 
    private void refreshStockData() { 
     if (stocks != null && stocks.size() > 0){ 

      new AsyncTask<Void, Void, ArrayList<Stock>>(){ 
       @Override 
       protected void onPostExecute(ArrayList<Stock> result) { 
        Log.d(LOGGING_TAG, "Got new stock data from service"); 
        if (result != null){ 
         stocks = result; 
         refresh(); 
        } else { 
         Toast.makeText(ViewStocks.this, "Exception getting " + 
           "latest stock data", Toast.LENGTH_SHORT).show(); 
        } 
       } 

       @Override 
       protected ArrayList<Stock> doInBackground(Void... nada){ 
        try { 
         return (ArrayList<Stock>) stockService.getPortfolio(); 
        } catch (Exception e) { 
         Log.e(LOGGING_TAG, "Exception getting stock data", e); 
        } 
        return null; 
       } 
      }.execute(); 

     } 
    } 

    private void refresh(){ 
     Log.d(LOGGING_TAG, "Refreshing UI with new data"); 
     for (Stock s : stocks){ 
      Log.d(LOGGING_TAG, "Got stock: " + s.toString()); 
     } 
     BaseAdapter adapter = (BaseAdapter) this.getListAdapter(); 
     adapter.notifyDataSetChanged(); 
    } 
} 

這裏是代碼在服務類從網絡獲取數據(請注意,我只從該類複製此方法)。

private ArrayList<Stock> fetchStockData(Stock[] stocks) throws IOException { 
    Log.d(TAG, "Fetching stock data from Yahoo"); 
    ArrayList<Stock> newStocks = new ArrayList<Stock>(stocks.length); 
    if (stocks.length > 0) { 
     StringBuilder sb = new StringBuilder(); 
     for (Stock stock : stocks) { 
      sb.append(stock.getSymbol()); 
      sb.append('+'); 
     } 
     sb.deleteCharAt(sb.length() - 1); 
     String urlStr = "http://finance.yahoo.com/d/quotes.csv?f=sb2n&s=" 
       + sb.toString(); 
     HttpClient client = new DefaultHttpClient(); 
     HttpGet request = new HttpGet(urlStr.toString()); 
     HttpResponse response = client.execute(request); 
     BufferedReader reader = new BufferedReader(new InputStreamReader(
       response.getEntity().getContent())); 
     String line = reader.readLine(); 
     int i = 0; 
     Log.d(TAG, "Parsing stock data from Yahoo"); 
     while (line != null) { 
      Log.d(TAG, "Parsing: " + line); 
      String[] values = line.split(","); 
      Stock stock = new Stock(stocks[i], stocks[i].getId()); 
      stock.setCurrentPrice(Double.parseDouble(values[1])); 
      stock.setName(values[2]); 
      Log.d(TAG, "Parsed Stock: " + stock); 
      newStocks.add(stock); 
      line = reader.readLine(); 
      i++; 
     } 
    } 
    return newStocks; 
} 

回答

3

我不知道爲什麼,當我啓動它,屏幕是黑的,直到從服務應用程序retreives數據。

服務在與應用程序相同的進程中運行。綁定到服務的代碼和來自ServiceConnection的回調...一切都在主線程中運行。當你說bindService()異步工作時,你錯了。

您仍然需要創建一個單獨的線程來完成這項工作,以防止UI被阻塞。

documentation說:

注意:一種服務,它的託管過程中 服務的主線程中運行不創建自己的線程,並在單獨 過程中不運行(除非您指定除此以外)。這意味着,如果您的服務將執行任何CPU密集型工作或阻止操作(如MP3播放或聯網) ,則應在服務內創建一個新線程 以完成此項工作。通過使用單獨的線程,您可以降低應用程序不響應(ANR)錯誤的風險,並且應用程序的主線程可以保持專用於用戶與您的活動的交互 。

+0

該服務在清單中聲明爲單獨進程(這是作者寫的至少:p)。我更新了問題。 – ashur

+0

@ashur你可以在這種情況下使用IntentService,或者你可以在單獨的線程中執行此操作 – Triode

+0

@RajeshCP simple'Runnable'會好嗎?或者我需要用'AsyncTask'類來完成它? – ashur