yichao firstname, zeaster nickname, zhang lastname

Showing posts with label mobile. Show all posts
Showing posts with label mobile. Show all posts

tips on learning android

目前Android的源代码完全开放给了社区. 由于它的开放性, 易移植性, 它将不仅仅是一个手机操作系统, 近日社区中已经有人把Android移植到Asus Eee PC 1000H以及HP mini note 2133上. 其中蓝牙和无线也可以正常驱动.

可以预见的, Android将在更多的设备上生根发芽!

对于那些希望锻炼技术的同学们, Android的源代码是一个难得的学习机会!
整套Android源代码包括2部分, 一是platform部分, 另外一个是tools部分.
git://android.git.kernel.org/platform/manifest.git
git://android.git.kernel.org/tools/manifest.git

整个Android系统是由OHA各个厂商贡献的小系统组成的一个大系统.
Platform就是这个大系统, 目前它由16个核心系统, 61个外部系统, 21个内置的android应用程序或服务组成.每个小系统的功能简介可以参考http://source.android.com/projects

tools部分就是用来管理这些小系统的工具集, 可以方便小系统各自独立开发, 或者日后替换掉某个小系统, 又可以方便的把各个小系统集成到一起.
它包括了一个repo工具, 用来更方便的管理git代码库. 一个基于Rietveld的Gerrit代码review在线工具.

'''我很想知道这2个工具名字的来历?!
难度是为了纪念Gerrit Rietveld, 这位荷兰家具设计师?!
'''

啰唆了这么多, 才回到主题上, 学习android的tips,
一个tip就是善用这个Gerrit代码review在线工具. 通过它可以查看Android的code changes以及code review过程中的代码点评. 从中可以学习很多大师们的编程技巧. 所有open changes在这里
http://review.source.android.com/

比如change-6647, http://review.source.android.com/6647
是memcached以及MogileFS的作者Brad Fitzpatrick提交的. 他希望把系统是否在24小时模式这个boolean变量缓存下来,而不是每次都调用开销比较大的方法去判断.
这是代码的diff输出
http://review.source.android.com/6647/diff/1/za3656e3b0c4d095fd434b4c6a3dd70f07501f8f3
为了缓存这个boolean变量, Brad Fitzpatrick增加了2个static变量,
private static Context m24HourLastContext = null;
private static boolean mCached24HourMode;
但Code reviewer Romain Guy 不同意这次change, 原因也记录到了Gerrit上.
因为增加的static变量m24HourLastContext会造成内存泄露, 并且Romain给了一篇介绍类似内存泄露的blog.
http://www.curious-creature.org/2008/12/18/avoid-memory-leaks-on-android/
在这篇blog中, 可以看到更多的Android实现细节. 比如android.graphics.drawable.Drawable对象由于callback方法而对其所属android.view.View的引用, 可以更加清晰的理解为什么Android平台上会出现这种内存泄露.
另外, Romain也说明了
(a)Android内置的Home Screen应用程序是如何解决Drawable对象对View的引用问题的.
(b)以及常见的2种避免类似内存泄露的方法.

Romain Guy这个同学也是个UI高手, 他学生时代在SUN参与了JDK中Swing的开发, 毕业后去了Google.

还有一些change可以反映出一些细节问题, 比如, http://review.source.android.com/6601
在Reviewer的评语中看到, 他要求能重用StringBuilder的地方绝不新建一个StringBuilder对象. 可见其对代码控制之严, 之细.
精致的软件就需要这样精致的控制.

以上这些仅仅是我看过5,6个chagnes后总结的, 相信如果阅读更多, 可以从中学习到更多的tips.

PS1,
Steve Guo同学的blog中有数十篇其工作中对android底层代码的分析, 值得推荐
http://letsgoustc.spaces.live.com/

