Android 实例剖析之 notepad

简介

  android提供了三种菜单类型,分别为options menu,context menu,sub menu。

  options menu就是通过按home键来显示,context menu需要在view上按上2s后显示。这两种menu都有可以加入子菜单,子菜单不能种不能嵌套子菜单。options menu最多只能在屏幕最下面显示6个菜单选项,称为icon menu,icon menu不能有checkable选项。多于6的菜单项会以more icon menu来调出,称为expanded menu。options menu通过activity的onCreateOptionsMenu来生成,这个函数只会在menu第一次生成时调用。任何想改变options menu的想法只能在onPrepareOptionsMenu来实现,这个函数会在menu显示前调用。onOptionsItemSelected 用来处理选中的菜单项。

  context menu是跟某个具体的view绑定在一起,在activity种用registerForContextMenu来为某个view注册context menu。context menu在显示前都会调用onCreateContextMenu来生成menu。onContextItemSelected用来处理选中的菜单项。

  android还提供了对菜单项进行分组的功能,可以把相似功能的菜单项分成同一个组,这样就可以通过调用setGroupCheckable,setGroupEnabled,setGroupVisible来设置菜单属性,而无须单独设置。

  Options Menu

  Notepad中使用了options menu和context menu两种菜单。首先来看生成options menu的onCreateOptionsMenu函数。

  menu.add(0, MENU_ITEM_INSERT, 0, R.string.menu_insert)

  .setShortcut('3', 'a')

  .setIcon(android.R.drawable.ic_menu_add);

  这是一个标准的插入一个菜单项的方法,菜单项的id为MENU_ITEM_INSERT。

  有意思的是下面这几句代码:

  Intent intent = new Intent(null, getIntent().getData());

  intent.addCategory(Intent.CATEGORY_ALTERNATIVE);

  menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,

  new ComponentName(this, NotesList.class), null, intent, 0, null);

  这到底有何用处呢?其实这是一种动态菜单技术(也有点像插件机制),若某一个activity,其类型是”android.intent.category.ALTERNATIVE”,数据是”vnd.android.cursor.dir/vnd.google.note”的话,系统就会为这个activity增加一个菜单项。在androidmanfest.xml中查看后发现,没有一个activity符合条件,所以这段代码并没有动态添加出任何一个菜单项。

为了验证上述分析,我们可以来做一个实验,在androidmanfest.xml中进行修改,看是否会动态生成出菜单项。

  实验一

  首先我们来创建一个新的activity作为目标activity,名为HelloAndroid,没有什么功能,就是显示一个界面。

  public class HelloAndroid extends Activity {

  @Override

  protected void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);

  this.setContentView(R.layout.main);

  }

  }

  它所对应的布局界面XML文件如下:

<? xml version="1.0" encoding="utf-8" ?>
< LinearLayout  xmlns:android ="http://schemas.android.com/apk/res/android"
    android:orientation ="vertical"
    android:layout_width ="fill_parent"
    android:layout_height ="fill_parent"
     >
< TextView  
     android:layout_width ="fill_parent"  
    android:layout_height ="wrap_content"  android:id ="@+id/TextView01" />

< Button  android:id ="@+id/Button01"  android:layout_height ="wrap_content"  android:layout_width ="fill_parent"  android:text ="@string/txtInfo" ></ Button >
</ LinearLayout >

  然后修改androidmanfest.xml,加入下面这段配置,让HelloAndroid满足上述两个条件:

 <activity android:name="HelloAndroid" android:label="@string/txtInfo">
            <intent-filter>
                <action android:name="com.android.notepad.action.HELLO_TEST" />
                <category android:name="android.intent.category.ALTERNATIVE"/>
                <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
            </intent-filter>
        </activity>

  好了,运行下试试,哎,还是没有动态菜单项加入呀!

  怎么回事呢?查看代码后发现,原来是onPrepareOptionsMenu搞的鬼!这个函数在onCreateOptionsMenu之后运行,下面这段代码中,由于Menu.CATEGORY_ALTERNATIVE是指向同一个组,所以把onCreateOptionsMenu中设置的菜单项给覆盖掉了,而由于onPrepareOptionsMenu没有给Menu.CATEGORY_ALTERNATIVE附新值,故Menu.CATEGORY_ALTERNATIVE还是为空。

  Intent intent = new Intent(null, uri);

  intent.addCategory(Intent.CATEGORY_ALTERNATIVE);

  menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0, null, specifics, intent, 0,items);

