Android基础(1)—四大组件之Activity和Service

一. Activity

是什么?
Activity在应用中的表现为一个用户界面,负责加载指定的布局文件来显示各种UI元素,例如TextView、Button、ImageView等,并且为这些UI元素设置事件处理函数,使得用户可以与这些UI进行交互。同时,Acitvity还可以在不同Activity之间跳转,将不同的页面串连在一起,共同完成特定的操作流程。每个应用都是由一个或多个Activity组成,它是Android应用程序中不可缺少的部分。

基本用法:
创建Activity:

要创建Activity,必须创建Activity的子类。在子类中实现Activity在生命周期的各种状态之间转变时(例如创建 Activity、停止 Activity、恢复 Activity 或销毁 Activity)系统调用的回调方法。

Android Studio中新建项目默认创建的代码为:

  ```
  public class MainActivity extends AppCompatActivity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {  
          //onCreate()是必须实现的方法
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);  // 加载布局文件
      }
  }
  
  ```

在AndroidMainfest.xml中声明Activity:

每次新建的Activity都需要在AndroidManifest.xml文件中添加如下内容,并将元素添加为元素的子项。

```
<application 
     ....
    <activity android:name=".MainActivity"
          android:lable="this is FirstActivity" 
            <intent-filter 
                <action android:name="android.intent.action.MAIN" / 
                <category android:name="android.intent.category.LAUNCHER" / 
            </intent-filter 
     </activity 
     ....
</application     

/*
<activity 标签中使用android:name来指明具体注册活动名、android:lable来指明Activity标题栏内容
<activity 标签中加入<intent-filter 标签来配置活动
<action android:name 标签配置活动为主活动
<category android:name 标签配置活动为启动Activity
*/
```

**注意:**

  项目首次创建的Activity会在AndroidManifest.xml文件中自动添加相应代码,并设置为主活动,也就是说首次启动应用时给用户呈现的Activity;但其后创建的Activity在调用时,必须在AndroidManifest.xml文件中声明Activity,否则会报错。(可将别的Activity修改为主活动)

启动Activity:

该部分用于描述如何启动Activity。作为主活动,在应用开启的时候就会系统创建,而用户不仅仅只需要主活动界面,用户需要界面的跳转,而界面的跳转也是其他活动界面(Activity)启动。

在该部分仅仅只提及利用显示Intent方式跳转活动,代码如下:

```
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
/*
this,为本Activity的上下文;第二个参数为你要跳转的目的Activity.class,这里我所创建的第二个Actvity名为SecondActivity。
*/
```

结束Activity:

通过调用Activity的finish()方法来结束Activity;可以通过调用finishActivity()结束之前启动的活动。

关于finishActivity()的理解:

通过 MainActivity 来启动 ActivityA(使用startActivityForResult 方法),那么你在 MainActivity 这个类中需要重写 onActivityResult() 这个方法, 然后,可以在 onActivityResult() 中通过 finishActivity() 方法去结束掉 ActivityA。

生命周期:

Activity生命周期.png

各生命周期介绍:

方法 说明 是否能事后终结 后接
onCreate() 首次创建 Activity 时调用,在此方法中执行所有正常的静态设置 — 创建视图、将数据绑定到列表等等。始终后接onStart()。 onStart()
onStart() Activity正在启动,此时Activity可见但不可操作。onStart()之后如果Activity转入前台,则后接 onResume() ;如果 Activity 转入隐藏状态,则后接 onStop()。 onResume()或onStop()
onResume() Activity可见且可操作,onResume方法与onStart的相同点是两者都表示Activity可见,只不过onStart回调时Activity还是后台无法与用户交互,而onResume则已显示在前台,可与用户交互。始终后接onPause()。 onPause()
onPause() Activity正在停止(Paused形态),系统即将开始继续另一个Activity时会调用此方法(不能做耗时任务)。 如果 Activity 返回前台,则后接 onResume(),如果 Activity 转入对用户不可见状态,则后接 onStop()。 onResume()或onStop()
onStop() Activity 不可见。如果 Activity 被销毁或另一个Activity 继续执行并将其覆盖,就可能发生这种情况。如果Activity恢复与用户的交互,则后接onRestart(),如果Activity被销毁则后接onDestroy()。 onRestart()或onDestory()
onDestory() Activity 被销毁前调用。这是 Activity 将收到的最后调用。当 Activity 结束或系统为节省空间而暂时销毁该 Activity 实例时,可能会调用它。
onRestart() 在 Activity 已停止并即将再次启动前调用。 onStart()

