2014-02-17 3 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() работает асинхронно.

Вам все равно нужно создать отдельный поток для выполнения задания, чтобы предотвратить блокировку пользовательского интерфейса.

documentation говорит:

Внимание: служба работает в основном потоке своего хостинга процесса-на службы не создает свой собственный поток и не запускается в отдельном процессе (если не указан в противном случае). Это означает, что если ваша услуга будет выполнять любые активные или блокирующие операции с ЦП (например, воспроизведение MP3 или сеть), вы должны создать новый поток в службе для выполнения этой работы. Используя отдельный поток, вы, , уменьшите риск ошибок приложения, не отвечающих (ANR), и основной поток приложения может оставаться посвященным взаимодействию пользователя с вашими действиями.

+0

Служба объявлена ​​в манифесте как отдельный процесс (это то, что автор написал не менее: p). Я обновил вопрос. – ashur

+0

@ashur Вы можете использовать IntentService в этом случае или можете сделать это в отдельном потоке – Triode

+0

@RajeshCP simple 'Runnable' будет в порядке? Или мне нужно сделать это с классом AsyncTask? – ashur

Смежные вопросы