好的,那我们暂时把上面这几句给注释掉,当然,也可以不注释这几句,在onCreateOptionsMenu中改groupid号,即将Menu.CATEGORY_ALTERNATIVE改为Menu.first,其他的也行,但注意不要改为menu.none,这样会覆盖掉

  menu.add(0, MENU_ITEM_INSERT, 0, R.string.menu_insert)

  .setShortcut('3', 'a')

  .setIcon(android.R.drawable.ic_menu_add);

  添加的菜单。因为menu.none也为0

  运行后就可以看到动态菜单出来了!

上面这个options menu是在NotesList界面上没有日志列表选中的情况下生成的,若先选中一个日志,然后再点”menu”,则生成的options menu是下面这样的:

哎,又动态增加了两个菜单项”Edit note”和”Edit title”,这又是如何动态加入的呢?这就是onPrepareOptionsMenu的功劳了。

  Uri uri = ContentUris.withAppendedId(getIntent().getData(), getSelectedItemId());

  首先获取选中的日志(若没有选择,则uri为空)

  Intent[] specifics = new Intent[1];

  specifics[0] = new Intent(Intent.ACTION_EDIT, uri);

  MenuItem[] items = new MenuItem[1];

  然后为选中的日志创建一个intent,操作类型为Intent.ACTION_EDIT,数据为选中日志的URI.于是会为选中的日志创建一个”Edit note”菜单项。

  Intent intent = new Intent(null, uri);

  intent.addCategory(Intent.CATEGORY_ALTERNATIVE);

  menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0, null, specifics, intent, 0,

  items);

  这几句和上面onCreateOptionsMenu函数中类似,用于动态增加菜单项,若某一个activity,其类型是”android.intent.category.ALTERNATIVE”,数据是”vnd.android.cursor.item/vnd.google.note”的话,系统就会为这个activity增加一个菜单项。在androidmanfest.xml中查看后发现,TitleEditor这个activity符合条件,于是系统就为TitleEditor这个activity动态添加一个菜单项”Edit title”。

 

  else {

  menu.removeGroup(Menu.CATEGORY_ALTERNATIVE);

  }

  若日志列表为空,则从菜单中删除组号为Menu.CATEGORY_ALTERNATIVE的菜单项,只剩下”Add note”菜单项。

  处理“选中菜单项”事件

  菜单项选中事件的处理非常简单,通过onOptionsItemSelected来完成,这里只是简单地调用 startActivity(new Intent(Intent.ACTION_INSERT, getIntent().getData()));这个intent的操作类型为Intent.ACTION_INSERT,数据为日志列表的URI,即”content:// com.google.provider.NotePad/notes”

  @Override

  public boolean onOptionsItemSelected(MenuItem item) {

  switch (item.getItemId()) {

  case MENU_ITEM_INSERT:

  // Launch activity to insert a new item

  startActivity(new Intent(Intent.ACTION_INSERT, getIntent().getData()));

  return true;

  }

  return super.onOptionsItemSelected(item);

  }

  Context Menu

  下面介绍另一种菜单---上下文菜单,这通过重载onCreateContextMenu函数实现。

  首先确认已经选中了日志列表中的一个日志,若没选择,则直接返回。Cursor指向选中的日志项。

  Cursor cursor = (Cursor) getListAdapter().getItem(info.position);

  if (cursor == null) {

  // For some reason the requested item isn't available, do nothing

  return;

  }

  然后,设置上下文菜单的标题为日志标题

  // Setup the menu header

  menu.setHeaderTitle(cursor.getString(COLUMN_INDEX_TITLE));

  最后为上下文菜单增加一个菜单项

  // Add a menu item to delete the note

  menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_delete);

  对于上下文菜单项选中的事件处理,是通过重载onContextItemSelected实现的。

  switch (item.getItemId()) {

  case MENU_ITEM_DELETE: {

  // Delete the note that the context menu is for

  Uri noteUri = ContentUris.withAppendedId(getIntent().getData(), info.id);

getContentResolver().delete(noteUri, null, null);

  return true;

  }

  }

  return false;

  }

  对于日志的删除,首先调用ContentUris.withAppendedId(getIntent().getData(), info.id);来拼接出待删除日志的URI.然后getContentResolver().delete(noteUri, null, null);调用下层的Content Provider去删除此日志。

  实验二

  来做个简单实验,在上述代码基础上增加一个上下文菜单项。首先在onCreateContextMenu函数中增加一个上下文菜单项:

  menu.add(0,MENU_ITEM_INSERT,0,R.string.menu_insert);

  然后为其在onContextItemSelected函数中增加一个处理过程:

  case MENU_ITEM_INSERT:

  {

  new AlertDialog.Builder(this).setIcon(R.drawable.app_notes)

  .setTitle(R.string.app_name).setMessage(R.string.error_message).setPositiveButton(R.string.button_ok, new OnClickListener(){

  public void onClick(DialogInterface dialog, int which) {

  // TODO Auto-generated method stub

  }

  }).show();

  return true;

  }

  实验结果如下:

 

 

 

 

