您的位置:首页 > 移动开发 > Android开发

android in practice_Communicating with a Service (portfolio project)

2012-12-25 14:21 381 查看
IPC (InterProcess cummunication)mechanism. This mechanism allows Services to be exposed to other processes and for serialized data to be sent between the processes.

create The Stock class, a Parcelable class that can be sent over IPC:

//The Stock class, a Parcelable class that can be sent over IPC.

//sent back and forth between main application and background service.

public class Stock implements Parcelable {

// user defined

private String symbol;

private double maxPrice;

private double minPrice;

private double pricePaid;

private int quantity;

// dynamic retrieved

private String name ="";

private double currentPrice=0D;

// db assigned

private int id;

//private constructor for Parcel

private Stock(Parcel parcel){

this.readFromParcel(parcel);

}

// constructors

public Stock(String symbol, double pricePaid, int quantity, int id) {

this.symbol = symbol;

this.pricePaid = pricePaid;

this.quantity = quantity;

this.id = id;

}

public Stock(String symbol, double pricePaid, int quantity) {

this(symbol, pricePaid, quantity, 0);

}

public Stock(Stock old, int id){

this.symbol = old.symbol;

this.maxPrice = old.maxPrice;

this.minPrice = old.minPrice;

this.pricePaid = old.pricePaid;

this.quantity = old.quantity;

this.name = old.name;

this.currentPrice = old.currentPrice;

this.id = id;

}

// getters and setters

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public double getCurrentPrice() {

return currentPrice;

}

public void setCurrentPrice(double currentPrice) {

this.currentPrice = currentPrice;

}

public String getSymbol() {

return symbol;

}

public double getMaxPrice() {

return maxPrice;

}

public double getMinPrice() {

return minPrice;

}

public double getPricePaid() {

return pricePaid;

}

public int getQuantity() {

return quantity;

}

public void setMaxPrice(double maxPrice) {

this.maxPrice = maxPrice;

}

public void setMinPrice(double minPrice) {

this.minPrice = minPrice;

}

public int getId() {

return id;

}

@Override

public String toString() {

StringBuilder sb = new StringBuilder();

if (name != null){

sb.append(name).append(' ');

}

sb.append('(').append(symbol.toUpperCase()).append(')')

.append(" $").append(currentPrice);

return sb.toString();

}

/**

* Any Parcelable needs a static field called CREATOR that

* acts as a factory class for the Parcelable.

* deserialize that Parcel back into a Stock.

*/

public static final Parcelable.Creator<Stock> CREATOR = new Parcelable.Creator<Stock>() {

//a factory method

public Stock createFromParcel(Parcel source) {

return new Stock(source);

}

public Stock[] newArray(int size) {

return new Stock[size];

}

};

public int describeContents() {

return 0;

}

//Serialize stock to Parcel

public void writeToParcel(Parcel parcel, int flags) {

parcel.writeString(symbol);

parcel.writeDouble(maxPrice);

parcel.writeDouble(minPrice);

parcel.writeDouble(pricePaid);

parcel.writeInt(quantity);

parcel.writeDouble(currentPrice);

parcel.writeString(name);

}

//read values from the Parcel in the same order as you wrote them to the Parcel

//Deserialize stock from Parcel

public void readFromParcel(Parcel parcel){

symbol = parcel.readString();

maxPrice = parcel.readDouble();

minPrice = parcel.readDouble();

pricePaid = parcel.readDouble();

quantity = parcel.readInt();

currentPrice = parcel.readDouble();

name = parcel.readString();

}

}

create the StocksDb class:

/**

* A data access object for persisting and retrieving stock data. This uses

* a SQLite database for persistence and retrieval.

*/

