我有“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;
}
答案 0 :(得分:3)
我不知道为什么当我启动它时,屏幕会变黑,直到应用程序从服务中检索数据。
服务在与应用程序相同的进程中运行。绑定到服务的代码和来自ServiceConnection的回调......一切都在主线程中运行。当你说bindService()
异步工作时,你错了。
您仍然需要创建一个单独的线程来完成这项工作,以防止UI阻塞。
警告:服务在其托管进程的主线程中运行 - service不会创建自己的线程,也不会单独运行 过程(除非您另行指定)。这意味着,如果你的 服务将进行任何CPU密集型工作或阻塞操作 (如MP3播放或网络),你应该创建一个新的线程 在服务范围内做这项工作。通过使用单独的线程,你 将降低应用程序无响应(ANR)错误的风险 应用程序的主线程可以保持专用于用户交互 与您的活动。