intent (receiver)

刚才上面多次提到 intent , 那个 Intent 就是指的的我要讲的 intent, 那intent receiver又为什么呢,其实她和activity,service一样响应相对应的intent,只不过intent-receiver多为接收事 件,然后启动具体的activity,接收外部的intent ,它没有ui界面,有点像是:activity是老板,而intent-reciever像是秘书,intent-recieve为activity打理 各种事情,然后通知老板即可,其实intent-reciever和sevice是比较接近的,intent-receiver可以看成是 activity的代理,或者是输入事件型的service,是事件来了就处理,且很快能处理完,而service只是service是长生命周期的,往 往需要长时间运行,通常为多个activity服务,正因为这个,他们的资源回收优先级也不同。既然activity,intent- reciever,service都用到intent ,那我们来说说 intent 

 intent 究竟是什么呢?

    上面我们提到过, activity 是相互独立,一般他们是独立进程。但是刚才提到的面向服务要求又要求各个 activity 有高频率的交互,这个怎么解决。这个解决方案就是使用 intent.

其实我们可以将 intent 看成是 activity 通信的标准的。比如 Intent 中的内容告诉了系统激发 intent  activity 需要什么服务,服务者 activity 应该满足的条件。然后就是 android 系统的事了,它负责找出符合条件的 activity 服务者,并将 intent  activity 服务者,然后这个 acitivity 根据 intent 中剩余的信息做出相应的操作。

由上面可知, intent 包含两部分信息:

1.        activity 服务者的信息,这个是给 android 用来找到合适 activity 的。

2.        activity 服务者要做什么操作的信息,这个是给 activity 服务者用的。

我们就以 android 自带 notepad 实例程序来说这个过程;

      android  notepad 实例程序中,入口 activity  notelist 

这里有一个 onlistItenclick 函数。