PS2,
上面那个Dalvik中Drawable对象内存泄露的问题, 实际上是由于Dalvik默认就给Drawable增加了callback功能而引起的内存泄露.
由此联想到Sun的JDK中java.io.ObjectOutputStream的一个设计问题.
详见http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4363937
其中也是由于ObjectOutputStream默认就认为其write的objects有依赖关系而将它们都保存到一个table中, 所以在传输大量对象时, 如果不间断的调用reset方法,就会导致OutOfMemoryError.
这些JVM设计者默认就给这些类设计了超级完备的功能, 而实际上大多数情况并用不上这些功能, 比如, drawable的callback功能, ObjectOutputStream输出对象的依赖关系.
如果默认使用最简单常用的功能, 而在特殊情况下, 由调用jdk的开发人员根据实际需要配置复杂的功能, 那么不仅可以提高运行效率, 也可以降低出现OutOfMemoryError的几率.
那么为什么他们还都要往复杂上设计?!

Google Android Custom Widget RichEdit - 1

I developed two custom widgets for ZInfo.

One is called RichText that can show links with custom display text, rather than the link itself.
The other is a more powerful dialog. It will be discussed later.

Android provides a widget called TextView with auto link feature.
That means you can show this:
This is http://zeaster.com/blog/

but, how to show this:
This is my blog.

How to show a link with custom display text?
RichText helps us in this way:

RichText rt = (RichText) findViewById(R.id.rich_text);
StringBuilder sb = new StringBuilder();
sb.append("This is a demo about how to use RichText.\n");
sb.append("a custom android widegt show links with custom display text, rather than the link itself.\n");
sb.append("This is <a href='http://zeaster.com/blog'>my blog</a>\n");
sb.append("This is <a href='http://infosword.com/zinfo'>ZInfo</a>\n");
rt.setRichText(sb.toString());


With this code, you got:
http://lh4.ggpht.com/yichao.zhang/SA9r6PZYGWI/AAAAAAAAAGo/mTDyAvex4MQ/device.png

SpannableString is used in RichText to show custom style.
For more details, check its source code.

ZInfo - Information sharing platfrom on Android

ZInfo is finally released for Android Developer Challenge after our many hard working days.
It's
an information sharing platform based on your contacts and location.
The video demo can be found at http://infosword.com/zinfo/video.html.

We gave a presentation about ZInfo on Beijing Open Party - Before the Rain.
It makes me the most excited that I meet a lot of open source followers.
They are very friendly, full of passion, enjoy coding! I learned a lot from them.

Special thanks to cleverpig!
He is so kind and want to sponsor us a server to host ZInfo.
And he understands us fully when we decided to put off releasing publicly recently.

It's so great to meet you guys in the community!

Inconvenienced when using Contact Content Provider on Android

These days, I've been developing an application that reads and writes entries from/into Android Contacts.
Everything goes fine until I need to insert email entry into Android Contact by Contact Content Provider API.

This is how to insert a people or phone entry into contact (people, phones table):
getContentProvider().insert(People.CONTENT_URI, values);
getContentProvider().insert(Phones.CONTENT_URI, values);

That's fine according to
http://code.google.com/android/devel/data/contentproviders.html

However when I insert an email entry into contact (in contact_methods table) using:
ContentURI uri = getContentProvider().insert(ContactMethods.CONTENT_URI, values);

It just returns null, nothing inserted, and nothing shows up on logcat.
Why? the link above says it should be fine!
To find the answer, I post a thread in Google Group:
http://groups.google.com/group/android-developers/browse_thread/thread/9ed4ff7fc1338b7c
However no response.
then I have been diving into build-in Contacts Application for 2 days to find how Google used their own api.
Luckily, I got the key.
see details on my another post:
http://zeaster.blogspot.com/2007/11/how-to-decompile-dex-file-on-android_28.html

You should insert an email like this:
ContentURI uri = People.CONTENT_URI.addId(user_id).addPath("contact_methods");
ContentValues values = ...
uri = getContentResolver().insert(uri, values);

why? I guess Google should provide one same way to insert content.

That's the main inconvenience.
I guess what's even worse is Google does not provide enough docs or log warnings for this tip.
Here are the others:

2.
when insert an email for a not-created-yet user id, It's still inserted successfully.
when the user id created in the future, the user would have the email address.
I guess Google should prevent this happen.
Actually many checks Google need to do.That's really a beta SDK.

3.
The content provider API is not Object-Oriented enough.
I feel it tedious to use.

