Android 中的context, service,active和intent使用详解

来源:互联网 发布:金融数据分析 编辑:程序博客网 时间:2024/06/05 18:11

在一个Android应用中,主要是由四种组件组成的,这四种组件分别是Context,Activity,Intent,Service。

Content被译为上下文,是应用程序中心,应用程序所有功能可以通过他访问。Activity活动,可以理解为窗口,是人机交互的核心,所以又被人们说成是所有程序的根本。Intent意图,负责所有Activity窗口通讯。Service服务是相对Activity来说的,不需要人机交互,但可以为Activity提供交互必需要的一些东西。这四种组件是独立的,它们之间可以互相调用,协调工作,最终组成一个真正的Android应用。

 

1.context ,用来获得应用程序信息。Context位于framework package的android.content.Context中,其实该类为LONG型,类似Win32中的Handle句柄。很多方法需要通过 Context才能识别调用者的实例:比如说Toast的第一个参数就是Context,一般在Activity中我们直接用this代替,代表调用者的实例为Activity,如:Button myButton =newButton(this);这里的this就是Context,而到了一个button的onClick(Viewview)等方法时,我们用this时就会报错,所以我们可能使用ActivityName.this来解决,主要原因是因为实现Context的类主要有Android特有的几个模型。

a:在文档中声明如下,方法非常丰富。

// 编译自ContextWrapper.java (版本 1.5:49.0,超级位)

public class android.content.ContextWrapper extendsandroid.content.Context {

  publicContextWrapper(android.content.Context base);

  protected voidattachBaseContext(android.content.Context base);

  public android.content.ContextgetBaseContext();

  public android.content.res.AssetManagergetAssets();

  publicandroid.content.res.Resources getResources();

  publicandroid.content.pm.PackageManager getPackageManager();

  publicandroid.content.ContentResolver getContentResolver();

  public android.os.LoopergetMainLooper();

  public android.content.ContextgetApplicationContext();

  public void setTheme(int resid);

  publicandroid.content.res.Resources.Theme getTheme();

  public java.lang.ClassLoadergetClassLoader();

  public java.lang.StringgetPackageName();

  public android.content.pm.ApplicationInfogetApplicationInfo();

  public java.lang.StringgetPackageResourcePath();

  public java.lang.StringgetPackageCodePath();

  publicandroid.content.SharedPreferences getSharedPreferences(java.lang.String name,int mode);

  public java.io.FileInputStreamopenFileInput(java.lang.String name) throws java.io.FileNotFoundException;

  public java.io.FileOutputStreamopenFileOutput(java.lang.String name, int mode) throwsjava.io.FileNotFoundException;

  public boolean deleteFile(java.lang.Stringname);

  public java.io.FilegetFileStreamPath(java.lang.String name);

  public java.lang.String[]fileList();

  public java.io.File getFilesDir();

  public java.io.FilegetExternalFilesDir(java.lang.String type);

  public java.io.File getObbDir();

  public java.io.File getCacheDir();

  public java.io.FilegetExternalCacheDir();

  public java.io.FilegetDir(java.lang.String name, int mode);

   publicandroid.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.Stringname, int mode, android.database.sqlite.SQLiteDatabase.CursorFactoryfactory); 

  publicandroid.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.Stringname, int mode, android.database.sqlite.SQLiteDatabase.CursorFactory factory,android.database.DatabaseErrorHandler errorHandler);

  public booleandeleteDatabase(java.lang.String name);

  public java.io.FilegetDatabasePath(java.lang.String name);

  public java.lang.String[] databaseList();

public android.content.ComponentNamestartService(android.content.Intent service);

public android.content.ContextcreatePackageContext(java.lang.String packageName, int flags) throwsandroid.content.pm.PackageManager$NameNotFoundException;

