使用 Android 实现联网

[b]Android SDK 网络包[/b]
[table]
|包 | 描述|
|java.net | 提供与联网有关的类,包括流和数据包(datagram)sockets、Internet 协议和常见 HTTP 处理。该包是一个多功能网络资源。有经验的 Java 开发人员可以立即使用这个熟悉的包创建应用程序。|
|java.io | 虽然没有提供显式的联网功能,但是仍然非常重要。该包中的类由其他 Java 包中提供的 socket 和连接使用。它们还用于与本地文件(在与网络进行交互时会经常出现)的交互。|
|java.nio | 包含表示特定数据类型的缓冲区的类。适合用于两个基于 Java 语言的端点之间的通信。|
|org.apache.* | 表示许多为 HTTP 通信提供精确控制和功能的包。可以将 Apache 视为流行的开源 Web 服务器。|
|android.net | 除核心 java.net.* 类以外,包含额外的网络访问 socket。该包包括 URI 类,后者频繁用于 Android 应用程序开发,而不仅仅是传统的联网方面。|
|android.net.http | 包含处理 SSL 证书的类。|
|android.net.wifi | 包含在 Android 平台上管理有关 WiFi(802.11 无线 Ethernet)所有方面的类。并不是所有设备都配备了 WiFi 功能,特别是 Android 在 Motorola 和 LG 等手机制造商的 “翻盖手机” 领域获得了成功。|
|android.telephony.gsm | 包含用于管理和发送 SMS(文本)消息的类。一段时间后,可能会引入额外的包来来为非 GSM 网络提供类似的功能,比如 CDMA 或 android.telephony.cdma 等网络。|
[/table]

------------------------------------------------------------------------------
[size=medium]Android 网络[/size]
通过网络使用 Java 编程进行调用是简单的,我们熟悉的 java.net 包含几个执行此操作的类。这些类大多数在 Android 中都可用,事实上,您可以使用像 java.net.URL 和 java.net.URLConnection 这样的类,就像您在其他 Java 应用程序中那样。然而,Android 包括 pache HttpClient 库,这是在 Android 上连接网络的首选方法。即使您使用常用 Java 类,Android 实现仍然使用 HttpClient
[b]在 Android 上使用 Http Client 库[/b]

private ArrayList<Stock> fetchStockData(Stock[] oldStocks)
throws ClientProtocolException, IOException{
StringBuilder sb = new StringBuilder();
for (Stock stock : oldStocks){
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;
ArrayList<Stock> newStocks = new ArrayList<Stock>(oldStocks.length);
while (line != null){
String[] values = line.split(",");
Stock stock = new Stock(oldStocks[i], oldStocks[i].getId());
stock.setCurrentPrice(Double.parseDouble(values[1]));
stock.setName(values[2]);
newStocks.add(stock);
line = reader.readLine();
i++;
}
return newStocks;
}


[size=medium]Android 并发性实践[/size]
衍生线程根本不能修改 UI。因此在不冻结 UI ,但另一方面,在数据收到之后又允许您修改 UI 的情况下,您怎样检索数据?android.os.Handler 类允许您在线程之间协调和通信. 下面代码显示了一个使用 Handler 的已更新 refreshStockData 方法。

[b]实际工作的多线程 — 通过使用 Handler[/b]

private void refreshStockData(){
final ArrayList<Stock> localStocks =
new ArrayList<Stock>(stocks.size());
for (Stock stock : stocks){
localStocks.add(new Stock(stock, stock.getId()));
}
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
for (int i=0;i<stocks.size();i++){
stocks.set(i, localStocks.get(i));
}
refresh();
}
};
Runnable task = new Runnable(){
public void run() {
try {
ArrayList<Stock> newStocks =
fetchStockData(localStocks.toArray(
new Stock[localStocks.size()]));
for (int i=0;i<localStocks.size();i++){
Stock ns = newStocks.get(i);
Stock ls = localStocks.get(i);
ls.setName(ns.getName());
ls.setCurrentPrice(ns.getCurrentPrice());
}
handler.sendEmptyMessage(RESULT_OK);
} catch (Exception e) {
Log.e("StockPortfolioViewStocks",
"Exception getting stock data", e);
}
}
};
Thread dataThread = new Thread(task);
dataThread.start();
}


在衍生线程中,您不能修改 UI。相反的,当您将消息发送到 Handler,然后由 Handler 来修改 UI。也要注意,在线程中您不能修改 stocks 成员变量,正如您之前所做的。相反地您可以修改数据的本地副本。严格地来说,这是不是必须的,但这更为安全。