4.
Maybe this is greedy.
I hope there is API to manipulate database that's similar to ActiveRecord for Rails.
Maybe I will implement one like that if time permits.

How to decompile .dex file on Android

Lucky to see the Google Android build-in Contacts is also developed by Android SDK.
This article will take it for example and discuss these How-To's:

1 How to find the Contacts App file on Google Android
2 How to find the Contacts App's classes.dex on Google Android
3 How to dump the Contacts App's classes
4 How to decompile the dumped file

{
Actually, I think the Contacts Content Provider that Google provides has some inconvenience to use.
And I will post another blog to list what's inconvenience and request some improvement from Google.
The blog link is here:
http://zeaster.blogspot.com/2007/11/inconvenienced-when-using-contact.html
}

1 How to find the Contacts App on Google Android
Using adb tool

$ adb shell
# cd /system/app
cd /system/app
# ls
ls
-rw-r--r-- root root 25519 2007-11-14 20:40 ContactsProvider.apk
-rw-r--r-- root root 7544 2007-11-14 20:40 GoogleAppsProvider.apk
-rw-r--r-- root root 16198 2007-11-14 20:40 ImProvider.apk
-rw-r--r-- root root 20308 2007-11-14 20:40 MediaProvider.apk
-rw-r--r-- root root 21272 2007-11-14 20:40 TelephonyProvider.apk
-rw-r--r-- root root 11809 2007-11-14 20:40 SettingsProvider.apk
-rw-r--r-- root root 418688 2007-11-14 20:41 Browser.apk
-rw-r--r-- root root 68077 2007-11-14 20:41 Contacts.apk
-rw-r--r-- root root 96287 2007-11-14 20:41 Development.apk
-rw-r--r-- root root 44790 2007-11-14 20:41 GoogleApps.apk
-rw-r--r-- root root 8637 2007-11-14 20:41 Fallback.apk
-rw-r--r-- root root 99431 2007-11-14 20:41 Home.apk
-rw-r--r-- root root 171614 2007-11-14 20:41 Maps.apk
-rw-r--r-- root root 424601 2007-11-14 20:41 Phone.apk
-rw-r--r-- root root 192119 2007-11-14 20:41 XmppService.apk
-rw-r--r-- root root 6614 2007-11-14 20:41 XmppSettings.apk
#

The Contacts.apk is the Contacts App and ContactsProvider.apk is the app that provides the Contact Content Provider for reading and writing "content://contacts/" resources.
we can see many system apps developed by Android Java SDK are put here.
However what is inside *.apk file. Actually it is a .zip file. It is easy to unzip and get the answer.
take Contacts.apk for example, it contains:

META-INF\
res\
AndroidManifest.xml
classes.dex
resources.arsc

The classes.dex file contains all compiled Java code.

2 How to find the Contacts App's classes.dex on Google Android
No need to unzip Contacts App's classes.dex file from Contacts.apk file.
The Dalvik VM has a cached file for us. Go head to find it.

$ adb shell
# cd /data/dalvik-cache
cd /data/dalvik-cache
# ls
ls
-rw-rw-rw- root root 73852 2007-11-27 05:16 system@app@Contacts.apk@classes.dex
-rw-rw-rw- app_0 app_0 64172 2007-11-27 05:17 system@app@ContactsProvider.apk@classes.dex
-rw-rw-rw- root root 15204 2007-11-27 05:17 system@framework@am.jar@classes.dex
-rw-rw-rw- app_3 app_3 3012 2007-11-27 07:33 system@app@Fallback.apk@classes.dex
-rw-rw-rw- root root 7252804 2007-11-27 05:16 system@framework@core.jar@classes.dex
....and many other cached files.

3 How to dump the Contacts App's classes
Unfortunately the classes.dex is not a .jar file. It's Dalvik executable format that is optimized for efficient.
so we can not pull the .class files as .jar format. However Google Android provides a tool named dexdump included in the emulator to dump .dex file. Here is how to use it.
# dexdump
dexdump: no file specified
dexdump: [-f] [-h] dexfile...

-d : disassemble code sections
-f : display summary information from file header
-h : display file header details
-C : decode (demangle) low-level symbol names
-S : compute sizes only