public android.content.IntentregisterReceiver(android.content.BroadcastReceiver receiver,android.content.IntentFilter filter

public android.content.IntentregisterReceiver(android.content.BroadcastReceiver receiver,android.content.IntentFilter filter, java.lang.String broadcastPermission,android.os.Handler scheduler);

public android.graphics.drawable.DrawablegetWallpaper();

publicandroid.graphics.drawable.Drawable peekWallpaper();

public booleanbindService(android.content.Intent service, android.content.ServiceConnectionconn, int flags);

public boolean isRestricted();

public booleanstartInstrumentation(android.content.ComponentName className, java.lang.StringprofileFile, android.os.Bundle arguments);

public booleanstopService(android.content.Intent name);

public intcheckCallingOrSelfPermission(java.lang.String permission);

public intcheckCallingOrSelfUriPermission(android.net.Uri uri, int modeFlags);

public intcheckCallingPermission(java.lang.String permission);

public intcheckCallingUriPermission(android.net.Uri uri, int modeFlags);

public intcheckPermission(java.lang.String permission, int pid, int uid);

public intcheckUriPermission(android.net.Uri uri, int pid, int uid, int modeFlags);

public intcheckUriPermission(android.net.Uri uri, java.lang.String readPermission,java.lang.String writePermission, int pid, int uid, int modeFlags);

public intgetWallpaperDesiredMinimumHeight();

public intgetWallpaperDesiredMinimumWidth();

public java.lang.ObjectgetSystemService(java.lang.String name);

public void clearWallpaper() throwsjava.io.IOException;

public void enforceCallingOrSelfPermission(java.lang.Stringpermission, java.lang.String message);

public voidenforceCallingOrSelfUriPermission(android.net.Uri uri, int modeFlags,java.lang.String message);

public voidenforceCallingPermission(java.lang.String permission, java.lang.Stringmessage);

public voidenforceCallingUriPermission(android.net.Uri uri, int modeFlags,java.lang.String message);

public voidenforcePermission(java.lang.String permission, int pid, int uid,java.lang.String message);

public void enforceUriPermission(android.net.Uriuri, int pid, int uid, int modeFlags, java.lang.String message);

public voidenforceUriPermission(android.net.Uri uri, java.lang.String readPermission,java.lang.String writePermission, int pid, int uid, int modeFlags,java.lang.String message);

public voidgrantUriPermission(java.lang.String toPackage, android.net.Uri uri, intmodeFlags);

public voidremoveStickyBroadcast(android.content.Intent intent);

public voidrevokeUriPermission(android.net.Uri uri, int modeFlags);

public void sendBroadcast(android.content.Intentintent);

public voidsendBroadcast(android.content.Intent intent, java.lang.StringreceiverPermission);

public voidsendOrderedBroadcast(android.content.Intent intent, java.lang.StringreceiverPermission);

public void sendOrderedBroadcast(android.content.Intentintent, java.lang.String receiverPermission, android.content.BroadcastReceiverresultReceiver, android.os.Handler scheduler, int initialCode, java.lang.StringinitialData, android.os.Bundle initialExtras);

public void sendStickyBroadcast(android.content.Intentintent

public voidsendStickyOrderedBroadcast(android.content.Intent intent,android.content.BroadcastReceiver resultReceiver, android.os.Handler scheduler,int initialCode, java.lang.String initialData, android.os.BundleinitialExtras);

public voidsetWallpaper(android.graphics.Bitmap bitmap) throws java.io.IOException;

public voidsetWallpaper(java.io.InputStream data) throws java.io.IOException;

public voidstartActivities(android.content.Intent[] intents);

public voidstartActivity(android.content.Intent intent);

public voidstartIntentSender(android.content.IntentSender intent, android.content.IntentfillInIntent, int flagsMask, int flagsValues, int extraFlags) throwsandroid.content.IntentSender$SendIntentException;

public voidunbindService(android.content.ServiceConnection conn);

public voidunregisterReceiver(android.content.BroadcastReceiver receiver);

}

B:Context提供了关于应用环境全局信息的接口。它是一个抽象类,它的执行被Android系统所提供。它允许获取以应用为特征的资源和类型。同时启动应用级的操作,如启动Activity,broadcasting和接收intents。

C:我们最常用的是其中的GET方法,通过这些get方法可以获取应用环境全局信息:

1.public abstract Context getApplicationContext() 返回应用的上下文,生命周期是整个应用,应用摧毁它才摧毁 

2.publicabstract ApplicationInfo getApplicationInfo() 
返回上下文包信息.

如:   //得到application对象

  ApplicationInfo appInfo = getApplicationInfo();

  //得到该图片的id(name 是该图片的名字,"drawable" 是该图片存放的目录,appInfo.packageName是应用程序的包)

  int resID = getResources().getIdentifier(name, "drawable",appInfo.packageName);

3.public abstractContentResolver getContentResolver() 
为包返回一个内容解析器

4.public abstractPackageManager getPackageManager() 
获得资源包信息,常用于获取安装信息,
如:
ArrayList appList = new ArrayList(); //用来存储获取的应用信息数据

List packages= getPackageManager().getInstalledPackages(0);

for(inti=0;PackageInfo packageInfo = packages.get(i);

AppInfotmpInfo = new AppInfo();

tmpInfo.appName= packageInfo.applicationInfo.loadLabel(getPackageManager()).toString();

tmpInfo.packageName= packageInfo.packageName;

tmpInfo.versionName= packageInfo.versionName;

tmpInfo.versionCode= packageInfo.versionCode;

tmpInfo.appIcon= packageInfo.applicationInfo.loadIcon(getPackageManager());

appList.add(tmpInfo);

}

5.public abstractString getPackageName() 
获得包名

6.public abstractResources getResources() 
获得包资源实例

7.public abstractSharedPreferences getSharedPreferences(Stringname, int mode) 
获得SharedPreferences配置参数

8.public finalString getString(int resId) 
获得包里的资源字符串信息

9.public abstractObject getSystemService(String name) 
返回系统服务句柄

10.public abstract File getCacheDir();获取缓存目录,

11.publicabstract Drawable getWallpaper,获取壁纸,

12.publicabstract void sendBroadcast(Intent intent);,发送广播,

13.publicabstract Intent registerReceiver(BroadcastReceiver receiver,IntentFilterfilter);注册广播接收器,

14.publicabstract ComponentName startService(Intent service);开启关闭Service服务,绑定服务等,

D.Application context和Activity context。

这是两种不同的context,也是最常见的两种。第一种中context的生命周期与Application的生命周期相关的,context随 着Application的销毁而销毁,第二种中的context跟Activity的生命周期是相关的,但是对一个Application来 说,Activity可以销毁几次,那么属于Activity的context就会销毁多次。至于用哪种context,得看应用场景,个人感觉用 Activity的context好一点,不过也有的时候必须使用Application的context。

 

2 .Android中,Activity是所有程序的根本,所有程序的流程都运行在Activity之中,Activity具有自己的生命周期. 由系统控制生命周期,程序无法改变,但可以用onSaveInstanceState保存其状态对于Activity,关键是其生命周期的把握(如下图),其次就是状态的保存和恢复(onSaveInstanceState onRestoreInstanceState),以及Activity之间的跳转和数据传输(intent)。

 

Activity中常用的函数有SetContentView()  findViewById()    finish()   startActivity(),其生命周期涉及的函数有:

void onCreate(Bundle savedInstanceState)
void onStart()
void onRestart()
void onResume()
void onPause()
void onStop()
void onDestroy()

注意的是,Activity的使用需要在Manifest文件中添加相应的<Activity>,并设置其属性和intent-filter。

在 Android 中,多数情况下每个程序都是在各自独立的 Linux进程中运行的。当一个程序或其某些部分被请求时,它的进程就“出生”了;当这个程序没有必要再运行下去且系统需要回收这个进程的内存用于其他程序时,这个进程就“死亡”了。可以看出,Android程序的生命周期是由系统控制而非程序自身直接控制

在 Android 系统中,当某个 activity调用 startActivity(myIntent) 时,系统会在所有已经安装的程序中寻找其intent filter 和 myIntent 最匹配的一个activity,启动这个进程,并把这个 intent 通知给这个 activity。这就是一个程序的“生”。比如我们在 Home application 中选择 “Web browser”,系统会根据这个 intent 找到并启动 Web browser 程序,显示 Web browser 的一个 activity 供我们浏览网页(这个启动过程有点类似我们在在个人电脑上双击桌面上的一个图标,启动某个应用程序)。在 Android 中,所有的应用程序“生来就是平等的”,所以不光 Android 的核心程序甚至第三方程序也可以发出一个 intent 来启动另外一个程序中的一个 activity。Android 的这种设计非常有利于“程序部件”的重用。

  一个 Android 程序的进程是何时被系统结束的呢?通俗地说,一个即将被系统关闭的程序是系统在内存不足(low memory)时,根据“重要性层次”选出来的“牺牲品”。一个进程的重要性是根据其中运行的部件和部件的状态决定的。各种进程按照重要性从高到低排列如下:
  1. 前台进程。这样的进程拥有一个在屏幕上显示并和用户交互的 activity 或者它的一个IntentReciver 正在运行。这样的程序重要性最高,只有在系统内存非常低,万不得已时才会被结束。
  2. 可见进程。在屏幕上显示,但是不在前台的程序。比如一个前台进程以对话框的形式显示在该进程前面。这样的进程也很重要,它们只有在系统没有足够内存运行所有前台进程时,才会被结束。
  3. 服务进程。这样的进程在后台持续运行,比如后台音乐播放、后台数据上传下载等。这样的进程对用户来说一般很有用,所以只有当系统没有足够内存来维持所有的前台和可见进程时,才会被结束。
  4. 后台进程。这样的程序拥有一个用户不可见的 activity。这样的程序在系统内存不足时,按照LRU的顺序被结束。
  5. 空进程。这样的进程不包含任何活动的程序部件。系统可能随时关闭这类进程。
 

从 某种意义上讲,垃圾收集机制把程序员从“内存管理噩梦”中解放出来,而 Android 的进程生命周期管理机制把用户从“任务管理噩梦”中解放出来。我见过一些 Nokia S60 用户和 Windows Mobile 用户要么因为长期不关闭多余的应用程序而导致系统变慢,要么因为不时查看应用程序列表而影响使用体验。Android 使用 Java 作为应用程序 API,并且结合其独特的生命周期管理机制同时为开发者和使用者提供最大程度的便利。

Activity有三种基本状态:

  1. Active:处于屏幕前景(当前task的栈顶Activity处于Active状态),同一时刻只能有一个Activity处于Active状态;
  2. Paused状态:处于背景画面画面状态,失去了焦点,但依然是活动状态;
  3. stopped:不可见,但依然保持所有的状态和内存信息。

可以调用finish()结束处理Paused或者stopped状态的Activity。

 

3 Intent

Android中提供了Intent机制来协助 应用间的交互与通讯,Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对 应的组件,将 Intent传递给调用的组件,并完成组件的调用。Intent不仅可用于应用程序之间,也可用于应用程序内部的Activity/Service之间的交互。因此,Intent在这里起着一个媒体中介的作用,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦。在SDK中给出了 Intent作用的表现形式为:

  • 通过Context.startActivity() orActivity.startActivityForResult() 启动一个Activity;
  • 通过 Context.startService() 启动一个服务,或者通过Context.bindService() 和后台服务交互;
  • 通过广播方法(比如 Context.sendBroadcast(),Context.sendOrderedBroadcast(), Context.sendStickyBroadcast()) 发给broadcast receivers。

Intent属性的设置,包括以下几点:(以下为XML中定义,当然也可以通过Intent类的方法来获取和设置)

(1)Action,也就是要执行的动作

SDk中定义了一些标准的动作,包括

onstant        Target component       Action
ACTION_CALL        activity        初始化一个通话.
ACTION_EDIT        activity       为用户的edit显示数据.
ACTION_MAIN        activity       Start up as the initial activity of a task, with no data input and no returnedoutput.
ACTION_SYNC        activity       Synchronize data on a server with data on the mobile device.
ACTION_BATTERY_LOW        broadcast receiver       A warning that the battery is low.
ACTION_HEADSET_PLUG        broadcast receiver       A headset has been plugged into the device, or unplugged from it.
ACTION_SCREEN_ON        broadcast receiver       The screen has been turned on.
ACTION_TIMEZONE_CHANGED        broadcast receiver       The setting for the time zone has changed.
VIEW_ACTION
PICK_ACTION
GET_CONTENT_ACTION
DIAL_ACTION

SENDTO_ACTION
ANSWER_ACTION
INSERT_ACTION
DELETE_ACTION
RUN_ACTION
LOGIN_ACTION
CLEAR_CREDENTIALS_ACTION
PICK_ACTIVITY_ACTION
WEB_SEARCH_ACTION

也可以自定义动作(自定义的动作在使用时,需要加上包名作为前缀,如"com.example.project.SHOW_COLOR”),并可定义相应的Activity来处理我们的自定义动作。

(2)Data,也就是执行动作要操作的数据

Android 中采用指向数据的一个URI来表示,如在联系人应用中,一个指向某联系人的URI可能为:content://contacts/1。对于不同的动作,其 URI数据的类型是不同的(可以设置type属性指定特定类型数据),如ACTION_EDIT指定Data为文件URI,打电话为tel:URI,访问 网络为http:URI,而由content provider提供的数据则为content: URIs。

(3)type(数据类型),显式指定Intent的数据类型(MIME)。一般Intent的数据类型能够根据数据本身进行判定,但是通过设置这个属性,可以强制采用显式指定的类型而不再进行推导。

(4)category(类 别),被执行动作的附加信息。例如 LAUNCHER_CATEGORY 表示Intent 的接受者应该在Launcher中作为顶级应用出现;而ALTERNATIVE_CATEGORY表示当前的Intent是一系列的可选动作中的一个,这 些动作可以在同一块数据上执行。还有其他的为

Constant Meaning

CATEGORY_BROWSABLE The target activity can be safely invoked by the browserto display data referenced by a link — for example, an image or an e-mailmessage.

CATEGORY_GADGET The activity can be embedded inside of another activitythat hosts gadgets. CATEGORY_HOMEThe activity displays the home screen, the first screenthe user sees when the device is turned on or when the HOME key is pressed.

 CATEGORY_LAUNCHER The activity can be the initial activity of a task and islisted in the top-level application launcher.

CATEGORY_PREFERENCE The target activity is a preference panel.

(5)component(组件),指定Intent的的目标组件的类名称。通常 Android会根据Intent 中包含的其它属性的信息,比如action、data/type、category进行查找,最终找到一个与之匹配的目标组件。但是,如果component这个属性有指定的话,将直接使用它指定的组件,而不再执行上述查找过程。指定了这个属性以后,Intent的其它所有属性都是可选的。

(6)extras(附加信息),是其它所有附加信息的集合。使用extras可以为组件提供扩展信息,比如,如果要执行“发送电子邮件”这个动作,可以将电子邮件的标题、正文等保存在extras里,传给电子邮件发送组件。

理解Intent的关键之一是理解清楚Intent的两种基本用法:一种是显式的Intent,即在构造Intent对象时就指定接收者;另一种是隐式的Intent,即Intent的发送者在构造Intent对象时,并不知道也不关心接收者是谁,有利于降低发送者和接收者之间的耦合。

对于显式Intent,Android不需要去做解析,因为目标组件已经很明确,Android需要解析的是那些隐式Intent,通过解析,将 Intent映射给可以处理此Intent的Activity、IntentReceiver或Service。        

Intent 解析机制主要是通过查找已注册在AndroidManifest.xml中的所有IntentFilter及其中定义的Intent,最终找到匹配的 Intent。在这个解析过程中,Android是通过Intent的action、type、category这三个属性来进行判断的,判断方法如下:

  • 如果Intent指明定了action,则目标组件的IntentFilter的action列表中就必须包含有这个action,否则不能匹配;
  • 如果Intent没有提供type,系统将从data中得到数据类型。和action一样,目标组件的数据类型列表中必须包含Intent的数据类型,否则不能匹配。
  • 如果Intent中的数据不是content: 类型的URI,而且Intent也没有明确指定它的type,将根据Intent中数据的scheme (比如 http: 或者mailto:) 进行匹配。同上,Intent 的scheme必须出现在目标组件的scheme列表中。
  • 如果Intent指定了一个或多个category,这些类别必须全部出现在组建的类别列表中。比如Intent中包含了两个类别:LAUNCHER_CATEGORY 和 ALTERNATIVE_CATEGORY,解析得到的目标组件必须至少包含这两个类别。

Intent-Filter的定义

一些属性设置的例子:

<action android:name="com.example.project.SHOW_CURRENT"/>

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

<dataandroid:mimeType="video/mpeg" android:scheme="http" . . ./>

<dataandroid:mimeType="image/*" />

<data android:scheme="http"android:type="video/*" />

完整的实例

<activityandroid:name="NotesList"android:label="@string/title_notes_list">
            
<intent-filter>
               
<actionandroid:name="android.intent.action.MAIN"/>
               
<categoryandroid:name="android.intent.category.LAUNCHER"/>
            
</intent-filter>
            
<intent-filter>
               
<actionandroid:name="android.intent.action.VIEW"/>
               
<actionandroid:name="android.intent.action.EDIT"/>
               
<actionandroid:name="android.intent.action.PICK"/>
               
<categoryandroid:name="android.intent.category.DEFAULT"/>
               
<dataandroid:mimeType="vnd.android.cursor.dir/vnd.google.note"/>
            
</intent-filter>
            
<intent-filter>
               
<actionandroid:name="android.intent.action.GET_CONTENT"/>
               
<categoryandroid:name="android.intent.category.DEFAULT"/>
               
<dataandroid:mimeType="vnd.android.cursor.item/vnd.google.note"/>
            
</intent-filter>
        
</activity>

Intent用法实例

1.无参数Activity跳转

Intent it = newIntent(Activity.Main.this, Activity2.class);

startActivity(it);  

 

2.向下一个Activity传递数据(使用Bundle和Intent.putExtras)

Intent it = newIntent(Activity.Main.this, Activity2.class);

Bundle bundle=newBundle();

bundle.putString("name", "This is from MainActivity!");

it.putExtras(bundle);       // it.putExtra(“test”, "shuju”);

startActivity(it);            // startActivityForResult(it,REQUEST_CODE);

 

对于数据的获取可以采用:

Bundle bundle=getIntent().getExtras();

String name=bundle.getString("name");

 

3.向上一个Activity返回结果(使用setResult,针对startActivityForResult(it,REQUEST_CODE)启动的Activity)

       Intent intent=getIntent();

       Bundle bundle2=new Bundle();

       bundle2.putString("name", "This is from ShowMsg!");

       intent.putExtras(bundle2);

       setResult(RESULT_OK, intent);

4.回调上一个Activity的结果处理函数(onActivityResult)

@Override

    protectedvoid onActivityResult(intrequestCode, int resultCode, Intent data) {

       // TODOAuto-generated method stub

       super.onActivityResult(requestCode, resultCode,data);

       if (requestCode==REQUEST_CODE){

           if(resultCode==RESULT_CANCELED)

                  setTitle("cancle");

           elseif (resultCode==RESULT_OK) {

                 String temp=null;

                 Bundle bundle=data.getExtras();

                 if(bundle!=null)   temp=bundle.getString("name");

                 setTitle(temp);

           }

       }

    }

下面是转载来的其他的一些Intent用法实例(转自javaeye)

显示网页
   1. Uri uri =Uri.parse("http://google.com");  
   2. Intent it = new Intent(Intent.ACTION_VIEW, uri);  
   3. startActivity(it);

显示地图
   1. Uri uri =Uri.parse("geo:38.899533,-77.036476");  
   2. Intent it = new Intent(Intent.ACTION_VIEW, uri);   
   3. startActivity(it);   
   4. //其他geo URI 範例  
   5. //geo:latitude,longitude  
   6. //geo:latitude,longitude?z=zoom  
   7. //geo:0,0?q=my+street+address  
   8. //geo:0,0?q=business+near+city  
   9. //google.streetview:cbll=lat,lng&cbp=1,yaw,,pitch,zoom&mz=mapZoom

路径规划
   1. Uri uri =Uri.parse("http://maps.google.com/maps?f=d&saddr=startLat%20startLng&daddr=endLat%20endLng&hl=en"); 
   2. Intent it = new Intent(Intent.ACTION_VIEW, uri);  
   3. startActivity(it);  
   4. //where startLat, startLng, endLat, endLng are a long with 6decimals like: 50.123456 

打电话
   1. //叫出拨号程序 
   2. Uri uri = Uri.parse("tel:0800000123");  
   3. Intent it = new Intent(Intent.ACTION_DIAL, uri);  
   4. startActivity(it);  
   1. //直接打电话出去  
   2. Uri uri = Uri.parse("tel:0800000123");  
   3. Intent it = new Intent(Intent.ACTION_CALL, uri);  
   4. startActivity(it);  
   5. //用這個,要在AndroidManifest.xml 中,加上 
   6. //<uses-permissionid="android.permission.CALL_PHONE" /> 

传送SMS/MMS
   1. //调用短信程序 
   2. Intent it = new Intent(Intent.ACTION_VIEW, uri);  
   3. it.putExtra("sms_body", "The SMStext");   
   4. it.setType("vnd.android-dir/mms-sms");  
   5. startActivity(it); 
   1. //传送消息 
   2. Uri uri = Uri.parse("smsto://0800000123");  
   3. Intent it = new Intent(Intent.ACTION_SENDTO, uri);  
   4. it.putExtra("sms_body", "The SMStext");  
   5. startActivity(it); 
   1. //传送 MMS  
   2. Uri uri =Uri.parse("content://media/external/images/media/23");  
   3. Intent it = new Intent(Intent.ACTION_SEND);   
   4. it.putExtra("sms_body", "sometext");   
   5. it.putExtra(Intent.EXTRA_STREAM, uri);  
   6. it.setType("image/png");   
   7. startActivity(it); 

传送 Email
   1. Uri uri = Uri.parse("mailto:xxx@abc.com");  
   2. Intent it = new Intent(Intent.ACTION_SENDTO, uri);  
   3. startActivity(it); 


   1. Intent it = new Intent(Intent.ACTION_SEND);  
   2. it.putExtra(Intent.EXTRA_EMAIL, "me@abc.com");  
   3. it.putExtra(Intent.EXTRA_TEXT, "The email bodytext");  
   4. it.setType("text/plain");  
   5. startActivity(Intent.createChooser(it, "Choose EmailClient")); 


   1. Intent it=new Intent(Intent.ACTION_SEND);    
   2. String[] tos={"me@abc.com"};    
   3. String[] ccs={"you@abc.com"};    
   4. it.putExtra(Intent.EXTRA_EMAIL, tos);    
   5. it.putExtra(Intent.EXTRA_CC, ccs);    
   6. it.putExtra(Intent.EXTRA_TEXT, "The email bodytext");    
   7. it.putExtra(Intent.EXTRA_SUBJECT, "The email subjecttext");    
   8. it.setType("message/rfc822");    
   9. startActivity(Intent.createChooser(it, "Choose EmailClient"));


   1. //传送附件
   2. Intent it = new Intent(Intent.ACTION_SEND);  
   3. it.putExtra(Intent.EXTRA_SUBJECT, "The email subjecttext");  
   4. it.putExtra(Intent.EXTRA_STREAM,"file:///sdcard/mysong.mp3");  
   5. sendIntent.setType("audio/mp3");  
   6. startActivity(Intent.createChooser(it, "Choose EmailClient"));

播放多媒体
       Uri uri =Uri.parse("file:///sdcard/song.mp3");  
       Intent it = new Intent(Intent.ACTION_VIEW,uri);  
       it.setType("audio/mp3");  
       startActivity(it); 
       Uri uri =Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI,"1");  
       Intent it = new Intent(Intent.ACTION_VIEW,uri);  
       startActivity(it);

Market 相关
1.       //寻找某个应用 
2.        Uri uri =Uri.parse("market://search?q=pname:pkg_name"); 
3.        Intent it = newIntent(Intent.ACTION_VIEW, uri);  
4.        startActivity(it);  
5.        //where pkg_name is the fullpackage path for an application 
1.        //显示某个应用的相关信息 
2.        Uri uri =Uri.parse("market://details?id=app_id");  
3.        Intent it = newIntent(Intent.ACTION_VIEW, uri); 
4.        startActivity(it);  
5.        //where app_id is the applicationID, find the ID   
6.        //by clicking on your applicationon Market home   
7.        //page, and notice the ID from theaddress bar

Uninstall 应用程序
1.        Uri uri =Uri.fromParts("package", strPackageName, null); 
2.        Intent it = newIntent(Intent.ACTION_DELETE, uri);   
3.        startActivity(it); 

 

4.Service的种类

  

按运行地点分类:

类别

区别

优点

缺点

应用

本地服务(Local)

该服务依附在主进程上,

服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。

主进程被Kill后,服务便会终止。

非常常见的应用如:HTC的音乐播放服务,天天动听音乐播放服务。

远程服务(Remote)

该服务是独立的进程,

服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。

该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。

一些提供系统服务的Service,这种Service是常驻的。

其实remote服务还是很少见的,并且一般都是系统服务。



按运行类型分类:

类别

区别

应用

前台服务

会在通知一栏显示 ONGOING 的 Notification,

当服务被终止的时候,通知一栏的 Notification 也会消失,这样对于用户有一定的通知作用。常见的如音乐播放服务。

后台服务

默认的服务即为后台服务,即不会在通知一栏显示 ONGOING 的 Notification。

当服务被终止的时候,用户是看不到效果的。某些不需要运行或终止提示的服务,如天气更新,日期同步,邮件同步等。

有 同学可能会问,后台服务我们可以自己创建 ONGOING 的Notification 这样就成为前台服务吗?答案是否定的,前台服务是在做了上述工作之后需要调用startForeground ( android 2.0 及其以后版本 )或 setForeground (android 2.0 以前的版本)使服务成为前台服务。这样做的好处在于,当服务被外部强制终止掉的时候,ONGOING 的Notification 任然会移除掉。

按使用方式分类:

类别

区别

startService 启动的服务

主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService

bindService 启动的服务

该方法启动的服务要进行通信。停止服务使用unbindService

startService 同时也 bindService 启动的服务

停止服务应同时使用stepService与unbindService

以上面三种方式启动的服务其生命周期也有区别,将在随后给出。

2、Service 与 Thread 的区别

  

很多时候,你可能会问,为什么要用 Service,而不用Thread 呢,因为用 Thread 是很方便的,比起Service 也方便多了,下面我详细的来解释一下。

1). Thread:Thread是程序执行的最小单元,它是分配CPU的基本单位。可以用Thread 来执行一些异步的操作。

