[转载]短链接算法收集与分析

mikel阅读(1120)

[转载]短链接算法收集与分析 – Cocowool – 博客园.

短链接就不说了,大家已经都清楚了,如下所示就是短链接:

新浪微博     http://t.cn/SVpONM

腾讯微博     http://url.cn/302yor

Yun.io         http://d.yun.io/PNri2v

短链接的好处:1、内容需要;2、用户友好;3、便于管理。

如何实现呢,大概有三个步骤:

1、定义一个URL映射算法,可以将长的URL映射成短字符串;

2、使用一个存储(数据库?NoSQL?)来存储完成的映射;

3、实现自己的URL映射算法;

一般来说,第三步是我们比较头疼的,如何将一个长的URL字符串,映射成一个较短的字符串呢。我总结了三种办法:

普通实现

我想以前大家学习过十进制和二进制的互相转换,或者十进制和十六进制的互相转换,那么为了更短,我们可以使用62进制,对于一个数字ID进行转码,转换成一个短字符串。

这种做法的缺点是没有办法保证所有链接都是固定的位数的长度,而且在高并发的情况下,如何保证能够快速分发是个问题。

具体实现方法:

/**
* 利用62进制对数字ID进行短链接编码,缺点不能保证每个短链接是固定长度
*
* @author  wanshiqiang<wangshiqiang@360.cn>
* @param integer $integer
* @param string $base
*/
private function getShortenedURLFromID ($integer, $base = ALLOWED_CHARS)
{
$length = strlen($base);
while($integer > $length – 1)
{
$out = $base[fmod($integer, $length)] . $out;
$integer = floor( $integer / $length );
}
return $base[$integer] . $out;
}
/**
* 对62进制编码的短链接进行解码
*
* @author  wangshiqiang<wangshiqiang@360.cn>
* @param string $string
* @param string $base
*/
private function getIDFromShortenedURL ($string, $base = ALLOWED_CHARS)
{
$length = strlen($base);
$size = strlen($string) – 1;
$string = str_split($string);
$out = strpos($base, array_pop($string));
foreach($string as $i => $char)
{
$out += strpos($base, $char) * pow($length, $size – $i);
}
return $out;
}

文艺实现

算法描述:使用6个字符来表示短链接,我们使用ASCII字符中的’a’-‘z’,’0′-‘5’,共计32个字符做为集合。每个字符有32种状态,六个字符就可以表示32^6(1073741824),那么如何得到这六个字符,描述如下:

对 传入的长URL进行Md5,得到一个32位的字符串,这个字符串变化很多,是16的32次方,基本上可以保证唯一性。将这32位分成四份,每一份8个字 符,这时机率变成了16的8次方,是4294967296,这个数字碰撞的机率也比较小啦,关键是后面的一次处理。我们将这个8位的字符认为是16进制整 数,也就是1*(‘0x’.$val),然后取0-30位,每5个一组,算出他的整数值,然后映射到我们准备的32个字符中,最后就能够得到一个6位的短 链接地址。

PHP实现如下:

function shorten( $long_url )
{
$base32 = “abcdefghijklmnopqrstuvwxyz012345″;
$hex = md5( $long_url );
$hexLen = strlen( $hex );
$subHexLen = $hexLen / 8;
$output = array();
for( $i = 0; $i < $subHexLen; $i++ )
{
$subHex = substr( $hex, $i * 8, 8 );
$subHex = 0x3FFFFFFF & ( 1 * (‘0x’ . $subHex ) );

$out = ”;

for( $j = 0; $j < 6; $j++ )
{
$val = 0x0000001F & $int;
$out .= $base32[$val];
$int = $int >> 5;
}
$output[] = $out;
}
return $output;
}

二逼实现

下面这个函数使用了纯随机的方式来生成一个短链接,虽然我们可以通过查询操作来确保不重复使用短链接,可是… 这样真的靠谱吗~~

function random($length, $pool = ”) {
$random = ”;
if (empty($pool)) { $pool    = ‘abcdefghkmnpqrstuvwxyz’; $pool   .=
‘23456789’; }
srand ((double)microtime()*1000000);
for($i = 0; $i < $length; $i++) { $random .=
substr($pool,(rand()%(strlen ($pool))), 1); }
return $random;
}

Technorati 标签: ,,,

参考资料:

1、微博短地址原理解析

2、微博短域名原理及作用

3、Yours.org

4、Free PHP URL Shorten script that kicks ass

5、PHP Short Url Algorithm Implementation

6、Implement your own short URL

7、短网址算法初步汇总

8、Short Url 实现方式

[转载]认识aspx页面处理程序的页面生成关系和页面继承关系

mikel阅读(1022)

[转载]【认识aspx】页面处理程序【页面生成关系】【页面继承关系】 – DebugLZQ – 博客园.

对于 Web 开发来说,最重要的就是生成 HTML 页面,除了用流的方式一个字符一个字符地输出,有没有简单的方式来创建一个处理程序生成网页呢?

有!就是什么代码也不写的方式。当然,是我们自己不写,由程序生成。

对于网站开发来说,最常用的功能就是生成 HTML 网页,虽然通过处理程序再结合文本输出流,从理论上就可以生成任何网页,但是开发效率显然不容乐观。

为了从根本上解决这个主要的开发问题,ASP.NET 使用模板的方式来生成一个处理程序。模板的扩展名为 aspx ,通过一个内置的处理程序工厂 PageHandlerFactory 将 aspx 形式的模板编译生成处理程序代码,然后,将这个处理程序返回给 HttpApplication 完成请求的处理。aspx 形式的模板文件可以直接通过文本编辑器进行编辑,甚至可以通过 DreamWeaver 之类的网页设计软件进行排版,极大地提高了网站开发的效率,带来了网站开发技术的革命性升级。

ASP.NET 的系统配置文件中,已经进行了如下的配置,可以看到,对于扩展名为 aspx 的请求,将由 PageHandlerFactory 这个处理程序工厂进行处理,代码如下:

<add  path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="True" />

这个类定义在命名空间 System.Web.UI 下,具体的定义如下:

public class PageHandlerFactory : IHttpHandlerFactory

aspx 模板文件与 Page 类和生成类之间的关系如图所示:

需要注意的是, aspx 模板的解析和代码生成仅仅出现在第一次处理的时候,以后的请求将直接使用已经编译生成的程序集,所以,这个处理过程并不会降低网站的处理速度。

