接口被“阻止”,直到获取数据

时间:2014-02-17 18:58:14

标签: java android service android-service

我有“Android in practice”一书的示例应用程序,允许用户检查股价。应用程序创建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();
    }
}

以下是从服务类中的Web获取数据的代码(请注意,我只是从该类复制了此方法)。

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

1 个答案:

答案 0 :(得分:3)

我不知道为什么当我启动它时,屏幕会变黑,直到应用程序从服务中检索数据。

服务在与应用程序相同的进程中运行。绑定到服务的代码和来自ServiceConnection的回调......一切都在主线程中运行。当你说bindService()异步工作时,你错了。

您仍然需要创建一个单独的线程来完成这项工作,以防止UI阻塞。

documentation说:

  

警告:服务在其托管进程的主线程中运行 -   service不会创建自己的线程,也不会单独运行   过程(除非您另行指定)。这意味着,如果你的   服务将进行任何CPU密集型工作或阻塞操作   (如MP3播放或网络),你应该创建一个新的线程   在服务范围内做这项工作。通过使用单独的线程,你   将降低应用程序无响应(ANR)错误的风险   应用程序的主线程可以保持专用于用户交互   与您的活动。