protected void onListItemClick(ListView l, View v, int position, long id) {

        Uri uri = ContentUris.withAppendedId (getIntent().getData(), id);

       

        String action = getIntent().getAction();

        if (Intent. ACTION_PICK .equals(action) || Intent. ACTION_GET_CONTENT .equals(action)) {

            // The caller is waiting for us to return a note selected by

            // the user.  The have clicked on one, so return it now.

            setResult( RESULT_OK , new Intent().setData(uri));

        } else {

            // Launch activity to view/edit the currently selected item

             startActivity(new Intent(Intent.ACTION_EDIT , uri));

        }

这个函数在 listview  item 被单击时执行。

我们首先注意的就是这个函数。

startActivity( new Intent(Intent. ACTION_EDIT , uri));

这个函数有一个 intent 对象,这个就是我们上面谈到的。

new Intent(Intent.ACTION_EDIT , uri) 这个可以看出 intent 承载了两个数据,一个是 uri 对像,一个是 Intent. ACTION_EDIT , 这个其实就是一个字符串变量,系统定义一些常量,当然也可使用你自己定义的。

如上图是程序运行后的效果,当我们点 First 项时就会执行 onListItemClick, 然后就会执行 startActivity(new Intent(Intent.ACTION_EDIT , uri)); 这个函数然后就到 android 系统了 ,android 通过 intent 中的信息找到对应的 activity, 在这里是 NoteEditor activity ,然后就会显示如下 .

 

  

 android 是怎样找到的对应的 activity 的呢?

这个就是 intent 发挥作用的时候了。

下面我们就来讲讲:

     new Intent(Intent.ACTION_EDIT , uri)

这个函数中 Intent.ACTION_EDIT =” android.intent.action.EDIT”

Uri 中一个重要的元素是  Uristring

这个其实是 uri 特征标志,通过设断点,我们看下这里的 uri 值。

从上图可以看出是 Uristring =content://com.google.provider.NotePad /notes/1

然后我们看下 Androidmanfest.xml,

其中有这个

< provider android:name = "NotePadProvider"

            android:authorities = " com.google.provider.NotePad "

        />

发现没,也有 com.google.provider.NotePad ,这个是 content://com.google.provider.NotePad/notes/1 的一部分,同时

< activity android:name = "NoteEditor"

            android:theme = "@android:style/Theme.Light"

            android:label = "@string/title_note"

        >

            <!-- This filter says that we can view or edit the data of

                 a single note -->

            < intent-filter android:label = "@string/resolve_edit" >

                < action android:name = "android.intent.action.VIEW" />

                < action android:name = " android.intent.action.EDIT " />

                < action android:name = "com.android.notepad.action.EDIT_NOTE" />

                < category android:name = "android.intent.category.DEFAULT" />

                < data android:mimeType = "vnd.android.cursor.item/vnd.google.note" />

            </ intent-filter >

 

            <!-- This filter says that we can create a new note inside

                 of a directory of notes. -->

            < intent-filter >

                < action android:name = "android.intent.action.INSERT" />

                < category android:name = "android.intent.category.DEFAULT" />

                < data android:mimeType = "vnd.android.cursor.dir/vnd.google.note" />

            </ intent-filter >

 

        </ activity >

上面有 android.intent.action.EDIT 正好是

Intent.ACTION_EDIT =” android.intent.action.EDIT 

这个时候估计大家看出了一点端倪。

现在就开始进入 activity 选择机制:

在函数 startActivity 执行后,系统接管,然后系统通过获取 intent 中的 uri ,找到了 content://com.google.provider.NotePad/notes/1, 去掉开始的 content: 标识,得到 com.google.provider.NotePad/notes/1, 然后获取前面的 com.google.provider.NotePad ,然后就到注册了的 Androidmanfest.xml  authorities  com.google.provider.NotePad  provider ,这个就是后面要讲的 contentprovider, 然后就加载 provider 

< provider android:name = "NotePadProvider"

            android:authorities = " com.google.provider.NotePad "

        />

在这里是 NotePadProvider, 然后调用 NotePadProvider  gettype 函数得到服务的类型。

public String getType(Uri uri) {

        switch ( sUriMatcher .match(uri)) {

        case NOTES :

            return Notes. CONTENT_TYPE ;

 

        case NOTE_ID :

            return Notes. CONTENT_ITEM_TYPE ;

 

        default :

            throw new IllegalArgumentException( "Unknown URI " + uri);

        }

上面的 sUriMatcher.match 用来检测 uri 看自己能不能处理,在这里会返回 return Notes.CONTENT_ITEM_TYPE;

这个是 public static final String CONTENT_ITEM_TYPE = " vnd.android.cursor.item/vnd.google.note ";

sUriMatcher .match(uri) 返回值其实是由

sUriMatcher = new UriMatcher(UriMatcher. NO_MATCH );

        sUriMatcher .addURI(NotePad. AUTHORITY , "notes" , NOTES );

        sUriMatcher .addURI(NotePad. AUTHORITY , "notes/#" , NOTE_ID );

决定的。

如果我们人为的将前面 uri  string 改为 content://com.google.provider.NotePad/notes/100, 然后 match 会正常返回 Notes. CONTENT_ITEM_TYPE . 可以找到相应 activity ,但是在 activity 执行 managedQuery 时,调用 content-provider ,这个然后发现不存在 id=100 的数据项,就会异常,然后这个 activity 就会 stop.

在这里是正常返回了 vnd.android.cursor.item/vnd.google.note

然后系统将这个和 android.intent.action.EDIT   androidmanfest.xml 去找匹配的 activity.

< intent-filter android:label = "@string/resolve_edit" >

                < action android:name = "android.intent.action.VIEW" />

                < action android:name = " android.intent.action.EDIT " />

                < action android:name = "com.android.notepad.action.EDIT_NOTE" />

                < category android:name = "android.intent.category.DEFAULT" />

                < data android:mimeType = " vnd.android.cursor.item/vnd.google.note " />

            </intent-filter>

正好满足上面 noteedit activity  intent-filter 条件 , 这样就找到了 noteedit activity.

然后就加载这个类实例化,运行,然后就到了 notedit  oncreate 

protected void onCreate(Bundle savedInstanceState) {

        super .onCreate(savedInstanceState);

 

        final Intent intent = getIntent();

 

        // Do some setup based on the action being performed.

final String action = intent.getAction();

        if (Intent. ACTION_EDIT .equals(action)) {

            // Requested to edit: set that state, and the data being edited.

            mState = STATE_EDIT ;

            mUri = intent.getData();

        } else if (Intent. ACTION_INSERT .equals(action)) {

            // Requested to insert: set that state, and create a new entry

            // in the container.

            mState = STATE_INSERT ;

            mUri = getContentResolver().insert(intent.getData(), null );

 

            // If we were unable to create a new note, then just finish

            // this activity.  A RESULT_CANCELED will be sent back to the

            // original activity if they requested a result.

            if ( mUri == null ) {

                 Log.e ( TAG , "Failed to insert new note into " + getIntent().getData());

                finish();

                return ;

            }

 

            // The new entry was created, so assume all will end well and

            // set the result to be returned.

            setResult( RESULT_OK , ( new Intent()).setAction( mUri .toString()));

 

        } else {

            // Whoops, unknown action!  Bail.

            Log.e ( TAG , "Unknown action, exiting" );

            finish();

            return ;

        }

 

         // Set the layout for this activity.  You can find it in res/layout/note_editor.xml

        setContentView(R.layout. note_editor );

       

        // The text view for our note, identified by its ID in the XML file.

        mText = (EditText) findViewById(R.id. note );

 

        // Get the note!

        mCursor = managedQuery( mUri , PROJECTION , null , null , null );

 

        // If an instance of this activity had previously stopped, we can

        // get the original text it started with.

        if (savedInstanceState != null ) {

            mOriginalContent = savedInstanceState.getString( ORIGINAL_CONTENT );

        }

}

 

上面 mCursor = managedQuery( mUri , PROJECTION , null , null , null ); 其实就是返回符合要求的数据 , 然后就是显示而已。

而其中 managedQuery 函数,又会用到 content-provider ,这里是 notepadprovider, 它创建一个 notepadprovider 实例,然后调用 query 函数,这个函数以 uri 类型的参数为依据返回合适的数据,这个 query 函数就是我们具体 content-provider 要实现的了,如果是用 sqlite, 那就通过 sqlitedatabase 取数据,如果是文件存储的,就通过 ifile 相关接口读取数据,其实在 query 函数中, content://com.google.provider.NotePad/notes/1, 后面的 /notes/1 才用到,对于 sqlite 实现的 content-privide:  notes 对应一个数据库的 table  1 是这个 table  id  

这个在如下可以看到

Contentprovider  query 函数里

case NOTE_ID :

        qb.setTables( NOTES_TABLE_NAME );

        qb.setProjectionMap( sNotesProjectionMap );

         qb.appendWhere(Notes. _ID + "=" + uri.getPathSegments().get(1) );

    break ;

上面讲的 Intent 包含的是 uri,action 对象 , 其实还有一种指定方式,这个下一章节再讲。

 

前面说到intent的另外一种赋值方式:

这个就是指定其component属性(调用setComponent(ComponentName)或者setClass(Context, Class)来指定)。通过这种方式的赋值的intent,其实就直接指出了activity类,这样就没有必要到androidmanfest.xmlprovider,然后通过providergettype返回加上action来找activity了,而是直接创建activity类的实例。

其实android还提供了一种创建activity的方式,那就是动态menu的方式,通过

public boolean onCreateOptionsMenu(Menu menu) {

        super.onCreateOptionsMenu(menu);

 

        // This is our one standard application action -- inserting a

        // new note into the list.

        menu.add(0, MENU_ITEM_INSERT, 0, R.string.menu_insert)

                .setShortcut('3', 'a')

                .setIcon(android.R.drawable.ic_menu_add);

 

        // Generate any additional actions that can be performed on the

        // overall list.  In a normal install, there are no additional

        // actions found here, but this allows other applications to extend

        // our menu with their own actions.

        Intent intent = new Intent(null, getIntent().getData());

        intent.addCategory(Intent.CATEGORY_ALTERNATIVE);

        menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,

                new ComponentName(this, NotesList.class), null, intent, 0, null);

 

        return true;

}

上面的addIntentOptions就是这种方式。

这个函数其实是,系统通过Intent查询是否有符合条件的activity,这个过程和上面的一样,如果找到了,就生成一个menu,这个menu的名字是androidmanfest.xml中的符合条件的activity中的label中指定的字符。

<intent-filter android:label="@string/resolve_edit">

如这个就是string/resolve_edit,这个也是指向一个string资源。

在上面的onCreateOptionsMenu中:

由于intenturicontent://com.google.provider.NotePad/notes,因此得到的typevnd.android.cursor.dir/vnd.google.note

categoryIntent.CATEGORY_ALTERNATIVE

然后到androidmanfest.xml去找,大家自己看一下,应该找不到合适的。这样也是为什么menu只有一个add_note

如果修改Intent.CATEGORY_ALTERNATIVEIntent.CATEGORY_DEFAULT,然后

再执行,这个时相当于uri=content://com.google.provider.NotePad/notes

category=android.intent.category.DEFAULT

androidmanfest.xml查找,发现有一个满足。

 <intent-filter>

                <action android:name="android.intent.action.INSERT" />

                <category android:name="android.intent.category.DEFAULT" />

                <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />

            </intent-filter>

但是执行发现没有增加菜单,为什么啊,其实这个是是由于onCreateOptionsMenuonPrepareOptionsMenu函数中都有menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE),相当在同一个groupid建菜单,这样后一个执行的会覆盖掉前一个的。