Now we dump the system@app@Contacts.apk@classes.dex and system@app@ContactsProvider.apk@classes.dex.

# dexdump -d -f -h -C system@app@Contacts.apk@classes.dex >> Contacts.apk.dump
# dexdump -d -f -h -C system@app@ContactsProvider.apk@classes.dex >> ContactsProvider.apk.dump

and then pull it out.
# exit
$ adb pull /data/dalvik-cache/Contacts.apk.dump ~/android
$ adb pull /data/dalvik-cache/ContactsProvider.apk.dump ~/android

Now we get the dumped files on ~/android folder.

4 How to decompile the dumped file
.dex file is optimized, so we can not decompile it as normal .class file.
Fortunately the dumped file is a bit readable for me.
As it is too big to analyze all the code, so I just take "how to create a contact" for example.
It's easy to get this info from the dumped file.
It has these classes:
...
Class name : 'com/google/android/contacts/AttachImage'
Class name : 'com/google/android/contacts/ContactEntryAdapter'
Class name : 'com/google/android/contacts/EditContactActivity'
Class name : 'com/google/android/contacts/EditContactActivity$EditEntry'
...

The EditContactActivity class has a private void create() method.
#3 : (in com/google/android/contacts/EditContactActivity)
name : 'create'
type : '()V'
access : 0x0002 (PRIVATE)

So all the code that create a contact is here from the dumped file, my comments inlined:
// private void create()
009b24: |[009b24] com/google/android/contacts/EditContactActivity.create:()V

// ContentValues v9 = new ContentValues();
009b28: 2109 3100 |0000: new-instance v9, android/content/ContentValues // class@0031
009b2c: 6f01 0f00 0900 |0002: invoke-direct {v9}, android/content/ContentValues.:()V // method@000f

// Entry v12 = this.getCurrentEntry();
009b32: f4fc a800 |0005: +iget-object-quick v12, v15, [obj+00a8]

// int v11 = v12.lines
009b36: f801 1500 0c00 |0007: +invoke-virtual-quick {v12}, [0015] // vtable #0015
009b3c: 0a0b |000a: move-result v11

// int v5 = 0
009b3e: 1205 |000b: const/4 v5, #int 0 // #0

// if v5 >= v11 goto 0046
009b40: 3ab5 3a00 |000c: if-ge v5, v11, 0046 // +003a

// EditEntry v8 = (EditEntry)this.getCurrentEntry();
009b44: f4fc a800 |000e: +iget-object-quick v12, v15, [obj+00a8]
009b48: f802 1a00 5c00 |0010: +invoke-virtual-quick {v12, v5}, [001a] // vtable #001a
009b4e: 0c0c |0013: move-result-object v12
009b50: 07c0 |0014: move-object v0, v12
009b52: 1e00 3d00 |0015: check-cast v0, com/google/android/contacts/EditContactActivity$EditEntry // class@003d
009b56: 0708 |0017: move-object v8, v0

// int v12 = v8.kind // The Instance field kind is defined in its superclass Entry. see details in dumped Entry and EditEntry sections.
009b58: f28c 1400 |0018: +iget-quick v12, v8, [obj+0014]

// int v13 = -3 // PHOTO_KIND = -3 defined in v8's superclass Entry
009b5c: 12dd |001a: const/4 v13, #int -3 // #fd

// if v12 != v13 goto 0020 // a photo entry, goto 0020
009b5e: 38dc 0500 |001b: if-ne v12, v13, 0020 // +0005

// int v5++;
009b62: d805 0501 |001d: add-int/lit8 v5, v5, #int 1 // #01

// goto 000c
009b66: 33ed |001f: goto 000c // -0013

// String v7 = v8.getData();
009b68: f801 0c00 0800 |0020: +invoke-virtual-quick {v8}, [000c] // vtable #000c
009b6e: 0c07 |0023: move-result-object v7

// boolean v12 = TextUtils.isEmpty(v7)
009b70: 7001 4200 0700 |0024: invoke-static {v7}, android/text/TextUtils.isEmpty:(Ljava/lang/CharSequence;)Z // method@0042
009b76: 0a0c |0027: move-result v12

if v12!=false/0 goto 0030
009b78: 3e0c 0800 |0028: if-nez v12, 0030 // +0008