2). Service:Service是android的一种机制,当它运行的时候如果是LocalService,那么对应的 Service 是运行在主进程的main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。因此请不要把 Service 理解成线程,它跟线程半毛钱的关系都没有!



既 然这样,那么我们为什么要用 Service 呢?其实这跟android 的系统机制有关,我们先拿 Thread 来说。Thread的运行是独立于 Activity 的,也就是说当一个Activity 被 finish 之后,如果你没有主动停止Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。



举 个例子:如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。



因 此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、 Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。



3、Service的生命周期

onCreate  onStart  onDestroy  onBind

1). 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都 在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建 Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的 Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。

2). 被绑定的服务的生命周期:如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用 Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。

3). 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate 始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止 Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。

4). 当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。

特别注意:

1、你应当知道在调用bindService 绑定到Service的时候,你就应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自      动解除,并且Service会自动停止);

2、你应当注意 使用startService 启动服务之后,一定要使用 stopService停止服务,不管你是否使用bindService;

3、 同时使用startService 与 bindService 要注意到,Service 的终止,需要unbindService与stopService同时调用,才能终止 Service,不管 startService 与 bindService 的调用顺序,如果先调用 unbindService 此时服务不会自动终止,再调用 stopService 之后服务才会停止,如果先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者之前调用 bindService 的 Context 不存在了(如Activity 被 finish 的时候)之后服务才会自动停止;

