“Gphone”目录存档

Ubuntu Linxu安装G1设备驱动方法

2009年07月30日,星期四

1.sudo vi /etc/udev/rules.d/50-android.rules
添加内容: SUBSYSTEM==”usb”, SYSFS{”High Tech Computer Corp.”}==”0bb4″, MODE=”0666″

2. sudo vi /etc/udev/rules.d/90-android.rules
添加内容: SUBSYSTEM==”usb”, ATTR{”High Tech Computer Corp.”}==”0bb4″, MODE=”0666″

3.执行一下命令
sudo chmod +x /etc/udev/rules.d/50-android.rules
sudo chmod +x /etc/udev/rules.d/90-android.rules

4. 执行命令: sudo /etc/init.d/udev restart

5.执行命令: sudo killall -9 adb

6.执行命令:sudo adb start-server

建议插入G1时执行第5,6步,直到执行命令 adb devices 能看到手机设备列表时即可。

注意事项:
1. 必须使用sudo及root帐号来执行
2. adb命令应设置好PATH,否则会提示无此命令

HWOTO install and setup Android NDK for Windows(Android NDK 开发环境安装和配置)

2009年06月27日,星期六

标 题: HWOTO install and setup Android NDK for Windows
发布日期: 2009-06-27 18:00
原文链接: http://emck.avaw.com/?p=250
作 者: Emck
说 明: 此文档严格遵循GPL协议,转贴请保持此文档的完整性!

1. 下载Android 1.5 NDK, Release 1 For Windows 并解压到D:\android-ndk-1.5_r1

2.下载Cygwin, 执行setup.exe并开始安装(根据网络速度快慢而定, 预计安装需要20分钟左右)
注意:1. 选择台湾的http站点,速度会快很多; 2.开发选项里必须勾选gcc和make,否则后期无法编译

3.执行桌面图标Cygwin,出现linux shell提示符后, 修改当前目录(/home/username)下.bash_profile文件, 加入如下内容在文件尾部
ANDROID_NDK_ROOT=/cygdrive/d/android-ndk-1.5_r1
export ANDROID_NDK_ROOT

4.退出Cygwin,重新进入,执行以下命令
cd $ANDROID_NDK_ROOT
build/host-setup.sh
一切顺利的话会提示设置成功, 如果出错, 则检查错误并修复

5.编译sample hello-jni
进入Cygwin
cd $ANDROID_NDK_ROOT
make APP=hello-jni
一切顺利则会生成libhello-jni.so文件在 $ANDROID_NDK_ROOT/apps/hello-jni/project/libs/armeabi目录下

6.启动预先配置好的Eclipse + ADT环境,创建一个Android Project,选择Create project form existring source
Location = D:\android-ndk-1.5_r1\apps\hello-jni\project, 直接确认即可开始编译并生成apk文件

7.运行这个Project,及可看到效果.

8.祝大家好运

相关资源: Google 讨论组

Android G1 操作技巧,硬启,快捷键,同步联系人,升级rom

2009年03月6日,星期五

如何硬启:

方法1:在机器还能正常启动时:

Press Alt+L路径:Menu—Settings—–SD Card and Storage—-Factory Data Reset—-Reset Phone

方法2: 一旦机器死翘翘不能正常启动:

关机,  持续同时按住Home和红键,大约20s,此时机器会启动出现开始屏幕,等看到有个三角警告标示的黑屏时,放手, 滑开键盘,
按下Alt+L,(这个组合键的作用是显示log 文本)
然后,按下 Alt+W 就是  “wipe data /factory reset”, 这个重启的效果是彻底重启,再启动就和刚开箱一样,需要重新绑定google account了。
然后同时按下Home 和返回键,这时候机器应该顺利重启了。 如果此时还不能重启,卸下电池再装上即可。

btw: 还有个命令: Alt+S,应该是执行SD card上的update.zip文件。 这样可以手工升级  (目前G1的升级是通过Tmobile的OTA升级的, 昨天刚刚push了RC19的包,,,但俺这边没反应,应该是不在Tmobile网络里无法OTA的缘故,看来以后要自己手工升级了~~~)

快捷键和操作

全局快捷键启动(默认,可以自己定义修改,search键+任意字母键)quick launch:
Browser SEARCH + b
Contacts SEARCH + c
E-Mail SEARCH + e
Google Mail SEARCH + g
Calendar SEARCH + l
Maps SEARCH + m
Music SEARCH + p
Messaging SEARCH + s
YouTube SEARCH + y
弹出长按菜单:  触屏上选中长按,或是 长按轨迹球

列表中的浏览:Navigation within lists
下翻页 SPACEBAR
上翻页 SHIFT + SPACEBAR
跳转到页尾 ALT + TRACKBALL roll DOWN
跳转到页首 ALT + TRACKBALL roll UP        文本输入和浏览 Typing and text navigation tips:
插入特殊符号 ALT + SPACEBAR opens special character selector
从左起删除 DEL
从右起删除 SHIFT + DEL
删除整行 ALT + DEL
CAPS大写锁定; 双按 SHIFT ,再按取消
光标跳转到首字/尾字 ALT + roll TRACKBALL left/right
制表符 ALT + q
高亮选中文本 SHIFT + roll TRACKBALL
剪切 MENU + x, 或是高亮选中文本后,长按轨迹球,选择弹出菜单中的Cut
复制 MENU + c, 或是高亮选中文本后,长按轨迹球,选择弹出菜单中的Copy
粘贴 MENU + v, 或是高亮选中文本后,长按轨迹球,选择弹出菜单中的Paste
Undo MENU + z
全选  MENU + a, 或是高亮选中文本后,长按轨迹球,选择弹出菜单中的

