服务:长期后台运行的没有界面的组件
服务的目的:长期后台运行
系统不容易回收掉进程。即使回收了,内存充足的时候,会把进程重新创建。
创建服务
1.创建服务,并在清单文件中配置服务1
2
3
4
5
6class MyService extends Service{
	
	public IBinder onBind(Intent intent){
		return null;
	}
}
2.开启服务1
2Intent intent = new Intent(this, MyService.class);
startService(intent);
3.关闭服务1
2Intent intent = new Intent(this, MyService.class);
stopService(intent);
进程分为5个等级的优先级:(从高到低)
1.Foreground process 前台进程 用户正在玩的应用程序对应的进程
2.Visible process 可视进程 用户仍然可以看到这个进程的页面
3.Service process 服务进程 应用程序有一个服务组件在后台运行
4.Background process 后台进程 应用程序没有服务在运行 并且最小化(activity onStop)
5.Empty process 空进程 没有任何正在运行的activity 任务栈空了
android系统进程管理是按照一定的规则的:
应用程序一旦被打开,通常情况下关闭后(清空任务栈)进程不会停止,方便下一次应用启动。
android系统有一套内存清理机制,按照优先级回收系统的内存。
服务的生命周期
onCreate():服务第一次创建时调用
onStartCommand():服务启动时调用
onDestory():服务销毁时调用
电话录音机
电话的状态
1.空闲状态
2.响铃状态
3.摘机状态(接听)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
53public class RecordService extends Service{
	
	public IBinder onBind(Intent intent){
		return null;
	}
	
	public void onCreate(){
		super.onCreate();
		TelephonyManager tm = (TelephonyManager)getSystemServie(TELEPHONY_SERVICE);
		//第二个参数决定监听什么内容
		tm.listen(new MyListener(), PhoneStateListener.LISTEN_CALL_STATE);
	}
}
class MyListener extends PhoneStateListener{
	private MediaRecorder recorder;
	
	onCallStateChanged(int state, String incomingNumber){
		super.onCallStateChanged(state, incomingNumber);
		switch(state){
			case TelephonyManager.CALL_STATE_IDLE:
				if(recorder != null){
					recorder.stop();
					recorder.release();
					recorder = null;
				}
				break;
			case TelephonyManager.CALL_STATE_RINGING:
				//初始化录音机
				if(recorder == null){
					recorder = new MediaRecorder();
					recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
					recorder.setOutFormat(MediaRecorder.OutputFormat.THREE_GPP);
					recorder.setOutputFile(getFilesDir().getPath() + "/recorder.3gp");
					recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
					try{
						recorder.prepare();
					}catch(Exception e){
						e.printStackTrace();
					}
				}
				break;
			case TelephonyManager.CALL_STATE_OFFHOOK:
				//开始录音
				recorder.start();
				break;
			default:
				break;
		}
	}
}
服务的两种启动方式及生命周期
- startService(): 启动服务所在的进程属于服务进程
 activity一旦启动服务,服务就跟activity没有关系了
 onCreate()->onStartCommand()->onDestory()
- bindService(): 启动服务所在的进程不属于服务进程
 activity一旦与服务建立连接,activity销毁,服务也会销毁
自定义一个接口:1
2
3
4interface  Person{
	//定义公共访问的方法
	public void visitService();
}
在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
39MyServiceConnection conn;
Intent intent;
public void onCreate(Bundle onSavedInstanceStste){
	super.onCreate(onSavedInstanceStste);
	setContentView(R.layout.main);
	intent = new Intent(this, MyService.class);
	conn = new MyServiceConnection();
	bindService(intent, conn, BIND_AUTO_CREATE);
}
public void bind(View v){
	//绑定服务 onCreate()->onBind()
	bindService(intent, conn, BIND_AUTO_CREATE);
}
public boolean unbind(View v){
	//解绑服务 onUnbind()->onDestory()
	unbindService(conn);
}
Person p;
public void click(View v){
	p.visitService();
}
class MyServiceConnection implements ServiceConnection{
	//连接服务成功,第二个参数即为中间人对象
	
	public void onServiceConnected(ComponentName name, IBinder service){
		p = (Person)service;
	}
	
	public void onServiceDisconnected(ComponentName name){
	}
}
自定义Service中代码: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
27class MyService extends Service{
	//绑定时调用
	
	public IBinder onBind(Intent intent){
		//返回一个Binder对象,即中间人对象
		return new XiaoLi();
	}
	//创建内部类作为中间人,来访问服务中方法
	class XiaoLi extends Binder implements Person{
		//实现接口中用于公共访问的方法
		public void visitService(){
			//访问service中的方法
			help();
		}
		//自己的方法,不让外界访问
		public void daMaJiang(){
		}
	}
	//自定义服务的方法
	public void help(){
		System.out.println("帮人办事");
	}
}
服务模拟音乐播放
| 1 | interface MusicInterface{ | 
| 1 | class MainActivity extends Activity{ | 
| 1 | class MusicService extends Service{ | 
服务的混合调用
onCreate()->onStartCommand()->onBind()->onUnbind()->onDestory()
使用代码配置广播接收者
- 使用清单文件注册
 广播一旦发出,系统就会去所有清单文件中寻找哪个广播接收者的action和广播的action是匹配的。如果找到了,就会把该广播接收者的进程启动起来
- 使用代码注册
 需要使用广播接收者时,执行注册的代码;不需要时,解除注册
特殊的广播接收者
安卓中有一些广播接收者,必须使用代码注册,清单文件注册是无效的
1). 屏幕锁屏与解锁
2). 电量改变
使用服务注册广播接收者
| 1 | public class ScreenReceiver extends BroadcastReceiver{ | 
自定义服务代码中: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
27public MyService extends Service{
	ScreenReceiver receiver;
	
	public IBinder onBind(Intent intent){
		return null;
	}
	
	public void onCreate(){
		super.onCreate();
		//创建广播接收者
		receiver = new ScreenReceiver();
		//创建intent-filter
		IntentFilter filter= new IntentFilter();
		filter.addAction(Intent.ACTION_SCREEN_ON);
		filter.addAction(Intent.ACTION_SCREEN_OFF);
		//注册广播接收者
		registerReceiver(receiver, filter);
	}
	
	public void onDestory(){
		super.onDestory();
		unregisterReceiver(receiver);
	}
}
服务的分类
- 本地服务
 服务与启动服务的activity在同一个进程中
- 远程服务
 服务与启动服务的activity不在同一个进程中
远程服务
| 1 | public MemoteService extends Service{ | 
启动远程服务1
2
3
4
5Intent intent = new Intent();
intent.setAction("远程服务清单文件中配置的action");
//android 5.0 之后需要指定要启动的应用程序包名
intent.setPackage("com.jockio.learnandroid");
startService(intent);
AIDL: 进程间通信
Android Interface Definition Language
步骤:
- 将远程服务的方法抽取成一个单独的接口 java 文件
- 将接口文件的后缀名 java 改为 aidl,
- 在自动生成的接口 .java 文件中,有一个静态抽象类 Stub,它已经继承了 Binder 类,实现了抽取方法后的接口,这个类就是中间人
- 把 aidl 文件复制粘贴到要访问远程服务的项目中
 注意:aidl 包名跟原包名必须完全一致
- 在要访问远程服务的项目中,强转中间人对象时,直接使用 Stub.asInterface( Service service)
用 AIDL 模拟支付宝服务
PayInterface.aidl1
2
3interface PayInterface{
	public void pay();
}
| 1 | class PayService extends Service{ | 
远程调用模拟支付宝服务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//把 PayInterface.aidl 文件复制粘贴到项目中
//注意:PayInterface.aidl 包名跟原包名必须完全一致
class MainActivity extends Activity{
	PayServiceConnection conn;
	Intent intent;
	PayInterface interface;
	public void onCreate(Bundle onSavedInstanceStste){
		super.onCreate(onSavedInstanceStste);
		setContentView(R.layout.main);
		intent = new Intent();
		intent.setAction("支付宝服务的action");
		//android 5.0 之后需要指定要启动的应用程序包名
        intent.setPackage("com.jockio.learnandroid");
		conn = new PayServiceConnection();
		//混合调用,为了把服务所在进程变为服务进程
		startService(intent);
		//为了拿到中间人对象
		bindService(intent, conn, BIND_AUTO_CREATE);		
	}
	public void click(View v){
		interface.pay();
	}
	class PayServiceConnection implements ServiceConnection{
		//连接服务成功,第二个参数即为中间人对象
		
		public void onServiceConnected(ComponentName name, IBinder service){
			interface = Stub.asInterface(service);
		}
		
		public void onServiceDisconnected(ComponentName name){
		}
	}
}
进程优先级补充
前台进程
- 拥有一个正在与用户进行交互的activity(onResume方法调用)的进程
- 拥有一个与正在和用户交互的activity绑定的服务的进程
- 拥有一个正在“运行于前台”的服务–服务的startForeground方法调用
- 拥有一个正在执行以下三个生命周期方法中任意一个的服务
 onCreate() onStart() onDestory
- 拥有一个正在执行 onReceive 方法的广播接收者的进程
可见进程
- 拥有一个不在前台,但是用户依然可见的activity(onPause方法调用)的进程
- 拥有一个与可见(或前台)activity绑定的服务的进程
样式与主题
资源目录下,在 values 文件夹中,新建 styles.xml 文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<?xml version="1.0" encoding="utf-8">
<resources>
	<style name="myStyle">
		<item name="android:textSize">20sp</item>
	</style>
	<!--继承方式一-->
	<style name="textviewStyle" parent="myStyle"/>
	<!--继承方式二-->
	<style name="myStyle.another"/>
	<!--主题-->
	<style name="themeStyle">
		<item name="android:background">#f0f0ff</item>
	</style>
</resources>
布局文件中1
2
3<TextView
	android:text="标题"
	style="@style/myStyle.another"/>
国际化
新建文件夹 values-en, 新建strings.xml1
2
3
4<?xml version="1.0" encoding="utf-8">
<resources>
	<string name="app_name">hello</string>
</resources>
新建文件夹 values-zh, 新建strings.xml1
2
3
4<?xml version="1.0" encoding="utf-8">
<resources>
	<string name="app_name">你好</string>
</resources>