四种状态:

1.运行(Active/Running):

Activity处于活动状态,此时Activity处于栈顶,是可见状态,可以与用户进行交互。

2.暂停(Paused):

当Activity失去焦点时,或被一个新的非全面屏的Activity,或被一个透明的Activity放置在栈顶时,Activity就转化为Paused状态。此刻并不会被销毁,只是失去了与用户交互的能力,其所有的状态信息及其成员变量都还在,只有在系统内存紧张的情况下,才有可能被系统回收掉。

3.停止(Stopped):

当Activity被系统完全覆盖时,被覆盖的Activity就会进入Stopped状态,此时已不在可见,但是资源还是没有被收回。

4.系统回收(Killed):

如果一个活动在处于停止或者暂停的状态下,系统内存缺乏时会将其结束(finish)或者杀死(kill)。这种非正常情况下,系统在杀死或者结束之前会调用onSaveInstance()方法来保存信息,同时,当Activity被移动到前台时,重新启动该Activity并调用onRestoreInstance()方法加载保留的信息,以保持原有的状态。

常见场景所走的生命周期:

1.手机加载应用至显示界面时,Activity启动– onCreate()– onStart()– onResume()依次被调用。此时MainActivity处于可交互的状态。

2.点击Home键回到主界面(Activity不可见)– onPause()– onStop()依次被调用。

3.当点击Home键回到主界面后,再次点击App回到Activity时,onRestart()– onStart()– onResume()依次被调用。

4.按下返回键时,应用退出,onPause()- onStop()- onDestroy()依次被调用,MainActivity被销毁。

5.当加载应用进入主界面,并点击按钮进行页面跳转时,在原Activity调用了onPause()和onStop()方法,在进行MainActivity进行完onPause()之后SecondActivity的生命周期方法才能被回调,所以这就是为什么onPause()方法不能操作耗时任务的原因了。

6.点击Back键回退时,点击之后SecondActivity的onPause()方法,onStop()方法,onDestroy()方法依次调用,MainActivity的onRestart(),onStart(),onResume()会依次调用。在进行SecondActivity进行完onPause()之后MainActivity的生命周期方法才能被回调。

7.点击SecondActivity界面中的按钮跳到MainActivity,发现MainActivity并不会调用onRestart(),而是直接进行onCreate(),onStart(),onResume()的调用,跳转完毕之后SecondActivity并没有被销毁,而是处于onStop()状态,这表明SecondActivity()并没有被销毁。

构成:

Activity构成.png

一个Activity包含一个window对象,这个对象是由PhoneWindow来实现的,PhoneWindow将DecorView做为整个应用窗口的根View,而这个DecorView又将屏幕划分为两个区域一个是TitleView一个是ContentView,而我们平常做应用所写的布局正是展示在ContentView中的。

启动模式:
1.Android使用任务(Task)来管理活动。一个任务就是一组存放在栈(也称返回栈 Back Stack)里的活动的集合。栈是一种先进后出的数据结构

2.在默认情况下,每当我们启动一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。每当我们销毁一个活动(按Back键或调用finish()方法),处于栈顶的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置

3.系统总是会显示处于栈顶的活动给用户

4.默认情况下所有Activity所需的任务栈的名字为应用的包名。任务栈有前台和后台之分,后台栈中的Activity处于暂停状态,用户可以通过切换,将后台任务栈再次调到前台

5.可以通过指定TaskAffinity(任务相关性,是一个字符串,默认为应用包名)属性来指定任务栈的名称(不能与包名相同,否则没有意义)。TaskAffinity属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,其他情况没有意义。

6.查看当前任务栈的方法:adb shell dumpsys activity dir\1.txt

  在txt使用查找命令查找:``Running activities (most recent first)``。