onPrepareOptionsMenuonCreateOptionsMenu后执行,所以onPrepareOptionsMenu中覆盖了onCreateOptionsMenu添加的菜单,而由于onPrepareOptionsMenu没有给Menu.CATEGORY_ALTERNATIVE附新值,故Menu.CATEGORY_ALTERNATIVE还是为空。

 

怎么解决,好解决,在onCreateOptionsMenu中改groupid号,即将Menu.CATEGORY_ALTERNATIVE改为Menu.first,其他的也行,但注意不要改为menu.none,这样会覆盖掉

menu.add(0, MENU_ITEM_INSERT, 0, R.string.menu_insert)

                .setShortcut('3', 'a')

                .setIcon(android.R.drawable.ic_menu_add);

添加的菜单。因为menu.none也为0

改完后运行可以看到。

 

 

 

总结下函数
int android.view.Menu.addIntentOptions(int groupId, int itemId, int order, ComponentName caller, Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems)

函数参数分析:

1.       groupid就是菜单组的编号;

2.       itemId

3.       Call就是发起activityactivity

4.       Specifics 是用来指定菜单顺序的,一般以uri+action来选择的activity

5.       Intent 这个一般式uri+categroy形式来选择的activity

6.       outSpecificItems这个是返回MenuItem值,用来给你跟多的设置菜单。