4、 当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同。

5、 在 sdk 2.0 及其以后的版本中,对应的 onStart 已经被否决变为了 onStartCommand,不过之前的 onStart 任然有效。这意味着,如果你开发的应用程序用的 sdk 为 2.0 及其以后的版本,那么你应当使用 onStartCommand 而不是 onStart。

4、startService 启动服务

  

想要用 startService  启动服务,不管Local 还是 Remote 我们需要做的工作都是一样简单。当然要记得在 Androidmanifest.xml 中注册 service。

根据上面的生命周期,我们便会给出 Service 中的代码框架:

1.         package com.newcj.test;

2.          

3.         import android.app.Service;

4.         import android.content.Intent;

5.         import android.os.IBinder;

6.          

7.         public class LocalService1 extends Service {

8.          

9.                 /**

10.               * onBind  Service 的虚方法,因此我们不得不实现它。

11.               * 返回 null,表示客服端不能建立到此服务的连接。

12.               */

13.              @Override

14.              public IBinder onBind(Intent intent){

15.                      returnnull;

16.              }

17.              

18.              @Override

19.              public void onCreate() {

20.                     super.onCreate();

21.              }

22.              

23.              @Override

24.              public void onStart(Intent intent,int startId) {

25.                     super.onStart(intent, startId);

26.              }

27.              

28.              @Override

29.              public void onDestroy() {

30.                     super.onDestroy();

31.              }

32.       

33.      }

