Launcher.apk是android runtime的一部分,是系统起来运行的第一个应用程序。它的主要功能就是设置桌面背景图片,加载两个widget(桌面的小应用程序),分别是clock 和search。然后设置menu按键后的内容,并实现具体的功能。Oncreate()是它运行的第一个函数。
Oncreate()
|--mInflater = getLayoutInflater() 新建一个layout的解压缩器
|--setWallpaperDimension() 设置横屏和竖屏的坐标参数
|-- enableSensors(); 检查sensor是否已经打开
|-- enableOpenGL(); 检查opengl是否已经打开
|-- setupViews(); 主要是画桌面背景
|--registerIntentReceivers(); 注册IntentReceivers来监测状态的变化以便相应的产生动作
|-- registerContentObservers(); 注册ContentObservers
|--restoreState() 保存状态
|--startLoaders() 用来加载其他应用程序和用户items,函数具体实现在LauncherModel.java中
|--loadApplications() Loads the list of installed applications in mApplications.
|--new ApplicationsAdapter()
|--mLoader.join(APPLICATION_NOT_RESPONDING_TIMEOUT) (mLoader是一个线程)
|--new ApplicationsLoader() (mApliacationsLoader是一个线程)
|--Intent(Intent.ACTION_MAIN, null) 其他应用程序Manifest.xml中会用到
|--mainIntent.addCategory(Intent.CATEGORY_LAUNCHER)
|--Launcher launcher = mLauncher.get() {AllAppsGridView.java}
|--PackageManager manager = launcher.getPackageManager()
|-- manager.queryIntentActivities(mainIntent, 0)
|--ChangeNotifier action = new ChangeNotifier(applicationList) (ChangeNotifier 是一个线程)
|--用一个循环把所有的app都加载进来
|--loadUserItems()
|--launcher.onDesktopItemsLoaded() {launcher.java中onDesktopItemsLoaded()}
|--bindDesktopItems() Refreshes the shortcuts shown on the workspace
|--ItemInfo item = shortcuts.get(i)
|--switch (item.itemType)
|--case Settings.Favorites.ITEM_TYPE_APPLICATION:
case Settings.Favorites.ITEM_TYPE_SHORTCUT:
|-- createShortcut((ApplicationInfo) item)
|-- createShortcut() 新建一个快捷方式
|--case Settings.Favorites.ITEM_TYPE_USER_FOLDER
|-- 建立一个新的USER_FOLDER(用户文件夹)
|-- default:
|-- createWidget(mInflater, widget)
|--openUserFolder() 建立一个新的USER_FOLDER(用户文件夹)
|--new DesktopItemsLoader(launcher) DesktopItemsLoader是一个线程
|--int itemType = c.getInt(itemTypeIndex)
|--switch (itemType)
case Settings.Favorites.ITEM_TYPE_APPLICATION:
case Settings.Favorites.ITEM_TYPE_SHORTCUT:
|--intent = Intent.getIntent(c.getString(intentIndex))
|--case Settings.Favorites.ITEM_TYPE_USER_FOLDER
|--findOrMakeFolder(userFolders, id)
|--case Settings.Favorites.CONTAINER_DESKTOP
|--desktopItems.add(folderInfo) 添加到桌面上
|--case Settings.Favorites.ITEM_TYPE_WIDGET_CLOCK:
case Settings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
case Settings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME:
|--case Settings.Favorites.ITEM_TYPE_WIDGET_CLOCK
|--Widget.makeClock() 添加一个时钟在当前界面上
|--case Settings.Favorites.ITEM_TYPE_WIDGET_SEARCH
|--Widget.makeSearch() 在当前界面上添加一个google search
|--case Settings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME
|--Widget.makePhotoFrame() 在当前界面添加一章小图片
还有一个重要的部分就是当menu按键按下时定义的内容。下面是函数的调用情况以及函数的功能
OnCreateOptionsMenu() 来定义menu的菜单
ADD 、SEARCH_SETTING、NOTIFICATION、WALLPAPER_SETTING、SETTING
OnPrepareOptionsMenu() display the menu
OnOptionsItemSelected() 当选择了Menu中的选项时执行相应动作
|--addItems()
|--showAddDilog()
|--showDialog()
|--MENU_WALLPAPER_SETTING
|--startWallpaper() {home.java}
|--intent(ACTION_SET_WALLPAPER) 启动一个intent来设置背景图片
|--MENU_SEARCH
|--onSearchRequested() {SearchInvoke.java}
|--MENU_NOTIFICATIONS
|--showNotifications()
|--getSystemService(STATUB_BAR_SERVICE) 启动系统的sevice来显示notification
|--MENU_SETTING
|--Intent(android.provider.Settings.ACTION_SETTINGS) 启动一个intent来进行系统的设置
startActivityForResult(Intent intent, int requestCode)用来启动一个intent并接收从它返回的数据,用setResult()来设置返回值,用onActivityResult(int requestCode, int resultCode, Intent data)对返回的数据进行相应的处理。
onActivityResult(int requestCode, int resultCode, Intent data)
|--completeAddShortcut(data, mAddItemCellInfo, !mDesktopLocked);
(requestCode=REQUEST_CREATE_SHORTCUT)
|-- sModel.addDesktopItem(info) {LauncherModel.java}
|-- mDesktopItems.add(info) 将ItemInfo info 加入到ArrayList 中
|-- createShortcut(info)
|-- createShortcut() 新建一个快捷方式
|--completeAddPhotoFrame(data, mAddItemCellInfo); Add a PhotFrame to the workspace
(requestCode=REQUEST_CHOOSE_PHOTO)
|--completeUpdatePhotoFrame(data, mAddItemCellInfo); Updates a workspace PhotoFrame
(requestCode=REQUEST_UPDATE_PHOTO)
相关知识链接:
ContentObservers http://tech.it168.com/j/2008-02-25/200802250125273.shtml
Widget http://baike.baidu.com/view/704016.htm
IntentReceivers http://bbs2.chinaunix.net/archiver/tid-1320757.html
Intent http://developer.android.com/reference/android/content/Intent.html
Activity http://developer.android.com/reference/android/app/Activity.html
相关知识:
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有四种本质区别的状态:
在屏幕的前台(Activity栈顶),叫做活动状态或者运行状态(active or running)
如果一个Activity失去焦点,但是依然可见(一个新的非全屏的Activity 或者一个透明的Activity 被放置在栈顶),叫做暂停状态(Paused)。一个暂停状态的Activity依然保持活力(保持所有的状态,成员信息,和窗口管理器保持连接),但是在系统内存极端低下的时候将被杀掉。
如果一个Activity被另外的Activity完全覆盖掉,叫做停止状态(Stopped)。它依然保持所有状态和成员信息,但是它不再可见,所以它的窗口被隐藏,当系统内存需要被用在其他地方的时候,Stopped的Activity将被杀掉。
如果一个Activity是Paused或者Stopped状态,系统可以将该Activity从内存中删除,Android系统采用两种方式进行删除,要么要求该Activity结束,要么直接杀掉它的进程。当该Activity再次显示给用户时,它必须重新开始和重置前面的状态。
下面的图显示了Activity的重要状态转换,矩形框表明Activity在状态转换之间的回调接口,开发人员可以重载实现以便执行相关代码,带有颜色的椭圆形表明Activity所处的状态。
在上图中,Activity有三个关键的循环:
整个的生命周期,从onCreate(Bundle)开始到onDestroy()结束。Activity在onCreate()设置所有的“全局”状态,在onDestory()释放所有的资源。例如:某个Activity有一个在后台运行的线程,用于从网络下载数据,则该Activity可以在onCreate()中创建线程,在onDestory()中停止线程。
可见的生命周期,从onStart()开始到onStop()结束。在这段时间,可以看到Activity在屏幕上,尽管有可能不在前台,不能和用户交互。在这两个接口之间,需要保持显示给用户的UI数据和资源等,例如:可以在onStart中注册一个IntentReceiver来监听数据变化导致UI的变动,当不再需要显示时候,可以在onStop()中注销它。onStart(),onStop()都可以被多次调用,因为Activity随时可以在可见和隐藏之间转换。
前台的生命周期,从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();
}
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建议采用“编辑到位”的用户模型,也就是说,用户的编辑动作是立即有效的,无须进行额外的确认步骤,支持这个模式只需要简单遵守下面的两个规则:
当创建一个新的文档时,相关的数据库条目和文件都是立即创建的,例如:用户选择写一个新的电子邮件,一旦开始写,则新的电子邮件条目创建出来了。所以如果进入其他的activity,则这封电子邮件将会出现在草稿箱中。
当一个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,需要在他们自己的manifest资源文件中的 元素中进行声明。
更多的权限和安全问题,可以参见Security Model 文档。
进程的生命周期
Android系统试图确保一个应用进程尽可能长久,但是在内存减少的时候最终要删除旧的进程。像Activity Lifecycle描述的一样,决定哪一个进程被删除是依据于和用户打交道的状态。一般来说,基于运行的activity的进程有四种状态,下面列出了重要的顺序,Android系统在杀掉重要进程之前先杀掉次要的进程。
前台的activity(activity在屏幕的最前面,正和用户进行交互)是最重要的,如果这个activity使用的内存超过了设备提供的内存,作为最后手段才杀掉这个activity的进程。通常在这一点上表明设备已经达到了memory paging state(?),因为需要确保用户界面的交互。
可见的activity(对用户可见,但不是在前台,如位于前台弹出对话框下面的界面)是很重要的,一般不会杀掉这个进程,除非为了确保前台activity的运行不得已而杀之。
后台的activity(对用户不可见,已经暂停)不再重要,系统可以安全杀掉这个进程,回收内存给其他前台或者可见的进程。当用户按下“返回”键浏览这个activity(使它重新在屏幕上可见),它的onCreate(Bundle)接口将被调用,在暂停的时候由于已经调用过onFreeze(Bundle)接口重置了状态,因此这个activity能够重新以相同的设置启动。
空进程是没有activity组件和其他应用程序组件(Service or IntentReceiver)的进程,在系统内存变低的时候很快就被删除了。出于这个原因,任何在activity之外的后台操作必须在IntentReceiver 或者Service的环境下执行,这样才能确保进程能够一直运行。
有时候activity希望能够执行一些长时间的操作,自立存在于activity的生命周期之外。例如:相机应用程序允许用户上传一幅图片到网站,上传将花费很长的时间,应用程序应该允许用户离开正在执行的程序。为了完成这些,activity应该启动一个Service来执行上传任务,在上传区间,系统能够给进程一个良好的优先级别(比那些不可见的进程更高的优先级别),而不用考虑源activity是否处于暂停,停止或者结束状态。