7.allowTaskReparenting允许任务栈重复,即允许Activity运行在与TaskAffinity值不同的任务栈中。

8.给Activity指定启动模式的两种方式:

(1).在AndroidManifest.xml中指定:

```
android:launchMode=“singleTask”
```

(2).在Intent中设置标志位:

```
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
```

**区别:**

1.优先级代码里比xml高,要以代码里为准。 

2.限定范围不同,xml里无法为Activity直接指定FLAG_ACTIVITY_CLEAR_TOP标识,代码里无法指定singleInstance模式。

standard(标准模式):

每启动一个Activity就会创建一个新的实例,不管这个实例是否已经存在,并具有典型情况下的生命周期。谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中。

注意:使用ApplicationContext去启动standard模式的Activity会报错,非Activity类型的Context没有所谓的任务栈,要给它设立一个FLAG_ACTIVITY_NEW_TASK标记位,这样启动时就会为它建立一个新的任务栈,而不是去找原Context的任务栈。

singleTop(栈顶复用模式):

新的Activity已经位于任务栈的栈顶,那么此Activity不会重新创建实例,因此它的onCreate、onStart方法不会被系统调用,而是会调用一个onNewIntent方法,通过这个方法,我们可以取得当前请求的信息。

注意:如果新的Activity不在栈顶,即使已经存在,仍然是会重新创建Activity实例。

singleTask(栈内复用模式):

可以说是singleTop模式的“子类”。single in task,taskAffinity应该指定。

A:新的Activity要求的任务栈S存在。 检查栈S里是否存在新的Activity A,存在,因为clearTop效果,将A之上的Activity全部出栈,使其到达栈顶。

B:新的Activity要求的任务栈S不存在。 创建新的A的实例,并入栈S。

注意:这种模式也是和singeTop模式类似,会复用Activity实例,所以当重复创建时,不会调用onCreate、onStart方法,而是会调用onNewIntent方法。

sigleInstance(单实例模式):

它具有singleTask模式的所有特性,它的特殊性在于,singleInstance模式下的Activity实例在一个单独任务栈S中。

启动过程:

1.Launcher向ActivityManagerService发送一个启动MainActivity的请求;

2.ActivityManagerService首先将MainActivity的相关信息保存下来,然后向Launcher发送一个使之进入中止状态的请求;

3.Launcher收到中止状态之后,就会想ActivityManagerService发送一个已进入中止状态的请求,便于ActivityManagerService继续执行启动MainActivity的操作;

4.ActivityManagerService检查用于运行MainActivity的进程,如果不存在,则启动一个新的进程;

5.新的应用程序进程启动完成之后,就会向ActivityManagerService发送一个启动完成的请求,便于ActivityManagerService继续执行启动MainActivity的操作;

6.ActivityManagerService将第(2)步保存下来的MainActivity相关信息发送给新创建的进程,便于该进程启动MainActivity组件。

回收过程:

1.内存不足时,Activity 被回收了,而且进程被杀死了,而且一般情况下该进程是后台进程。当内存不足时,系统会杀死优先级低的后台进程,进程内的 Activity 肯定也就被回收了。

2.当一个activity要启动另一个activity时要传递数据的话,普遍的做法是将数据放在intent中:系统维护的task和activity栈帮我们处理了Intent(以及其中的数据)的保存和恢复。简单来说,所有“曾用于启动activity的intent”和“还没有被销毁的activity”都会被系统维护在task栈中,并且当进程被回收时,安卓系统会自动帮我们把这些信息保存起来。当用户尝试把一个被回收的app切回前台时,系统会用之前保存的task栈信息来尝试把app恢复到被回收之前的状态,而通过intent传递的数据也在这个过程中得到了还原。

小结:

1.当app处于后台被系统回收时,app的进程被杀死了,Activity 也被回收了,而app的task和activity栈以及相应的intent和数据会被系统保存起来。当app被切回前台时,系统会恢复task和activity栈以及相应的intent和数据。
   
2.不要在Application类和全局单例类中存放数据,会导致app无法正确恢复状态。运行时的临时数据应存放在SharedPreference、临时文件或数据库中.
   
