Android操作系统本身就是一个巨大的开源软件仓库,熟悉它既可以了解到Android系统的设计框架,也可以获得高效的应用程序编写方式。本文所分析的源码来自于Google官方的AOSP源码4.0.1_r1,手机的Android版本是CM 4.2.2。对于Android系统分析而言,手机的操作系统版本和源码版本有些不一致的地方不会有太大的影响,但是如果需要将源码中的修改安装到手机里面的话,最不容易遇到问题的办法就是保持手机中系统的版本和源码的版本完全一致了。

1.程序入口发现

  每一个应用程序都应当有各自的入口,使用JAVA编写的Android应用程序也不例外。确认应用的入口是确认应用在系统中对应源码的第一步,也是应用程序分析的第一步。我采用的办法是将需要分析的短信app运行起来,通过Android自带工具获取当前占据主界面的activity名称。

  Stackoverflow中的回答http://stackoverflow.com/questions/13193592/adb-android-getting-the-name-of-the-current-activity给出了这个问题的三种解决办法:

1)打Hierarchy View(Window->Open Perspective->Other->Hierarchy View),在Windows栏中用粗体显示了当前占据屏幕activity和package,如下图所示。其中com.android.mms是应用的包名,com.adnroid.mms.ui.ConversationList是具体的Activity名称。

Android系统自带APP分析——短信app-风君雪科技博客

2)也可以直接打开Windows栏进行查看。(Window->Show View->Others->Windows),结果和上图一致。

3) 使用dumpsys命令:adb shell “dumpsys window windows | grep -E ‘mCurrentFocus|mFocusedApp'” ,结果如下:

dumpsys使用方法见:http://stackoverflow.com/questions/11201659/whats-android-adb-shell-dumpsys-tool-and-its-benefits

2.程序功能分析

  确定了短信app的报名以及主Activity之后,可以通过软件搜索(这里用的是everything)确定短信app在源码中的位置是 packagesappsMmssrccomandroidmmsui,并且短信app的启动activity就是继承自ListActivity的ConversationList。

  1)ActionBar的构造

在onCreateOptionsMenu函数中通过导入conversation_list_menu加载menu项目。

    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.conversation_list_menu, menu);
conversation_list_menu.xml中设置了:发送、搜索、设置、删除所有等5个按钮。
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:id="@+id/action_compose_new"
        android:title="@string/new_message"
        android:icon="@drawable/ic_menu_msg_compose_holo_dark"
        android:showAsAction="always|withText" />

    <item android:id="@+id/search"
          android:title="@string/menu_search"
          android:icon="@drawable/ic_menu_search_holo_dark"
          android:showAsAction="ifRoom|collapseActionView"
          android:actionViewClass="android.widget.SearchView" />

    <item android:id="@+id/action_settings"
        android:title="@string/menu_preferences"
        android:icon="@android:drawable/ic_menu_preferences" />

    <item android:id="@+id/action_delete_all"
        android:title="@string/menu_delete_all"
        android:icon="@drawable/ic_menu_trash_holo_dark" />

    <item android:id="@+id/action_debug_dump"
        android:title="@string/menu_debug_dump" />
</menu>

  oncreate函数中的setupActionBar(),从功能上看是用来设置Actionbar的view的,并且可以显示未读短信的条数,但是在CM的手机上没有看到对应的view。

    private void setupActionBar() {
        ActionBar actionBar = getActionBar();

        ViewGroup v = (ViewGroup)LayoutInflater.from(this)
            .inflate(R.layout.conversation_list_actionbar, null);
        actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
                ActionBar.DISPLAY_SHOW_CUSTOM);
        actionBar.setCustomView(v,
                new ActionBar.LayoutParams(ActionBar.LayoutParams.WRAP_CONTENT,
                        ActionBar.LayoutParams.WRAP_CONTENT,
                        Gravity.CENTER_VERTICAL | Gravity.RIGHT));

        mUnreadConvCount = (TextView)v.findViewById(R.id.unread_conv_count);
    }

  2)listview的构造

  在oncreate()中设置listview的属性,其中setOnCreateContextMenuListener设置了ContextMenu,通过这个选项的设置,在用户单击了listview一栏之后,调用onContextItemSelected函数中的MENU_VIEW: {openThread(threadId);break;}。可以跳转至发送短信界面。

        mQueryHandler = new ThreadListQueryHandler(getContentResolver());

        ListView listView = getListView();
        listView.setOnCreateContextMenuListener(mConvListOnCreateContextMenuListener);
        listView.setOnKeyListener(mThreadListKeyListener);
        listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        listView.setMultiChoiceModeListener(new ModeCallback());

        // Tell the list view which view to display when the list is empty
        View emptyView = findViewById(R.id.empty);
        listView.setEmptyView(emptyView);

        initListAdapter();

  listview的初始化,oncreate函数中的initListAdapter()如下:

    private void initListAdapter() {
        mListAdapter = new ConversationListAdapter(this, null);
        mListAdapter.setOnContentChangedListener(mContentChangedListener);
        setListAdapter(mListAdapter);
        getListView().setRecyclerListener(mListAdapter);
    }