内置浏览器 Browser shortcuts
Open Go to window MENU + s
Open Bookmarks MENU + b
Open Windows MENU + w
View history MENU + h
Refresh or stop page MENU + r
Go back a page MENU + j
Go forward a page MENU + k
Find on page MENU + f
Go to home page MENU + ENTER
Zoom in MENU + i
Zoom out MENU + o
Go to Settings MENU + p
Page down SPACEBAR
Page up SHIFT + SPACEBAR
————————
GoogleMap shortcuts
Directions MENU + d
Select map mode MENU + m
History MENU + h
My Location MENU + 0 (zero)
Go to Settings MENU + p
Zoom in MENU + i
Zoom out MENU + o
————————-

Gmail:
F – Forward (while viewing message)
R – Reply (while viewing message)
A – Reply All (while viewing message)
Y – Archive (while viewing message or from list)

更多的G1 操作小技巧

通用技巧

1.长按home键会将当前程序置入背景运行

2.调整音量至最低,就会依次进入振动模式和电话铃声静音
3.当然,你也可以长按红键,在菜单里选择静音模式(此时全部静音,包括游戏和音乐)
4.可以在Market里下载一个免费小软,ToggleSetting, 可以一键切换 WiFi开关 GPS开关, 振动模式开关,屏幕超时关闭开关,静音模式开关等,很方便!
5.通过快捷方式在桌面上创建的文件夹,可以通过点击并按住标题栏来重命名,方便归类存放各类快捷方式。(需要安装AnyCut,这个也是must have app!!)
6.长按绿键,自动激活语音拨号, 当然是英文的.
7.可以通过在sdcard里建立诸如 “ringtones”"alarms” “notifications”,然后将铃声文件(ogg,mp3,wav,mid等)放进去,系统的选择声音就可以找到。当然,建议你装一个小软 RingtoneExtended,比这种方法好用!
8.短信可以复制或是转发,在对话模式里选中某条sms后长按即可弹出菜单
9.输入时, 想删除一个词是, 可以menu+del,一次删除一个单位而不是删除一个字母

Chromelite Browser

保存网页上的图片,或是设置为壁纸或是联系人大头贴:

在图片上长按3s, 弹出选择菜单.如果图片后面带着链接的话还有更多选项.

快速滚动:

按住ALT键,再滚动trackball.
按住空格键.
Shift+Space向上滚

其他:

图片上长按trackball激活 “save image/view image”
URL上长按trackball,激活菜单 “open/open in new window/bookmark link/save link/share link/copy link url”
按menu呼出浏览器菜单 (go to url/search/bookmarks/…etc);
快速双击轨迹球,可以激活放大镜模式进行浏览

如何将outlook里的联系人同步到G1

这个问题, 和 “如何将outlook里的联系人同步到Gmail的contacts里“ 是一个问题。

outlook和gmail之间的联系人导入导出, 是通过csv,但是你直接从outlook里导出csv,gamil里直接上载的话, 会发现好多字段映射有问题。。。。(Update: 据说,Outlook里导出csv时选择Dos格式而不是win格式,则可以顺利导入Gmail contacts!而不需要下面的live mail倒腾大法~~~我没验证过,老外说的

我用的最顺利的方法,(适合懒得去分析gmail的csv映射到底哪儿和outlook的有差别了)曲线救国:
1.   登录你的hotmail, 在联系人里,下载一个Windows Live Contacts 导入程序,这个官方程序执行,填入live id,就自动把outlook或是outlook express(可选)的联系人导入到hotmail 联系人里了(嗯嗯,最好有多个hotmail,heh,省的和你本来的msn联系人冲突 )
2.  登录hotmail, 联系人管理里,选择导出csv,
3.  登录gmail, 联系人管理里,选择导入刚刚从hotmail里导出的csv,嗯哪, 大功告成。

此时,如果你的G1设置的是autosync的(缺省就是),那, 看看G1上,contact是不是已经和gmail里的一样了~~

btw: gmail的contact里,是有维护mail的人,才可以加照片~~~~nnd,曾折腾了我好一阵子,怎么有人能加大头照有人就不行~~~

【如何升级Tmobile G1的ROM】

Tmobile G1,发售的机型初始rom版本号为RC19, 目前最新的版本号为RC29,修正了一些问题(fix list参见下面清单)

QUOTE:
Wrong field is focused when keyboard is openend inMessagingapplication: Now when the keyboard is opened, the focus willdefault tothe compose (text entry) field.
Cannot associate withWiFiaccess point using shared WEP key: Should now be able to associatetoaccess points with a shared WEP key.
Google Contacts/Calendar Sync (endless loop).
USBstorage:Now a USB notification will appear in the notifications areawhenconnected via USB. Users must select to use USB Storage ifdesired.
Email notification doesn’t disappear when the Email has been read (POP/IMAP Email client).
New Email notifications not received (POP/IMAP Email client).
Browser does not launch YouTube video when Settings > Enable Javascript is unchecked.
WiFi & Bluetooth occasionally disconnect while charging.
Support silencing of Email notifications (POP/IMAP Email client)
Amazon MP3 fix for files with underscores (_) in the filenames

按Tmobile官方的设计, 一律通过Tmobile通讯网络OTA方式推送升级包。 在Tmobile网络内的G1, 在升级包发布的几天内,都会自动被推送升级包, 用户会看到一个提示框,是否升级,需要重启。确认即可完成升级。很傻瓜化,很不错。

但, 俺们的G1都是在CMCC的网络, 不可能享受到Tmobile的OTA推送。  当然,米国也有很多At&t网络下的G1,没有OTA,照样有勤劳勇敢的劳动人民。。。heh, 下面介绍新鲜火辣的升级指南。

从Tmobile的升级网站下载升级包

将下载好的zip包重命名为update.zip
将update.zip复制到你的microsd 卡的根目录下
长按电源键,关闭G1, 然后,同时按住Home键, 长按红键,启动
一直保持同时长按home和红键的状态,大约20s,当你看到黄色的警告标示符后,正式进入工程模式。

滑开键盘, 按下 ALT-L ,此时显示工程模式的菜单,然后按下 ALT-S 开始刷新rom

等到刷新进度条完成,系统提示你同时按下Home + Back,重启你的机器,在你按下后, 会提示写入radio module, 等一会儿。

系统一会儿会自动重启。 ok,大功告成!  进Settings里的about phone看看, 已经升级完成啦!!!

【Howto】 如何将不断膨胀的浏览器缓存文件移到SDcard上,解决内存紧张问题

感谢sk163 提供的信息来源, 感谢米国网友V_RocKs (Modmygphone上的帖子)

前提:

G1 软件版本必须低于RC30(因为远程登录获得root权限,其实是一个可能有很大隐藏风险的漏洞,google马上发布的RC30更新包将填了这个坑~~~)
你有一张microsd Card,而且有空余空间,而且已经插入G1,而且不是usb mount状态~~~~(有点唐僧啊,)
你已经通过telnetd方式获得了G1的root权限(怎么做,我就不单独介绍了, 本帖里已经有网友转了, 网上搜索G1 越狱,是一坨一坨的文章)

方法:

注: 取得root权限是在pc上的telnet客户端上远程登录后输入命令的, 下面的那些操作,也都是通过telnet方式在pc上操作的。 您别在G1的terminal上操作,heh, 至少现在G1的terminal上还没有sudo这种命令。。
还有, linux是区分大小写的

Android 数据存取之Databases

2009年03月6日,星期五

在Android平台上可以操作数据库,这是第一次接触Android时的惊艳之一。在Android平台上,绑定了SQLite数据库,这个数据库系统也是极具性格的,它的最大的应用场景是嵌入式系统,进一步了解可以参看这里

如果有JDBC的经验,那么在这里会容易的多。Android中操作数据库首先要通过一个 类:android.database.sqlite.SQLiteOpenHelper。它封装了如何打开一个数据库,其中当然也包含如果数据库不存在 就创建这样的逻辑。看一个例子:

  1. pubilc class DatabaseHelper extends SQLiteOpenHelper {
  2. private static final String DATABASE_NAME = “com.roiding.simple.note”;
  3. private static final int DATABASE_VERSION = 1;
  4. private static final String NOTES_TABLE_NAME = “notes”;
  5. DatabaseHelper(Context context) {
  6. super(context, DATABASE_NAME, null, DATABASE_VERSION);
  7. }
  8. @Override
  9. public void onCreate(SQLiteDatabase db) {
  10. db.execSQL(“CREATE TABLE ” + NOTES_TABLE_NAME
  11. “ (id integer primary key autoincrement, name text);”);
  12. }
  13. @Override
  14. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  15. db.execSQL(“DROP TABLE IF EXISTS notes”);
  16. onCreate(db);
  17. }
  18. }