3.Activity之间传数据应该用系统提供的intent机制。
   
4.intent里面能放的数据大小是有限制的,最好是不超过500kb(不同的系统版本限制不一)。比如在intent里面放一个byte[ ]数组的话,如果数组大小太大,就会导致运行时抛异常。所以如果要在activity之间传大量数据的话,最好把数据存为临时文件,然后在intent中传文件的路径。
   
5.对于多个Activity的回收,可以新建活动管理类和基类BaseActivity,并使所有的Activity继承自基类。在创建时添加到活动管理器,销毁时,从活动管理器中移除。

其他:(借鉴于kingshingyeh学习报告)

1.Activity的finish() onDestory()和system.exit()区别?

``Activity.finish()``:在你的activity动作完成的时候,或者Activity需要关闭的时候,调用此方法。当你调用此方法的时候,系统只是将最上面的Activity移出了栈,并没有及时的调用onDestory()方法,其占用的资源也没有被及时释放。因为移出了栈,所以当你点击手机上面的“back”按键的时候,也不会再找到这个Activity。
   
``Activity.onDestory()``:系统销毁了这个Activity的实例在内存中占据的空间.在Activity的生命周期中,onDestory()方法是他生命的最后一步,资源空间等就被回收了。当重新进入此Activity的时候,必须重新创建,执行onCreate()方法.
   
``System.exit(0)``:退出整个应用程序

2.Activiity被回收了怎么办?

onSaveInstanceState()方法,保证活动回收前一定会被调用,可以用来解决临时数据得不到保存的问题。
   
但重要数据不能依靠此方法,需要在Activity销毁前做数据持久化操作。

3.parcelable和Serializable的区别?

①Serializable的作用是保存对象的属性到本地文件,数据库,网络流等方便数据传输,也可程序之间传递。
   
②parcelable的设计的目的是为了解决Serializable效率不高的问题,内存开销小,所以在内存间传递数据的方式用parcelable,缺点是不能持久化。

二. Service

是什么?
Service是Android中实现程序后台运行的解决方案,非常适合用于去执行哪些不需要和用户交互而且还要求长期运行的任务。不能运行在一个独立的进程当中,而是依赖与创建服务时所在的应用程序进程。只能在后台运行,并且可以和其他组件进行交互。Service可以在很多场合使用,比如播放多媒体的时候用户启动了其他Activity,此时要在后台继续播放;比如检测SD卡上文件的变化;比如在后台记录你的地理信息位置的改变等等,总之服务是藏在后台的。服务不会自动开启线程,我们需要在服务的内部手动创建子线程,并在这里执行具体的任务。

基本用法:
启动Service:

核心步骤:

1、创建一个类继承android.app.Service类,实现抽象方法onBind(),重写onCreate()、onStartCommand()、onDestry();

```
public class MyService extends Service {        
     public static final String TAG = "MyService";     
    //创建服务时调用
     @Override  
     public void onCreate() {  
         super.onCreate();  
         Log.d(TAG, "onCreate");  
     }    
     //服务执行的操作
     @Override  
     public int onStartCommand(Intent intent, int flags, int startId) {  
         Log.d(TAG, "onStartCommand");  
         return super.onStartCommand(intent, flags, startId);  
     }  
     //销毁服务时调用
     @Override  
     public void onDestroy() {  
         super.onDestroy();  
         Log.d(TAG, "onDestroy");  
     }     
     @Override  
     public IBinder onBind(Intent intent) {  
         return null;  
     }  
 }
```

2、在清单文件中配置Service。(和Activity标签并列)

```
<service android:name=".MyService" 
</service 
```

3、触发启动Service。Service可以有两种启动方式:一种是startService(),另一种是bindService()。第二种启动方式才会用到onBind()方法

```
Intent startIntent = new Intent(this, MyService.class);  
startService(startIntent); 
```

停止Service:

1.在外部使用stopService()

2.在服务内部(onStartCommand方法内部)使用stopSelf()方法。

注意:

1.服务对象同时只会有一个