// String v12 = v8.column // The Instance field column is defined in EditEntry. see details in dumped EditEntry section.
009b7c: f48c 2800 |002a: +iget-object-quick v12, v8, [obj+0028]

// v9.put(v12,v7) // v9 is a ContentValues, v12 is the column name, v7 is the value.
009b80: f803 0b00 c907 |002c: +invoke-virtual-quick {v9, v12, v7}, [000b] // vtable #000b

// goto 001d
009b86: 33ee |002f: goto 001d // -0012

// String v12 = v8.column // The Instance field column is defined in EditEntry. see details in dumped EditEntry section.
009b88: f48c 2800 |0030: +iget-object-quick v12, v8, [obj+0028]

// String v13 = "name";
009b8c: 180d 1c00 |0032: const-string v13, "name" // string@001c

// boolean v12 = v12.equals(v13);
009b90: ee02 0300 dc00 |0034: +execute-inline {v12, v13}, java/lang/String.equals:(Ljava/lang/Object;)Z // inline #0003
009b96: 0a0c |0037: move-result v12

// if v12 != false/0, goto 001d
009b98: 3d0c e5ff |0038: if-eqz v12, 001d // -001b

// String v12 = "EditContactActivity"
009b9c: 180c 0100 |003a: const-string v12, "EditContactActivity" // string@0001

// String v13 = "Name is required"
009ba0: 180d 0200 |003c: const-string v13, "Name is required" // string@0002

// Log.e(v12, v13); //log info using android/util/Log
009ba4: 7002 4700 dc00 |003e: invoke-static {v12, v13}, android/util/Log.e:(Ljava/lang/String;Ljava/lang/String;)I // method@0047
009baa: 0a0c |0041: move-result v12

// call this.XXX()
009bac: f801 8400 0f00 |0042: +invoke-virtual-quick {v15}, [0084] // vtable #0084

// return void
009bb2: 0e00 |0045: return-void

// ContentResolver v12 = this.getContentResolver();
009bb4: f4fc a400 |0046: +iget-object-quick v12, v15, [obj+00a4]

// CONTENT_URI v13 = People.CONTENT_URI;
009bb8: 610d 0300 |0048: sget-object v13, android/provider/Contacts$People.CONTENT_URI:Landroid/net/ContentURI; // field@0003

// CONTENT_URI v10 = v12.insert(v13, v9) // CONTENT_URI v10 = getContentResolver().insert(uri, values);
// insert people into contacts
009bbc: f803 1100 dc09 |004a: +invoke-virtual-quick {v12, v13, v9}, [0011] // vtable #0011
009bc2: 0c0a |004d: move-result-object v10

// List v12 = this.mContactEntries
009bc4: f4fc c000 |004e: +iget-object-quick v12, v15, [obj+00c0]
009bc8: 7001 8900 0c00 |0050: invoke-static {v12}, com/google/android/contacts/ContactEntryAdapter.countEntries:(Ljava/util/ArrayList;)I // method@0089
009bce: 0a06 |0053: move-result v6

// int v5 = v11 //contact Entry Count
009bd0: 01b5 |0054: move v5, v11

// if v5 >= v6, goto 009e
009bd2: 3a65 4900 |0055: if-ge v5, v6, 009e // +0049

// List v12 = this.mContactEntries
009bd6: f4fc c000 |0057: +iget-object-quick v12, v15, [obj+00c0]

// EditEntry v8 = (EditEntry) getEntry(v12, v5);
009bda: 7002 8b00 5c00 |0059: invoke-static {v12, v5}, com/google/android/contacts/ContactEntryAdapter.getEntry:(Ljava/util/ArrayList;I)Lcom/google/android/contacts/ContactEntryAdapter$Entry; // method@008b
009be0: 0c0c |005c: move-result-object v12
009be2: 07c0 |005d: move-object v0, v12
009be4: 1e00 3d00 |005e: check-cast v0, com/google/android/contacts/EditContactActivity$EditEntry // class@003d
009be8: 0708 |0060: move-object v8, v0

