存档

‘phone’ 分类的存档

google Android activity类 (2)

2007年11月21日 roboter 没有评论

启动activity,获得结果

startActivity(Intent)接口用于启动一个新的activity,新的activity将被放置到activity栈顶。使用一个单一的参数:描述activity的执行动作的Intent

有时候我们希望在activity结束的时候获得activity的反馈结果,例如:在一个activity中,让用户在一个联系人列表中选择某一个人,在该页面结束的时候,能够返回选中的人员信息。要做到这一点,需要调用startSubActivity(Intent, int)接口进行启动,第二个参数为调用者的标识。结果将在onActivityResult(int, int, String, Bundle)中返回。

当一个activity退出时,可以调用setResult(int)返回数据给上一级的activity。它必须支持结果编码,可以是标准的RESULT_CANCELED, RESULT_OK,或者从RESULT_FIRST_USER开始的自定义编码。此外,还可以返回一个字符串(通常是URL的某一项数据),也可以返回一个包含任意内容的包。所有的返回信息都会反馈到上一级的Activity.onActivityResult()接口,并带有上一级activity提供的原始标识号。

如果一个子activity失败了(如崩溃了),父activity将会接收到一个RESULT_CANCELED的结果编码。

public class MyActivity extends Activity
{
...

static final int PICK_CONTACT_REQUEST = 0;

protected boolean onKeyDown(int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// When the user center presses, let them pick a contact.
startSubActivity(
new Intent(Intent.PICK_ACTION,
new ContentURI(”content://contacts”)),
PICK_CONTACT_REQUEST);
return true;
}
return false;
}

protected void onActivityResult(int requestCode, int resultCode,
String data, Bundle extras)
{
if (requestCode == PICK_CONTACT_REQUEST) {
if (resultCode == RESULT_OK) {
// A contact was picked. Here we will just display it
// to the user.
startActivity(new Intent(Intent.VIEW_ACTION, data));
}
}
}
}

保存持久状态

一般来说,activity有两种持久状态需要处理:共享的文档类数据(典型的是使用了content provider存储在SQLite中),内在的状态(如用户的嗜好)。

针对内容提供的数据,google建议采用“编辑到位”的用户模型,也就是说,用户的编辑动作是立即有效的,无须进行额外的确认步骤,支持这个模式只需要简单遵守下面的两个规则:

  1. 当创建一个新的文档时,相关的数据库条目和文件都是立即创建的,例如:用户选择写一个新的电子邮件,一旦开始写,则新的电子邮件条目创建出来了。所以如果进入其他的activity,则这封电子邮件将会出现在草稿箱中。
  2. 当一个activity的onPause()接口被调用时,它应该提交用户的修改到内容提供者或者文件中。这样确保其他将要运行的activity能够看到这些变化。在整个activity的生命周期中,有很多时候都需要频繁提交数据,例如:在启动一个新的activity之前,在结束自己的activity之前,在输入字段进行切换的时候等等。

这个模型是为了防止用户在activity之间浏览时的数据丢失,允许系统在activity停止后的任何时间里安全地杀掉activity(因为系统资源要用在其他地方)。注意:这样意味着在activity里按了“返回”键并不等于“取消”,它意味着activity的当前数据被保存下来,离开了activity。在一个activity中要取消编辑动作必须有另外一套机制来实现,比如一个清晰的“恢复”或者“撤销”动作。

可以在content package里面查看更多的内容提供者信息,有一些重要的方面,在activity调用和activity之间的数据传递的不同。

activity也提供了一些API用于管理内在的状态,可以用在如浏览器中用户设置的缺省主页等。

activity的持久状态由getPreferences(int)来进行管理,允许获取或者修改一套“名字/值”的对。为了在多个应用程序组件(activities, receivers, services, providers)之间共享持久状态,可以使用Context.getSharedPreferences()接口获取一个共享对象。(注意:不可能跨越应用程序包来共享数据,但是可以使用内容提供者来做)

下面是一个例子,进行持久性设置:

public class CalendarActivity extends Activity
{
...

static final int DAY_VIEW_MODE = 0;
static final int WEEK_VIEW_MODE = 1;

private SharedPreferences mPrefs;
private int mCurViewMode;

protected void onCreate(Bundle icicle)
{
super.onCreate(icicle);

SharedPreferences mPrefs = getSharedPreferences();
mCurViewMode = mPrefs.getInt("view_mode" DAY_VIEW_MODE);
}

protected void onPause()
{
super.onPause();

SharedPreferences.Editor ed = mPrefs.edit();
ed.putInt("view_mode", mCurViewMode);
ed.commit();
}
}

权限许可

当activity在manifest的<activity> 标签中进行了声明,就有能力进行启动。其他的应用程序为了能够启动这个activity,需要在他们自己的manifest资源文件中的<uses-permission>元素中进行声明。

更多的权限和安全问题,可以参见Security Model 文档。

进程的生命周期

Android系统试图确保一个应用进程尽可能长久,但是在内存减少的时候最终要删除旧的进程。像Activity Lifecycle描述的一样,决定哪一个进程被删除是依据于和用户打交道的状态。一般来说,基于运行的activity的进程有四种状态,下面列出了重要的顺序,Android系统在杀掉重要进程之前先杀掉次要的进程。