上面的代码说明了在并发编程中一些非常普遍的模式:复制数据、将数据解析到执行长期任务的线程中、将结果数据传递回主 UI 线程、以及根据所属数据更新主 UI 线程。Handlers 是 Android 中的主要通信机制,它们使这个模式易于实现。

Android 提供方法来封装和消除大多数样本代码
[b]用一个 AsyncTask 使多线程更容易[/b]

private void refreshStockData() {
new AsyncTask<Stock, Void, ArrayList<Stock>>(){
@Override
protected void onPostExecute(ArrayList<Stock> result) {
ViewStocks.this.stocks = result;
refresh();
}

@Override
protected ArrayList<Stock> doInBackground(Stock... stocks){
try {
return fetchStockData(stocks);
} catch (Exception e) {
Log.e("StockPortfolioViewStocks", "Exception getting stock data", e);
}
return null;
}
}.execute(stocks.toArray(new Stock[stocks.size()]));
}


代码明显减少。您不能创建任何线程或 Handlers。使用 AsyncTask 来封装所有样本代码。要创建 AsyncTask,您必须实现 doInBackground 方法。该方法总是在独立的线程中执行,因此您可以自由调用长期运行任务。它的输入类型来自您所创建的 AsyncTask 的类型参数。在本例中,第一个类型参数是 Stock,因此 doInBackground 获得传递给它的一组 Stock 对象。类似地,它返回一个 ArrayList<Stock>,因为这是 AsyncTask 的第三个类型参数。在此例中,我也选择重写 onPostExecute 方法。这是一个可选方法,如果您需要使用从 doInBackground 返回的数据来进行一些操作,您可以选用这种方法来实现。这个方法总是在主 UI 线程上被执行,因此对于修改 UI 这是一个很好的选择。

有了 AsyncTask,您就完全可以简化多线程代码。它可以将许多并发陷阱从您的开发路径删除,您仍然可以使用 AsyncTask 寻找一些潜在问题,例如,在 doInBackground 方法对象执行的同时设备上的方向发生改变时可能发生什么。


[size=medium]Android 数据库连通性[/size]

Android 提供给您常用的 SQLite 数据库来进行处理,因为对于像 Android 这类嵌入式系统它是高度优化的。它被 Android 上的核心应用程序所用。例如,用户地址簿是存储在一个 SQLite 数据库中。现在,对于给定的 Android 的 Java 实现,您可以使用 JDBC 来访问这些数据库。出人意料的是,Android 甚至包括构成主要部分 JDBC API 的 java.sql 和 javax.sql 包。然而,当涉及使用本地 Android 数据库进行处理时,这毫无用处。相反地,您想要使用 android.database 和 android.database.sqlite 包

[b]使用 Android 进行数据库访问[/b]

public class StocksDb {
private static final String DB_NAME = "stocks.db";
private static final int DB_VERSION = 1;
private static final String TABLE_NAME = "stock";
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)";
private static final String INSERT_SQL = "INSERT INTO " + TABLE_NAME +
" (symbol, max_price, min_price, price_paid, quantity) " +
"VALUES (?,?,?,?,?)";
private static final String READ_SQL = "SELECT id, symbol, max_price, " +
"min_price, price_paid, quantity FROM " + TABLE_NAME;
private final Context context;
private final SQLiteOpenHelper helper;
private final SQLiteStatement stmt;
private final SQLiteDatabase db;
public StocksDb(Context context){
this.context = context;
helper = new SQLiteOpenHelper(context, DB_NAME, null,
DB_VERSION){
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion,
int newVersion) {
throw new UnsupportedOperationException();
}
};
db = helper.getWritableDatabase();
stmt = db.compileStatement(INSERT_SQL);
}
public Stock addStock(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());
int id = (int) stmt.executeInsert();
return new Stock (stock, id);
}
public ArrayList<Stock> getStocks() {
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");
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));
stocks.add(stock);
} while (results.moveToNext());
}
if (!results.isClosed()){
results.close();
}
return stocks;
}
public void close(){
helper.close();
}
}


类完全封装了一个用于存储股票信息的 SQLite 数据库。因为您将要使用一个嵌入式数据库,不仅是您的应用程序要使用它,而且也要通过应用程序来创建它。您需要提供代码来创建该数据库。Android 提供一个有用的抽象帮助类 SQLiteOpenHelper。要完成这一操作,您需要扩展这个抽象类并提供代码通过使用 onCreate 方法创建您的数据库。当您有一个帮助程序实例时,就可以获取一个 SQLiteDatabase 实例,您可以用来执行任意 SQL 语句。