// int v12 = v8.kind // The Instance field kind is defined in its superclass Entry. see details in dumped Entry and EditEntry sections.
009bea: f28c 1400 |0061: +iget-quick v12, v8, [obj+0014]

// int v13 = -1 // CONTACT_KIND = -1 defined in v8's superclass Entry
009bee: 12fd |0063: const/4 v13, #int -1 // #ff

// if v12 == v13 goto 007e
009bf0: 37dc 1a00 |0064: if-eq v12, v13, 007e // +001a

// v9.clear() // ContentValues.clear()
009bf4: f801 1500 0900 |0066: +invoke-virtual-quick {v9}, [0015] // vtable #0015

// boolean v12 = v8.toValues(v9) // boolean EditEnty.toValues(ContentValues cv)
009bfa: f802 0d00 9800 |0069: +invoke-virtual-quick {v8, v9}, [000d] // vtable #000d
009c00: 0a0c |006c: move-result v12

//if v12 !=false/0, goto 007b
009c02: 3d0c 0e00 |006d: if-eqz v12, 007b // +000e

// ContentResolver v12 = this.getContentResolver();
009c06: f4fc a400 |006f: +iget-object-quick v12, v15, [obj+00a4]

// String v13 = v8.contentDirectory
009c0a: f48d 2c00 |0071: +iget-object-quick v13, v8, [obj+002c]

// v10.addPath(v13);
009c0e: f802 1800 da00 |0073: +invoke-virtual-quick {v10, v13}, [0018] // vtable #0018
009c14: 0c0d |0076: move-result-object v13

// CONTENT_URI v12 = v12.insert(v13, v9) // CONTENT_URI v12 = getContentResolver().insert(uri, values);
009c16: f803 1100 dc09 |0077: +invoke-virtual-quick {v12, v13, v9}, [0011] // vtable #0011
009c1c: 0c0c |007a: move-result-object v12

// int v5++;
009c1e: d805 0501 |007b: add-int/lit8 v5, v5, #int 1 // #01

// goto 0055
009c22: 33d8 |007d: goto 0055 // -0028

// String v7 = v8.getData();
009c24: f801 0c00 0800 |007e: +invoke-virtual-quick {v8}, [000c] // vtable #000c
009c2a: 0c07 |0081: move-result-object v7

// v9.lines
009c2c: f801 1500 0900 |0082: +invoke-virtual-quick {v9}, [0015] // vtable #0015

// boolean v12 = TextUtils.isEmpty(v7)
009c32: 7001 4200 0700 |0085: invoke-static {v7}, android/text/TextUtils.isEmpty:(Ljava/lang/CharSequence;)Z // method@0042
009c38: 0a0c |0088: move-result v12

if v12!=false/0 goto 007b
009c3a: 3e0c f2ff |0089: if-nez v12, 007b // -000e

// String v12 = v8.column // The Instance field column is defined in EditEntry. see details in dumped EditEntry section.
009c3e: f48c 2800 |008b: +iget-object-quick v12, v8, [obj+0028]

// v9.put(v12,v7) // v9 is a ContentValues, v12 is the column name, v7 is the value.
009c42: f803 0b00 c907 |008d: +invoke-virtual-quick {v9, v12, v7}, [000b] // vtable #000b

// ContentResolver v12 = this.getContentResolver();
009c48: f4fc a400 |0090: +iget-object-quick v12, v15, [obj+00a4]

// int v13 = 0
009c4c: 120d |0092: const/4 v13, #int 0 // #0

// int v14 = 0
009c4e: 120e |0093: const/4 v14, #int 0 // #0

009c50: 07c0 |0094: move-object v0, v12
009c52: 07a1 |0095: move-object v1, v10
009c54: 0792 |0096: move-object v2, v9
009c56: 07d3 |0097: move-object v3, v13
009c58: 07e4 |0098: move-object v4, v14

// int v12 = v12.update(v1,v2,v3,v4) // getContentResolver(),update(uri, values, null, null)
// refer to this method, public final int update(ContentURI uri, ContentValues values, String where, String[] selectionArgs)
009c5a: f905 1400 0000 |0099: +invoke-virtual-quick/range {v0, v1, v2, v3, v4}, [0014] // vtable #0014
009c60: 0a0c |009c: move-result v12