public class StocksDb {

private static final String TAG = "StocksDb";

// database metadata

private static final String DB_NAME = "stocks.db";

private static final int DB_VERSION = 1;

private static final String TABLE_NAME = "stock";

// column names

private static final String ID = "id";

private static final String SYMBOL = "symbol";

private static final String MAX_PRICE = "max_price";

private static final String MIN_PRICE = "min_price";

private static final String PRICE_PAID = "price_paid";

private static final String QUANTITY = "quantity";

private static final String CURRENT_PRICE = "current_price";

private static final String NAME = "name";

// SQL statements

private static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME +

" ("+ID+" INTEGER PRIMARY KEY, "+SYMBOL+" TEXT, "+

MAX_PRICE+" DECIMAL(8,2), " + MIN_PRICE+" DECIMAL(8,2), " +

PRICE_PAID+ " DECIMAL(8,2), " + QUANTITY + " INTEGER, " +

CURRENT_PRICE + " DECIMAL(8,2), "+NAME+" TEXT)";

private static final String INSERT_SQL = "INSERT INTO " + TABLE_NAME +

" ("+SYMBOL+", "+MAX_PRICE+", "+MIN_PRICE+", "+PRICE_PAID+

", "+QUANTITY+", " + CURRENT_PRICE+", "+NAME+") " +

"VALUES (?,?,?,?,?,?,?)";

private static final String READ_SQL = "SELECT "+ID+", "+SYMBOL+", " +

MAX_PRICE+", " + MIN_PRICE +", "+PRICE_PAID+", "+

QUANTITY+", " +CURRENT_PRICE+ ", "+NAME+" FROM " +

TABLE_NAME;

private static final String UPDATE_SQL = "UPDATE " + TABLE_NAME +

" SET "+CURRENT_PRICE+"=? WHERE "+ID+"=?";

// The Context object that created this StocksDb

private final Context context;

private final SQLiteOpenHelper helper;

private SQLiteStatement stmt;

private SQLiteStatement updateStmt;

private final SQLiteDatabase db;

/**

* Constructor that takes a Contex object, usually the

* Service or Activity that created this

* instance. This will initialize the SQLiteOpenHelper used for the

* database, and pre-compile the insert and update SQL statements.

*

* @param ctx
The <code>Context</code> that created this instance

*/

public StocksDb(Context ctx){

context = ctx;

// initialize the database helper

helper = new SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION){

@Override

public void onCreate(SQLiteDatabase db) {

db.execSQL(CREATE_TABLE);

Log.d(TAG, "Created table: \n" + CREATE_TABLE);

}

@Override

public void onUpgrade(SQLiteDatabase db, int oldVersion,

int newVersion) {

throw new UnsupportedOperationException();

}

};

// open the database

db = helper.getWritableDatabase();

// pre-compile statements

stmt = db.compileStatement(INSERT_SQL);

updateStmt = db.compileStatement(UPDATE_SQL);

}

/**

* Saves a <code>Stock</code> to the database.

*/

public Stock addStock(Stock stock){

Log.d(TAG, "Adding stock to db, stock="+stock);

stmt.bindString(1, stock.getSymbol());

stmt.bindDouble(2, stock.getMaxPrice());

stmt.bindDouble(3, stock.getMinPrice());

stmt.bindDouble(4, stock.getPricePaid());

stmt.bindLong(5, stock.getQuantity());

stmt.bindDouble(6, stock.getCurrentPrice());

stmt.bindString(7, stock.getName());

int id = (int) stmt.executeInsert();

return new Stock (stock, id);

}

/**

* Updates the current price of a <code>Stock</code> stored in the

* database.

*/

public void updateStockPrice(Stock stock){

Log.d(TAG, "Updating stock price in DB stock="+stock.toString());

updateStmt.bindDouble(1, stock.getCurrentPrice());

updateStmt.bindLong(2, stock.getId());

updateStmt.execute();

}

/**

* Retrieve all of the <code>Stock</code>s stored in the database.

*

* @return
List of all of the Stocks stored in the database.

*/

public ArrayList<Stock> getStocks() {

Log.d(TAG, "Getting stocks form DB");

Cursor results = db.rawQuery(READ_SQL, null);

ArrayList<Stock> stocks = new ArrayList<Stock>(results.getCount());

if (results.moveToFirst()){

int idCol = results.getColumnIndex(ID);

int symbolCol = results.getColumnIndex(SYMBOL);

int maxCol = results.getColumnIndex(MAX_PRICE);

int minCol = results.getColumnIndex(MIN_PRICE);

int priceCol = results.getColumnIndex(PRICE_PAID);

int quanitytCol = results.getColumnIndex(QUANTITY);

int currentPriceCol = results.getColumnIndex(CURRENT_PRICE);

int nameCol = results.getColumnIndex(NAME);

do {

Stock stock = new Stock(results.getString(symbolCol),

results.getDouble(priceCol),

results.getInt(quanitytCol), results.getInt(idCol));

stock.setMaxPrice(results.getDouble(maxCol));

stock.setMinPrice(results.getDouble(minCol));

stock.setCurrentPrice(results.getDouble(currentPriceCol));

stock.setName(results.getString(nameCol));

Log.d(TAG, "Stock from db = " + stock.toString());

stocks.add(stock);

} while (results.moveToNext());

}

if (!results.isClosed()){

results.close();

}

return stocks;

}

/**

* Method to close the underlying database connection.

*/

public void close(){

helper.close();

}

}

Android IDL or AIDL.

create the Stock.aidl :

package example.stockportfolio;

parcelable Stock;

create the IStockService.aidl,The external interface into the stock portfolio service.

package example.stockportfolio.service;

import example.stockportfolio.Stock;

interface IStockService{

Stock addToPortfolio(in Stock stock);

List<Stock> getPortfolio();

}

