Android 实例剖析之 notepad

系统 1989 0

简介

  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文件如下:

<? xmlversion="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.xml provider ,然后通过 provider gettype 返回加上 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 中:

由于 intent uri content://com.google.provider.NotePad/notes ,因此得到的 type vnd.android.cursor.dir/vnd.google.note

category Intent. CATEGORY_ALTERNATIVE

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

如果修改 Intent.CATEGORY_ALTERNATIVE Intent.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 >

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

onPrepareOptionsMenu onCreateOptionsMenu 后执行,所以 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

改完后运行可以看到。

Android 实例剖析之 notepad

总结下函数
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 就是发起 activity activity

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

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

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

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

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

action android.app.action.MAIN, 类型为 android.app.category.LAUNCHER activity, 如果发现就创建这个 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 实例被创建,则该方法会调用 Service onCreate 方法来创建一个实例;否则调用 Service onStart 方法。参见下列代码:

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

其实在 activity intent 都说到了 content-provider. 不过说的只是 provider gettype 功能在寻找 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 不是直接创建一个对象,然后调用这些具体方法。

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

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

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

后记:

Anroid 平台 :

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

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

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

Android 实例剖析之 notepad


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论