复制代码

对应生命周期系统回调函数上面已经说明,在对应地方加上适当的代码即可。下面是启动与停止 Service 的代码:


1.         // 启动一个 Activity

2.         startActivity(new Intent(this, LocalService1.class));

3.         ...

4.         // 停止一个 Activity

5.         stopService(new Intent(this, LocalService1.class));

复制代码

对应的 Intent 为标志服务类的 Intent。5、Local 与 Remote 服务绑定  同样记得在Androidmanifest.xml 中注册 service1).Local 服务绑定:Local 服务的绑定较简单,首先在 Service 中我们需要实现 Service 的抽象方法 onBind,并返回一个实现 IBinder 接口的对象。Service 中的代码:

1.         package com.newcj.test;

2.          

3.         import android.app.Service;

4.         import android.content.Intent;

5.         import android.os.Binder;

6.         import android.os.IBinder;

7.          

8.         public class LocalService extends Service {

9.          

10.              /**

11.               *  Local Service 中我们直接继承 Binder 而不是 IBinder,因为 Binder 实现了 IBinder 接口,这样我们可以少做很多工作。

12.               * @author newcj

13.               */

14.              public class SimpleBinder extendsBinder{

15.                      /**

16.                      * 获取 Service 实例

17.                      * @return

18.                      */

19.                      publicLocalService getService(){

20.                             return LocalService.this;

21.                      }

22.                      

23.                      publicint add(int a, int b){

24.                             return a + b;

25.                      }

26.              }

27.              

28.              public SimpleBinder sBinder;

29.              

30.              @Override

31.              public void onCreate() {

32.                     super.onCreate();

33.                      // 创建 SimpleBinder

34.                      sBinder= new SimpleBinder();

35.              }

36.              

37.              @Override

38.              public IBinder onBind(Intent intent){

39.                      // 返回 SimpleBinder 对象

40.                      returnsBinder;

41.              }

42.       

43.      }

复制代码

上面的代码关键之处,在于 onBind(Intent) 这个方法 返回了一个实现了 IBinder 接口的对象,这个对象将用于绑定Service 的 Activity 与 Local Service 通信。下面是 Activity 中的代码:

1.         package com.newcj.test;

2.          

3.         import android.app.Activity;

4.         import android.content.ComponentName;

5.         import android.content.Context;

6.         import android.content.Intent;

7.         import android.content.ServiceConnection;

8.         import android.os.Bundle;

9.         import android.os.IBinder;

10.      import android.util.Log;

11.      import android.view.View;

12.      import android.view.View.OnClickListener;

13.       

14.      public class Main extends Activity {

15.              private final static String TAG ="SERVICE_TEST";

16.              private ServiceConnection sc;

17.              private boolean isBind;

18.       

19.          @Override

20.          public void onCreate(Bundle savedInstanceState) {

21.              super.onCreate(savedInstanceState);

22.              setContentView(R.layout.main);

23.              sc = new ServiceConnection() {

24.                             

25.                             @Override

26.                             public void onServiceDisconnected(ComponentNamename) {

27.       

28.                             }

29.                             

30.                             @Override

31.                             public void onServiceConnected(ComponentNamename, IBinder service) {

32.                                    LocalService.SimpleBinder sBinder =(LocalService.SimpleBinder)service;

33.                                    Log.v(TAG, "3 + 5 = " + sBinder.add(3, 5));

34.                                    Log.v(TAG, sBinder.getService().toString());

35.                             }

36.                      };

37.             findViewById(R.id.btnBind).setOnClickListener(newOnClickListener() {

38.                             

39.                             @Override

40.                             public void onClick(View v) {

41.                                    bindService(new Intent(Main.this, LocalService.class), sc,Context.BIND_AUTO_CREATE);

42.                                     isBind =true;

43.                             }

44.                      });

45.             findViewById(R.id.btnUnbind).setOnClickListener(newOnClickListener() {

46.                             

47.                             @Override

48.                             public void onClick(View v) {

49.                                    if(isBind){

50.                                            unbindService(sc);

51.                                            isBind = false;

52.                                     }

53.                             }

54.                      });

55.          }

56.      }

复制代码