you can only import other AIDL definitions.It only exposes two methods to the outside world.

A Java interface can be generated from the interface defined in the .aidl file.

Java interface generated from AIDL interface

public interface IStockService extends android.os.IInterface

{

/** Local-side IPC implementation stub class. */

public static abstract class Stub extends android.os.Binder

implements com.flexware.stocks.service.IStockService

{

// generated code

}

public void addToPortfolio(com.flexware.stocks.Stock stock)

throws android.os.RemoteException;

public java.util.List<com.flexware.stocks.Stock> getPortfolio()

throws android.os.RemoteException;

}

......

You’ll want to extend this abstract class,stub implementing the IStockService methods.

change the background service class PortfolioManagerService

public class PortfolioManagerService extends Service {

//Helper class for persisted data

private final StocksDb db=new StocksDb(this);

/*

* a Service usually run in its own process.Interprocess communication (IPC) is necessary.

* The onBind method is where the IPC channel is established.

*/

@Override

public IBinder onBind(Intent intent) {

// TODO Auto-generated method stub

//subclass extends Binder.return.

return new IStockService.Stub() {

@Override

public List<Stock> getPortfolio() throws RemoteException {

// TODO Auto-generated method stub

return db.getStocks();

}

@Override

public Stock addToPortfolio(Stock stock) throws RemoteException {

return db.addStock(stock);

}

};

}

}

create the main activity class ViewStocks

//The main Activity binding to and call the Service

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(){

@Override

public void onServiceConnected(ComponentName className, IBinder service) {

// TODO Auto-generated method stub

stockService=IStockService.Stub.asInterface(service);

Log.d(LOGGING_TAG,"Connected to service");

try {

//refresh();

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

}

}

@Override

public void onServiceDisconnected(ComponentName name) {

// TODO Auto-generated method stub

stockService=null;

}

};

@Override

public void onStart(){

super.onStart();

// create initial list

//bind to remote service

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");

}

this.setListAdapter(new BaseAdapter(){

@Override

public int getCount() {

// TODO Auto-generated method stub

if(stocks==null){

return 0;

}

return stocks.size();

}

@Override

public Object getItem(int position) {

// TODO Auto-generated method stub

if(stocks==null){

return null;

}

return stocks.get(position);

}

@Override

public long getItemId(int position) {

// TODO Auto-generated method stub

if(stocks==null){

return 0L;

}

return stocks.get(position).getId();

}

@Override

public View getView(int position, View convertView, ViewGroup parent) {

// TODO Auto-generated method stub

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 onPause(){

super.onPause();

if(bound){

bound=false;

unbindService(connection);

}

}

@Override

public void onCreate(Bundle savedInstanceState) {

// Create UI elements, data loaded by <code>onStart</code>

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_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(){

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

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) {

// TODO Auto-generated method stub

// 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);

} else {

refreshStockData();

}

}

}.execute(stock);

}

});

}

@Override

public void onDestroy(){

super.onDestroy();

// disconnect from the stock service

unbindService(connection);

}

//Refresh UI when data is retrieved

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();

}

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

}

@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);

}

}

}.execute();

}

}

}

at the same time,create the stock.xml:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:orientation="horizontal">

<TextView

android:id="@+id/rowTxt"

android:layout_width="wrap_content" />

</LinearLayout>

change the acitivity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<LinearLayout android:orientation="horizontal"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_gravity="top|left">

<EditText

android:id="@+id/inputSymbol"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:inputType="textNoSuggestions"

android:hint="@string/hintSymbol"/>

<EditText

android:id="@+id/inputMin"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:inputType="numberDecimal"

android:hint="@string/hintMin"/>

<EditText

android:id="@+id/inputMax"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:inputType="numberDecimal"

android:hint="@string/hintMax"

/>

</LinearLayout>

<LinearLayout android:orientation="horizontal"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_gravity="bottom|right">

<EditText

android:id="@+id/inputPrice"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:inputType="numberDecimal"

android:hint="@string/hintPrice"/>

<EditText

android:id="@+id/inputQuant"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:inputType="number"

android:hint="@string/hintQuant"/>

<Button

android:id="@+id/btn"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/btnLbl"/>

</LinearLayout>

<ListView

android:id="@android:id/list"

android:layout_width="fill_parent"

android:layout_height="wrap_content" />

</LinearLayout>

change the strings.xml, add:

<string name="hintSymbol">Stock Symbol</string>

<string name="hintMin">Min. Price</string>

<string name="hintMax">Max Price</string>

<string name="hintPrice">Price Paid</string>

<string name="hintQuant">Quantity</string>

<string name="btnLbl">Add to Portfolio</string>

<string name="app_name">StockManager</string>

<string name="service_name">Stock Portfolio Service</string>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