// goto 007b
009c62: 33de |009d: goto 007b // -0022

// int v12 = 4
009c64: 124c |009e: const/4 v12, #int 4 // #4

009c66: f5fc c800 |009f: +iput-quick v12, v15, [obj+00c8]
009c6a: f7fa 9000 |00a1: +iput-object-quick v10, v15, [obj+0090]

// int v12 = -1
009c6e: 12fc |00a3: const/4 v12, #int -1 // #ff
009c70: f801 0700 0a00 |00a4: +invoke-virtual-quick {v10}, [0007] // vtable #0007
009c76: 0c0d |00a7: move-result-object v13
009c78: f803 7f00 cf0d |00a8: +invoke-virtual-quick {v15, v12, v13}, [007f] // vtable #007f

// goto 0045
009c7e: 339a |00ab: goto 0045 // -0066
exceptions : (none)
positions : 30
0x0000 line=471
.....
0x00a3 line=515
locals : 10
//the 10 local variables defined here
0x0000 - 0x00ac reg=15 this Lcom/google/android/contacts/EditContactActivity;
0x0002 - 0x00ac reg=9 values Landroid/content/ContentValues;
0x000b - 0x00ac reg=11 contactEntryCount I
0x000c - 0x00ac reg=5 i I
0x0018 - 0x0045 reg=8 entry Lcom/google/android/contacts/EditContactActivity$EditEntry;
0x0024 - 0x0045 reg=7 data Ljava/lang/String;
0x004e - 0x00ac reg=10 contactUri Landroid/net/ContentURI;
0x0054 - 0x00ac reg=6 entryCount I
0x0061 - 0x009e reg=8 entry Lcom/google/android/contacts/EditContactActivity$EditEntry;
0x0082 - 0x009e reg=7 data Ljava/lang/String;

Mac OS X上成功使用dopod 696 wm5.0 连 cmwap上网

哈哈!
也是使用dopod 696/himalaya wm5.0 的“internet共享”方式上网
1 在Mac OS X 下,安装Missing Sync for Windows Mobile
2 去掉Missing Sync连接方式中的usb,以下和我上篇blog中的说明一样,详见:
http://zeaster.blogspot.com/2007/02/dopod-696-wm50-cmwap.html

蓝牙方式我没试,因为偶是在IBM thinkpad 上装的Mac OS X 10.4.7,没蓝牙驱动,不过道理是一样的,应该也能成功。

笔记本成功使用dopod 696 wm5.0 上的cmwap上网

在各个论坛上查了一圈,知道696升级到wm5.0后原来的USB modem是无法使用
新的方式是使用internet共享。
就是通过USB,蓝牙或红外等方式使得696和笔记本组成一个局域网,在局域网内笔记本可以共享696的 internet 连接
经测试使用USB和bluetooth方式,笔记本都可以连上696的cmwap或cmnet连接。
注明:
我使用的rom是haha2008 0207版 wm5.0 的rom,其他早些的版本需要自己配置696蓝牙网卡的信息,此版rom已设置好。
下载地址:http://www.hi-pda.com/forum/viewthread.php?tid=332225
gprsproxy的下载地址:
http://www.51nb.com/forum/viewthread.php?tid=280524
http://www.51nb.com/forum/viewthread.php?tid=299242

使用usb的方式
1 去掉activesync的usb连接
2 连接696和笔记本
3 此时笔记本上的连接中会新增一个Windows Mobile-based Device连接
4 在696上启动internet连接,pc连接选usb,网络连接选接入点为cmwap的连接(具体名是自己定义的),确定连接
5 这时Windows Mobile-based Device连接会重新启动一下,此时这个连接的网络信息是
        Connection-specific DNS Suffix  . :
        Description . . . . . . . . . . . : Windows Mobile-based Device #3
        Physical Address. . . . . . . . . : 80-00-60-0F-E8-00
        Dhcp Enabled. . . . . . . . . . . : Yes
        Autoconfiguration Enabled . . . . : Yes
        IP Address. . . . . . . . . . . . : 192.168.0.102              ========》共享局域网中笔记本的ip
        Subnet Mask . . . . . . . . . . . : 255.255.255.0
        Default Gateway . . . . . . . . . : 192.168.0.1             ========》共享局域网中696的ip
        DHCP Server . . . . . . . . . . . : 192.168.0.1
        DNS Servers . . . . . . . . . . . : 192.168.0.1