对于一个 aspx 模板文件来说,一般情况下将对应存在一个同名的 cs 后台代码文件,其中定义了一个与页面同名的类,通过代码文件可以看到这个类派生自 Page 基的类。

PageHandlerFactory 通过 aspx 文件将生成两个类,一个为与后台代码中定义的类同名的部分类,这个部分类将与后台代码中定义的类在编译的时候合并为一个派生自 Page 的页面派生类。但是,在 ASP.NET 中,创建实际页面对象的类并不是这个类,而是生成的第二个类,一般情况下,这个类的名字为页面的名字后面加上下划线和 aspx。这才是实际创建页面对象的页面类。

例如,我们有一个页面文件  MyPage.aspx,那么,应该存在一个对应的后台代码文件 MyPage.aspx.cs 或者 MyPage.aspx.vb,其中定义了 MyPage 这个  Page 的派生类。

通过 PageHandlerFactory,MyPage.aspx 将生成两个类,部分类 MyPage 和 MyPage_aspx,其中,后台代码文件中的 MyPage 将和生成的部分类 MyPage 在编译的时候合并为一个完整的 MyPage 类定义,而 MyPage_aspx 则是从 MyPage 派生的一个页面类,用来创建实际的页面对象实例。它们之间的关系如下图所示:

由于页面处理程序是通过模板生成的,所以,大多数情况下,我们只要创建好这个 aspx 的模板就可以了,HttpApplication 会通过 PageHandlerFactory 创建这个生成的处理程序,然后使用这个处理程序完成服务器的处理任务。

现在,如果我们需要通过处理程序生成一个网页的话,只需要设计一个 aspx 的页面即可。

在一些特殊的情况下,我们需要创建一些生成 HTML 输出的程序,但是这些程序并不希望能够被客户端直接请求,那么,可以借助 PageParser 或者 BuildManager 通过 aspx 格式的模板来创建一个自定义的处理程序完成处理任务。

[转载]Android学习笔记(六)SQLite

mikel阅读(974)

[转载]Android学习笔记(六)SQLite – Libo@Deng – 博客园.

一、废话

今天感冒了!… …(以下省略500字)

二、正文

1、  SQLite是个啥?

SQLite是一个极轻量型的数据库。它在提供了和大型数据库相当的功能,还具有轻便、跨平台等优点,SQLite使用非常方便,并不需要我们 像常规数据库(SQLServer,Mysql等)那样进行安装,在Android的JDK中,其实是已经包含了SQLite这个数据库的核心。当然我们 必须要强调一点,SQLite并不是只针对Android的,其实它还可以用在别的很多地方。

2、  在Android系统中如何使用SQLite数据库?

要在Android系统中使用SQLite数据库,其实是一件非常轻松的事情,只要继承来自android.database.sqlite包中的SQLiteOpenHelper类,并重写其中的几个重要方法后,你就可以对数据库进行相关的操作了。

 1 public class MyDatabaseHelper extends SQLiteOpenHelper {
 2  
 3     private static final String DB_NAME = "test_db"; //数据库名称
 4     private static final int VERSION = 1; //数据库版本
 5      
 6     public MyDatabaseHelper(Context context) {
 7         super(context, DB_NAME, null, VERSION);
 8         // TODO Auto-generated constructor stub
 9     }
10  
11     @Override
12     public void onCreate(SQLiteDatabase db) {
13         String sql = "create table user(username varchar(25) not null , password varchar(60) not null );";          
14         db.execSQL(sql);
15     }
16  
17     @Override
18     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
19         // TODO Auto-generated method stub
20  
21     }
22  
23 }

还是先来看看SQLiteOpenHelper这个类吧。“A helper class to manage database creation and version management.”官方的一个帮助文档这样描述这个类,也就是说,这个类主要的作用是用来创建数据库和对数据库进行版本更新的。

这个类中必须重写的两个方法分别为:onCreate(SQLiteDatabase db)和onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)。

  • onCreate(SQLiteDatabase db):用于创建数据库db中的表,而不是创建db数据库实例。这个方法并不需要我们自己去调用,当数据库第一次被创建时,这个方法会自动被调用。那么数 据库真正的创建是在什么时候呢?当我们调用getReadableDatabase()/getWritableDatabase()方法时,数据库会被 真实的创建。
  • onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion):这个方法用来对数据库进行更新操作,你可以在这个方法中定义你自己的一些操作。这个方法也不需要我们主动去调用,当 SQLiteOpenHelper类的最后一个参数比原值大的时候,这个方法会自动被调用。调用的时刻同onCreate()方法。

另外在SQLiteOpenHelper类中,还有两个方法也非常重要:getReadableDatabase(),getWritableDatabase()。

  • getReadableDatabase():返回一个只读的数据库实例,在访问数据库中数据但同时又不需要对其进行数据修改时(select操作)可以使用这个方法。
  • getWritableDatabase():返回一个可读写的数据库实例,当需要对数据库进行数据修改时,应该调用这个方法。记住在使用完成后,一定是要使用close()方法来释放内存,否则会造成内存泄露。

