- 浏览: 280577 次
- 性别:
- 来自: 荷兰
文章分类
最新评论
-
ice.k:
才发现,谢谢。
使用CXF框架提供Rest接口的一些设置 -
kucoll:
@Produces 是控制响应的content-type,如果 ...
使用CXF框架提供Rest接口的一些设置 -
SE_XiaoFeng:
写的好.讲出了原因,和解决办法,这才是锦囊妙计.
Android 中的ANR 问题,响应灵敏性 -
zhujinyuan:
怎么没有代码的额。
10个经典的Android开源项目 -
liuxuejin:
我回去试试好
ubuntu安装Mac OS X主题
http://www.ophonesdn.com/article/show/170
使用OPhone平台Service机制时,如果客户端所用的aidl文件和已安装的Service所使用的
aidl文件不一致时会导致接口调用的错误,甚至会导致程序错误退出。比如Service升级时,会在aidl文件里增加或修改接口,如果客户端不更新所使用的aidl文件,这就会出现上述不一致的情况。本文主要分析这个问题的原因和解决方案。
Service
下面的TestService类里的stub实现了3个方法,test1, test2, test3, 分别返回一个整数。这个service是在com.aidl.service这个包里的。
view plaincopy to clipboardprint?
package com.aidl.service;
public class TestService extends Service
{
public IBinder onBind(Intent intent) {
return binder;
}
private final ITestService.Stub binder = new ITestService.Stub(){
public int test1(){
return 1;
}
public int test2(){
return 2;
}
public int test3(){
return 3;
}
};
}
定义aidl: ITestService.aidl
package com.aidl.service;
interface ITestService{
int test1 ();
int test2 ();
int test3 ();
}
manifest定义
<application android:label="@string/app_name">
<service android:name="com.aidl.service.TestService">
<intent-filter>
<action android:name="com.aidl.intent.TEST_SERVICE" />
</intent-filter>
</service>
</application>
这样TestService作为一个apk,就是提供了一个service。而别的应用,只需要拿到ITestService.aidl文件就可以用这个service了。
Client的Activity
有时候,你实现的service给不同的Activity用,而且并不都是和service在一个包里的,甚至不是一个apk里的,这时要使用service的接口就需要把aidl文件复制到自己的src目录下。
view plaincopy to clipboardprint?
package com.aidl.client;
import com.aidl.service.ITestService;
public class TestActivity extends Activity
{
ITestService mService;
Button testButton1, testButton2, testButton3;
private Intent intent = new Intent("com.aidl.intent.TEST_SERVICE");
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = ITestService.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startService(intent);
}
public void onResume(){
super.onResume();
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
testButton1 = (Button)findViewById(R.id.test1);
testButton1.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v){
try {
showToast(mService.test1());
}catch(RemoteException e){
}
}
});
...
}
public void onDestroy(){
super.onDestroy();
unbindService(mConnection);
}
...
}
上面的TestActivity在com.aidl.test包里,使用的是TestService的接口,这时需要在这个apk的代码目录里面有一份ITestService.aidl的拷贝。这个TestActivity有3个按钮,分别是test1, test2, test3。预期的情况是,点testX按钮时,调用TestService的testX接口,然后显示一个Toast,内容是TextX方法被调用了。如下图所示
问题出现
这时,如果TestService的aidl有改变,比如增加或减少接口,别的使用旧的aidl的应用就会有问题。哪怕是aidl里面的接口顺序变化也会带来问题。(注:一般来讲service的接口一旦发布,是不好轻易改动的。但是在团队协作开发时,这个情况就会出现。)
下面是TestService的新aidl:
view plaincopy to clipboardprint?
package com.aidl.service;
interface ITestService{
int test2 ();
int test1 ();
int test3 ();
}
而TestActivity还使用旧的aidl,这时还点test1按钮:
预期应该调用service的test1接口,但是却调了test2接口...
客户端与service的通讯
为什么会出现上述的问题呢?客户端与service的通讯是通过binder进行的,在build的时候,客户端与service两边都会根据aidl文件生成具体的ITestService类。
客户端的ITestService
TestActivity是通过调用ITestService.Stub.asInterface(service)来得到Service在本进程的代理。下面是客户端生成的ITestService的asInterface函数,返回一个ITestService.Stub.Proxy的对象。
view plaincopy to clipboardprint?
public static com.aidl.service.ITestService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.aidl.service.ITestService))) {
return ((com.aidl.service.ITestService)iin);
}
return new com.aidl.service.ITestService.Stub.Proxy(obj);
}
Proxy里面实现了test1等方法,而且定义了对应的TRANSACTION_test1等TRANSACTION code。当TestActivity调mService的test1时,就调用了Proxy的test1方法,而test1是调用transact方法进行进程间通讯,把TRANSACTION code通过binder发送到service进程。
view plaincopy to clipboardprint?
private static class Proxy implements com.aidl.service.ITestService{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote){
mRemote = remote;
}
public android.os.IBinder asBinder(){
return mRemote;
}
public java.lang.String getInterfaceDescriptor(){
return DESCRIPTOR;
}
public int test1() throws android.os.RemoteException{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_test1, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
...
//接口的TRANSACTION code
static final int TRANSACTION_test1 = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_test2 = (IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_test3 = (IBinder.FIRST_CALL_TRANSACTION + 2);
}
service的ITestService
service生成的ITestService的TRANSACTION code如下
view plaincopy to clipboardprint?
static final int TRANSACTION_test1 = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_test2 = (IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_test3 = (IBinder.FIRST_CALL_TRANSACTION + 2);
这时,service进程的binder对象会调用onTransact函数,而这个函数是在service端生成的ITestService类里。在这里面,根据收到的TRANSACTION code是TRANSACTION_test1就会调TestService里面ITestService.Stub对象binder里面实现的test1函数然后返回。再通过ipc机制,把返回值发送给客户端进程。
view plaincopy to clipboardprint?
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_test1:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.test1();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_test2:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.test2();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_test3:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.test3();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
问题所在
如果客户端和service的aidl文件是不一致的,就会出现问题了。
当TestService使用新的aidl时
view plaincopy to clipboardprint?
package com.aidl.service;
interface ITestService{
int test2 ();
int test1 ();
int test3 ();
}
生成的ITestService里面定义的TRANSACTION code如下:
view plaincopy to clipboardprint?
static final int TRANSACTION_test2 = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_test1 = (IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_test3 = (IBinder.FIRST_CALL_TRANSACTION + 2);
客户端TestActivity还使用旧的aidl,生成的ITestService里面定义的TRANSACTION code如下:
view plaincopy to clipboardprint?
static final int TRANSACTION_test1 = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_test2 = (IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_test3 = (IBinder.FIRST_CALL_TRANSACTION + 2);
从上面的两组TRANSACTION code可以看出,TRANSACTION code是根据aidl里接口声明的顺序生成的。IBinder.FIRST_CALL_TRANSACTION的值是1,也就是说TRANSACTION_test1的值在客户端里是1,而在service端是2! 而service端onTransact函数里的switch,当收到的code是1的时候,认为是应该调用TRANSACTION_test2对应的test2方法了。所以就出现上面的例子中,诡异的错乱现象了。
所以当aidl里面函数的声明顺序改变,或者新加,删除函数,都会造成TRANSACTION code的值会不同。这样使用旧aidl文件的应用就可能出现问题!
解决方案
当service升级时,为了避免出现上面的问题,应该保证aidl的变化不影响到旧有接口的TRANSACTION code。所以新的aidl的编写有以下几个注意点。
新加函数接口应该在旧有函数的后面。
尽量避免删除旧有函数,如果真的要删的话,可以保留函数名字作为占位,返回一个错误码之类的来解决。
不能改变原来的接口声明顺序。
当然如果改变原来函数接口,导致函数签名都变了的话,肯定会出错了,不过不是上面的错乱了。如果你非要这样改的话,会被别的工程师骂精神错乱的!
如果是多人协作开发,使用同一个版本库的时候,所有使用service的应用程序,不是把aidl代码cp到自己的目录里,而是建一个文件链接link到service目录里面的aidl。这样在service aidl文件有变化的时候,客户端不需要手动更新aidl文件。
比如上面例子中,TestActivity的client/src/com/aidl/service/目录里面ITestService.aidl就是service/src/com/aidl/service/ITestService.aidl的link。
ln -s service/src/com/aidl/service/ITestService.aidl client/src/com/aidl/service/ITestService.aidl
建议
其实这个问题我觉得属于service机制设计上的一个缺陷。如果客户端和service都是以函数签名而不是code来标志aidl里的接口,在onTransact()里使用函数签名进行判断具体调用哪个接口的话,就能根本上解决这个问题。而字符串的比较和int的比较的开销即使有性能差别,也是可以接受的。这样设计的话对于开发人员来说使用起来和原来一样方便,而且不会出现上述问题。
作者简介
Bear,豆瓣网开发工程师,曾在新浪网,雅虎中国工作,有3年的互联网开发经验,热衷开源项目,通过豆瓣电台手机客户端项目接触Android/OPhone平台,对手机上的应用开发很感兴趣,希望和大家多做交流。beartung@gmail.com
(声明:本网的新闻及文章版权均属OPhone SDN网站所有,如需转载请与我们编辑团队联系。任何媒体、网站或个人未经本网书面协议授权,不得进行任何形式的转载。已经取得本网协议授权的媒体、网站,在转载使用时请注明稿件来源。)
使用OPhone平台Service机制时,如果客户端所用的aidl文件和已安装的Service所使用的
aidl文件不一致时会导致接口调用的错误,甚至会导致程序错误退出。比如Service升级时,会在aidl文件里增加或修改接口,如果客户端不更新所使用的aidl文件,这就会出现上述不一致的情况。本文主要分析这个问题的原因和解决方案。
Service
下面的TestService类里的stub实现了3个方法,test1, test2, test3, 分别返回一个整数。这个service是在com.aidl.service这个包里的。
view plaincopy to clipboardprint?
package com.aidl.service;
public class TestService extends Service
{
public IBinder onBind(Intent intent) {
return binder;
}
private final ITestService.Stub binder = new ITestService.Stub(){
public int test1(){
return 1;
}
public int test2(){
return 2;
}
public int test3(){
return 3;
}
};
}
定义aidl: ITestService.aidl
package com.aidl.service;
interface ITestService{
int test1 ();
int test2 ();
int test3 ();
}
manifest定义
<application android:label="@string/app_name">
<service android:name="com.aidl.service.TestService">
<intent-filter>
<action android:name="com.aidl.intent.TEST_SERVICE" />
</intent-filter>
</service>
</application>
这样TestService作为一个apk,就是提供了一个service。而别的应用,只需要拿到ITestService.aidl文件就可以用这个service了。
Client的Activity
有时候,你实现的service给不同的Activity用,而且并不都是和service在一个包里的,甚至不是一个apk里的,这时要使用service的接口就需要把aidl文件复制到自己的src目录下。
view plaincopy to clipboardprint?
package com.aidl.client;
import com.aidl.service.ITestService;
public class TestActivity extends Activity
{
ITestService mService;
Button testButton1, testButton2, testButton3;
private Intent intent = new Intent("com.aidl.intent.TEST_SERVICE");
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = ITestService.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startService(intent);
}
public void onResume(){
super.onResume();
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
testButton1 = (Button)findViewById(R.id.test1);
testButton1.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v){
try {
showToast(mService.test1());
}catch(RemoteException e){
}
}
});
...
}
public void onDestroy(){
super.onDestroy();
unbindService(mConnection);
}
...
}
上面的TestActivity在com.aidl.test包里,使用的是TestService的接口,这时需要在这个apk的代码目录里面有一份ITestService.aidl的拷贝。这个TestActivity有3个按钮,分别是test1, test2, test3。预期的情况是,点testX按钮时,调用TestService的testX接口,然后显示一个Toast,内容是TextX方法被调用了。如下图所示
问题出现
这时,如果TestService的aidl有改变,比如增加或减少接口,别的使用旧的aidl的应用就会有问题。哪怕是aidl里面的接口顺序变化也会带来问题。(注:一般来讲service的接口一旦发布,是不好轻易改动的。但是在团队协作开发时,这个情况就会出现。)
下面是TestService的新aidl:
view plaincopy to clipboardprint?
package com.aidl.service;
interface ITestService{
int test2 ();
int test1 ();
int test3 ();
}
而TestActivity还使用旧的aidl,这时还点test1按钮:
预期应该调用service的test1接口,但是却调了test2接口...
客户端与service的通讯
为什么会出现上述的问题呢?客户端与service的通讯是通过binder进行的,在build的时候,客户端与service两边都会根据aidl文件生成具体的ITestService类。
客户端的ITestService
TestActivity是通过调用ITestService.Stub.asInterface(service)来得到Service在本进程的代理。下面是客户端生成的ITestService的asInterface函数,返回一个ITestService.Stub.Proxy的对象。
view plaincopy to clipboardprint?
public static com.aidl.service.ITestService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.aidl.service.ITestService))) {
return ((com.aidl.service.ITestService)iin);
}
return new com.aidl.service.ITestService.Stub.Proxy(obj);
}
Proxy里面实现了test1等方法,而且定义了对应的TRANSACTION_test1等TRANSACTION code。当TestActivity调mService的test1时,就调用了Proxy的test1方法,而test1是调用transact方法进行进程间通讯,把TRANSACTION code通过binder发送到service进程。
view plaincopy to clipboardprint?
private static class Proxy implements com.aidl.service.ITestService{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote){
mRemote = remote;
}
public android.os.IBinder asBinder(){
return mRemote;
}
public java.lang.String getInterfaceDescriptor(){
return DESCRIPTOR;
}
public int test1() throws android.os.RemoteException{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_test1, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
...
//接口的TRANSACTION code
static final int TRANSACTION_test1 = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_test2 = (IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_test3 = (IBinder.FIRST_CALL_TRANSACTION + 2);
}
service的ITestService
service生成的ITestService的TRANSACTION code如下
view plaincopy to clipboardprint?
static final int TRANSACTION_test1 = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_test2 = (IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_test3 = (IBinder.FIRST_CALL_TRANSACTION + 2);
这时,service进程的binder对象会调用onTransact函数,而这个函数是在service端生成的ITestService类里。在这里面,根据收到的TRANSACTION code是TRANSACTION_test1就会调TestService里面ITestService.Stub对象binder里面实现的test1函数然后返回。再通过ipc机制,把返回值发送给客户端进程。
view plaincopy to clipboardprint?
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_test1:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.test1();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_test2:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.test2();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_test3:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.test3();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
问题所在
如果客户端和service的aidl文件是不一致的,就会出现问题了。
当TestService使用新的aidl时
view plaincopy to clipboardprint?
package com.aidl.service;
interface ITestService{
int test2 ();
int test1 ();
int test3 ();
}
生成的ITestService里面定义的TRANSACTION code如下:
view plaincopy to clipboardprint?
static final int TRANSACTION_test2 = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_test1 = (IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_test3 = (IBinder.FIRST_CALL_TRANSACTION + 2);
客户端TestActivity还使用旧的aidl,生成的ITestService里面定义的TRANSACTION code如下:
view plaincopy to clipboardprint?
static final int TRANSACTION_test1 = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_test2 = (IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_test3 = (IBinder.FIRST_CALL_TRANSACTION + 2);
从上面的两组TRANSACTION code可以看出,TRANSACTION code是根据aidl里接口声明的顺序生成的。IBinder.FIRST_CALL_TRANSACTION的值是1,也就是说TRANSACTION_test1的值在客户端里是1,而在service端是2! 而service端onTransact函数里的switch,当收到的code是1的时候,认为是应该调用TRANSACTION_test2对应的test2方法了。所以就出现上面的例子中,诡异的错乱现象了。
所以当aidl里面函数的声明顺序改变,或者新加,删除函数,都会造成TRANSACTION code的值会不同。这样使用旧aidl文件的应用就可能出现问题!
解决方案
当service升级时,为了避免出现上面的问题,应该保证aidl的变化不影响到旧有接口的TRANSACTION code。所以新的aidl的编写有以下几个注意点。
新加函数接口应该在旧有函数的后面。
尽量避免删除旧有函数,如果真的要删的话,可以保留函数名字作为占位,返回一个错误码之类的来解决。
不能改变原来的接口声明顺序。
当然如果改变原来函数接口,导致函数签名都变了的话,肯定会出错了,不过不是上面的错乱了。如果你非要这样改的话,会被别的工程师骂精神错乱的!
如果是多人协作开发,使用同一个版本库的时候,所有使用service的应用程序,不是把aidl代码cp到自己的目录里,而是建一个文件链接link到service目录里面的aidl。这样在service aidl文件有变化的时候,客户端不需要手动更新aidl文件。
比如上面例子中,TestActivity的client/src/com/aidl/service/目录里面ITestService.aidl就是service/src/com/aidl/service/ITestService.aidl的link。
ln -s service/src/com/aidl/service/ITestService.aidl client/src/com/aidl/service/ITestService.aidl
建议
其实这个问题我觉得属于service机制设计上的一个缺陷。如果客户端和service都是以函数签名而不是code来标志aidl里的接口,在onTransact()里使用函数签名进行判断具体调用哪个接口的话,就能根本上解决这个问题。而字符串的比较和int的比较的开销即使有性能差别,也是可以接受的。这样设计的话对于开发人员来说使用起来和原来一样方便,而且不会出现上述问题。
作者简介
Bear,豆瓣网开发工程师,曾在新浪网,雅虎中国工作,有3年的互联网开发经验,热衷开源项目,通过豆瓣电台手机客户端项目接触Android/OPhone平台,对手机上的应用开发很感兴趣,希望和大家多做交流。beartung@gmail.com
(声明:本网的新闻及文章版权均属OPhone SDN网站所有,如需转载请与我们编辑团队联系。任何媒体、网站或个人未经本网书面协议授权,不得进行任何形式的转载。已经取得本网协议授权的媒体、网站,在转载使用时请注明稿件来源。)
发表评论
-
DLNA」的介紹與應用
2012-07-18 11:19 2108還記得先前我們曾經介紹過的《多功能搖控器的應用》嗎?它是 ... -
10个经典的Android开源项目
2012-03-29 11:20 1429http://www.eoeandroid.com ... -
ubuntu下设置Android手机驱动
2012-02-20 12:12 4537原文:http://blog.csdn.net/flow ... -
通过网络使用ADB ( Connect to android with ADB over TCP )
2011-11-08 12:59 24357来自:http://lesca.me/blog/2011 ... -
Ubuntu 11.04 64位 编译 Android 2.3 源码
2011-09-17 17:41 4441首先,我建立了JNI的编译开发环境。 用VM安装Ubun ... -
浅谈Android系统的图标设计规范
2011-07-15 10:11 1918目前移动平台的竞争日益激烈,友好的用户界面可以帮助提高用户 ... -
DownloadProvider
2011-06-15 20:17 1019DownloadProvider -
android下载编译以及文件系统提取总结
2011-06-14 20:44 1585原文地址:http://bbs.android ... -
应用程序签名
2011-05-25 16:48 991这篇文章将阐述在应用 ... -
Android 利用隐藏API实现屏幕亮度调节
2011-05-14 21:02 3559Android 实现屏幕亮度调节 脚盆原创,转载请注明出处。 ... -
获取Android设备的唯一识别码|设备号|序号|UUID
2011-05-05 10:25 4085如何获取一个能唯一标识每台Android设备的序号? 这个问 ... -
Android调用WebService
2011-05-04 17:12 3350下面例子改自网上例子:http://express.ruank ... -
Android 采用pull生成XML数据
2011-05-03 15:26 1430/* 有些时候,我们需要生成一个XML文件,生成XML文件的 ... -
Android 应用程序之间数据共享—ContentResolver
2011-04-27 22:29 1086Android是如何实现应用程 ... -
Android平台上四种保存数据的方法
2011-04-27 21:50 876对于我们所熟悉的大部分软件都有一个比较典型的特点,应用现有的数 ... -
Android中的网络时间同步
2011-04-27 14:20 2140http://blog.csdn.net/absurd/arc ... -
Android IntentService 深入分析
2011-04-26 22:27 1443Android IntentService 什么是Intent ... -
关于Activity的onSaveInstanceState调用时机的说明
2011-04-26 22:01 3460Activity的生命周期里并没有提到onSaveInstan ... -
Android中的长度单位详解(dp、sp、px、in、pt、mm)
2011-04-25 17:10 1256看到有很多网友不太理解dp、sp和px的区别:现在这里介绍一下 ... -
获取CPU序列号
2011-04-13 17:07 4601/** * 获取CPU序列号 * * @return ...
相关推荐
OPhone UI开发者指南 OPhone联网应用开发中的线程管理与界面更新 OPhone平台开发环境常见问题 OPhone游戏编程 流媒体程序开发之:H264解码器移植到OPhone 如何在OPhone平台编写网络应用
7.1.3 OPhone平台支持的媒体格式 276 7.1.4 选择合适的媒体文件 277 7.2 音频和视频播放 277 7.2.1 三种不同的数据源 278 7.2.2 MediaPlayer的状态 281 7.2.3 音乐播放器实例 284 7.2.4 播放视频 296 7.3 ...
播思公司基于Android的OMS平台开发巡讲,OPhone平台概述。
本文主要介绍OPhone平台下多媒体应用程序开发涉及到的基本知识,常用概念,相关的类及一些参考代码。使您阅读完后,对OPhone平台的多媒体应用程序开发有一个大致了解,能够迅速上手开发多媒体应用。
OPhone平台2D游戏引擎实现是ophone官网上的博客!这里写的很多,为了方便看,就按照顺序整理了下!
Ophone平台蓝牙编程之蓝牙聊天分析 BluetoothChat详细解释
第1章 OPhone平台概述 1 1.1 OPhone的架构 1 1.1.1 Linux内核 2 1.1.2 本地库 2 1.1.3 OPhone运行环境 3 1.1.4 Widget运行环境 3 1.1.5 应用程序框架 4 1.1.6 应用程序 5 1.2 开发OPhone应用程序 5 1.2.1 开发语言 5...
Ophone平台开发入门指南手册,适合初学者
如何在电脑上安装ophone平台! 详细说明。。。。 Eclipse ;java
Box2d同时也提供了各种语言环境的实现,由于Ophone平台使用java作为变成语言,所以我们将选择使用Box2d的java版JBox2d,这也将产生一个问题,JBox2D是用processing库来处理图像显示,所以Ophone平台上则不适用,在...
OPhone平台架构和主要开发组件.pdf
OPhone平台架构和主要开发组件借鉴.pdf
中国移动OPhone平台网络公关传播方案.ppt
安装OPhone SDK详细过程 安装OPhone SDK详细过程
本书系统地介绍了OPhone平台的体系结构、应用程序开发... 本书适合有一定Java编程基础,希望从Symbian、Java ME或者Windows Mobile等平台过渡到OPhone及Android平台的软件开发人员阅读,也可以作为高校师生的参考教材。
OPhone平台与MobileMarket发布公关传播案.pptx
中国移动 Android OPhone SDK (OPhone-sdk_windows-1.5.part1) 下载全部后(共6个)解压将其后缀改为.jar
Ophone平台蓝牙编程之蓝牙聊天分析 BluetoothChat详细解释
OPhone SDK for Windows
创业计划书-中国移动OPhone平台网络公关传播方案