您的数据库类有两个较为方便的方法。第一个是 addStock,用于将新股票保存到数据库中。注意,您使用了一个 SQLiteStatement 实例,这类似于一个 java.sql.PreparedStatement。需要注意的是,在您的类构造器中如何对其进行编译,使其在每次调用 addStock 时都能重复利用。在每个 addStock 调用中,SQLiteStatement 的变量(INSERT_SQL 字符串中的问号)必然要将数据传递给 addStock。再一次强调,这类似于 PreparedStatement ,您可以从 JDBC 了解它。

另一个方法是 getStocks。顾名思义,它从数据库中检索所有股票。注意,您再次使用一个 SQL 字符串,正如您在 JDBC 中所用的那样。您可以在 SQLiteDatabase 类上通过使用 rawQuery 方法来进行处理。这个类也有几个查询方法,让您可以不使用 SQL 直接查询数据库。所有这些方法都返回一个 Cursor 对象,和 java.sql.ResultSet 非常相似。您可以将 Cursor 移动到从数据库中返回的数据所在行,在每一行,您可以使用 getInt、getString 和其他的方法来检索您要查询的数据库中各列相关的值。再一次强调,这和 ResultSet 十分相似。也和 ResultSet 比较相似,当您完成操作之后,关闭 Cursor 也十分重要的。如果您没有关闭 Cursors,那么可能会迅速地耗尽内存并导致您的应用程序崩溃。

查询本地数据库是一个比较慢的过程,特别是,如果您有多行数据或者您需要在多个表之间运行复杂的查询语句。然而,数据库查询或插入超过 5 秒且出现一个 Application Not Responding 对话框,这种情况不太可能发生,但是当您的数据库忙于读取和写入数据时,冻结您的 UI 是不明智的。当然,避免这种情况最好的办法是使用 AsyncTask。下面的代码 展示了这个示例。

[b]在一个单独的线程上插入数据库[/b]

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);
new AsyncTask<Stock,Void,Stock>(){
@Override
protected Stock doInBackground(Stock... newStocks) {
// There can be only one!
return stocksDb.addStock(newStocks[0]);
}
@Override
protected void onPostExecute(Stock s){
addStockAndRefresh(s);
}
}.execute(stock);
}
});


您可以先为按钮创建一个实践监听器。当用户点击按钮时,您可以从各个小部件(确切地说是 EditText 小部件)读取股票数据并填入一个新的 Stock 对象。
您可以创建一个 AsyncTask,并通过 doInBackground 方法从 上一段代码 中调用 addStock 方法。如此,addStock 将在一个背景线程上执行,而不是在主 UI 线程上。
完成之后,将新 Stock 对象从数据库传递到在主 UI 线程上执行的 addStockAndRefresh 方法。
------------------------------------------------------------------------------

Android SDCard操作(文件读写,容量计算)

android.os.Environment

提供访问环境变量
java.lang.Object android.os.Environment



Environment 静态方法:

方法 : getDataDirectory ()
返回 : File
解释 : 返回Data的目录


方法 : getDownloadCacheDirectory ()
返回 : File
解释 : 返回下载缓冲区目录

方法 : getExternalStorageDirectory ()
返回 : File
解释 : 返回扩展存储区目录(SDCard)


方法 : getExternalStoragePublicDirectory (String type)
返回 : File
解释 : 返回一个高端的公用的外部存储器目录来摆放某些类型的文件(来自网上)

方法 : getRootDirectory ()
返回 : File
解释 : 返回Android的根目录

方法 : getExternalStorageState ()
返回 : String
解释 : 返回外部存储设备的当前状态


getExternalStorageState () 返回的状态String 类型常量 :
常量 : MEDIA_BAD_REMOVAL
值 : "bad_removal"
解释 : 在没有正确卸载SDCard之前移除了

常量 : MEDIA_CHECKING
值 : "checking"
解释 : 正在磁盘检查


常量 : MEDIA_MOUNTED
值 : "mounted"
解释 : 已经挂载并且拥有可读可写权限


常量 : MEDIA_MOUNTED_READ_ONLY
值 : "mounted_ro"
解释 : 已经挂载,但只拥有可读权限


常量 : MEDIA_NOFS
值 : "nofs"
解释 : 对象空白,或者文件系统不支持