3、  在Android系统中如何对SQLite数据库进行常规操作?

  • (Cursor) query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)——查询操作,返回一个游标
    • table——表名;
    • columns——列名集合
    • selection——在where语句中的表达式,如id=?
    • selectionArgs——在where表达式中传入的参数,也就是上面“?”所占据的值
    • groupBy\having\orderby\limit——这些参数如果各位不清楚,请google下SQL语句基础
       1public Cursor Query(String table, String[] filed, long rowId)
       2throws SQLException {
       3
       4         Cursor mCursor = _db.query(true, table, filed, "ID=?" + rowId, null,null, null, null, null);
       5if (mCursor != null) {
       6             mCursor.moveToFirst();
       7         }
       8return mCursor;
       9     }
      10
      11public Cursor Query(String table) {
      12         Cursor mCursor = _db.query(true, table, new String[] { "*" }, null,null, null, null, null, null);
      13if (mCursor != null) {
      14             mCursor.moveToFirst();
      15         }
      16return mCursor;
      17     }
  • (int) update(String table, ContentValues values, String whereClause, String[] whereArgs)——进行更新操作
    • table——表名
    • values——ContentValues类型,其实就是键值对,键代表需要修改的列名,值代表对应的新值
    • whereClause——where语句中的表达式,可以直接组成一个完整的表达式如:id=1,此时whereArgs传入null,如果id=?,那么whereArgs和query(…)的selectionArgs一样。
    • whereArgs——在where表达式中传入的参数
       1 public boolean Update(String table, String[] fieldName,
       2             String[] fieldValue, long rowID) throws SQLException {
       3         ContentValues cv = new ContentValues();
       4 for (int i = 0; i < fieldName.length; i++) {
       5             cv.put(fieldName[i], fieldValue[i]);
       6         }
       7 if (_db.update(table, cv, "ID=" + rowID, null) > 0) {
       8 return true;
       9         }
      10 return false;
      11     }
  • (long) insert(String table,String nullColumnHack,ContentValues values)——进行插入操作
    • table——表名
    • nullcolumnHack——当插入空值的时候有用
    • values——键值对,键代表需要修改的列名,值代表对应的新值
       1 public boolean Insert(String table, String[] fieldName, String[] fieldValue) throws SQLException {
       2         ContentValues cv = new ContentValues();
       3 for (int i = 0; i < fieldName.length; i++) {
       4             cv.put(fieldName[i], fieldValue[i]);
       5         }
       6 if (_db.insert(table, null, cv) > 0) {
       7 return true;
       8         }
       9 return false;
      10     }
  • (int) delete(String table,String whereClause,String[] whereArgs)——进行删除操作
    • table——表名
    • whereClause/whereArgs——同update()方法
      1 public boolean Delete(String table, long rowID) throws SQLException {
      2 if (_db.delete(table, "ID=?", new String[] { rowID + "" }) > 0)
      3 return true;
      4 return false;
      5     }
  • (void) execSQL(String sql)——执行sql语句
    • 执行非select操作的语句,灵活性强。

在上面的方法中,有看不懂的地方,请看Demo源码。

三、总结

SQLite作为一个轻型数据库,在Android中可以用来存储用户数据,如短信、联系人等信息。在Android中使用SQLite是非常灵活的,所以不一定非要按照某一种特定的方式来操作数据库。下一篇笔记中将说说ContentProvider类。

[转载]Android开发教程 --- 数据存储(2) SQLite

mikel阅读(1082)

[转载]Android开发教程 — 数据存储(2) SQLite – Jason_CC – 博客园.

Hi,大家好!

今天我们主要来讲讲SQLite在Android中的使用。

轻松下:

写字楼里写字间,写字间里程序员;
程序人员写程序,又拿程序换酒钱。
酒醒只在网上坐,酒醉还来网下眠;
酒醉酒醒日复日,网上网下年复年。
但愿老死电脑间,不愿鞠躬老板前;
奔驰宝马贵者趣,公交自行程序员。
别人笑我忒疯癫,我笑自己命太贱;
不见满街漂亮妹,哪个归得程序员。  🙂 哎…

SQLite简介

大部分应用程序都要操作数据,,Android应用程序也不例外,本地数据应该存储在什么地方?Android使用开源的、与操作系统无关的SQL数据库 –大名鼎鼎的SQLite。SQLite是一款轻量级数据库,它的设计目的是嵌入式,而且它占用的资源非常少,在嵌入式设备中,只需要几百KB,很多大型公司所开发的产品都有它的存在。

轻量级
使用 SQLite 只需要带一个动态库,就可以享受它的全部功能,而且那个动态库的尺寸相当小。

独立性
SQLite 数据库的核心引擎不需要依赖第三方软件,也不需要所谓的“安装”。

隔离性
SQLite 数据库中所有的信息(比如表、视图、触发器等)都包含在一个文件夹内,方便管理和维护。

跨平台
SQLite 目前支持大部分操作系统,不止电脑操作系统更在众多的手机系统也是能够运行,比如:Android。

多语言接口
SQLite 数据库支持多语言编程接口。

安全性
SQLite 数据库通过数据库级上的独占性和共享锁来实现独立事务处理。这意味着多个进程可以在同一时间从同一数据库读取数据,但只能有一个可以写入数据。

SQLite虽然很小巧,但是支持的SQL语句不会逊色于其他开源数据库,它支持的SQL包括:

1

Sqlite基本数据类型有:

VARCHAR 字符型

NVARCHAR(15)可变字符型,

TEXT 文本型,

INTEGER 整型,

FLOAT 浮点型,

BOOLEAN布尔型,

CLOB字符大对象,

BLOB二进制大对象,

TIMESTAMP日期型,

NUMERIC(10,5) 数值型

VARYING CHARACTER (24),

NATIONAL VARYING CHARACTER(16)

Sqlite也提供了JDBC驱动程序

Class.forName(“org.sqlite.JDBC”);

Connection conn = DriverManager.getConnection(“jdbc:sqlite:filename”);

//filename为你的SQLite数据名称

使用SQLiteOpenHelper操作数据库

getReadableDatabase()获得可读的SQLiteDatabase

getWritableDatabase() 获得可写的SQLiteDatabase

onCreate(SQLiteDatabase)

onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion)

onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)

操作数据库

1. 封装一个SQLite的操作类,并且在此使用继承SQLiteOpenHelper的方式实现

SQLiteOpenHelper
SQliteOpenHelper是一个抽象类,来管理数据库的创建和版本的管理。要使用它必须实现它的

onCreate(SQLiteDatabase),

onUpgrade(SQLiteDatabase, int, int)方法
onCreate:当数据库第一次被建立的时候被执行,例如创建表,初始化数据等。
onUpgrade:当数据库需要被更新的时候执行,例如删除久表,创建新表。

2. 实现SQLiteOpenHelper类的构造函数及抽象方法.

package TSD.Jason.DB;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;

public class DBHelp extends SQLiteOpenHelper {

    private final static String DATABASE_NAME="StudentDB"; //数据库名称
    private final static int DATABASE_VERSION=1; //数据库默认版本
    private final static String TABLE_NAME="StudentInfo"; //数据表名称
    public final static String S_ID="sid";  // 列名
    public final static String S_NAME="sName"; 
    public final static String S_SEX="sSex"; 
    public final static String S_AGE="sAge";
    public final static String S_ADDRESS="sAddress"; 
    