  1. 前台的activity(activity在屏幕的最前面,正和用户进行交互)是最重要的,如果这个activity使用的内存超过了设备提供的内存,作为最后手段才杀掉这个activity的进程。通常在这一点上表明设备已经达到了memory paging state(?),因为需要确保用户界面的交互。
  2. 可见的activity(对用户可见,但不是在前台,如位于前台弹出对话框下面的界面)是很重要的,一般不会杀掉这个进程,除非为了确保前台activity的运行不得已而杀之。
  3. 后台的activity(对用户不可见,已经暂停)不再重要,系统可以安全杀掉这个进程,回收内存给其他前台或者可见的进程。当用户按下“返回”键浏览这个activity(使它重新在屏幕上可见),它的onCreate(Bundle)接口将被调用,在暂停的时候由于已经调用过onFreeze(Bundle)接口重置了状态,因此这个activity能够重新以相同的设置启动。
  4. 空进程是没有activity组件和其他应用程序组件(Service or IntentReceiver)的进程,在系统内存变低的时候很快就被删除了。出于这个原因,任何在activity之外的后台操作必须在IntentReceiver 或者Service的环境下执行,这样才能确保进程能够一直运行。

有时候activity希望能够执行一些长时间的操作,自立存在于activity的生命周期之外。例如:相机应用程序允许用户上传一幅图片到网站,上传将花费很长的时间,应用程序应该允许用户离开正在执行的程序。为了完成这些,activity应该启动一个Service来执行上传任务,在上传区间,系统能够给进程一个良好的优先级别(比那些不可见的进程更高的优先级别),而不用考虑源activity是否处于暂停,停止或者结束状态。

分类: phone 标签:

google Android activity类 (1)

2007年11月20日 roboter 没有评论

http://code.google.com/android/reference/android/app/Activity.html进行了翻译整理,以便对activity类有一个整体了解。

activity类处于android.app包中,继承体系如下:
1.java.lang.Object
2.android.content.Context
3.android.app.ApplicationContext
4.android.app.Activity

activity是单独的,用于处理用户操作。几乎所有的activity都要和用户打交道,所以activity类创建了一个窗口,开发人员可以通过setContentView(View)接口把UI放到activity创建的窗口上,当activity指向全屏窗口时,也可以用其他方式实现:作为漂浮窗口(通过windowIsFloating的主题集合),或者嵌入到其他的activity(使用ActivityGroup)。大部分的Activity子类都需要实现以下两个接口:

  • onCreate(Bundle)接口是初始化activity的地方. 在这儿通常可以调用setContentView(int)设置在资源文件中定义的UI, 使用findViewById(int) 可以获得UI中定义的窗口.
  • onPause()接口是使用者准备离开activity的地方,在这儿,任何的修改都应该被提交(通常用于ContentProvider保存数据).

为了能够使用Context.startActivity(),所有的activity类都必须在AndroidManifest.xml文件中定义有相关的“activity”项。

activity类是Android 应用生命周期的重要部分。

Activity生命周期
在系统中的Activity被一个Activity栈所管理。当一个新的Activity启动时,将被放置到栈顶,成为运行中的Activity,前一个Activity保留在栈中,不再放到前台,直到新的Activity退出为止。

Activity有四种本质区别的状态:

  1. 在屏幕的前台(Activity栈顶),叫做活动状态或者运行状态(active or running)
  2. 如果一个Activity失去焦点,但是依然可见(一个新的非全屏的Activity 或者一个透明的Activity 被放置在栈顶),叫做暂停状态(Paused)。一个暂停状态的Activity依然保持活力(保持所有的状态,成员信息,和窗口管理器保持连接),但是在系统内存极端低下的时候将被杀掉。
  3. 如果一个Activity被另外的Activity完全覆盖掉,叫做停止状态(Stopped)。它依然保持所有状态和成员信息,但是它不再可见,所以它的窗口被隐藏,当系统内存需要被用在其他地方的时候,Stopped的Activity将被杀掉。
  4. 如果一个Activity是Paused或者Stopped状态,系统可以将该Activity从内存中删除,Android系统采用两种方式进行删除,要么要求该Activity结束,要么直接杀掉它的进程。当该Activity再次显示给用户时,它必须重新开始和重置前面的状态。

下面的图显示了Activity的重要状态转换,矩形框表明Activity在状态转换之间的回调接口,开发人员可以重载实现以便执行相关代码,带有颜色的椭圆形表明Activity所处的状态。
activity lifecycle

在上图中,Activity有三个关键的循环:

  1. 整个的生命周期,从onCreate(Bundle)开始到onDestroy()结束。Activity在onCreate()设置所有的“全局”状态,在onDestory()释放所有的资源。例如:某个Activity有一个在后台运行的线程,用于从网络下载数据,则该Activity可以在onCreate()中创建线程,在onDestory()中停止线程。
  2. 可见的生命周期,从onStart()开始到onStop()结束。在这段时间,可以看到Activity在屏幕上,尽管有可能不在前台,不能和用户交互。在这两个接口之间,需要保持显示给用户的UI数据和资源等,例如:可以在onStart中注册一个IntentReceiver来监听数据变化导致UI的变动,当不再需要显示时候,可以在onStop()中注销它。onStart(),onStop()都可以被多次调用,因为Activity随时可以在可见和隐藏之间转换。
  3. 前台的生命周期,从onResume()开始到onPause()结束。在这段时间里,该Activity处于所有Activity的最前面,和用户进行交互。Activity可以经常性地在resumed和paused状态之间切换,例如:当设备准备休眠时,当一个Activity处理结果被分发时,当一个新的Intent被分发时。所以在这些接口方法中的代码应该属于非常轻量级的。

Activity的整个生命周期都定义在下面的接口方法中,所有方法都可以被重载。所有的Activity都需要实现onCreate(Bundle)去初始化设置,大部分Activity需要实现onPause()去提交更改过的数据,当前大部分的Activity也需要实现onFreeze()接口,以便恢复在onCreate(Bundle)里面设置的状态。

public class Activity extends ApplicationContext {
protected void onCreate(Bundle icicle);

protected void onStart();

protected void onRestart();

protected void onResume();

protected void onFreeze(Bundle outIcicle);

protected void onPause();

protected void onStop();

protected void onDestroy();
}

分类: phone 标签:

google Android 应用组织

2007年11月16日 roboter 2 条评论

http://code.google.com/android/intro/anatomy.html进行了翻译

在一个Android 应用中有如下的四种组件:
* Activity 活动组件
* Intent Receiver 接受处理组件
* Service 服务组件
* Content Provider 内容提供组件
一个Android 应用并不需要同时具有这四个组件,应用可以组合这些组件。

在应用中一旦决定使用了哪些组件,就需要在AndroidManifest.xml文件中列举出来。可以参考Android manifest file documentation

Activity
Activities是最普遍的组件,在应用中通常用于显示一个屏幕(页面)的内容,仅仅一个屏幕(页面)的内容。开发人员需要从Activity基类继承下来,管理UI显示和UI的反馈事件。大多数应用程序都会有多个屏幕(页面)的内容,如一个文本消息的应用程序,第一个页面要显示联系人列表,第二个页面要写东西给选定的联系人,其他页面显示旧的文本内容或者其他等等。每一个页面都被当做一个Activity(活动组件),移动到其他页面意味着开始了一个新的Activity(活动组件)。在某些场景下,一个Activity可以返回值给前一个Activity,如一个页面选择了一张图片,可以返回这张图片给调用者(Activit)。

打开新的页面时,前一个页面将会暂停,放置到一个历史堆栈中,用户可以使用后退键浏览前面的页面,页面也可以从堆栈中删除。Android会为每一个从主页面home screen(idle界面?)启动的应用维护一个历史堆栈列表。

Intent and Intent Filters
Android使用一个叫Intent的类从一个页面移动到另外一个页面。一个Intent描述了应用将要做的事情,就是一种动作行为。最重要的有两点:动作的类型和动作所捎带的参数(数据),典型的动作类型为MAIN(activity的正门),VIEW, PICK, EDIT等等。参数数据以URI形式来表示。例如,为了查看一个人的联系信息,需要创建一个Intent,动作类型为VIEW,人名为参数数据(需要格式化为URI)。

一个Activity为了表明能够处理某些Intent,Android设计了一个相关的类IntentFilter,Activity在IntentFilter中决定它能够处理的Intent动作类型,IntentFilter需要在AndroidManifest.xml文件中进行声明。

从一个页面到另外一个页面跳转的依据在于发出的Intent,为了向前浏览,Activity调用startActivity(myIntent). Android系统在所有安装的应用程序中查找intent filters,找出最能够匹配myIntent的Activity。新的activity将被启动,intent被传入。有如下两个关键点:
* Activities 能够重新被使用
* Activities 在任何时候都可以被替换,只要一个Activity设置了相同的IntentFilter

Intent Receiver
在应用程序中,针对外部事件需要执行一些动作来反馈,可以使用IntentReceiver类。例如:来了一个电话,或者数据通道可用,或者晚上到了。Intent Receiver没有UI的显示,尽管可以通过NotificationManager 通知用户某些有趣的事情发生了。Intent Receivers也需要在AndroidManifest.xml中进行声明,当然也可以在程序中调用Context.registerReceiver()接口动态声明。同时在应用程序没有跑起来的时候也能够调用Intent Receivers,当一个Intent Receivers被触发时,Android系统将会启动这个应用程序。应用程序也能够通过Context.broadcastIntent()广播他们的Intent。

Service
一个Service是没有UI,一直运行在后台的代码组件。一个好的例子:播放器从一个播放列表中播放歌曲。在一个播放器应用中,大概会有一个或多个Activity组件允许用户选择歌曲和播放它们,歌曲播放代码不应该放在Activity组件中,因为我们希望一直能够听歌,甚至切换到了其他页面。所以,播放器页面应该启动一个服务组件(Context.startService())来播放歌曲。Android系统将会保持service运行直到它结束(可以参考“Android 应用的生命周期”一文)。可以使用Context.bindService()接口连接上一个服务(如果这时候服务没有启动,连接会导致服务启动),连接上之后,就可以和服务进行通讯。像播放器的例子,可以允许暂停,重绕等等。

Content Provider
应用程序可以在文件中,数据库或者其他设备存储他们的数据。Content Provider用于将各个应用程序的数据进行共享,Content Provider是一个类,执行一套标准的方法,让其他的应用程序存储和获取Content Provider所处理的数据。更多的内容可以参考Accessing Content Providers.

分类: phone 标签:

d-bus的封装

2007年8月20日 roboter 没有评论

d-bus系统相当庞大,源代码有100多文件,大小差不多有2M,接口很复杂,即使使用glib的绑定接口,应用程序需要编写的代码都比较多,也需要了解d-bus的一些细节。因此开发平台时,希望能够把d-bus完全封装掉,上层应用程序不用知道d-bus的概念,他们对是否采用d-bus,dcop,或者直接使用scoket都不关心。

qt4发布了一个QtDBus,用于qt的绑定,与glib的绑定基本类似,很适合qt的使用习惯。

有几个问题:
1.变参无法封装掉,c里面使用可变参数是很通常的做法,dbus提供的很多接口都是可变参数,如dbus_message_get_args,可以一次性把所有参数内容取出来,应用程序需要直接填充参数字段。但是用C++来封装似乎很麻烦,还没有找到较好的方法,但是可以不用dbus的变参接口。
2.dbus-glib做了很好的封装,对消息的发送和处理过程都封装得不错,应用可以不用知道底层的一些细节。但是dbus-glib的消息分派有自己的一套,会和将要开发的消息处理框架有冲突。相当于一套系统中有两套消息处理系统,这样会打架的,因此也不能使用dbus-glib,只能从dbus底层接口来考虑,gpe phone也是这样做的。

准备借鉴dbus-glib的封装方式,使用一个xml文件描述接口内容,再分别生成client端的发送消息接口,server端的消息接收处理接口。client的应用可以直接调用发送消息接口,server端应用可以直接处理消息结束接口,在dbus-glib中,server端是使用回调函数来处理,C++中可以使用虚函数来处理。这样的方式其实就是dbus-glib的方式,但是封装dbus底层接口的好处是可以将dbus控制到自己手中,和其他的消息处理系统一块运行。

分类: Uncategorized, phone 标签:

D-Bus 体系 (5)

2007年8月6日 roboter 3 条评论

server端的编写

在GLib中,通过dbus表现出GObject,必须写XML文件描述这个对象的方法等属性。像上一篇文章中提到的例子:

<?xml version="1.0" encoding="UTF-8" ?>
<node name="/com/example/MyObject">
<interface name="com.example.MyObject">
<method name="ManyArgs">
<arg type="u" name="x" direction="in" />
<arg type="s" name="str" direction="in" />
<arg type="d" name="trouble" direction="in" />
<arg type="d" name="d_ret" direction="out" />
<arg type="s" name="str_ret" direction="out" />
</method >
</interface >
</node >

一旦写完XML,运行dbus-binding-tool工具,如 dbus-binding-tool –mode=glib-server my-object.xml > my-object-glue.h.

然后在本地代码中include产生的头文件,调用dbus_g_object_class_install_info进行类的初始化,传递对象和对象信息进去,如 dbus_g_object_type_install_info (COM_FOO_TYPE_MY_OBJECT, &com_foo_my_object_info);每个对象类都需要这样做。

为了执行方法,需要定义一个C函数,如my_object_many_args,需要遵守的规则如下:
1.函数返回gboolean,true表示成功,false标识失败。
2.第一个参数必须是对象实例的指针。
3.跟在实例指针后面的参数是方法的输入参数。
4.输入参数后面是输出参数。
5.最后一个参数必须是GError **,如果函数返回失败,必须使用g_set_error填充该错误参数。
如下的xml文件

<method name="Increment">
<arg type="u" name="x" />
<arg type="u" direction="out" />
</method>

对应的函数定义为:
gboolean
my_object_increment (MyObject *obj, gint32 x, gint32 *ret, GError **error);

最后可以使用dbus_g_connection_register_g_object输出一个对象,如
dbus_g_connection_register_g_object (connection,”/com/foo/MyObject”, obj);

server端的声明(Annotations):
org.freedesktop.DBus.GLib.CSymbol
org.freedesktop.DBus.GLib.Async
org.freedesktop.DBus.GLib.Const
org.freedesktop.DBus.GLib.ReturnVal

分类: phone 标签:

D-Bus 体系 (4)

2007年8月5日 roboter 1 条评论

client端编写

我们的程序在使用dbus的时候,首先需要连接上dbus,使用dbus_g_bus_get获得dbus连接。然后可以创建代理对象。

需要调用方法的时候,可以有两种方式:1.同步调用,使用dbus_g_proxy_call发送方法请求到远端对象,dbus会阻塞等待远端对象的回应,输出参数里将会带有相应的回应数据,以G_TYPE_INVALID作为终止符。2.异步调用,使用dbus_g_proxy_begin_call,它将返回一个DBusGPendingCall对象,可以使用dbus_g_pending_call_set_notify连接到自己的处理函授中。

可以使用dbus_g_proxy_add_signal 和 dbus_g_proxy_connect_signal来连接信号,dbus_g_proxy_add_signal用来声明信号处理函数,属于必须被调用的接口,dbus_g_proxy_connect_signal可以调用多次。

Generated Bindings
使用内置的xml文件,可以很方便地自动创建出易于使用的dbus代理对象。如下的一个xml文件描述了了一个方法:


<?xml version="1.0" encoding="UTF-8" ?>
<node name="/com/example/MyObject">
<interface name="com.example.MyObject">
<method name="ManyArgs">
<arg type="u" name="x" direction="in" />
<arg type="s" name="str" direction="in" />
<arg type="d" name="trouble" direction="in" />
<arg type="d" name="d_ret" direction="out" />
<arg type="s" name="str_ret" direction="out" />
</method >
</interface >
</node >

“in”标识输入参数,“out”标识输出参数。
使用dbus-binding-tool工具来生成头文件,如dbus-binding-tool –mode=glib-client my-object.xml > my-object-bindings.h,会产生如下的内联函数原型:

/* This is a blocking call */
gboolean
com_example_MyObject_many_args (DBusGProxy *proxy, const guint IN_x,
const char * IN_str, const gdouble IN_trouble,
gdouble* OUT_d_ret, char ** OUT_str_ret,
GError **error);

/* This is a non-blocking call */
DBusGProxyCall*
com_example_MyObject_many_args_async (DBusGProxy *proxy, const guint IN_x,
const char * IN_str, const gdouble IN_trouble,
com_example_MyObject_many_args_reply callback,
gpointer userdata);

/* This is the typedef for the non-blocking callback */
typedef void
(*com_example_MyObject_many_args_reply)
(DBusGProxy *proxy, gdouble OUT_d_ret, char * OUT_str_ret,
GError *error, gpointer userdata);

所有函数的第一个参数都是DBusGProxy对象,一般是使用dbus_g_proxy_new_*函数创建出来的。客户端发送方法请求可以增加标记,目前只有org.freedesktop.DBus.GLib.NoReply标记,dbus可以不要回应消息,没有“out”参数,这样运算速度会快一点。

分类: phone 标签:

D-Bus 体系 (3)

2007年8月5日 roboter 1 条评论

Glib绑定接口在"dbus/dbus-glib.h"头文件中定义。
dbus和glib的数据类型映射如下:

D-Bus basic type GType Free function Notes
BYTE G_TYPE_UCHAR    
BOOLEAN G_TYPE_BOOLEAN    
INT16 G_TYPE_INT   Will be changed to a G_TYPE_INT16 once
GLib has it
UINT16 G_TYPE_UINT   Will be changed to a G_TYPE_UINT16 once
GLib has it
INT32 G_TYPE_INT   Will be changed to a G_TYPE_INT32 once
GLib has it
UINT32 G_TYPE_UINT   Will be changed to a G_TYPE_UINT32 once
GLib has it
INT64 G_TYPE_GINT64    
UINT64 G_TYPE_GUINT64    
DOUBLE G_TYPE_DOUBLE    
STRING G_TYPE_STRING g_free  
OBJECT_PATH DBUS_TYPE_G_PROXY g_object_unref The returned proxy does not have an interface set; use
dbus_g_proxy_set_interface to invoke methods

Container type mappings
dbus数据也有包容器类型,像DBUS_TYPE_ARRAY 和 DBUS_TYPE_STRUCT,dbus的数据类型可以是嵌套的,如有一个数组,内容是字符串的数组集合。

但是,并不是所有的类型都有普通的使用,DBUS_TYPE_STRUCT应该可以包容非基本类型的数据类型。glib绑定尝试使用比较明显的方式进行声明。

D-Bus type signature Description GType C typedef Free function Notes
as Array of strings G_TYPE_STRV char ** g_strfreev  
v Generic value container G_TYPE_VALUE GValue * g_value_unset The calling conventions for values expect that method callers have
allocated return values; see below.

同时定义了新的数组类型集合。

D-Bus type signature Description GType C typedef Free function Notes
ay Array of bytes DBUS_TYPE_G_BYTE_ARRAY GArray * g_array_free  
au Array of uint DBUS_TYPE_G_UINT_ARRAY GArray * g_array_free  
ai Array of int DBUS_TYPE_G_INT_ARRAY GArray * g_array_free  
ax Array of int64 DBUS_TYPE_G_INT64_ARRAY GArray * g_array_free  
at Array of uint64 DBUS_TYPE_G_UINT64_ARRAY GArray * g_array_free  
ad Array of double DBUS_TYPE_G_DOUBLE_ARRAY GArray * g_array_free  
ab Array of boolean DBUS_TYPE_G_BOOLEAN_ARRAY GArray * g_array_free  

定义了字典类型

D-Bus type signature Description GType C typedef Free function Notes
a{ss} Dictionary mapping strings to strings DBUS_TYPE_G_STRING_STRING_HASHTABLE GHashTable * g_hash_table_destroy  
分类: phone 标签:

D-Bus 体系 (2)

2007年8月2日 roboter 2 条评论

Bus Names
当一个应用程序连接上bus daemon时,daemon会分配一个唯一的名字给它。以冒号(:)开始,这些名字在daemon的生命周期中是不会改变的,可以认为这些名字就是一个IP地址。当这个名字映射到应用程序的连接上时,应用程序可以说拥有这个名字。同时应用可以声明额外的容易理解的名字,比如可以取一个名字com.mycompany.TextEditor,可以认为这些名字就是一个域名。其他应用程序可以往这个名字发送消息,执行各种方法。

名字还有第二个重要的用途,可以用于跟踪应用程序的生命周期。当应用退出(或者崩溃)时,与bus的连接将被OS内核关掉,bus将会发送通知,告诉剩余的应用程序,该程序已经丢失了它的名字。名字还可以检测应用是否已经启动,这往往用于只能启动一个实例的应用。

Addresses
使用d-bus的应用程序既可以是server也可以是client,server监听到来的连接,client连接到server,一旦连接建立,消息就可以流转。如果使用dbus daemon,所有的应用程序都是client,daemon监听所有的连接,应用程序初始化连接到daemon。

dbus地址指明server将要监听的地方,client将要连接的地方,例如,地址:unix:path=/tmp/abcdef表明server将在/tmp/abcdef路径下监听unix域的socket,client也将连接到这个socket。一个地址也可以指明是TCP/IP的socket,或者是其他的。

当使用bus daemon时,libdbus会从环境变量中(DBUS_SESSION_BUS_ADDRESS)自动认识“会话daemon”的地址。如果是系统daemon,它会检查指定的socket路径获得地址,也可以使用环境变量(DBUS_SESSION_BUS_ADDRESS)进行设定。

当dbus中不使用daemon时,需要定义哪一个应用是server,哪一个应用是client,同时要指明server的地址,这不是很通常的做法。

Big Conceptual Picture
要在指定的对象中调用指定的方法,需要知道的参数如下:
Address -> [Bus Name] -> Path -> Interface -> Method
bus name是可选的,除非是希望把消息送到特定的应用中才需要。interface也是可选的,有一些历史原因,DCOP不需要指定接口,因为DCOP在同一个对象中禁止同名的方法。

Messages – Behind the Scenes
如果使用dbus的高层接口,就可以不用直接操作这些消息。DBUS有四种类型的消息:
1.方法调用(method call) 在对象上执行一个方法
2.方法返回(method return)返回方法执行的结果
3.错误(error)调用方法产生的异常
4.信号(signal)通知指定的信号发生了,可以想象成“事件”。

要执行 D-BUS 对象的方法,需要向对象发送一个方法调用消息。它将完成一些处理并返回一个方法返回消息或者错误消息。信号的不同之处在于它们不返回任何内容:既没有“信号返回”消息,也没有任何类型的错误消息。

每个消息都有一个消息头,包含多个字段,有一个消息体,包含多个参数。可以认为消息头是消息的路由信息,消息体作为一个载体。消息头里面的字段包含发送的bus name,目标bus name,方法或者信号名字等,同时消息头里面定义的字段类型规定了消息体里面的数据格式。例如:字符“i”代表了”32-bit integer”,“ii”就代表了消息体里面有两个”32-bit integer”。

Calling a Method – Behind the Scenes
在dbus中调用一个方法包含了两条消息,进程A向进程B发送方法调用消息,进程B向进程A发送应答消息。所有的消息都由daemon进行分派,每个调用的消息都有一个不同的序列号,返回消息包含这个序列号,以方便调用者匹配调用消息与应答消息。调用消息包含一些参数,应答消息可能包含错误标识,或者包含方法的返回数据。

方法调用的一般流程:
1.使用不同语言绑定的dbus高层接口,都提供了一些代理对象,调用其他进程里面的远端对象就像是在本地进程中的调用一样。应用调用代理上的方法,代理将构造一个方法调用消息给远端的进程。
2.在DBUS的底层接口中,应用需要自己构造方法调用消息(method call message),而不能使用代理。
3.方法调用消息里面的内容有:目的进程的bus name,方法的名字,方法的参数,目的进程的对象路径,以及可选的接口名称。
4.方法调用消息是发送到bus daemon中的。
5.bus daemon查找目标的bus name,如果找到,就把这个方法发送到该进程中,否则,daemon会产生错误消息,作为应答消息给发送进程。
6.目标进程解开消息,在dbus底层接口中,会立即调用方法,然后发送方法的应答消息给daemon。在dbus高层接口中,会先检测对象路径,接口,方法名称,然后把它转换成对应的对象(如GObject,QT中的QObject等)的方法,然后再将应答结果转换成应答消息发给daemon。
7.bus daemon接受到应答消息,将把应答消息直接发给发出调用消息的进程。
8.应答消息中可以包容很多返回值,也可以标识一个错误发生,当使用绑定时,应答消息将转换为代理对象的返回值,或者进入异常。

bus daemon不对消息重新排序,如果发送了两条消息到同一个进程,他们将按照发送顺序接受到。接受进程并需要按照顺序发出应答消息,例如在多线程中处理这些消息,应答消息的发出是没有顺序的。消息都有一个序列号可以与应答消息进行配对。

Emitting a Signal – Behind the Scenes
在dbus中一个信号包含一条信号消息,一个进程发给多个进程。也就是说,信号是单向的广播。信号可以包含一些参数,但是作为广播,它是没有返回值的。

信号触发者是不了解信号接受者的,接受者向daemon注册感兴趣的信号,注册规则是”match rules”,记录触发者名字和信号名字。daemon只向注册了这个信号的进程发送信号。

信号的一般流程如下:
1.当使用dbus底层接口时,信号需要应用自己创建和发送到daemon,使用dbus高层接口时,可以使用相关对象进行发送,如Glib里面提供的信号触发机制。
2.信号包含的内容有:信号的接口名称,信号名称,发送进程的bus name,以及其他参数。
3.任何进程都可以依据”match rules”注册相关的信号,daemon有一张注册的列表。
4.daemon检测信号,决定哪些进程对这个信号感兴趣,然后把信号发送给这些进程。
5.每个进程收到信号后,如果是使用了dbus高层接口,可以选择触发代理对象上的信号。如果是dbus底层接口,需要检查发送者名称和信号名称,然后决定怎么做。

相关链接:
D-Bus 体系 (1)

分类: phone 标签:

D-Bus 体系 (1)

2007年8月1日 roboter 3 条评论

D-Bus Tutorial进行了一些翻译加上自己的一些理解。

有很多种IPC或者网络通信系统,如:CORBA, DCE, DCOM, DCOP, XML-RPC, SOAP, MBUS, Internet Communications Engine (ICE)等等,可能会有数百种,dbus的目的主要是下面两点:
1.在同一个桌面会话中,进行桌面应用程序之间的通讯
2.桌面程序与内核或者守护进程的通信。

Dbus是一套进程通信体系,它有以下几层:
1.libdbus库,提供给各个应用程序调用,使应用程序具有通信和数据交换的能力,两个应用程序可以直接进行通信,就像是一条socket通道,两个程序之间建立通道之后,就可以通讯了。
2.消息守护进程,在libdbus的基础上创建,可以管理多个应用程序之间的通信。每个应用程序都和消息守护进程建立dbus的链接,然后由消息守护进程进行消息的分派。
3.各种包装库,有libdbus-glib,libdbus-qt等等,目的是将dbus的底层api进行一下封装。

下面有一张图可以很方便说明dbus的体系结构。
dbus

dbus中的消息由一个消息头(标识是哪一种消息)和消息数据组成,比socket的流式数据更方便一些。bus daemon 就像是一个路由器,与各个应用程序进行连接,分派这些消息。bus daemon 在一台机器上有多个实例,第一个实例是全局的实例,类似于sendmail和或者apache,这个实例有很严格的安全限制,只接受一些特定的系统消息,用于系统通信。其他bus daemon是一些会话,用于用户登录之后,在当前会话(session)中进行的通讯。系统的bus daemon 和会话的bus daemon 是分开的,彼此不会互相影响,会话bus daemon 不会去调用系统的bus daemon 。

Native Objects and Object Paths
在不同的编程语言中,都定义了一些“对象”,如java中的java.lang.Object,GLIB中的GObject,QT中的QObject等等。D-BUS的底层接口,和libdbus API相关,是没有这些对象的概念的,它提供的是一种叫对象路径(object path),用于让高层接口绑定到各个对象中去,允许远端应用程序指向它们。object path就像是一个文件路径,可以叫做/org/kde/kspread/sheets/3/cells/4/5等。

Methods and Signals
每个对象都有一些成员,两种成员:方法(methods)和信号(signals),在对象中,方法可以被调用。信号会被广播,感兴趣的对象可以处理这个信号,同时信号中也可以带有相关的数据。每一个方法或者信号都可以用一个名字来命名,如”Frobate” 或者 “OnClicked”。

Interfaces
每个对象都有一个或者多个接口,一个接口就是多个方法和信号的集合。dbus使用简单的命名空间字符串来表示接口,如org.freedesktop.Introspectable。可以说dbus接口相当于C++中的纯虚类。

Proxies
代理对象用于模拟在另外的进程中的远端对象,代理对象像是一个正常的普通对象。d-bus的底层接口必须手动创建方法调用的消息,然后发送,同时必须手动接受和处理返回的消息。高层接口可以使用代理来替换这些,当调用代理对象的方法时,代理内部会转换成dbus的方法调用,等待消息返回,对返回结果解包,返回给相应的方法。可以看看下面的例子,使用dbus底层接口编写的代码:
Message message = new Message("/remote/object/path", "MethodName", arg1, arg2);
Connection connection = getBusConnection();
connection.send(message);
Message reply = connection.waitForReply(message);
if (reply.isError()) {

} else {
Object returnValue = reply.getReturnValue();
}

使用代理对象编写的代码:
Proxy proxy = new Proxy(getBusConnection(), "/remote/object/path");
Object returnValue = proxy.MethodName(arg1, arg2);

客户端代码减少很多。

相关链接:
D-Bus 体系 (2)

分类: phone 标签:

dbus启动问题

2007年7月31日 roboter 没有评论

首先需要启动守护进程
dbus-daemon –system –print-pid –print-address
结果提示 Failed to start message bus: Could not get UID and GID for username “messagebus”
dbus需要有一个messagebus用户,创建该用户即可,useradd messagebus,问题解决。

执行一个dbus测试程序,提示:D-Bus library appears to be incorrectly set up; failed to read machine uuid: Failed to open “/usr/var/lib/dbus/machine-id”: No such file or directory
没有machine-id文件,查了一下,需要给它定义一个id,使用dbus-uuidgen >/usr/var/lib/dbus/machine-id
产生这个文件,该问题解决。

再次执行测试程序,又有问题:Couldn’t connect to session bus: Failed to execute dbus-launch to autolaunch D-Bus session,看了帮助http://dbus.freedesktop.org/doc/dbus-launch.1.html
AUTOMATIC LAUNCHING一节,需要设置DBUS_SESSION_BUS_ADDRESS环境变量的值,先执行dbus-launch,获得了DBUS_SESSION_BUS_ADDRESS值,再export一下,最后执行测试程序,OK了

在dbus帮助中有一篇关于dbus-launch的文章,可以在脚本中启动dbus-launch,同时自动设置DBUS_SESSION_BUS_ADDRESS环境变量,脚本文件rundbus如下:

if test -z "$DBUS_SESSION_BUS_ADDRESS" ; then
## if not found, launch a new one
eval `dbus-launch --sh-syntax --exit-with-session`
echo "D-Bus per-session daemon address is: $DBUS_SESSION_BUS_ADDRESS"
fi

执行. rundbus即可。

分类: phone 标签: