Android基础第九篇

本文详细介绍了Android中进程和线程的基础知识,包括进程的五种级别和线程与UI的关系。Service作为后台组件的使用,包括其启动方式、生命周期以及电话监听器案例。文章还展示了如何通过服务监听电话状态并录制通话,以及自动开启服务的方法。同时,讨论了服务的启动和绑定方式,如何调用服务中的方法,特别是通过IBinder实现。最后,探讨了混合方式开启Service的音乐播放器案例,以及使用服务注册特殊广播接收者和AIDL进行进程间通信的概念和应用。
摘要由CSDN通过智能技术生成

转载请标明出处:
http://blog.csdn.net/gj782128729/article/details/52510925
本文出自:【高境的博客】

1. Android中进程和线程概述


1.1. Android中的进程

当一个程序第一次启动的时候,Android会启动一个Linux进程和一个主线程。默认的情况下,所有该程序的组件都将在该进程和线程中运行。Android会尽量保留一个正在运行进程,只在内存资源出现不足时,Android会尝试停止一些进程从而释放足够的资源给其他新的进程使用,也能保证用户正在访问的当前进程有足够的资源去及时地响应用户的事件。
我们可以将一些组件运行在其他进程中,并且可以为任意的进程添加线程。组件运行在哪个进程中是在manifest文件里设置的,其中<activity><service><receiver><provider>都有一个process属性来指定该组件运行在哪个进程之中。我们可以设置这个属性,使得每个组件运行在它们自己的进程中,或是几个组件共同享用一个进程,或是不共同享用。<application>元素也有一个process属性,用来指定所有的组件的默认属性。
Android会根据进程中运行的组件类别以及组件的状态来判断该进程的重要性,Android会首先停止那些不重要的进程。按照重要性从高到低一共有五个级别:

1.前台进程:

前台进程是用户当前正在使用的进程。只有一些前台进程可以在任何时候都存在。他们是最后一个被结束的,当内存低到根本连他们都不能运行的时候。一般来说,在这种情况下,设备会进行内存调度,中止一些前台进程来保持对用户交互的响应。
如果有以下的情形的那么就是前台进程:
(a)这个进程运行着一个正在和用户交互的Activity(这个Activity的onResume()方法被调用)。
(b)这个进程里有绑定到当前正在和用户交互的Activity的一个service。
(c)这个进程里有一个service对象,这个service对象正在执行一个它的生命周期的回调函数(onCreate(), onStart(), onDestroy())
(d)这个进程里有一个正在运行onReceive()方法的BroadCastReiver对象。

2.可见进程

  可见进程是不包含前台的组件但是仍会影响用户在屏幕上所见内容的进程,除非前台进程需要获取它的资源,不然不会被中止。
  如果有如下的一种情形就是可见进程:
(a)这个进程中含有一个不位于前台的Activity,但是仍然对用户是可见的(这个Activity的onPause()方法被调用),这是很可能发生的,例如,如果前台Activity是一个对话框的话,就会允许在它后面看到前一个Activity。
(b)这个进程里有一个绑定到一个可见的Activity的Service。

3.服务进程

  运行着一个通过startService() 方法启动的service,它不会升级为上面两种级别。service所在的进程虽然对用户不是直接可见的,但是他们执行了用户非常关注的任务(比如播放mp3,从网络下载数据)。只要前台进程和可见进程有足够的内存,系统不会回收他们。

4.后台进程

运行着一个对用户不可见的activity(调用过 onStop() 方法)。这些进程对用户体验没有直接的影响,可以在服务进程、可见进程、前台进程需要内存的时候回收。通常,系统中会有很多不可见进程在运行,他们被保存在LRU (least recently used) 列表中,以便内存不足的时候被第一时间回收。如果一个activity正确的执行了它的生命周期,关闭这个进程对于用户体验没有太大的影响。

5.空进程

  未运行任何程序组件。运行这些进程的唯一原因是作为一个缓存,缩短下次程序需要重新使用的启动时间。系统经常中止这些进程,这样可以调节程序缓存和系统缓存的平衡。
Android 对进程的重要性评级的时候,选取它最高的级别。例如,如果一个进程含有一个service和一个可视activity,进程将被归入一个可视进程而不是service进程。

1.2. Android中的线程

应用程序启动时,系统会为它创建一个名为“main”的主线程。主线程非常重要,因为它负责把事件分发给相应的用户界面widget——包括屏幕绘图事件。它也是应用程序与Android UI组件包(来自android.widget和android.view包)进行交互的线程。因此,主线程有时也被叫做UI线程。

系统并不会为每个组件的实例都创建单独的线程。运行于同一个进程中的所有组件都是在UI线程中实例化的,对每个组件的系统调用也都是由UI线程分发的。因此,对系统回调进行响应的方法(比如报告用户操作的onKeyDown()或生命周期回调方法)总是运行在UI线程中。

如果UI线程需要处理每一件事情,那些耗时很长的操作——诸如访问网络或查询数据库等将会阻塞整个UI(线程)。一旦线程被阻塞,所有事件都不能被分发,包括屏幕绘图事件。从用户的角度看来,应用程序看上去像是挂起了。更糟糕的是,如果UI线程被阻塞超过一定时间(目前大约是5秒钟),用户就会被提示那个可恶的“应用程序没有响应”(ANR)对话框。

此外,Andoid的UI组件包并不是线程安全的。因此不允许从工作线程中操作UI——只能从UI线程中操作用户界面。于是,Andoid的单线程模式必须遵守两个规则:
(a)不要阻塞UI线程。
(b)不要在UI线程之外访问Andoid的UI组件包。

2. Service


2.1. Service概述

Service是Android中四大组件之一,在Android开发中有非常重要的作用。
Service(服务)是一个没有用户界面的在后台运行执行耗时操作的应用组件。其他应用组件能够启动Service,并且当用户切换到另外的应用场景,Service将持续在后台运行。另外,一个组件能够绑定到一个service与之交互(IPC机制),例如,一个service可能会处理网络操作,播放音乐,操作文件 I/O或者与内容提供者(content provider)交互,所有这些活动都是在后台进行。

2.2. Service的两种状态

Service有两种状态,“启动的”和“绑定”:

Started

通过startService() 启动的服务处于“启动的”状态,一旦启动,Service就在后台运行,即使启动它的应用组件已经被销毁了。通常started状态的Service执行单任务并且不返回任何结果给启动者。比如当下载或上传一个文件,当这项操作完成时,Service应该停止它本身。

Bound

还有一种“绑定”状态的Service,通过调用bindService()来启动,一个绑定的Service提供一个允许组件与Service交互的接口,可以发送请求、获取返回结果,还可以通过跨进程通信来交互(IPC)。绑定的Service只有当应用组件绑定后才能运行,多个组件可以绑定一个Service,当调用unbind()方法时,这个Service就会被销毁了。

另外,在官方的说明文档中还有一个警告:
Service 与Activity一样都存在与当前进程的主线程中,所以,一些阻塞UI的操作,比如耗时操作不能放在Service里进行,比如另外开启一个线程来处理 诸如网络请求的耗时操作。如果在Service里进行一些耗CPU和耗时操作,可能会引发ANR警告,这时应用会弹出强制关闭还是等待的对话框。所以,对Service的理解就是和Activity平级的,只不过是看不见的,在后台运行的一个组件,这也是为什么和Activity同被说为Android的基本组件。

2.3. Service的简单使用

1) 创建一个Service,需要继承Service类。

2) 覆盖一些回调函数来处理服务生命周期的一些关键要素,并且如果合适的话,需要提供一个机制让组件绑定到服务。这些最重要的需要覆盖的函数如下:

onStartCommand():

onStartCommand()方法当另一个组件(如Activity)通过调用startService()请求Service启动时调用。一旦这个方法执行,这个服务就被开启并且在后台无限期地运行。如果你实现了这个方法,你就有责任在服务工作完毕后通过调用stopSelf()或者stopService()关闭它(如果仅仅用来提供绑定,就不需要实现这个方法)。

onBind():

onBind()方法当另一个组件(如执行RPC)通过bindService()和这个服务绑定的时候调用。在这个方法的实现中,需要通过返回一个IBinder对象提供客户端用来和Service通讯的接口,你必须一致实现该方法,除非不想绑定服务,这时候需要返回null。

onCreate()

Service第一次创建的时候调用该方法来执行一次性安装程序(之前调用要么onStartCommand()要么onBind())。如果Service已经运行了,这个方法不会被调用。

onDestory()

当Service不再使用并且被销毁的时候调用。服务需要实现这个方法来清理资源如线程,注册的监听器,接受者等。这个方法是Service最后一个调用的。

3) 在清单文件中注册服务

<manifest ... >
  ...
  <application ... >
    <service android:name=".ExampleService" />
    ...
  </application>
</manifest>

3. 服务的第一种启动方式


服务的第一种启动方式是调用startService()方法。
创建一个服务,必须实现onBind()方法,实现onCreate(),onStartCommand()和onDestory()方法:

public class DemoService extends Service {
   
    @Override
    public IBinder onBind(Intent intent) {      
        return null;
    }
    @Override
    public void onCreate() {
        System.out.println("onCreate");
        super.onCreate();
    }   
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public void onDestroy() {

        System.out.println("onDestroy");
        super.onDestroy();
    }   
}

在清单文件中注册

<service android:name="com.itheima.servicedemo.DemoService"></service>

MainActivity中实现开启服务
这里写图片描述
点击按钮开启服务

public void click(View v){
    Intent intent = new Intent(MainActivity.this,DemoService.class);
    startService(intent);   
}

查看Logcat日志发现开启服务调用onCreate()和onStartCommand()方法:
这里写图片描述

在此点击按钮,查看日志,发现onStartCommand()又执行了一次:
这里写图片描述

那么如何查看服务真的被启动了呢?我们可以在模拟器设置界面中的应用界面查看,如下图:
这里写图片描述

那么如何停止服务呢?点开上图标出的条目,跳转到如下图的另一个界面,点击停止按钮。如下图:
这里写图片描述

这时候,服务就停止了。我们可以查看Logcat日志,发现调用了onDestory()方法:
这里写图片描述

通过上面的操作可以得出调用startService()方法时Service的生命周期如下图:
这里写图片描述

4. 电话窃听器案例


本案例实现在后台监听电话状态,当手机来电并且接通后,开始录音,并且保存到sdcard中。
为什么需要在服务中监听电话状态呢?因为服务开启后可以在后台一直运行,如果放在Activity中监听电话状态,当Activity销毁后就不能监听到电话状态了。
使用Thread(){}.start()方法也可以在后台实现监听,那么为什么不使用子线程而使用Service呢?因为之前我们已经了解了进程的等级,当应用程序退出时,当前应用的进程就变成一个空进程,最容易被系统回收。开启服务是在服务进程中运行,服务进程的优先级比后台进程高。

4.1. 监听电话状态

监听电话状态需要使用TelephonyManager类。TelephonyManager类主要提供了一系列用于访问与手机通讯相关的状态和信息的get方法。其中包括手机SIM的状态和信息、电信网络的状态及手机用户的信息。

Context.getSystemService(Context.TELEPHONY_SERVICE)方法可以用来获取到TelephonyManager类的对象。需要注意的是有些通讯信息的获取,对应用程序的权限有一定的限制,在开发的时候需要为其添加相应的权限。

查看api文档,找到如下图方法,可以用来监听电话状态:
这里写图片描述

listen()方法,注册一个监听对象用来接收指定电话状态变化的通知。
在服务中利用TelephonyManager监听电话状态:

public class PhoneService extends Service {
   
    private TelephonyManager tManager;
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public void onCreate() {
        tManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
        //调用TelephonyManager的listen()方法监听电话状态。参数1表示电话状态监听器,参数2表示需要监听的电话事件
        tManager.listen(new MyPhoneStateListener(),PhoneStateListener.LISTEN_CALL_STATE);
        super.onCreate();
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

创建自定义电话状态监听器:

private class MyPhoneStateListener extends PhoneStateListener {
   
    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        super.onCallStateChanged(state, incomingNumber);
        //判断state状态
        switch (state) {
        case TelephonyManager.CALL_STATE_IDLE: // CALL_STATE_IDLE表示空闲状态
            System.out.println("判断用户是否已经开启录音机,如果开启,上传");
            break;
        case TelephonyManager.CALL_STATE_OFFHOOK: // CALL_STATE_OFFHOOK表示接通状态
            System.out.println("开始录");
            break;
        case TelephonyManager.CALL_STATE_RINGING: // CALL_STATE_RINGING表示电话响铃状态
            System.out.println("电话响铃的时候  我就准备一个录音机 ");
 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值