在 Activity 中,我们通过 ServiceConnection 接口来取得建立连接 与 连接意外丢失的回调。bindService有三个参数,第一个是用于区分 Service 的Intent 与startService 中的 Intent 一致,第二个是实现了 ServiceConnection 接口的对象,最后一个是 flag 标志位。有两个flag,BIND_DEBUG_UNBIND 与 BIND_AUTO_CREATE,前者用于调试(详细内容可以查看javadoc 上面描述的很清楚),后者默认使用。unbindService 解除绑定,参数则为之前创建的ServiceConnection 接口对象。另外,多次调用 unbindService 来释放相同的连接会抛出异常,因此我创建了一个 boolean 变量来判断是否 unbindService 已经被调用过。运行结果:  2). Remote 服务绑定:Remote的服务绑定由于服务是在另外一个进程,因此需要用到 android 的 IPC 机制。这将又是一个很长的话题,因此,我打算写另外一篇 android 的 IPC 机制分析 ,并在其中进行详述,然后在这里更新链接,敬请关注。特别注意:1、 Service.onBind如果返回null,则调用 bindService 会启动 Service,但不会连接上 Service,因此 ServiceConnection.onServiceConnected 不会被调用,但你任然需要使用 unbindService 函数断开它,这样 Service 才会停止。  
6、创建前台服务  前 台服务的优点上面已经说明,但设置服务为前台服务,我们需要注意在 sdk 2.0 及其以后版本使用的方法是 startForeground 与 stopForeground,之前版本使用的是 setForeground ,因此如果你应用程序的最低运行环境要求是 2.0,那么这里可以直接运用新方法,如果运行环境是2.0以下,那么为了保证向后兼容性,这里必须使用反射技术来调用新方法。下面是我仿照 ApiDemos 重新敲的代码,对某些地方进行了修改,因此更具有说明性:

1.         package com.newcj.test;

2.          

3.         import java.lang.reflect.InvocationTargetException;

4.         import java.lang.reflect.Method;

5.          

6.         import android.app.Notification;

7.         import android.app.NotificationManager;

8.         import android.app.PendingIntent;

9.         import android.app.Service;

10.      import android.content.Context;

11.      import android.content.Intent;

12.      import android.os.IBinder;

13.       

14.      public class ForegroundService extends Service {

15.       

16.          private static final Class[] mStartForegroundSignature = newClass[] {

17.              int.class, Notification.class};

18.          private static final Class[] mStopForegroundSignature = newClass[] {

19.              boolean.class};

20.          private NotificationManager mNM;

21.          private Method mStartForeground;

22.          private Method mStopForeground;

23.          private Object[] mStartForegroundArgs = new Object[2];

24.          private Object[] mStopForegroundArgs = new Object[1];

25.              

26.              @Override

27.              public IBinder onBind(Intent intent){

28.                      returnnull;

29.              }

30.              

31.              @Override

32.              public void onCreate() {

33.                     super.onCreate();

34.                      mNM =(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

35.                      try {

36.                             mStartForeground =ForegroundService.class.getMethod("startForeground",mStartForegroundSignature);

37.                             mStopForeground =ForegroundService.class.getMethod("stopForeground",mStopForegroundSignature);

38.                      } catch(NoSuchMethodException e) {

39.                             mStartForeground = mStopForeground = null;

40.                      }

41.                      // 我们并不需要为 notification.flags 设置 FLAG_ONGOING_EVENT,因为

42.                      // 前台服务的 notification.flags 总是默认包含了那个标志位

43.              Notification notification = newNotification(R.drawable.icon, "Foreground Service Started.",

44.                     System.currentTimeMillis());

45.              PendingIntent contentIntent =PendingIntent.getActivity(this, 0,

46.                      newIntent(this, Main.class), 0);

47.             notification.setLatestEventInfo(this, "ForegroundService",

48.                             "Foreground Service Started.",contentIntent);

49.              // 注意使用  startForeground id  0 将不会显示 notification

50.              startForegroundCompat(1,notification);

51.              }

52.              

53.              @Override

54.              public void onDestroy() {

55.                     super.onDestroy();

56.                      stopForegroundCompat(1);

57.              }

58.              

59.              // 以兼容性方式开始前台服务

60.              private voidstartForegroundCompat(int id, Notification n){

61.                     if(mStartForeground != null){

62.                             mStartForegroundArgs[0] = id;

63.                             mStartForegroundArgs[1] = n;

64.                             

65.                             try {

66.                                    mStartForeground.invoke(this, mStartForegroundArgs);

67.                             } catch (IllegalArgumentException e) {

68.                                    e.printStackTrace();

69.                             } catch (IllegalAccessException e) {

70.                                    e.printStackTrace();

71.                             } catch (InvocationTargetException e) {

72.                                    e.printStackTrace();

73.                             }

74.                             

75.                             return;

76.                      }

77.                     setForeground(true);

78.                     mNM.notify(id, n);

79.              }

80.              

81.              // 以兼容性方式停止前台服务

82.              private void stopForegroundCompat(intid){

83.                     if(mStopForeground != null){

84.                             mStopForegroundArgs[0] = Boolean.TRUE;

85.                             

86.                             try {

87.                                    mStopForeground.invoke(this, mStopForegroundArgs);

88.                             } catch (IllegalArgumentException e) {

89.                                    e.printStackTrace();

90.                             } catch (IllegalAccessException e) {

91.                                    e.printStackTrace();

92.                             } catch (InvocationTargetException e) {

93.                                    e.printStackTrace();

94.                             }

95.                             return;

96.                      }

97.                      

98.                     //   setForeground 之前调用 cancel,因为我们有可能在取消前台服务之后

99.                     //  的那一瞬间被kill掉。这个时候 notification 便永远不会从通知一栏移除

100.                  mNM.cancel(id);

101.                  setForeground(false);

102.           }

103.    

104.   }

复制代码


特别注意:  1、使用 startForeground ,如果 id 为 0 ,那么notification 将不会显示。  7、在什么情况下使用 startService 或 bindService 或 同时使用startService 和 bindService  如 果你只是想要启动一个后台服务长期进行某项任务那么使用 startService 便可以了。如果你想要与正在运行的 Service 取得联系,那么有两种方法,一种是使用 broadcast ,另外是使用 bindService ,前者的缺点是如果交流较为频繁,容易造成性能上的问题,并且 BroadcastReceiver 本身执行代码的时间是很短的(也许执行到一半,后面的代码便不会执行),而后者则没有这些问题,因此我们肯定选择使用 bindService(这个时候你便同时在使用 startService 和 bindService 了,这在 Activity 中更新 Service 的某些运行状态是相当有用的)。另外如果你的服务只是公开一个远程接口,供连接上的客服端(android 的 Service 是C/S架构)远程调用执行方法。这个时候你可以不让服务一开始就运行,而只用 bindService ,这样在第一次 bindService 的时候才会创建服务的实例运行它,这会节约很多系统资源,特别是如果你的服务是Remote Service,那么该效果会越明显(当然在 Service 创建的时候会花去一定时间,你应当注意到这点)。  
8、在 AndroidManifest.xml 里 Service 元素的常见选项android:name  -------------  服务类名
android:label  --------------  服务的名字,如果此项不设置,那么默认显示的服务名则为类名
android:icon  --------------  服务的图标
android:permission  -------  申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务
android:process  ----------  表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字
android:enabled  ----------  如果此项设置为 true,那么 Service 将会默认被系统启动,不设置默认此项为 false
android:exported  ---------  表示该服务是否能够被其他应用程序所控制或连接,不设置默认此项为 false

Serviceandroid 系统中的一种组件,它跟Activity的级别差不多,但是他不能自己运行,只能后台运行,并且可以和其他组件进行交互。Service的启动有两种方式:context.startService() context.bindService()
 
使用context.startService() 启动Service是会会经历:
context.startService()  ->onCreate()->onStart()->Service running
context.stopService() | ->onDestroy() ->Servicestop 
 
如果Service还没有运行,则android先调用onCreate()然后调用onStart();如果Service已经运行,则只调用onStart(),所以一个ServiceonStart方法可能会重复调用多次。 
 
stopService的时候直接onDestroy,如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。该Service的调用者再启动起来后可以通过stopService关闭Service
 
所以调用startService的生命周期为:onCreate --> onStart(可多次调用)--> onDestroy
 
使用使用context.bindService()启动Service会经历:
context.bindService()->onCreate()->onBind()->Service running
onUnbind() -> onDestroy()->Service stop
 
onBind 将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。这个时候把调用者Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用 onUnbind->onDestroy相应退出。 
      
所以调用bindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定)--> onUnbind --> onDestory
 