pubilc class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "com.roiding.simple.note";
private static final int DATABASE_VERSION = 1;
private static final String NOTES_TABLE_NAME = "notes";

DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + NOTES_TABLE_NAME
+ " (id integer primary key autoincrement, name text);");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS notes");
onCreate(db);
}
}

这里面,如下的语句需要解释:

  • super(context, DATABASE_NAME, null, DATABASE_VERSION)
    数据库连接的初始化,中间的那个null,是一个CursorFactory参数,没有仔细研究这个参数,暂时置空吧。
  • public void onCreate(SQLiteDatabase db)
    这里面的onCreate是指数据库onCreate时,而不是DatabaseHelper的onCreate。也就是说,如果已经指定 database已经存在,那么在重新运行程序的时候,就不会执行这个方法了。要不然,岂不是每次重新启动程序都要重新创建一次数据库了!在这个方法中, 完成了数据库的创建工作。也就是那个execSQL()方法。
  • public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
    在程序的开发维护过程中,数据库的结构可能会有变化,那么这个方法就有用处了。在DatabaseHelper这个对象一创建时,就已经把参数 DATABASE_VERSION传入,这样,如果Android发现此版本与现有版本不一致,就会调用这个onUpgrate方法。于是,可以在这里面 实现一些数据的upgrade工作,比如说创建一个临时表,将数据由临时表中转到新的表结构中。需要注意的是,这里面的onUpgrade是在版本不一致 时调用,也就是说不管当前需要的版本高于现有版本还是低于现有版本,都会出发这个方法,类似的这种情况,就需要对oldVersion和 newVersion进行判断之后再决定使用什么策略来更新数据。

在Android中,数据库存放在 /data/data/PACKAGE_NAME/databases 目录下。

接下来就可以使用这个Helper来操作数据库了,操作数据库也就无非是增、删、改、查。先看一个增的例子:

  1. public static void insert(Context context, String s) {
  2. DatabaseHelper mOpenHelper = new DatabaseHelper(context);
  3. String table = “notes”;
  4. String nullColumnHack = “id”;
  5. ContentValues values = new ContentValues();
  6. values.put(“name”, “www.roiding.com”);
  7. long id = mOpenHelper.getReadableDatabase().insert(table,
  8. nullColumnHack, values);
  9. mOpenHelper.getReadableDatabase().close();
  10. }
public static void insert(Context context, String s) {
DatabaseHelper mOpenHelper = new DatabaseHelper(context);

String table = "notes";
String nullColumnHack = "id";

ContentValues values = new ContentValues();
values.put("name", "www.roiding.com");

long id = mOpenHelper.getReadableDatabase().insert(table,
nullColumnHack, values);

mOpenHelper.getReadableDatabase().close();
}
  • DatabaseHelper mOpenHelper = new DatabaseHelper(context);
    如果和JDBC例子的话,这一步貌似就像是获得了一个Statement,用它就可以操作数据库了。
  • ContentValues values = new ContentValues();
    Android在向数据库中插入数据的时候,要求数据存放到ContentValues中,这里面的ContentValues其实就是一个 Map,Key值是字段名称,Value值是字段的值。这样,也许你会发现一个问题,那数据类型怎么办?其实在SQLite数据库中就是没有数据类型的, 一切都是字符串。
  • mOpenHelper.getReadableDatabase().insert(table,nullColumnHack, values);
    将数据入库,注意这里面有一个nullColumnHack,看文档是和所有字段都是空的记录有关系,我没有去实验他具体的效果,只是随便给他一个字段名称。