    /**SQLiteOpenHelper类的构造函数  (注意: 必须实现的构造函数)
     * 
     * 此构造函数用来 创建库和表 (第一次执行时,如果没有库、表,将创建库、表,以后将不再创建)
     * @param context  上下文对象
     * @param name 数据库名称
     * @param factory 创建Cursor的工厂类。参数是为了可以自定义Cursor创建
     * @param version 数据库版本号
     */
    public DBHelp(Context context, String name, CursorFactory factory,
            int version) {
        super(context, name, factory, version);
        
    }
    
    /**操作数据时,(增删改查)
     * 
     * @param context  上下文对象
     * @param name 数据库名称
     * @param factory 创建Cursor的工厂类。参数是为了可以自定义Cursor创建
     * @param version 数据库版本号
     */
    public DBHelp(Context context) {
        this(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    
    /**需要根据版本号来实现修改
     * 
     *此构造函数用来 修改库和表 (修改现有的库、表)
     * @param context  上下文对象
     * @param name 数据库名称
     * @param factory 创建Cursor的工厂类。参数是为了可以自定义Cursor创建
     * @param version 数据库版本号
     */
    public DBHelp(Context context ,int version) {
        this(context,DATABASE_NAME,null,version);
    }
    
    @Override
    public void onCreate(SQLiteDatabase db) {
        // TODO Auto-generated method stub
        System.out.println("当第一次运行或者修改数据库时,将会自动执行");
        
        String createTableSQL = "create table " + TABLE_NAME + "("
                                +S_ID         + " integer  primary key autoincrement,"
                                +S_NAME     + " nvarchar(10),"
                                +S_SEX         + " nvarchar(2),"
                                +S_AGE         + " int,"
                                +S_ADDRESS     + " nvarchar(50)"
                                +")";
        db.execSQL(createTableSQL); // 执行SQL 需要使用execSQL()
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        System.out.println("-------------------------调用了一个修改库函数");
        
    }
    
    @Override
    public void onOpen(SQLiteDatabase db) {
        // TODO Auto-generated method stub
        super.onOpen(db);
        System.out.println("当数据库打开时,执行");
    }
    
    

}

创建数据库

DBHelp dbHelp = new DBHelp(SQLiteActivity.this);
                //当获得了SQLiteDatabase对象,会自动检测是否第一次创建了库,表,如果没有将执行DBHelp类中的onCreate函数
                SQLiteDatabase db = dbHelp.getReadableDatabase();
                Toast.makeText(SQLiteActivity.this, "创建成功", Toast.LENGTH_SHORT).show();


getWritableDatabase() 如果需要操作(增删改)数据库时,使用

getReadableDatabase()查询数据

删除及重命名数据库

2

在adb下查看数据库

3

1通过 ls 命令查询目录结构

2通过cd 文件夹名称 进入对应的文件夹中

3需要进入2次data文件夹

4进入自己创建项目的包下

5进入databases

6通过 sqlite3 StudentDB 进入数据库

在sqlite>提示符下输入

.help 这个命令让我们看到许多命令

.tables 查看所有表

通过.tables 命令,可以查询此数据库下所有表名

查询语句和SQLServer中一样,每一句Sql语句结束都需要分号;

添加数据

btn5.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                
                DBHelp dbHelp = new DBHelp(SQLiteActivity.this);
                SQLiteDatabase db = dbHelp.getWritableDatabase();
                ContentValues contentValues = new ContentValues();
                contentValues.put("sName", txtName.getText().toString());
                contentValues.put("sSex", "男");
                if(dbHelp.InsertData(db, contentValues))
                    Toast.makeText(SQLiteActivity.this, "添加成功", Toast.LENGTH_SHORT).show();
                else
                    Toast.makeText(SQLiteActivity.this, "添加失败", Toast.LENGTH_SHORT).show();
            }
        });

修改数据

btn6.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                
                DBHelp dbHelp = new DBHelp(SQLiteActivity.this);
                SQLiteDatabase db = dbHelp.getWritableDatabase();
                ContentValues contentValues = new ContentValues();
                contentValues.put("sName", txtsName.getText().toString());
                String whereClause = "sid=?"; //要修改数据的条件 值用?占位符
                String[] whereArgs = new String[]{txtsID.getText().toString()};
                if(dbHelp.UpdateData(db, contentValues, whereClause, whereArgs))
                    Toast.makeText(SQLiteActivity.this, "修改成功", Toast.LENGTH_SHORT).show();
                else
                    Toast.makeText(SQLiteActivity.this, "修改失败", Toast.LENGTH_SHORT).show();
            }
        });

删除数据

    btn7.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                
                DBHelp dbHelp = new DBHelp(SQLiteActivity.this);
                SQLiteDatabase db = dbHelp.getWritableDatabase();
                String whereClause = "sid=?"; //要删除数据的条件 值用?占位符
                String[] whereArgs = new String[]{txtID.getText().toString()};
                if(dbHelp.DeleteData(db, whereClause, whereArgs))
                    Toast.makeText(SQLiteActivity.this, "删除成功", Toast.LENGTH_SHORT).show();
                else
                    Toast.makeText(SQLiteActivity.this, "删除失败", Toast.LENGTH_SHORT).show();
            }
        });

查询数据

rawQuery()

rawQuery() 是最简单的解决方法。例如:

Cursor c=db.rawQuery(
“SELECT * FROM Student WHERE sid=1 AND sname=‘zhangsan'”, null);

query()

都会返回一个 Cursor,这是 Android 的 SQLite 数据库游标

操作游标

通过使用 getCount() 方法得到结果集中有多少记录;
通过 moveToFirst(), moveToNext(), 和 isAfterLast() 方法遍历所有记录;
通过 getColumnNames() 得到字段名;
通过 getColumnIndex() 转换成字段号;
通过 getString(),getInt() 等方法得到给定字段当前记录的值;
通过 requery() 方法重新执行查询得到游标;
通过 close() 方法释放游标资源;
SimpleCursorAdapter
SimpleCursorAdapter
可使用此类来绑定ListView

SimpleCursorAdapter simple = new SimpleCursorAdapter(SQLiteActivity.this, android.R.layout.simple_list_item_1, cur, new String[]{“sName”,”sSex”}, new int[]{android.R.id.text1,android.R.id.text2});

studentList.setAdapter(simple);

注意: 使用此类绑定数据,,必须注意sqlite的主键命名。由于simpleCursorAdapter的方法只识别_id,所以,当你用到sqlite的 simpleCursorAdapter时,必须把数据表的主键命名为_id。否则就会出现 java.lang.IllegalArgumentException: column ‘_id’ does not exist 错误。

查询数据
public void BinderData(){
        DBHelp dbHelp = new DBHelp(SQLiteActivity.this);
        SQLiteDatabase db = dbHelp.getReadableDatabase();
        /*参数
         * table 表名
         * columns 要查询的列名数组,所有列用 null
         * selection 要查询的条件 没有用null
         * selectionArgs 要查询的条件对应的值 没有用null
         * groupBy 分组 没有用null
         * having 分组后再次筛选   没有用null
         * orderBy 排序  没有用null
         * */
        Cursor cur = db.query("StudentInfo", null, null, null, null, null, null);
        //Cursor cur = db.rawQuery("select * from StudentInfo", null);
        
        //由于本例中使用的主键名不是_id 所以不能直接使用SimpleCursorAdapter 原因在PPT中已经做了说明
        //SimpleCursorAdapter simple = new SimpleCursorAdapter(SQLiteActivity.this, android.R.layout.simple_list_item_1, cur, new String[]{"sName","sSex"}, new int[]{android.R.id.text1,android.R.id.text2});
        //studentList.setAdapter(simple);
        ArrayList<HashMap<String, Object>> students = new ArrayList<HashMap<String,Object>>();
        HashMap<String, Object> hs ;
        for (cur.moveToFirst();!cur.isAfterLast();cur.moveToNext()) {
            System.out.println(cur.getInt(cur.getColumnIndex("sid"))+"aaaaaaaaa");
            hs = new HashMap<String, Object>();
            hs.put("sID", cur.getInt(cur.getColumnIndex("sid")));
            hs.put("sName", cur.getString(cur.getColumnIndex("sName")));
            hs.put("sSex", cur.getString(cur.getColumnIndex("sSex")));
            students.add(hs);
        }
        SimpleAdapter sim = new SimpleAdapter(SQLiteActivity.this, students, android.R.layout.simple_list_item_1, new String[]{"sName","sSex"}, new int[]{android.R.id.text1,android.R.id.text2});
        studentList.setAdapter(sim);
    }


代码已上传北京天圣达网站,欢迎大家下载

[转载]Eclipse中Android项目引用其他项目时链接源码的方法

mikel阅读(923)

[转载]Eclipse中Android项目引用其他项目时链接源码的方法 – 斯克迪亚 – 博客园.

参照前一篇文章《Eclipse中Android公共库的正确建立及调用方法》,以前只要在Android库中添加了引用,就能自动在项目中引入源码:

image

但是更新了新版SDK及ADT插件之后,源码不会被自动引用进来,在当前项目中只能以只读方式访问经编译后的源码:

image

那么如何手动引入源码呢?

你需要在项目上右键点选属性菜单,然后左栏选择“Java Build Path”,在右侧“Source”标签中点击“Link Source…”按钮:

SNAGHTML3c8397a

单击“Browse…”按钮:

SNAGHTML3c99c42

浏览并选取引用项目的src目录:

SNAGHTML3ca8a58

确定后修改一下文件夹显示名称:

image

这样源码就被正确链接引用,可以随时跳转后修改了:

image

另 需注意的是,这种方法在编译时会引发臭名昭著傻逼至极的“Conversion to Dalvik format failed with error 1”错误,无法通过编译,这时只需从项目中删除该链接源码目录,即可通过编译,但你在编译之后还得按上述方法添加回来以方便编码,不得不称赞 Android SDK研发小组真乃绝世脑瘫组合,每次升级SDK都会带来大把大把诡异的错误给开发者。

[转载]android应用开发全程实录-实现甩动拨打和挂断电话

mikel阅读(841)

[转载]android应用开发全程实录-实现甩动拨打和挂断电话 – noTice501 – 博客园.

今天继续给大家带来《Android应用开发全程实录》中的章节,这部分是讲传感器中的一个实例。

通过上面的例子我们学会了如何获得某种类型的传感器,下面通过一个实例来学习如何使用某一个类型的传感器。我们以加速传感器为例,来实现这样一个功能:摇动手机便自动拨打某亲情号码,并实现再次摇动则挂机的功能。

工程目录:EX_12_03

第一步,UI布局main.xml的代码如下,其运行效果如图12-10所示。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
    <TextView  android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="当前状态:就绪"
    android:id="@+id/state"
    android:textColor="#ff0000" />
    <Button android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:id="@+id/call"
    android:text="打电话(10086)"/>    
    <Button android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:id="@+id/stop"
    android:text="挂机"/>  
    </LinearLayout>

第二步,挂电话需要用到系统的Itelephony类的支持,由于Google从Android 2.1 SDK版本后,屏蔽了com.android.internal.telephony. Itelephony类,因此,需要从Android源码下载,找到该类并导入到工程目录下,记得包名一致。

第三步,Activity类ShakeSensorActivity的实现。该类实现SensorListener接口,添加 加速度侦听事件,通过判断设备X、Y、Z方向的总晃动值来判断是否启动打电话和挂机操作。以拨打10086测试为例,当设备总晃动值大于100作为一个测 试判断点,如果当前没有在通话界面,就通过Intent启动拨打电话,否则就挂机操作。设备摇动时,启动电话、挂机的界面状态如图图12-11、图 12-12所示。

▲图12-10  软件运行效果图            12-11 电话启动界面 12-12 摇动设备挂机时的状态界面

下面就来看看代码:

public class ShakeSensorActivity extends Activity implements SensorListener {
    private float lastX;
    private float lastY;
    private float lastZ;
    private View mainView;
    private long currTime;
    private long lastTime;
    private long duration;// 持续时间 
    private float currShake;
    private float totalShake;
    private ITelephony iTelephony;
    private boolean isCalling = false;
    SensorManager sm = null;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mainView = LinearLayout.inflate(this, R.layout.main, null);
        setContentView(mainView);
        ((Button) mainView.findViewById(R.id.call)).setOnClickListener(new     
OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        // 打电话
                        callPhoneNumber10086();
                    }
                });
        ((Button) mainView.findViewById(R.id.stop)).setOnClickListener(new OnClick-         Listener() {
                    @Override
                    public void onClick(View v) {
                        // 挂机
                        closePhone();
                    }
                });
        // 获取传感器管理器
        sm = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        // 注册加速度传感器 
        sm.registerListener(this, 
SensorManager.SENSOR_ACCELEROMETER,SensorManager.         SENSOR_DELAY_NORMAL);

    }
    @Override
    public void finish() {
        // TODO Auto-generated method stub
        super.finish();
        sm.unregisterListener(this);// 注销侦听

    }
    @Override
    public void onAccuracyChanged(int sensor, int accuracy) {
        // 精度改变,该方法实质上不做任何操作,它只在每次调用时,添加一个日志项
    }
    @Override
    public void onSensorChanged(int sensor, float[] values) {
        float x = values[0];
        float y = values[1];
        float z = values[2];
        currTime = System.currentTimeMillis();
        if (lastX == 0 && lastY == 0 && lastZ == 0) {
            // 第一次shake
            lastTime = currTime;
        }
        if (currTime - lastTime > 200) {// 200毫秒检测一次
            duration = currTime - lastTime;
            currShake = (Math.abs(x - lastX) + Math.abs(y - lastY) + Math.abs(z    - lastZ))/ duration * 200;
        }
        totalShake = totalShake + currShake;
        if (totalShake > 100) {
            totalShake = 0;// 重置为0,重新累计计数
            lastX = 0;
            lastY = 0;
            lastZ = 0;
            lastTime = 0;
            currTime = 0;
            if (!isCalling) {
                callPhoneNumber10086();
                ((TextView) mainView.findViewById(R.id.state)).setText("当前状态:                通话中...");
            } else {
                closePhone();
                ((TextView) mainView.findViewById(R.id.state)).setText("当前状态:通话结束...");
            }
        }
        lastX = x;
        lastY = y;
        lastZ = z;
        lastTime = currTime;
    }

    /**
     * tell 10086打开通话界面
     */
    private synchronized void callPhoneNumber10086() {
        isCalling = true;
        Intent myIntentDial = new Intent(Intent.ACTION_CALL,Uri.parse("tel:" + 10086));
        startActivity(myIntentDial);
    }

    /**
     * 结束通话
     */
    private synchronized void closePhone() {
        try {
            getTelephony();
            iTelephony.endCall();
            isCalling = false;
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /**
     * 初始电话 实例
     */
    public void getTelephony() {

        TelephonyManager telMgr = (TelephonyManager) 
this.getSystemService(Service.         TELEPHONY_SERVICE);
        Class<TelephonyManager> c = TelephonyManager.class;
        Method getITelephonyMethod = null;
        try {
            getITelephonyMethod = c.getDeclaredMethod("getITelephony",(Class[]) null);
            getITelephonyMethod.setAccessible(true);
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        try {
            iTelephony = (ITelephony) 
getITelephonyMethod.invoke(telMgr,(Object[])null);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

通过getTelephony()方法,初始一个iTelephony实例,方便调用,目前只用到了挂机关闭通话,打电话也可以通过iTelephony.dial(“10086”)直接拨打。这样就轻松实现了用传感器实现甩动打、挂电话功能。

[转载]T-SQL查询高级--理解SQL SERVER中非聚集索引的覆盖,连接,交叉和过滤

mikel阅读(1238)

[转载]T-SQL查询高级–理解SQL SERVER中非聚集索引的覆盖,连接,交叉和过滤 – CareySon – 博客园.

写在前面:这是第一篇T-SQL查询高级系列文章.但是T-SQL查询进阶系列还远远没有写完。这个主题放到高级我想是因为这个主题需要一些进阶的知识作为基础..如果文章中有错误的地方请不吝指正.本篇文章的内容需要索引的知识作为基础。

简介


在SQL SERVER中,非聚集索引其实可以看作是一个含有聚集索引的表.但相比实际的表而言.非聚集索引中所存储的表的列数要窄很多,因为非聚集索引仅仅包含原表中非聚集索引的列和指向实际物理表的指针。

1

并且,对于非聚集索引表来说,其中所存放的列是按照聚集索引来进行存放的.所以查找速度要快了很多。但是对于性能的榨取来说,SQL SERVER总是竭尽所能,假如仅仅是通过索引就可以在B树的叶子节点获取所需数据,而不再用通过叶子节点上的指针去查找实际的物理表,那性能的提升将会 更胜一筹.

下面我们来看下实现这一点的几种方式.

非聚集索引的覆盖


正如前面简介所说。非聚集索引其实可以看作一个聚集索引表.当这个非聚集索引中包含了查询所需要的所有信息时,则查询不再需要去查询基本表,而仅仅是从非聚集索引就能得到数据:

2

下面来看非聚集索引如何覆盖的:

在adventureWorks的SalesOrderHeader表中,现在只有CustomerID列有非聚集索引,而BillToAddressID没有索引,我们的查询计划会是这样:

3

查询会根据CustomerID列上的非聚集索引找到相应的指针后,去基本表上查找数据.从执行计划可以想象,这个效率并不快。

下面我们来看覆盖索引,通过在CustomerID和BillToAddressID上建立非聚集索引,我们覆盖到了上面查询语句的所有数据:

4

通过覆盖索引,可以看到执行计划简单到不能再简单,直接从非聚集索引的叶子节点提取到数据,无需再扫描基本表!

这个性能的提升可以从IO统计看出来,下面我们来看有覆盖索引和没有覆盖索引的IO对比:

5

索引的覆盖不仅仅带来的是效率的提升,还有并发的提升,因为减少了对基本表的依赖,所以提升了并发,从而减少了死锁!

理解INCLUDE的魔力

上面的索引覆盖所带来的效率提升就像魔术一样,但别着急,正如我通篇强调的一样,everything has price.如果一个索引包含了太多的键的话,也会带来很多副作用。INCLUDE的作用使得非聚集索引中可以包含更多的列,但不作为“键”使用。

比如:假设我们上面的那个查询需要增加一列,则原来建立的索引无法进行覆盖,从而还需要查找基本表:

6

但是如果要包含SubTotal这个总金额,则索引显得太宽,因为我们的业务很少根据订单价格作为查询条件,则使用INCLUDE建立索引:

8

理解INCLUDE包含的列和索引建立的列可以这样理解,把上述建立的含有INCLUDE的非聚集索引想像成:

7

使用INCLUDE可以减少叶子“键”的大小!

非聚集索引的交叉


非聚集索引的交叉看以看作是覆盖索引的扩展!

由于很多原因,比如:

  • 在生产环境中,我们往往不能像上面建立覆盖索引那样随意改动现有索引,这可能导致的结果是你会更频繁的被客户打电话“关照”
  • 现有的非聚集索引已经很“宽”,你如果继续拓宽则增改查带来的性能下降的成本会高过提高查询带来的好处

这时候,你可以通过额外建立索引。正如我前面提到的,非聚集索引的本质是表,通过额外建立表使得几个非聚集索引之间进行像表一样的Join,从而使非聚集索引之间可以进行Join来在不访问基本表的情况下给查询优化器提供所需要的数据:

比如还是上面的那个例子.我们需要查取SalesOrderHeader表,通过BillToAddressID,CustomerID作为选择条件,可以通过建立两个索引进行覆盖,下面我们来看执行计划:

9

非聚集索引的连接


非聚集索引的连接实际上是非聚集索引的交叉的一种特例。使得多个非聚集索引交叉后可以覆盖所要查询的数据,从而使得从减少查询基本表变成了完全不用查询基本表:

比如还是上面那两个索引,这时我只查询非聚集索引中包含的数据,则完全不再需要查询基本表:

10

看起来这样的查询意义不大?但当你把查询条件变为<号时呢?或者给定范围时。还是有一定实际意义的。

非聚集索引的过滤


很多时候,我们并不需要将基本表中索引列的所有数据全部索引,比如说含有NULL的值不希望被索引,或者根据具体的业务场景,有一些数据我们不想索引。这样可以:

  • 减少索引的大小
  • 索引减少了,从而使得对索引的查询得到了加速
  • 小索引对于增删改的维护性能会更高

比如说,如下语句:

11

我们为其建立聚集索引后:

12

这时我们为其加上过滤条件,形成过滤索引:

13

由上面我们可以看出,使用过滤索引的场景要和具体的业务场景相关,对于为大量相同的查询条件建立过滤索引使得性能进一步提升

总结


本文从介绍了SQL SERVER中非聚集索引的覆盖,连接,交叉和过滤。对于我们每一点从SQL SERVER榨取的性能的提升往往会伴随着另一方面的牺牲。作为数据库的开发人员或者管理人员来说,以全面的知识来做好权衡将会是非常重要.系统的学习数 据库的知识不但能大量减少逻辑读的数据,也能减少客户打电话”关照”的次数:-)

[转载]ASP.NET MVC 3 开发的20个秘诀(十七)[20 Recipes for Programming MVC 3]:卷帘式分页加载

mikel阅读(1291)

[转载][翻译]ASP.NET MVC 3 开发的20个秘诀(十七)[20 Recipes for Programming MVC 3]:卷帘式分页加载 – O2DS – 博客园.

议题

现在很多网站都与数据库进行交互。如果网站流量很大,使用SQL来检索数据会带来非常大的压力。更重要的是,用户希望在点击链接之后15秒 内得到响应的内容,而在页面加载的时,显示之外滚动条下面的内容可能多数内容都是不必要的(滚动条之外没显示的部分)。为了解决这个问题,采取内容“需求 点播”方式加载。页面首先会加载足够的内容,当用户在阅读并向下滚动的时候,页面会在不影响用户阅读体验的情况下继续加载更多的内容。

解决方案

当用户开始滚动网站内容时,使用JQuery将前期加载的内容具体数值传回异步控制器,然后按需加载相关的内容。

讨论

异步控制器可能是MVC程序集中迄今为止被利用最少的或最不为人所知的控制器,当然也有可能是不知道怎么用它。以下内容是摘抄自MSDN网站的介绍信息:

在 可能出现线程不足的应用程序中,您可以配置通过异步方式处理操作。异步请求与同步请求所需的处理时间相同。例如,如果某个请求生成一个需要两秒钟来完成的 网络调用,则该请求无论是同步执行还是异步执行都需要两秒钟。但是,在异步调用的过程中,服务器在等待第一个请求完成的过程中不会阻塞对其他请求的响应。 因此,当有许多请求调用长时间运行的操作时,异步请求可以防止出现请求排队的情况。

在这个示例中,使用异步请求将是个完美的解决方案,当新用户在发起更为重要的请求时,它将会自动释放IIS资源,因为其中用户的大多数“需求点播”是不太重要的,因为大多数人甚至不会注意到正在加载的额外的内容。

在大多数社交网站中,用户的批注信息更多可能包含的是活动信息。在以前的秘诀中,实现了为书记添加评论的功能。在这个例子中,将会修改页面,列出最近的评论。当用户为了查看更多的评论,他们就会开始滚动,一旦用户开始滚动页面,就发起Ajax请求,请求异步控制器获取剩余部分评论。

首先,修改Home/Index视图,使其显示最近的评论信息。提供书籍最近的相关评论并显示查看书籍基本资料的相关链接。创建新的控制器用来显示评论,这个视图将会调用render方法来显示剩下的信息。

@model IEnumerable<MvcApplication4.Models.BookComment>
@{
    ViewBag.Title = "Home Page";
}
<h2>@ViewBag.Message</h2>
<p>
    To learn more about ASP.NET MVC visit
    <a href="http://asp.net/mvc"
    title="ASP.NET MVC Website">
    http://asp.net/mvc </a>.
</p>
<script type="text/javascript">
    var lastY = 0;
    var currentY = 0;
    var page = 1;
    var maxPages = @ViewBag.maxPages;
    $(window).scroll(function () {
        if (page < maxPages) {
            currentY = $(window).scrollTop();
            if (currentY - lastY > 200 * (page - 1)) {
                lastY = currentY;
                page++;
                $.get('CommentFeed/Comments?page=' + page,
                    function(data) {
                        $('#comments').append(data);
                });
            }
        }
    });
</script>
<div id="comments">
    <h2>Recent Comments</h2>
    @Html.Partial("../CommentFeed/Comments", Model)

</div>

在上面示例代码中,当浏览器窗体滚动,JavaScript代码就开始执行。在这段代码中定义了一些JavaScript全局变量,保持追踪当前滚动条“Y”坐标的位置,最后“Y”坐标的位置和当前被检索到的页面位置。当窗口的ScrollTop减去“Y”坐标最后的位置大于某个具体数字,则通过Ajax请求书籍的其他评论信息。为确保新内容能及时被加载,必须要根据自己网站的内容高度,调整到最佳的像素值。

接下来,需要修改HomeController添加检索书籍评论列表。为了确保最新的评论首先显示,排序使用创建日期降序的排列方法。为了减轻数据库的负载,每次只加载固定数量的评论而不是全部,但是也要保证,在滚动时显示足够的内容。在下面的示例当中,评论的加载数量将会限制为3条。页面的最大加载次数也被限制为评论总数除以3的结果。设置最大页数,以防止在加载完毕后,产生无效Ajax请求。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Globalization;
using System.Data.Entity;
using MvcApplication4.Models;
namespace MvcApplication4.Controllers
{
    public class HomeController : Controller
    {
        private BookDBContext db = new BookDBContext();
        public ActionResult Index()
        {
            ViewBag.Message = "Welcome to ASP.NET MVC!";
            
            // Get our recent comments
            var bookcomments = db.BookComments.Include(
                b => b.Book).OrderByDescending(b => b.Created).
                    Take(3);
            var count = db.BookComments.Count();
            ViewBag.maxPages = count / 3 + 1;
            return View(bookcomments);
        }
        ...
    }
}

接下来,需要复制一个新的异步控制器。选中Controllers文件夹,右键单击选择“添加”→“控制器”,将新控制器命名为“CommentFeedController”。这个控制器不需要设置基架选项以及其他内容,直接点击“添加”即可。(译者注:然后将新创建的控制器类的父类改为“AsyncController

这个控制器与之前的默认控制器看起来会有一些区别。异步控制器,每个视图都会有两个方法。第一个方法是用来实现异步请求(例如,获取评论信息)。第二个方法,是在异步调用时返回或显示接收到的结果。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplication4.Models;
using System.Data.Entity;

namespace MvcApplication4.Controllers
{
    public class CommentFeedController : AsyncController
    {
        private BookDBContext db = new BookDBContext();
        public void CommentsAsync(int page)
        {
            AsyncManager.OutstandingOperations.Increment();
            
            AsyncManager.Sync(() =>
            {
                var bookcomments = db.BookComments.Include(
                    b => b.Book).OrderByDescending(b =>
                        b.Created).Skip(page * 3).Take(3);
                AsyncManager.Parameters["bookcomments"] =
                    bookcomments;
                AsyncManager.OutstandingOperations.Decrement();
            });
        }
        
        public ActionResult CommentsCompleted(
            IEnumerable<BookComment> bookcomments)
        {
            return PartialView(bookcomments);
        }
    }
}

第一个方法,“CommentsAsync”,接收从JavaScript传回的当前的页码,使用这个值来检索接下来的3条评论。首先是调用OutstandingOperations来通知未完成的请求挂起。然后再将检索评论的代码作为一个方法变量执行第二步操作,最后,再从未完成的计数器中将执行方法减去。在这里最重要的是递增和递减在计数器中的匹配,当递增和递减计数器相同的一段时间后,同步管理器将取消请求,否则,请求永远无法结束。

第二个方法,接收书籍的评论信息,并返回一个分部视图结果。这是一个与Home/Index视图相同的分部视图。在这里最后一步就是创建这个分部视图。右键单击“Views”文件夹,选择“添加”→“新建文件夹”,并将文件夹命名为“CommentFeed”,然后右键单击此文件夹,选择“添加”→“视图”,将其命名为“Comments”,然后确保选中创建视图对话框中的“创建分部视图”选项,然后点击“添加”。

@model IEnumerable<MvcApplication4.Models.BookComment>
@foreach (var item in Model) {
    <h3><a href="@Url.Action("Details", "Books", new {
            ID=item.Book.ID } )">
        @Html.DisplayFor(modelItem => item.Book.Title)
    </a></h3>
    <h4>Comment Posted: @Html.DisplayFor(
        modelItem => item.Created)</h4>
    <p>@MvcHtmlString.Create(Html.Encode(item.Comment).Replace(
        Environment.NewLine, "<br />"))</p>

}

如上所示,首先将评论按照创建时间排序循环取出,显示书籍标题以及详情链接,评论的创建日期以及最后的评论文本。因为评论中有可能会包含换行符,将其中换行符替换为“<br />”标记。

参考

Asynchronous Controllers 原书地址 书籍源代码

[转载]教你10分钟内在Windows上完成Rails开发环境的安装和配置

mikel阅读(1110)

[转载]教你10分钟内在Windows上完成Rails开发环境的安装和配置 – 开源天宝 – 博客园.

一般来说,Windows开发者需要自己搭建好Ruby、RubyGems、Rails以及SQLite才能开始创建Rails应用。

对于初学者,在Windwos上安装rails最简单的方式是RailsInstaller安装包。
RailsInstaller是一键安装的软件包,能够帮助你尽快上手,快速安装好开发环境。

本文使用的railsinstaller 2.0.1包含了:

RailsInstaller的官方网站http://railsinstaller.org/

1,railsinstaller 2.0.1版本下载快速通道:下载

2,双击railsinstaller.exe进行安装,是一个58m的文件。

3,railsinstaller安装步骤:

点击Next,进入下一步:

同意安装协议,进入下一步:

这一步设置文件安装的位置,推荐使用英文或者拼音,字母间一定不要带空格,方便以后通过cmd的方式进行操作,点击install进行安装。

点击finish完成安装。

railsinstaller的安装大概需要6分钟,安装后文件大小为437兆,如图。

点击完成后,会弹出git和shh的设置提示,如下图。

git是一个程序版本控制管理工具,需要到github.com上注册一个免费用户,注册步骤如下图。

注册后,回到刚才的cmd窗口,分别输入用户名和邮箱地址:

完成设置后,会提示sshkey已经复制到剪贴板,建立一个txt文件=>粘贴,把它保存下来,留着以后使用。程序同时在c盘下建立了一个sites的目录。

到这一步,整个ruby on rails的开发环境就配置好了,以后可以通过cmd来测试和使用,如下图:

可以看到安装的gem的版本是1.7.2,ruby 的版本是1.9.2,rails的版本是3.1.1。

到此,我们使用railsinstaller在windows上快速的安装了rails。

开始我们的ruby on rails之旅吧。