Service每一次的开启关闭过程中,只有onStart可被多次调用(通过多次startService调用),其他onCreateonBindonUnbindonDestory在一个生命周期中只能被调用一次。
 
service可以在和多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务嘛,总是藏在后头的。
 
下面我做了一个简单的音乐播放的应用,分别使用startServicebindService来启动本地的服务。
而在下一篇《androidservice 学习(下) 》会介绍通过AIDLService进行远程调用。
 
下面是整个应用启动界面:
先从使用startService启动Service学起
 
 
首先编写一个Activity

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

public class PlayMusic extends Activity implements OnClickListener {

    private static final String TAG = "PlayMusic";

    private Button playBtn;

    private Button stopBtn;

    private Button pauseBtn;

    private Button exitBtn;

    private Button closeBtn;

 

    //....(详见源码)

 

@Override

    public void onClick(View v) {

        int op = -1;

        Intent intent = new Intent("org.allin.android.musicService");

         

        //广播用

//      Intent intent = new Intent("org.allin.android.musicReceiver");

         

        switch (v.getId()) {

        case R.id.play:

            Log.d(TAG, "onClick: playing muic");

            op = 1;

            break;

        case R.id.stop:

            Log.d(TAG, "onClick: stoping music");

            op = 2;

            break;

        case R.id.pause:

            Log.d(TAG, "onClick: pausing music");

            op = 3;

            break;

        case R.id.close:

            Log.d(TAG, "onClick: close");

            this.finish();

            break;

        case R.id.exit:

            Log.d(TAG, "onClick: exit");

            op = 4;

            stopService(intent);

            this.finish();

            break;

        }

         

        Bundle bundle  = new Bundle();

        bundle.putInt("op", op);

        intent.putExtras(bundle);

        startService(intent);

         

//      sendBroadcast(intent);

    }

 

 

}


 
通过重写onClick方法来实现对播放音乐的控制。这里把播放音乐的各种操作用数字的方式通过Intent传递给service 
构造一个Intent ntent intent = new Intent("org.allin.android.musicService");
"org.allin.android.musicService"是在AndroidManifest.xml文件中对service类的定义

?

1

2

3

4

5

<service android:enabled="true" android:name=".MusicService">

<intent-filter>

<action android:name="org.allin.android.musicService" />

</intent-filter>

</service>

把操作码放在Bundle 
Bundle bundle  = new Bundle();
bundle.putInt("op", op);
intent.putExtras(bundle);
最后使用startService(intent);启动服务。 
下面看看Service是怎么实现的。
 
MusicService.java
 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

/**

 * @author allin.dev

 * http://allin.cnblogs.com/

 *

 */

public class MusicService extends Service {

 

    private static final String TAG = "MyService";

    private MediaPlayer mediaPlayer;

 

    /*

     * (non-Javadoc)

     *

     * @see android.app.Service#onBind(android.content.Intent)

     */

    @Override

    public IBinder onBind(Intent arg0) {

        return null;

    }

 

    @Override

    public void onCreate() {

        Log.v(TAG, "onCreate");

        if (mediaPlayer == null) {

            mediaPlayer = MediaPlayer.create(this, R.raw.tmp);

            mediaPlayer.setLooping(false);

        }

    }

 

    @Override

    public void onDestroy() {

        Log.v(TAG, "onDestroy");

        if (mediaPlayer != null) {

            mediaPlayer.stop();

            mediaPlayer.release();

        }

    }

 

    @Override

    public void onStart(Intent intent, int startId) {

        Log.v(TAG, "onStart");

        if (intent != null) {

            Bundle bundle = intent.getExtras();

            if (bundle != null) {

 

                int op = bundle.getInt("op");

                switch (op) {

                case 1:

                    play();

                    break;

                case 2:

                    stop();

                    break;

                case 3:

                    pause();

                    break;

                }

 

            }

        }

 

    }

 

    public void play() {

        if (!mediaPlayer.isPlaying()) {

            mediaPlayer.start();

        }

    }

 

    public void pause() {

        if (mediaPlayer != null && mediaPlayer.isPlaying()) {

            mediaPlayer.pause();

        }

    }

 

    public void stop() {

        if (mediaPlayer != null) {

            mediaPlayer.stop();

            try {

                // 在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数

                mediaPlayer.prepare();

            } catch (IOException ex) {

                ex.printStackTrace();

            }

        }

    }

 

}

 
 
服务 使用了系统自带MediaPlayer进行音乐的播放控制。 当调用了startService后服务会先调用onCreate,我们在里面对MediaPlayer进行初始化。接着会调用onStart,可以看到传递给startService()Intent对象会传递给onStart()方法,这样我们就可以得到intent里面的操作码: 
Iundle bundle = intent.getExtras(); 
int op = bundle.getInt("op");
然后更具定义好的操作码进行相应的f播放操作。启动后界面如下图: 
 
图中的”close”“exit”是不同的,close只是调用finish()退出当前的Activity,但是Service并没有关掉,音乐会继续播放。而exit就是调用了stopService(intent);来停止服务,Service会调用onDestroy()方法来对mediaPlayer进行停止和释放资源。
 
有时候如果服务只提供一些操作接口,我们也可以通过广播的g方式来启动服务。
首先要定义一个Receiver,并继承BroadcastReceiver,然后在AndroidManifest.xml中进行注册:

?

1

2

3

4

5

<receiver android:name=".MusicReceiver">

<intent-filter>

<action android:name="org.allin.android.musicReceiver" />

</intent-filter>

</receiver>

 
Receiver的实现:
 
MusicReceiver.java

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

/**

 * @author allin.dev

 * http://allin.cnblogs.com/

 *

 */

public class MusicReceiver extends BroadcastReceiver {

 

    private static final String TAG = "MusicReceiver";

    @Override

    public void onReceive(Context context, Intent intent) {

        Log.d(TAG, "onReceive");

        Intent it = new Intent("org.allin.android.musicService");

        Bundle bundle = intent.getExtras();

        it.putExtras(bundle);

         

        if(bundle != null){

            int op = bundle.getInt("op");

            if(op == 4){

                context.stopService(it);

            }else{

                context.startService(it);

            }

        }

         

    }

 

}

 
然后对PlayMusic中的onclick方法进行些改造,把Intent指向Receiver
Intent intent = new Intent("org.allin.android.musicReceiver");
intent中绑定的操作码都不变,再调用sendBroadcast(intent);intentg广播出去。
MusicReceiver接受到广播后根据操作码进行相应的操作。




接下来的例子就是使用bindService来启动Service
首先一样是写一个Activity

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

public class PlayBindMusic extends Activity implements OnClickListener {

 

    private static final String TAG = "PlayBindMusic";

    private Button playBtn;

    private Button stopBtn;

    private Button pauseBtn;

    private Button exitBtn;

     

    private BindMusicService musicService;

 

    @Override

    public void onClick(View v) {

 

        switch (v.getId()) {

        case R.id.play:

            Log.d(TAG, "onClick: binding srvice");

            musicService.play();

            break;

        case R.id.stop:

            Log.d(TAG, "onClick: stoping srvice");

            if(musicService != null){

                musicService.stop();

            }

            break;

        case R.id.pause:

            Log.d(TAG, "onClick: pausing srvice");

            if(musicService != null){

                musicService.pause();

            }

            break;

        case R.id.exit:

            Log.d(TAG, "onClick: exit");

            this.finish();

            break;

        }

    }

 

 

private void connection(){

        Log.d(TAG, "connecting.....");

        Intent intent = new Intent("org.allin.android.bindService");

        bindService(intent, sc, Context.BIND_AUTO_CREATE);

         

    }

private ServiceConnection sc = new ServiceConnection() {

        @Override

        public void onServiceDisconnected(ComponentName name) {

            musicService = null;

            Log.d(TAG, "in onServiceDisconnected");

        }

         

        @Override

        public void onServiceConnected(ComponentName name, IBinder service) {

            musicService = ((BindMusicService.MyBinder)(service)).getService();

            if(musicService != null){

                musicService.play();

            }

             

            Log.d(TAG, "in onServiceConnected");

        }

    };

}

 

 