2.默认情况下,一个started的Service与启动他的组件在同一个线程中。服务就是在主线程中运行的,如果是在服务中完成耗时操作的话,容易造成主线程阻塞。一般会在Service中开启一个子线程来执行耗时操作。

3.如果Service的启动模式是bindService,在停止Service后,需要在与其绑定的Activity销毁前调用unbindService()进行解绑。

生命周期:

Service生命周期.png

手动调用的方法:

手动调用的方法 作用
startService() 启动服务
stopService() 关闭服务
bindService() 绑定服务
unbindService() 解绑服务

自动调用的方法:

自动调用的方法 作用
onCreate() 创建服务
onStartCommand() 开始服务
onDestroy() 销毁服务
onBind() 绑定服务
onUnbind() 解绑服务

常见场景所走的生命周期:
1.启动Service服务:

单次:startService() — onCreate() — onStartCommand()
多次:startService() — onCreate() — onStartCommand() — onStartCommand()

2.停止Service服务:stopService() — onDestroy()

3.绑定Service服务:bindService() — onCreate() — onBind()

4.解绑Service服务:unbindService() — onUnbind() — onDestroy()

5.启动绑定Service服务:
startService() — onCreate() — onStartCommand() — bindService() — onBind()

6.解绑停止Service服务:
unbindService() — onUnbind() — stopService() — onDestroy()

7.解绑绑定Service服务:
unbindService() — onUnbind(ture) — bindService() — onRebind()

Service和Activity之间的通信:

1.简单通信:不推荐

直接通过Intent进行传值,启动一个Service的时候通过Intent的对象向Service进行传值,这种方式传递值比较不方便,性能不是很高。

1.在MainActivity中通过启动服务调用startService(intent)来传值。

intent = new Intent(MainActivity.this, MyService.class);
intent.putExtra("data", editText.getText().toString()); //获取输入内容作为所传值
startService(intent);

2.在MyService,通过onStartCommand(final Intent intent, int flags, int startId)接收Activity所传值。

@Override
    public int onStartCommand(final Intent intent, int flags, int startId) {
        data = intent.getStringExtra("data");
        return super.onStartCommand(intent, flags, startId);
    }

2.通过Binder对象:

在绑定服务的时候,首先是让MainActivity实现ServiceConnection类,实现这个类之后,重写ServiceConnection类中的两个方法onServiceConnected和onServiceDisconnected,这两个方法分别是在绑定成功和服务所在进程崩溃的时候被调用,如果绑定成功了,那么onServiceConnected(ComponentName componentName, IBinder iBinder) 就会被执行,然后第二个参数IBinder正是MyService中onBind()方法的返回值,因此可以通过这个返回值来向MyService传递数据。

1.MyService中创建一个Binder类,让其实现android.os.Binder类,并且定义一个方法setData,然后通过onBind()方法将其对象返回MainActivity。

public class MyService extends Service {
    private Boolean myflags = false;
    private String data = "服务器正在执行";
    private Callback callback;
    public MyService() {
    }
    @Override
    public IBinder onBind(Intent intent) {
       return new Binder();
    }
    public class Binder extends android.os.Binder{
        public void setData(String data){
            MyService.this.data = data;
        }
    }
    @Override
    public void onCreate() {
        super.onCreate();
        myflags = true;
        new Thread(){
            @Override
            public void run() {
                super.run();
                while(myflags){
                    try {
                        String str = data;
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        Toast.makeText(MyService.this, "出错了", Toast.LENGTH_SHORT).show();
                    }
                }
            } 
        }.start();
    } 
    @Override
    public int onStartCommand(final Intent intent, int flags, int startId) {
        data = intent.getStringExtra("data");
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        myflags = false;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    } 
}

2.在MainActivity中,首先添加一个Binder对象,然后在ServiceConnection中获取MyService中返回的Binder对象,通过Binder对象调用它的方法setData向其传递数据。

public class MainActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {
    private Intent intent;
    private EditText editText;
    private TextView textView;
    private MyService.Binder myBinder = null;//①
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intent = new Intent(MainActivity.this, MyService.class);
        editText = (EditText) findViewById(R.id.editText);
        textView = (TextView) findViewById(R.id.textView);
 