再看一个查的例子:

  1. public static void select(Context context) {
  2. DatabaseHelper mOpenHelper = new DatabaseHelper(context);
  3. String table = “notes”;
  4. String[] columns = new String[] { “id”, “name” };
  5. String selection = “id>? and name<>?”;
  6. String[] selectionArgs = new String[] { “0″, “roiding.com” };
  7. String groupBy = null;
  8. String having = null;
  9. String orderBy = “id desc”;
  10. String limit = “1″;
  11. Cursor c = mOpenHelper.getReadableDatabase().query(table,
  12. columns, selection, selectionArgs, groupBy, having, orderBy, limit);
  13. c.moveToFirst();
  14. for (int i = 0; i < c.getCount(); i++) {
  15. String s = c.getString(1);
  16. c.moveToNext();
  17. }
  18. c.close();
  19. mOpenHelper.getReadableDatabase().close();
  20. }
public static void select(Context context) {
DatabaseHelper mOpenHelper = new DatabaseHelper(context);

String table = "notes";
String[] columns = new String[] { "id", "name" };
String selection = "id>? and name<>?";
String[] selectionArgs = new String[] { "0", "roiding.com" };
String groupBy = null;
String having = null;
String orderBy = "id desc";
String limit = "1";

Cursor c = mOpenHelper.getReadableDatabase().query(table,
columns, selection, selectionArgs, groupBy, having, orderBy, limit);

c.moveToFirst();
for (int i = 0; i < c.getCount(); i++) {
String s = c.getString(1);
c.moveToNext();
}
c.close();
mOpenHelper.getReadableDatabase().close();
}
  • DatabaseHelper mOpenHelper = new DatabaseHelper(context);
    于前文中的相同
  • mOpenHelper.getReadableDatabase().query();
    通过mOpenHelper.getReadableDatabase(),会得到一个SQLiteDatabase类型的只读的数据库连接,在这里直接调用了他的query方法。这个query方法相对复杂,因为他将一个完整的SQL语句拆成了若干个部分:

    • table:表名。相当于SQL的from后面的部分。那如果是多表联合查询怎么办?那就用逗号将两个表名分开,拼成一个字符串作为table的值。
    • columns:要查询出来的列名。相当于SQL的select后面的部分。
    • selection:查询条件,相当于SQL的where后面的部分,在这个语句中允许使用“?”,也就是说这个用法和JDBC中的PreparedStatement的用法相似。
    • selectionArgs:对应于selection的值,selection有几个问号,这里就得用几个值。两者必须一致,否则就会有异常。
    • groupBy:相当于SQL的group by后面的部分
    • having:相当于SQL的having后面的部分
    • orderBy:相当于SQL的order by后面的部分,如果是倒序,或者是联合排序,可以写成类似这样:String orderBy = “id desc, name”;
    • limit:指定结果集的大小,它和Mysql的limit用法不太一样,mysql可以指定从多少行开始之后取多少条,例如“limit 100,10”,但是这里只支持一个数值。
  • c.moveToFirst();
    这一句也比较重要,如果读取数据之前,没有这一句,会有异常。
  • c.getString(1);
    与JDBC一致了,Android不支持按字段名来取值,只能用序号。

再看一个删除和修改的例子:

  1. public static void delete(Context context) {
  2. DatabaseHelper mOpenHelper = new DatabaseHelper(context);
  3. String table = “notes”;
  4. String selection = “id>? and name<>?”;
  5. String[] selectionArgs = new String[] { “0″, “roiding.com” };
  6. String whereClause = selection;
  7. String[] whereArgs = selectionArgs;
  8. mOpenHelper.getWritableDatabase().delete(table, whereClause, whereArgs);
  9. mOpenHelper.getWritableDatabase().close();
  10. }
public static void delete(Context context) {
DatabaseHelper mOpenHelper = new DatabaseHelper(context);

String table = "notes";
String selection = "id>? and name<>?";
String[] selectionArgs = new String[] { "0", "roiding.com" };
String whereClause = selection;
String[] whereArgs = selectionArgs;

mOpenHelper.getWritableDatabase().delete(table, whereClause, whereArgs);
mOpenHelper.getWritableDatabase().close();
}

有了上面的基础这里就容易理解了,这里的whereClause相当于前面的selection,whereArgs相当于前面的selectionArgs。

  1. public static void update(Context context) {
  2. DatabaseHelper mOpenHelper = new DatabaseHelper(context);
  3. String table = “notes”;
  4. String selection = “id>? and name<>?”;
  5. String[] selectionArgs = new String[] { “0″, “roiding.com” };
  6. String whereClause = selection;
  7. String[] whereArgs = selectionArgs;
  8. ContentValues values = new ContentValues();
  9. values.put(“name”, “www.roiding.com”);
  10. mOpenHelper.getWritableDatabase().update(table, values,
  11. whereClause, whereArgs);
  12. mOpenHelper.getWritableDatabase().close();
  13. }
public static void update(Context context) {
DatabaseHelper mOpenHelper = new DatabaseHelper(context);

String table = "notes";
String selection = "id>? and name<>?";
String[] selectionArgs = new String[] { "0", "roiding.com" };
String whereClause = selection;
String[] whereArgs = selectionArgs;

ContentValues values = new ContentValues();
values.put("name", "www.roiding.com");

mOpenHelper.getWritableDatabase().update(table, values,
whereClause, whereArgs);
mOpenHelper.getWritableDatabase().close();
}

这个update的用法,综合select和delete就可以理解。

注意:

  • Cursor和Databases要及时关闭,不然也会有异常。
  • getWritableDatabase()和getReadableDatabase()在当前的Android版本中貌似可以通用,像上面的insert,用的就是getReadableDatabase。

在真实的应用中,会对上面这些基本操作做更高一级的抽象和封装,使之更容易使用。在select时,除了用上述的方法,将分段的SQL语句传进去之 外,Android还支持一种方法:使用SQLiteQueryBuilder。如果使用的是上述的分段SQL语句的方法,在Android的内部实现 中,也是先将分段的SQL使用SQLiteQueryBuilder的静态方法来生成一个真正的SQL的,而且,我没有看出来使用 SQLiteQueryBuilder的优势。

Android 数据存取之Files

2009年03月6日,星期五

如同前面说的使用Preferences一样,使用File来读写文件也属于常规思路。在Android中没有提供像J2SE里面多的让人抓狂的有关于IO的API。所以使用起来非常简单轻巧。

在Android系统中,这些文件保存在 /data/data/PACKAGE_NAME/files 目录下。

数据读取

  1. public static String read(Context context, String file) {
  2. String data = “”;
  3. try {
  4. FileInputStream stream = context.openFileInput(file);
  5. StringBuffer sb = new StringBuffer();
  6. int c;
  7. while ((c = stream.read()) != -1) {
  8. sb.append((char) c);
  9. }
  10. stream.close();
  11. data = sb.toString();
  12. catch (FileNotFoundException e) {
  13. catch (IOException e) {
  14. }
  15. return data;
  16. }
public static String read(Context context, String file) {
String data = "";
try {
FileInputStream stream = context.openFileInput(file);
StringBuffer sb = new StringBuffer();
int c;
while ((c = stream.read()) != -1) {
sb.append((char) c);
}
stream.close();
data = sb.toString();

} catch (FileNotFoundException e) {
} catch (IOException e) {
}
return data;
}

从代码上,看起来唯一的不同就是文件的打开方式了: context.openFileInput(file); Android中的文件读写具有权限控制,所以使用context(Activity的父类)来打开文件,文件在相同的Package中共享。这里的 Package的概念同Preferences中所述的Package,不同于Java中的Package。

数据写入

  1. public static void write(Context context, String file, String msg) {
  2. try {
  3. FileOutputStream stream = context.openFileOutput(file,
  4. Context.MODE_WORLD_WRITEABLE);
  5. stream.write(msg.getBytes());
  6. stream.flush();
  7. stream.close();
  8. catch (FileNotFoundException e) {
  9. catch (IOException e) {
  10. }
  11. }
public static void write(Context context, String file, String msg) {
try {
FileOutputStream stream = context.openFileOutput(file,
Context.MODE_WORLD_WRITEABLE);
stream.write(msg.getBytes());
stream.flush();
stream.close();
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
}

在这里打开文件的时候,声明了文件打开的方式。

一般来说,直接使用文件可能不太好用,尤其是,我们想要存放一些琐碎的数据,那么要生成一些琐碎的文件,或者在同一文件中定义一下格式。其实也可以将其包装成Properties来使用:

  1. public static Properties load(Context context, String file) {
  2. Properties properties = new Properties();
  3. try {
  4. FileInputStream stream = context.openFileInput(file);
  5. properties.load(stream);
  6. catch (FileNotFoundException e) {
  7. catch (IOException e) {
  8. }
  9. return properties;
  10. }
  11. public static void store(Context context, String file, Properties properties) {
  12. try {
  13. FileOutputStream stream = context.openFileOutput(file,
  14. Context.MODE_WORLD_WRITEABLE);
  15. properties.store(stream, “”);
  16. catch (FileNotFoundException e) {
  17. catch (IOException e) {
  18. }
  19. }

Android数据存取之Preferences

2009年03月6日,星期五

这种方式应该是用起来最简单的Android读写外部数据的方法了。他的用法基本上和 J2SE(java.util.prefs.Preferences)中的用法一样,以一种简单、 透明的方式来保存一些用户个性化设置的字体、颜色、位置等参数信息。一般的应用程序都会提供“设置”或者“首选项”的这样的界面,那么这些设置最后就可以 通过Preferences来保存,而程序员不需要知道它到底以什么形式保存的,保存在了什么地方。当然,如果你愿意保存其他的东西,也没有什么限制。只 是在性能上不知道会有什么问题。

在Android系统中,这些信息以XML文件的形式保存在 /data/data/PACKAGE_NAME/shared_prefs 目录下。

数据读取

  1. String PREFS_NAME = “Note.sample.roiding.com”;
  2. SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
  3. boolean silent = settings.getBoolean(“silentMode”, false);
  4. String hello = settings.getString(“hello”, “Hi”);
String PREFS_NAME = "Note.sample.roiding.com";
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
boolean silent = settings.getBoolean("silentMode", false);
String hello = settings.getString("hello", "Hi");
这段代码中:
  • SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
    通过名称,得到一个SharedPreferences,顾名思义,这个Preferences是共享的,共享的范围据现在同一个Package中,这里 面说所的Package和Java里面的那个Package不同,貌似这里面的Package是指在AndroidManifest.xml文件中:

    1. <manifest xmlns:android=“http://schemas.android.com/apk/res/android”
    2. package=“com.roiding.sample.note”
    3. android:versionCode=“1″
    4. android:versionName=“1.0.0″>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.roiding.sample.note"
    android:versionCode="1"
    android:versionName="1.0.0">

    这里面的package。根据我目前的实验结果看,是这样的,欢迎指正。后面的那个int是用来声明读写模式,先不管那么多了,暂时就知道设为0(android.content.Context.MODE_PRIVATE)就可以了。

  • boolean silent = settings.getBoolean(”silentMode”, false);
    获得一个boolean值,这里就会看到用Preferences的好处了:可以提供一个缺省值。也就是说如果Preference中不存在这个值的话,那么就用后面的值作为返回指,这样就省去了我们的if什么什么为空的判断。

数据写入

  1. String PREFS_NAME = “Note.sample.roiding.com”;
  2. SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
  3. SharedPreferences.Editor editor = settings.edit();
  4. editor.putBoolean(“silentMode”, true);
  5. editor.putString(“hello”, “Hello~”);
  6. editor.commit();
String PREFS_NAME = "Note.sample.roiding.com";
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("silentMode", true);
editor.putString("hello", "Hello~");
editor.commit();

有了上面数据读取的代码,这里面的就容易理解了,只是别忘了最后的commit();

Google Android ADC-I Top 50 作品

2009年03月6日,星期五
以下其中46个获奖作品,其他4个据说作者要求保密

* AndroidScan – Jeffrey Sharkey

* Beetaun – Sergey Gritsyuk and Dmitri Shipilov

* BioWallet – Jose Luis Huertas Fernandez

* BreadCrumbz – Amos Yoffe

* CallACab – Konrad Huebner and Henning Boeger

* City Slikkers – PoroCity Media and Virtual Logic Systems

* Commandro – Alex Pisarev, Andrey Tapekha

* Cooking Capsules – Mary Ann Cotter and Muthuselvam Ramadoss

* Diggin – Daniel Johansson, Aramis Waernbaum, Andreas Hedin

* Dyno – Virachat Boondharigaputra

* e-ventr – Michael Zitzelsberger

* Eco2go – Taneem Talukdar, Gary Pong, Jeff Kao and Robert Lam

* Em-Radar – Jack Kwok

* fingerprint – Robert Mickle

* FreeFamilyWatch – Navee Technologies LLC

* goCart – Rylan Barnes

* GolfPlay – Inizziativa Networks

* gWalk – Prof. Dr.-Ing. Klaus ten Hagen, Christian Klinger, Marko Modsching, Rene Scholze

* HandWx – Weathertop Consulting LLC

* IMEasy – Yan Shi(这个可能是中国选手)

* Jigsaw – Mikhail Ksenzov

* JOYity – Zelfi AG

* LifeAware – Gregory Moore, Aaron L. Obrien, Jawad Akhtar

* Locale – Clare Bayley, Christina Wright, Jasper Lin, Carter Jernigan

* LReady Emergency Manager – Chris Hulls, Dilpreet Singh, Luis Carvalho, Phuong Nguyen

* Marvin – Pontier Laurent

* Mobeedo – Sengaro GmbH

* Multiple Facets Instant Messenger – Virgil Dobjanschi

* MyCloset – Mamoru Tokashiki

* PedNav – RouteMe2 Technologies Inc.

* Phonebook 2.0 – Voxmobili

* PicSay – Eric Wijngaard

* PiggyBack – Christophe Petit and Sebastien Petit

* Pocket Journey – Anthony Stevens and Rosie Pongracz

* Rayfarla – Stephen Oldmeadow

* Safety Net – Michael DeJadon

* SocialMonster – Ben Siu-Lung Hui and Tommy Ng

* SplashPlay

* Sustain- Keeping Your Social Network Alive – Niraj Swami

* SynchroSpot – Shaun Terry

* Talkplay – Sung Suh Park

* Teradesk – José Augusto Athayde Ferrarini

* The Weather Channel for Android – The Weather Channel Interactive Inc.

* TuneWiki – TuneWiki Inc.

* Wikitude-the Mobile Travel Guide – Philipp Breuss

* Writing Pad – ShapeWriter Inc

在android上测试C/C++程序及库

2009年02月26日,星期四

1. 建立交叉编译环境
首先下载安装交叉编译工具GNU/ARM Linux gcc:
http://www.codesourcery.com/gnu_toolchains/arm/download.html
安装时 直接解压就行了,要设置好PATH环境变量。

2.编辑代码
简单的C代码:test.c
#include <stdio.h>
int main()
{
int i,j;
for(i=0;i<=10;i++)
{
for(j=0;j<=i;j++)
printf(”*”);
printf(”\n”);
}
return 0;
}

3.编译
# arm-none-linux-gnueabi-gcc test.c -o test -static
-static选项在这里是必须的,不然android平台就不运行此程序。
这也说明了此平台上的C/C++库是不能被C/C++程序动态连接的 。

4.上传(此前先启动android emulator)
$adb push test /data/data

5.执行
进入/data/data目录运行程序。
# cd /data/data
# ./test

补充:
a. C++程序一样的方法,只不过编译器换成:arm-none-linux-gnueabi-g++
b. 执行之前,需要chmod 777 test之

Android – 用程序改变屏幕垂直或水平(旋屏)

2009年02月26日,星期四

android.view.IWindowManager windowService = android.view.IWindowManager.Stub.asInterface(android.os.ServiceManager.getService(”window”));

try
{
if (windowService.getOrientation() == 0)    //Orientation vertical
{
windowService.setOrientation(1);  //Orientation horizontal
Log.i(”info”, “orientation 1 “+windowService.getOrientation());
}
else
{
Log.i(”info”, “orientation 0 “+windowService.getOrientation());
}
}
catch (DeadObjectException e)
{
e.printStackTrace();
}

转载: Android原生(Native)C开发之四 SDL移植笔记

2009年02月25日,星期三

SDL(Simple DirectMedia Layer)是一套开放源码的跨平台多媒体开发库,使用C语言写成。SDL提供了多种图像、声音、键盘等的实现,可配置性与移植性非常高,开发者可以开发出跨多个平台(Linux、Windows、Mac OS X、Symbian、Widnows Mobiel等嵌入式系统,当然也包括今天要移植的平台:Android)的应用,目前SDL多用于开发游戏、模拟器、媒体播放器等多媒体应用。

目前,SDL的稳定版本是 1.2.13,1.3还在开发中,可以通过SVN得到最新的代码,本次移植以 1.2.13为准,没有测试 1.3版的源码。请从 SDL 的官方网站下载 1.2.13 的源码,文件名为:SDL-1.2.13.zip,并解压,将得到一个 SDL-1.2.13 目录。

在Native编译SDL之前,要先装 Code Sourcery公司的arm交叉编译器,如果是用Windows操作系统,则一定要装 Cygwin(一个在windows上模拟linux的软件),因为在编译时要用到一些 linux命令,具体的步骤请参见:Port SDL/TinySDGL to android with native C,或自已在网上搜一些资料。

因为SDL是用纯C写的一套类库,所以移植性非常好,官方支持的系统有:Linux, Windows, Windows CE, BeOS, MacOS, Mac OS X, FreeBSD, NetBSD, OpenBSD, BSD/OS, Solaris, IRIX, and QNX,非官方支持的有:AmigaOS, Dreamcast, Atari, AIX, OSF/Tru64, RISC OS, SymbianOS, and OS/2,而且网上也有人将SDL移植到很多其他嵌入式系统,甚至有人将SDL移植到 Moto A1200,如此强大的可移植性,其架构真是值得好好学习。

现在切入正题,如何为Android量身定做一个 SDL,下面就从视频,音频,输入事件,定时器(video,audio,events[key,mouse],timer),多线程等几个方面来分析:

1.首先讲视频方面,Android是一个定制的Linux操作系统,Linux显示要么用 X11,要么用framebuffer技术,很显然Android并没有集成 X11(也许Android的软件界面是基于X11的?!),那只有唯一的选择:framebuffer!

打开$SDL/src/video目录,可以发现SDL支持多达30多种的视频显示技术,其中包括我们想要的fbcon及directfb,directfb我没有做测试,也许显示效果会比linux自带的fbcon好,有兴趣的朋友可以试一下,成功了别忘了告诉我;

2.再来谈音频,记得一个广告词:没有声音,再好的戏也出不来!可见音频对多媒体应用的重要性。

这次用的是OSS的driver,但用的是dsp及dma的实现,但在打开Android指定的音频文件 /dev/eac 时有误,所以音频这一块只是能编译通过,不能正常运行,正在考虑用ALSA (Advanced Linux Sound Architecture) 替代;

关于OSS大家可以参看IBM的文章: OSS–跨平台的音频接口简介,写得比较详细。

3.输入事件(键盘,鼠标)中的键盘事件不需要任何更改,就能正常处理,用的设备文件是 /dev/tty0,

但鼠标事件却不能正常处理,加上DEBUG_MOUSE发现用的是PS2的鼠标,但其实Android用的不是PS2的鼠标,用的应该是触摸屏(TouchScreen)鼠标,设备文件是 /dev/input/event0,详情请参见本人的blog: Android原生(Native)C开发之三:鼠标事件篇(捕鼠记),经过改动后,基本能实现鼠标的处理;

4.定时器用的是unix的实现;

5.多线程用的是pthread的实现,unix系统都是用pthread来实现多线程的,在 ln demo时别忘了加 -lpthread;

6.加载动态库用的是unix 的 dl库,同样,在ln demo时别忘了加 -ldl。

SDL提供了一个最小化的Makefile:Makefile.minimal,所有的实现都是 dummy,就是一个空的实现,编译能通过,但运行时什么都不能做,根据上面的分析,将 Makefile.minimal 的内容改成如下:

# Makefile to build the SDL library

INCLUDE = -I./include
CFLAGS  = -g -s -O2 $(INCLUDE)
CC  = arm-none-linux-gnueabi-gcc
AR  = arm-none-linux-gnueabi-ar
RANLIB = arm-none-linux-gnueabi-ranlib

CONFIG_H = include/SDL_config.h
TARGET  = libSDL.a
SOURCES = \
src/*.c \
src/audio/*.c \
src/cdrom/*.c \
src/cpuinfo/*.c \
src/events/*.c \
src/file/*.c \
src/joystick/*.c \
src/stdlib/*.c \
src/thread/*.c \
src/timer/*.c \
src/video/*.c \
src/audio/dsp/*.c \
src/audio/dma/*.c \
src/video/fbcon/*.c \
src/joystick/dummy/*.c \
src/cdrom/dummy/*.c \
src/thread/pthread/*.c \
src/timer/unix/*.c \
src/loadso/dlopen/*.c \

OBJECTS = $(shell echo $(SOURCES) | sed -e ’s,\.c,\.o,g’)

all: $(TARGET)

$(TARGET): $(CONFIG_H) $(OBJECTS)
$(AR) crv $@ $^
$(RANLIB) $@

$(CONFIG_H):
cp $(CONFIG_H).default $(CONFIG_H)

clean:
rm -f $(TARGET) $(OBJECTS)

最后将$SDL\include\SDL_config_minimal.h的内容改成如下:

#ifndef _SDL_config_minimal_h
#define _SDL_config_minimal_h

#include “SDL_platform.h”

#include <stdarg.h>

typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef signed short int16_t;
typedef unsigned short uint16_t;
typedef signed int int32_t;
typedef unsigned int uint32_t;
typedef unsigned int size_t;
//typedef unsigned long uintptr_t;

#define HAVE_LIBC 1

#ifdef  HAVE_LIBC

#define HAVE_ALLOCA_H  1
#define HAVE_SYS_TYPES_H 1
#define HAVE_STDIO_H  1
#define STDC_HEADERS  1
#define HAVE_STDLIB_H  1
#define HAVE_STDARG_H  1
#define HAVE_MALLOC_H  1
#define HAVE_MEMORY_H  1
//#define HAVE_STRING_H   1
//#define HAVE_STRINGS_H  1
#define HAVE_INTTYPES_H  1
#define HAVE_STDINT_H  1
#define HAVE_CTYPE_H  1
#define HAVE_MATH_H   1
//#define HAVE_ICONV_H   1
#define HAVE_SIGNAL_H  1
#define HAVE_ALTIVEC_H  1
#define HAVE_MALLOC   1
#define HAVE_CALLOC   1
#define HAVE_REALLOC  1
#define HAVE_FREE   1
#define HAVE_ALLOCA   1
#define HAVE_GETENV   1
#define HAVE_PUTENV   1
#define HAVE_UNSETENV  1
#define HAVE_QSORT   1
#define HAVE_ABS   1
//#define HAVE_BCOPY   1
//#define HAVE_MEMSET   1
//#define HAVE_MEMCPY   1
//#define HAVE_MEMMOVE   1
//#define HAVE_MEMCMP   1
//#define HAVE_STRLEN   1
//#define HAVE_STRLCPY   1
//#define HAVE_STRLCAT   1
//#define HAVE_STRDUP   1
#define HAVE__STRREV  1
#define HAVE__STRUPR  1
#define HAVE__STRLWR  1
//#define HAVE_INDEX   1
#define HAVE_RINDEX   1
//#define HAVE_STRCHR   1
#define HAVE_STRRCHR  1
#define HAVE_STRSTR   1
#define HAVE_ITOA   1
#define HAVE__LTOA   1
#define HAVE__UITOA   1
#define HAVE__ULTOA   1
#define HAVE_STRTOL   1
#define HAVE_STRTOUL  1
#define HAVE__I64TOA  1
#define HAVE__UI64TOA  1
#define HAVE_STRTOLL  1
#define HAVE_STRTOULL  1
#define HAVE_STRTOD   1
#define HAVE_ATOI   1
#define HAVE_ATOF   1
#define HAVE_STRCMP   1
#define HAVE_STRNCMP  1
#define HAVE__STRICMP  1
#define HAVE_STRCASECMP  1
#define HAVE__STRNICMP  1
#define HAVE_STRNCASECMP 1
#define HAVE_SSCANF   1
#define HAVE_SNPRINTF  1
#define HAVE_VSNPRINTF  1
//#define HAVE_ICONV
#define HAVE_SIGACTION  1
#define HAVE_SETJMP   1
#define HAVE_NANOSLEEP  1
//#define HAVE_CLOCK_GETTIME 1
#define HAVE_DLVSYM   1
#define HAVE_GETPAGESIZE 1
#define HAVE_MPROTECT  1
#else

#include <stdarg.h>
#endif
//#define HAVE_STDIO_H 1
//#define HAVE_STDINT_H 1

//#define SDL_INPUT_TSLIB 1 //touch screen input
#define SDL_AUDIO_DRIVER_OSS 1 // SDL_AUDIO_DRIVER_DUMMY
#define SDL_CDROM_DISABLED  1
#define SDL_JOYSTICK_DISABLED 1
#define SDL_LOADSO_DLOPEN 1
//SDL_LOADSO_DISABLED 1 //#undef
#define SDL_THREAD_PTHREAD 1 //SDL_THREADS_DISABLED

//SDL_TIMERS_DISABLED
#define SDL_TIMER_UNIX 1

// SDL_VIDEO_DRIVER_DUMMY
#define SDL_VIDEO_DRIVER_FBCON 1

#endif

注意黑体部分,其实就是打开一些宏定义,将SDL的实现一一打开。

改完了这些以后,还需要改一些代码,主要是video方面的,因为Android Linux的framebuffer设备文件与标准Linux不同,Linux的fb设备文件一般是 /dev/fb0,但Android的设备文件是 /dev/graphics/fb0,打开 $SDL/src/video/fbcon/SDL_fbvideo.c,将191、499行的 “/dev/fb0” 替换成 “/dev/graphics/fb0“,保存即可。

再修改 $SDL/src/thread/pthread/SDL_sysmutex.c,将第30行改成: #define FAKE_RECURSIVE_MUTEX 1,就是在后面加一个“1”子,这可能是编译器的一个bug,define默认应该就是“1”的。

现在可以开始编译libSDL.a了,在cygwin或Linux下,进入SDL目录,输入:

make -f Makefile.minimal

视情况面定,一般几分钟后能顺利编译 Android版的 SDL,编译成功后,就需要编译几个SDL的test demo来测试一下 SDL的性能。

进入 test目录,复制 Makefile.in 文件,并改名为 Makefile,将前面一点内容改为:

# Makefile to build the SDL tests

srcdir  = .
INCLUDE = -I../include
CC      = arm-none-linux-gnueabi-gcc
EXE     =
CFLAGS  = -g -s -O2 $(INCLUDE) -static
LIBS    = -L.. -lSDL -lpthread
MATHLIB = -lm

并将所有的 @MATHLIB@ 替换成 $(MATHLIB),保存后,先编译一个testsprite demo,在命令行输入:

make testsprite

编译成功后,启动Android模拟器,将编译出来的testsprite及icon.bmp上传至一个目录,如 /dev/sample,命令如下:

adb push testspirte /dev/sample/testsprite

adb push icon.bmp /dev/sample/icon.bmp

最后进入 android 的shell: adb shell,再进入 /dev/sample目录,执行testsprite demo即可:

#cd /dev/sample

#chmod 755 testsprite

#./testsprite -width 320 -height 480 -bpp 32

可以根据模拟器的设置调整 width 及 height,因为程序默认的是 640×480的,初始化时会失败,如果一切正常的话,模拟器就会出现很多黄色的笑脸!按任意键退出,在本人机器上能达到 60 FPS左右,效果图如下:

Android Native SDL截图

Android Native SDL截图

其他的 demo有兴趣的朋友可以自己编译测试。