这里使用了bindService(intent, sc, Context.BIND_AUTO_CREATE);来启动服务的,
我们需要定义ServiceConnectionnn,并实现里面的方法,当服务绑定成功后会调用ServiceConnectionnn中的回调函数:
public void onServiceConnected(ComponentName name,IBinder service)
调函数里面使用musicService= ((BindMusicService.MyBinder)(service)).getService();来获取BindMusicService务对象,有了BindMusicService实例对象,就可以调用服务提供的各种控制音乐播放的哦功能。
下面看看BindMusicService.java的实现:

 

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

/**

 * @author allin.dev

 * http://allin.cnblogs.com/

 */

public class BindMusicService extends Service {

 

    private static final String TAG = "MyService";

    private MediaPlayer mediaPlayer;

 

    private final IBinder binder = new MyBinder();

 

    public class MyBinder extends Binder {

        BindMusicService getService() {

            return BindMusicService.this;

        }

    }

 

    /*

     * (non-Javadoc)

     *

     * @see android.app.Service#onBind(android.content.Intent)

     */

    @Override

    public IBinder onBind(Intent intent) {

        Log.d(TAG, "onBind");

        play();

        return binder;

    }

 

    @Override

    public void onCreate() {

        super.onCreate();

         

        Log.d(TAG, "onCreate");

        Toast.makeText(this, "show media player", Toast.LENGTH_SHORT).show();

         

         

    }

 

    @Override

    public void onDestroy() {

        super.onDestroy();

         

        Log.d(TAG, "onDestroy");

        Toast.makeText(this, "stop media player", Toast.LENGTH_SHORT);

        if(mediaPlayer != null){

            mediaPlayer.stop();

            mediaPlayer.release();

        }

    }

 

     

    public void play() {

        if (mediaPlayer == null) {

            mediaPlayer = MediaPlayer.create(this, R.raw.tmp);

            mediaPlayer.setLooping(false);

        }

        if (!mediaPlayer.isPlaying()) {

            mediaPlayer.start();

        }

    }

 

    public void pause() {

        if (mediaPlayer != null && mediaPlayer.isPlaying()) {

            mediaPlayer.pause();

        }

    }

 

    public void stop() {

        if (mediaPlayer != null) {

            mediaPlayer.stop();

            try {

                // 在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数

                mediaPlayer.prepare();

            } catch (IOException ex) {

                ex.printStackTrace();

            }

        }

    }

 

}



们看到Service中有个返回IBinder对象的onBind方法,这个方法会在Service被绑定到其他程序上时被调用,而这个IBinder象和之前看到的onServiceConnected方法中传入的那个IBinder是同一个东西。应用和Service间就依靠这个IBinder对象进行通信。
启动后的界面如下图:

 

 

 

常每个应用程序都在它自己的进程内运行,但有时需要在进程间传递对象,你可以通过应用程序UI的方式写个运行在一个不同的进程中的service。在 android平台中,一个进程通常不能访问其他进程中的内存区域。所以,他们需要把对象拆分成操作系统能理解的简单形式,以便伪装成对象跨越边界访问。编写这种伪装代码相当的枯燥乏味,好在android为我们提供了AIDL工具可以来做这件事。

 

AIDL(android 接口描述语言)是一个IDL语言,它可以生成一段代码,可以使在一个android设备上运行的两个进程使用内部通信进程进行交互。如果你需要在一个进程(例如:在一个Activity)访问另一个进程中(例如:一个Service)某个对象的方法,你就可以使用AIDL来生成这样的代码来伪装传递各种参数。

 

使用AIDLService需要以aidl文件的方式提供服务接口,AIDL工具将生成一个相应的java接口,并且在生成的服务接口中包含一个功能调用的stub服务桩类。Service的实现类需要去继承这个stub服务桩类。ServiceonBind方法会返回实现类的对象,之后你就可以使用它了,参见下例:

 
先创建一个IMyRemoteService.aidl文件

 

?

1

2

3

4

5

6

package org.allin.android.remote;

interface IMusicControlService{

        void play();

        void stop();

        void pause();

}


 
如果你正在使用eclipseAndroid插件,则它会根据这个aidl文件生成一个Java接口类。生成的接口类中会有一个内部类Stub类,你要做的事就是去继承该Stub类: 
 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

/**

 * @author allin.dev

 * http://allin.cnblogs.com/

 *

 */

public class RemoteMusicService extends Service {

 

    private static final String TAG = "RemoteMusicService";

    private MediaPlayer mediaPlayer;

 

    /*

     * (non-Javadoc)

     *

     * @see android.app.Service#onBind(android.content.Intent)

     */

    @Override

    public IBinder onBind(Intent intent) {

        return binder;

    }

 

    private final IMusicControlServic<script type="text/javascript"><!--mce:1--></script>e.Stub binder = new IMusicControlService.Stub() {

 

        @Override

        public void stop() throws RemoteException {

            Log.d(TAG,"stop....");

            if (mediaPlayer != null) {

                mediaPlayer.stop();

                try {

                    // 在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数

                    mediaPlayer.prepare();

                } catch (IOException ex) {

                    ex.printStackTrace();

                }

            }

 

        }

 

        @Override

        public void play() throws RemoteException {

            Log.d(TAG,"play....");

            if (mediaPlayer == null) {

                mediaPlayer = MediaPlayer.create(RemoteMusicService.this,

                        R.raw.tmp);

                mediaPlayer.setLooping(false);

            }

            if (!mediaPlayer.isPlaying()) {

                mediaPlayer.start();

            }

        }

 

        @Override

        public void pause() throws RemoteException {

            Log.d(TAG,"pause....");

             

            if (mediaPlayer != null && mediaPlayer.isPlaying()) {

                mediaPlayer.pause();

            }          

        }

 

    };

     

     

    @Override

    public void onDestroy() {

        super.onDestroy();

         

        Log.d(TAG, "onDestroy");

        if(mediaPlayer != null){

            mediaPlayer.stop();

            mediaPlayer.release();

        }

    }

}

 
 
当客户端应用连接到这个Service时,onServiceConnected方法将被调用,客户端就可以获得IBinder对象。参看下面的客户端onServiceConnected方法:
 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

private ServiceConnection sc = new ServiceConnection() {

         

        @Override

        public void onServiceDisconnected(ComponentName name) {

            musicService = null;

            Log.d(TAG, "in onServiceDisconnected");

        }

         

        @Override

        public void onServiceConnected(ComponentName name, IBinder service) {

            musicService = IMusicControlService.Stub.asInterface(service);

            Log.d(TAG, "in onServiceConnected");

        }

    };

 
 启动后的界面如下
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

http://blog.csdn.net/zhangqijie001/article/details/5891682

http://www.cnblogs.com/feisky/archive/2010/01/01/1637427.html

http://www.cnblogs.com/feisky/archive/2010/01/16/1649081.html

http://www.cnblogs.com/bgphone/archive/2011/11/01/2231505.html

http://www.cnblogs.com/allin/archive/2010/05/15/1736458.html

http://www.cnblogs.com/allin/archive/2010/05/15/1736462.html

http://www.apkbus.com/android-15415-1-1.html

 

文章转载地址:http://blog.csdn.net/cnbloger/article/details/7430258

作者:残诗

原创粉丝点击