        findViewById(R.id.btyStartService).setOnClickListener(this);
        findViewById(R.id.btyStopService).setOnClickListener(this);
        findViewById(R.id.btyBindService).setOnClickListener(this);
        findViewById(R.id.btyUnbindService).setOnClickListener(this);
        findViewById(R.id.btySend).setOnClickListener(this);
    }
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btyStartService://启动服务
                intent.putExtra("data", editText.getText().toString());
                startService(intent);
                break;
            case R.id.btyStopService://终止服务
                stopService(intent);
                break;
            case R.id.btyBindService://绑定服务
                bindService(intent, this, Context.BIND_AUTO_CREATE);
                break;
            case R.id.btyUnbindService://解除绑定
                unbindService(this);
                break;
            case R.id.btySend://向MyService传递数据
                if (myBinder != null) {
                    myBinder.setData(editText.getText().toString());//③
                }
                break;
        }
    }
    //一旦绑定成功就会执行该函数
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder){
        myBinder = (MyService.Binder) iBinder;//②
    } 
    @Override  //崩溃才执行该函数
    public void onServiceDisconnected(ComponentName componentName) { 
    } 
}

3.接口回调监听服务中进程的变化:

在MyService中:

1.添加一个公开的接口Callback。

public static interface Callback{
     void onDataChange(String data);
    }

2.在MyService内部添加一个变量。

private Callback callback;
public void setCallback(Callback callback) {
    this.callback = callback;
}
public Callback getCallback() {
    return callback;
}

3.向外界派发信息。

if (callback != null){
    callback.onDataChange(str);
}

4.在Binder中返回一个当前的MyService对象,外部可以添加事件的绑定。

public MyService getMyService(){
    return MyService.this;
}

5.在MainActivity中通过onServiceConnected方法中的iBinder对象来实现MyService中Callback的接口,由于 onDataChange() 是执行在子线程中的,因此需要再定义一个Handler对象,将任务由子线程切换到主线程中,让主线程来进行 UI 操作。

//①
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    myBinder = (MyService.Binder) iBinder;
    myBinder.getMyService().setCallback(new MyService.Callback(){
        @Override
        public void onDataChange(String data) {
            Message msg = new Message();
            Bundle b = new Bundle();
            b.putString("data",data);
            msg.setData(b);
            hander.sendMessage(msg);
        }
    });
}
 //②
private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        textView.setText(msg.getData().getString("data"));
    }
};

使用技巧:

使用前台服务:

Service默认是运行在后台的,因此,它的优先级相对比较低,当系统出现内存不足的情况时,它就有可能会被回收掉。如果希望Service可以一直保持运行状态,而不会由于系统内存不足被回收,可以将Service运行在前台。前台服务不仅不会被系统无情地回收,它还会在通知栏显示一条消息,下拉状态栏后可以看到更加详细的信息。

与普通服务不同的,只需要修改了MyService中onCreate()方法的代码。首先创建了一个Notification对象,然后调用了它的setLatestEventInfo()方法来为通知初始化布局和数据,并在这里设置了点击通知后就打开MainActivity。然后调用startForeground()方法就可以让MyService变成一个前台Service,并会将通知的图片显示出来。

public class MyService extends Service {    
    public static final String TAG = "MyService";   
    private MyBinder mBinder = new MyBinder();   
    @Override  
    public void onCreate() {  
        super.onCreate();  
        Notification notification = new Notification(R.drawable.ic_launcher,  
                "有通知到来", System.currentTimeMillis());  
        Intent notificationIntent = new Intent(this, MainActivity.class);  
        PendingIntent pendingIntent = 
                PendingIntent.getActivity(this, 0,  notificationIntent, 0);  
        notification.setLatestEventInfo(this, "通知的标题", "通知的内容",  
                pendingIntent);  
        startForeground(1, notification);  //区别于普通服务
        Log.d(TAG, "onCreate() executed");  
    }   
    //省略其他代码   
}  

使用IntentService:

作用:

 异步的、会自动停止的服务。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent()回调方法中执行,并且每次只会执行一个工作线程,执行完第一个后,再执行第二个,以此类推。

使用:

1.新建一个MyIntentService类,继承自IntentService,并重写父类的onHandleIntent()方法,代码如下:

```
public class MyIntentService extends IntentService{

    public MyIntentService() {
        super("MyIntentService");//调用父类有参构造函数。
    }

    //该方法在会在一个单独的线程中执行,来完成工作任务。任务结束后,该Service自动停止
    @Override
    protected void onHandleIntent(Intent intent) {
        for(int i = 0;i<3;i++) {
            //打印当前线程的id
            Log.d("MyIntentService","IntentService线程的id是:"+Thread.currentThread().getId());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }        
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("MyIntentService","onDestroy");
    }
}

/*
运行结果:
IntentService线程的id是:102
IntentService线程的id是:102
IntentService线程的id是:102
*/
```

其余步骤和普通Service操作一致,该Service结束后会自动停止。

其他:

started服务和bind服务的区别:

1.通过started方式的服务会一直运行在后台,需要由组件本身或外部组件来停止服务才会以结束运行;bind方式的服务,生命周期就要依赖绑定的组件。
   
2.started服务可以给启动的服务对象传递参数,但无法获取服务中方法的返回值;bind服务可以给启动的服务对象传递参数,也可以通过绑定的业务对象获取返回结果。
   
3.当手机屏幕切换时,started服务不会停止,bind服务会随着Activity的重构而停止。

Service和Thread的关系:

Service是运行在主线程里的,和Thread没有任何关系。
   
其实,后台和子线程是两个完全不同的概念:
   
Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现(开启一个子线程)。
   
既然在Service里也要创建一个子线程,那为什么不直接在Activity里创建呢?这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例;而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

使用Bind Service完成IPC进程间通信:(基础知识,未涉及具体代码)

在客户端绑定一个服务的步骤:

1.实现ServiceConnection抽象类。实现过程中,必须重写一下两个回调方法:

onServiceConnected()  和服务绑定成功后,系统会调用这个方法来发送由服务的onBind()方法返回的IBinder对象;

onServiceDisconnected()   当服务异常终止时会调用(如服务崩溃或被杀死时)。注意,在客户端解除绑定时不会调用该方法。

2.调用bindService()方法来传递ServiceConnection类的实现;

3.当系统调用你的onServiceConnected()回调方法时,你就可以开始使用接口中定义的方法来调用服务了

4.调用unbindService()方法断开与服务的链接。

**注意:**bindService()和unbindService()方法都是Context类中的方法。

IPC(Inter-Process Communication)进程间通信机制:

对于不同的进程中的组件来说,要进行通信,就需要用到Android的IPC机制了。对应用开发者来说,Android的IBinder/Binder框架实现了Android的IPC通信。当然,IBinder/Binder框架也可以用来实现**进程内通信(本地通信)**,也可以实现**进程间通信(远程通信)**。

从Android SDK中对IBinder/Binder的解释可知,IBinder/Binder是Android远程对象的基本接口,它是Android用于提供高性能IPC通信而设计的一套轻量级远程调用机制的核心部分。该接口描述了与一个远程对象进行通信的抽象协议。

AIDL(Android Interface Definition Language)Android接口定义语言:

AIDL它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。

AIDL支持的类型:八大基本数据类型、String类型、CharSequence、List、Map、自定义。

IPC(进程间通信)具体的步骤如下:

1.使用AIDL定义业务接口,通过ADT工具来生成一个java类,此类实现了进程间远程通信的代理

2.编写自己的业务类(继承生成的类中的Stub)来实现业务接口功能

3.再通过绑定Service的方式来暴露此业务对象,给其它组件提供功能

4.调用者组件通过bindService方法绑定服务,从而获取绑定成功后的远程业务对象或本地业务对象,然后就可以调用相关功能。

**注意:**一般在使用完绑定服务后,需要解除绑定。

上一篇:Java基础(6)—Java虚拟机 JVM
下一篇:Android基础(2)—四大组件之Broadcast和ContentProvider

精彩内容不够看?更多精彩内容,请到微信搜索 “危君子频道” 订阅号,每天更新,欢迎大家关注订阅!

微信公众号
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343