5 使用gprsproxy 1.3, 把ie或 firefox 中的代理设置成gprsproxy中配置的,即可上网了。

使用蓝牙的方式
1 笔记本上安装蓝牙驱动
2 开启696和笔记本的蓝牙,并且配对成功,这时笔记本上的连接中会新增一个Bluetooth PAN network Adapter
3 在696上启动internet连接,pc连接选蓝牙,网络连接选接入点为cmwap的连接(具体名是自己定义的),确定连接,696会显示“已完成设备配置,请在pc上连接蓝牙PAN”
4 在笔记本上连接696的蓝牙 PAN 服务即个人局域网服务Personal Area Network。(如果之前已经连接,断开重连一次即可)
5 这时Bluetooth PAN network Adapter会分配到和上面类似的网络配置信息
6 使用gprsproxy 1.3, 把ie或 firefox 中的代理设置成gprsproxy中配置的,即可上网了。

注:
1
cmwap下只要修改user-agent 并且设置10.0.0.172代理就可以上网,不用gprsproxy也能上网
firefox下修改user-agent 的插件:useragentswitcher.xpi
修改代理的插件:switchproxy_tool-1.4.1-fx+mz+tb.xpi

2
在cmwap下上某些网址,如groups.google.com仍会提示xml解析错误,这时可以使用opera9试一下。
在opera9上出现此错误时,旁边会有一个按钮提示让你“按html格式解析”,点击此按钮,就可正常显示html。

3
使用cmnet的情况就更简单了,直接省去上面的gprsproxy配置那一步,不用代理直接可以上网。

dopod 696/himalaya 上再次安装wm5.0手记

第一次安装时没留下点记录,结果这次重新刷rom时走了很多弯路。
1 首先几个概念的介绍:

696的内存 = 128M ram + 64 rom
windows mobile 2003se 下的使用情况:
128M ram = 存储空间 + 内存,并且存储空间和内存所占比例可以动态调整
64M rom = 32M 操作系统 + 16M ExtRom + 16M Storage,ExtRom是隐藏的,平时看不到

windows mobile 2005 下的使用情况:
128M ram = 内存 + RAMdisk + 缓冲池PagingPool,其中这三者的比例分配可以通过修改os rom文件来调整
64M rom = 32M 操作系统 + 32M 存储空间

我使用的是RAMdisk 48M + PagingPool 24M

2 通过这次找rom,发现国内民间的开发活动还不规范
首先对于同一个软件项目,没有一个同一的发布位置,总是分布在各个论坛的各个帖子里
其次对于软件的版本管理很混乱,下载的文件中没有详细的Changs更新说明,实际上这些说明也是分布在各个论坛的各个帖子里
总之信息都是分布于各个论坛的各个帖子,查找起来很困难
国外的类似项目如 xda-developers,osxproject 除了论坛,都会有一个专门的wiki来整理信息,使得信息有一个唯一的入口。
这样利于使用者,也利于其他contributor来奉献
造成这种局面,估计有2个原因:
1 国内的企业中软件开发本来就不规范,所以国内开发者普遍不知道如何规范的开发
2 国内资源太紧张,有些开发者不是不想放到一个同一的地方,而是找不到这样的服务器,没有钱买或者没有人捐钱来做这件事

3 基于上面原因,记录几个好的下载资源
haha2008的696 rom在Hi!PDA论坛中
修改rom,定制rom的论坛在pdafans中
gprs cmwap上网方面的讨论在51nb论坛的网络版

4 鄙视M$一下
Vista + Windows Mobile Device Center + wm5.0 + outlook 2007 + USB cable
这是噩梦般的组合,我是无论如何不能使用usb数据线来同步outlook和wm5.0,试了一上午都没成功,下午在一个坛子上看说M$要发布新的WMDC,呵呵,不过我没兴趣再去折腾了。
补充一下,用红外线是可以同步成功的,不过那速度真是相当的慢~~~