显示的菜单是满足specifics和满足intent的所有activity,然后显示时是满足specifics的菜单先显示。

actionandroid.app.action.MAIN,类型为 android.app.category.LAUNCHERactivity是特殊的activity,也就是入口activity,当一个应用执行时,其实是到androidmandfest.xml查看

actionandroid.app.action.MAIN,类型为 android.app.category.LAUNCHERactivity,如果发现就创建这个activity实例运行,如果没发现就不启动,大家可以试着去将android.app.action.MAIN改为android.app.action.MAIN1,然后运行,你发现,运行不了,没有窗体创建。

为什么要这样呢,因为android程序中没有main等函数,系统不知道该从哪个activity开始运行,必须有个约定。

 

service

service其实可以看成是activity的简化版,activity除去主窗体的功能外,就是service了。

一个简单的Servce实例如下:

package com.wissen.testApp.service;

public class MyService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Toast.makeText(this,
Service created…”, Toast.LENGTH_LONG).show();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Toast.makeText(this,
Service destroyed…”, Toast.LENGTH_LONG).show();
    }
}

上例中的这个Service主要做了这些事:当服务创建和销毁时通过界面消息提示用户。
Android中的其它部件一样, Service也会和一系列Intent关联。Service的运行入口需要在AndroidManifest.xml中进行配置,如下:

<service class=.service.MyService>
    <intent-filter>
        <action android:value=
com.wissen.testApp.service.MY_SERVICE
/>
    </intent-filter>
</service>

 之后我们的Service就可以被其他代码所使用了。
这个和activity几乎没什么差别,就是将

<activity android:name="NoteEditor"…>

改成<service class=.service.MyService>


使用Service:
  
应用程序可以通过调用 Context.startService方法来启动Service。如果当前没有指定的Service实例被创建,则该方法会调用 ServiceonCreate方法来创建一个实例;否则调用ServiceonStart方法。参见下列代码:

Intent serviceIntent = new Intent();
serviceIntent.setAction(
com.wissen.testApp.service.MY_SERVICE);
startService(serviceIntent);//
也许你会说为什么没有contexta啊,因为activity                      context的继承,也就是context上面等价this. startService

也就是context. startService

activity的使用也没什么差别,将startActivity

 变成startService,其他的就不详谈。

content-provider

      其实在activityintent都说到了content-provider.不过说的只是providergettype功能在寻找activity的作用,其实provider还有其他的作用,且是主要作用,那就是内容提供商。应用程序可以在Content Provider中执行如下操作:

 

Content-provider要提供五个功能。


查询数据

修改数据

添加数据

删除数据

返回数据类型

分别对应:

public int delete(Uri uri, String selection, String[] selectionArgs) {

       // TODO Auto-generated method stub

       return 0;

    }

 

    @Override

    public String getType(Uri uri) {

       // TODO Auto-generated method stub

       return null;

    }

 

    @Override

    public Uri insert(Uri uri, ContentValues values) {

       // TODO Auto-generated method stub

       return null;

    }

 

    @Override

    public boolean onCreate() {

       // TODO Auto-generated method stub

       return false;

    }

 

    @Override

    public Cursor query(Uri uri, String[] projection, String selection,

           String[] selectionArgs, String sortOrder) {

       // TODO Auto-generated method stub

       return null;

    }

 

    @Override

    public int update(Uri uri, ContentValues values, String selection,

           String[] selectionArgs) {

       // TODO Auto-generated method stub

       return 0;

    }

这些都要你自己实现,不同的实现就是对应不同的content-provider

但是activity使用content-provider不是直接创建一个对象,然后调用这些具体方法。

而是调用managedQuerygetContentResolver().delete,update等来实现,这些函数其实是先找到符合条件的content-provider,然后再调用具体的content-provider的函数来实现,那又是怎么找到content-provider,就是通过uri中的authority来找到content-provider,这些都是通过系统完成,应用不用管,这样有效的隔离了应用和内容提供商具体实现。

   利用uri如何找到content-provider在前面已经讲过了,就不讲了。

到此为止,android程序的框架就讲完了,具体就看对API的把握,及其他内容的深入了解了。

后记:

Anroid平台:

  非常佩服googlegoogleandroid刚出来就这么引人瞩目,且吸引这么都人去从事这个,这就是google的强大。android不仅仅是一个优化了的嵌入式linux操作系统,也不仅仅是java开发环境,也不仅仅是改版的java虚拟机Dalvik,也不仅仅是创新android窗体系统,而是整个平台,这一整个平台的各个部分google都针对嵌入式应用做了优化。结果是,这平台大大降低了开发者进入门槛,只要你会java你就可以开发,同时平台的设计考虑,使得其在嵌入式终端上运行的效率也能得到保证,估计也只有google这种大公司,极具创新的公司才有这种实力和魄力。google的几十万linux服务器可以看出其在Linux上的实力,同时google的创新能力不让人怀疑其能够提出一些创新的框架,有了这些,不难看出google搞出android是实至名归。

   估计大家当初用c#就是看好c#程序的开发非常方便,容易上手,现在嵌入式的软件的开发随着android平台的诞生也将变得如此容易,也难怪越来越多的手机厂商将采用android平台。

   同时手机应用商店的模式,使得应用程序员可以靠开发养活自己,靠开发赚钱,靠开发支撑一个公司,所以现在越来越多的商业公司切入到android应用软件开发中了,而不单单是个人或团队程序员的专利了。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值