常量 : MEDIA_REMOVED
值 : "removed"
解释 : 已经移除扩展设备


常量 : MEDIA_SHARED
值 : "shared"
解释 : 如果SDCard未挂载,并通过USB大容量存储共享


常量 : MEDIA_UNMOUNTABLE
值 : "unmountable"
解释 : 不可以挂载任何扩展设备


常量 : MEDIA_UNMOUNTED
值 : "unmounted"
解释 : 已经卸载

使用时只需先判断SDCard当前的状态然后取得SdCard的目录即可(见源代码)

---------------------------------------------------------------------------------------------------------

//SDcard 操作
ublic void SDCardTest() {
// 获取扩展SD卡设备状态
String sDStateString = android.os.Environment.getExternalStorageState();

// 拥有可读可写权限
if (sDStateString.equals(android.os.Environment.MEDIA_MOUNTED)) {

try {

// 获取扩展存储设备的文件目录
File SDFile = android.os.Environment
.getExternalStorageDirectory();

// 打开文件
File myFile = new File(SDFile.getAbsolutePath()
+ File.separator + "MyFile.txt");

// 判断是否存在,不存在则创建
if (!myFile.exists()) {
myFile.createNewFile();
}

// 写数据
String szOutText = "Hello, World!";
FileOutputStream outputStream = new FileOutputStream(myFile);
outputStream.write(szOutText.getBytes());
outputStream.close();

} catch (Exception e) {
// TODO: handle exception
}// end of try

}// end of if(MEDIA_MOUNTED)
// 拥有只读权限
else if (sDStateString
.endsWith(android.os.Environment.MEDIA_MOUNTED_READ_ONLY)) {

// 获取扩展存储设备的文件目录
File SDFile = android.os.Environment.getExternalStorageDirectory();

// 创建一个文件
File myFile = new File(SDFile.getAbsolutePath() + File.separator
+ "MyFile.txt");

// 判断文件是否存在
if (myFile.exists()) {
try {

// 读数据
FileInputStream inputStream = new FileInputStream(myFile);
byte[] buffer = new byte[1024];
inputStream.read(buffer);
inputStream.close();

} catch (Exception e) {
// TODO: handle exception
}// end of try
}// end of if(myFile)
}// end of if(MEDIA_MOUNTED_READ_ONLY)
// end of func


[size=x-large]
计算SDCard的容量大小 android.os.StatFs
[/size]

一个模拟linux的df命令的一个类,获得SD卡和手机内存的使用情况
java.lang.Object android.os.StatFs



构造方法:
StatFs (String path)

公用方法:

方法 : getAvailableBlocks ()
返回 : int
解释 :返回文件系统上剩下的可供程序使用的块


方法 : getBlockCount ()
返回 : int
解释 : 返回文件系统上总共的块



方法 : getBlockSize ()
返回 : int
解释 : 返回文件系统 一个块的大小单位byte


方法 : getFreeBlocks ()
返回 : int
解释 : 返回文件系统上剩余的所有块 包括预留的一般程序无法访问的


方法 : restat (String path)
返回 : void
解释 : 执行一个由该对象所引用的文件系统雷斯塔特.(Google翻译)


想计算SDCard大小和使用情况时, 只需要得到SD卡总共拥有的Block数或是剩余没用的Block数,再乘以每个Block的大小就是相应的容量大小了单位byte.(见代码)

public void SDCardSizeTest() {

// 取得SDCard当前的状态
String sDcString = android.os.Environment.getExternalStorageState();

if (sDcString.equals(android.os.Environment.MEDIA_MOUNTED)) {

// 取得sdcard文件路径
File pathFile = android.os.Environment.getExternalStorageDirectory();

android.os.StatFs statfs = new android.os.StatFs(pathFile.getPath());

// 获取SDCard上BLOCK总数
long nTotalBlocks = statfs.getBlockCount();

// 获取SDCard上每个block的SIZE
long nBlocSize = statfs.getBlockSize();

// 获取可供程序使用的Block的数量
long nAvailaBlock = statfs.getAvailableBlocks();

// 获取剩下的所有Block的数量(包括预留的一般程序无法使用的块)
long nFreeBlock = statfs.getFreeBlocks();

// 计算SDCard 总容量大小MB
long nSDTotalSize = nTotalBlocks * nBlocSize / 1024 / 1024;

// 计算 SDCard 剩余大小MB
long nSDFreeSize = nAvailaBlock * nBlocSize / 1024 / 1024;
}// end of if
}// end of func
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值