Android四大组件之service
2015-11-10 00:59 叫我小陈 阅读(2013) 评论(0) 编辑 收藏 举报Service
就是默默运行在后台的组件,可以理解为是没有前台的activity,适合用来运行不需要前台界面的代码
服务可以被手动关闭,不会重启,但是如果被自动关闭,内存充足就会重启
startService启动服务的生命周期
onCreate-onStartCommand-onDestroy
重复的调用startService会导致onStartCommand被重复调用
进程优先级
1. 前台进程:拥有前台activity(onResume方法被调用)
2. 可见进程:拥有可见activity(onPause方法被调用)
3. 服务进程:不到万不得已不会被回收,而且即便被回收,内存充足时也会被重启
4. 后台进程:拥有后台activity(activity的onStop方法被调用了),很容易被回收
5. 空进程:没有运行任何activity,很容易被回收
如何显示启动服务和关闭服务
1 写java类继承Service
2 在清单文件中注册service
3 intent = new Intent(this, MyService.class);
4 startService(intent);
5 stopService(intent);
服务的两种启动方式
服务两种启动方式
startService:服务被启动之后,跟启动它的组件没有一毛钱关系
bindService:跟启动它的组件同生共死
绑定服务和解绑服务的生命周期方法:onCreate->onBind->onUnbind->onDestroy
bindService可以做到startService:
我们想要在在activity中调用服务中的方法,无法通过startService调用,服务对象只能由系统来创建,这个时候我们需要调用服务中的方法就需要通过绑定服务实现
思路:首先在服务中 定义一个方法 定义一个内部类,这个内部类继承Binder,我们在这个内部类中定义一个方法,同时在这个方法中调用我们在服务中想要调用地方法,定义一个接口,在接口里面定义一个方法,内部类中的方法通过实现这个接口去实现这个方法。 在服务的onBind方法中,返回定义的内部类。
在activity中启动绑定服务, bindService(intent, conn, BIND_AUTO_CREATE);启动服务时 conn的类实现的ServiceConnection接口中的方法onServiceConnected(ComponentName name, IBinder service) 参数IBinder service就是我们在服务方法中的onBind方法中返回的内部类对象。通过返回的内部类对象去调用服务中的方法。
比喻:将服务比作成领导,领导有办法方法,我们想要找领导办证,也需要调用领导办法的方法,但是我们不能直接找到领导,只能通过领导的中间人对象,通过他去调用领导的办证方法,从而达到办证的目的。
用代码体现:
public class LeaderService extends Service { @Override public IBinder onBind(Intent intent) { // 返回一个Binder对象,这个对象就是中间人对象 return new ZhouMi(); } class ZhouMi extends Binder implements PublicBusiness{ public void QianXian(){ banZheng(); } } public void banZheng(){ System.out.println("办证"); } }
//注册服务
<service android:name="com.xiaochen.banzheng.LeaderService"></service>
//
public class MainActivity extends Activity { private Intent intent; private MyServiceConn conn; PublicBusiness pb; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //服务放进意图对象 intent = new Intent(this, LeaderService.class); conn = new MyServiceConn(); //绑定领导服务 bindService(intent, conn, BIND_AUTO_CREATE); } public void click(View v){ //调用服务的办证方法 pb.QianXian(); } class MyServiceConn implements ServiceConnection{ //连接服务成功,此方法调用 @Override public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub pb = (PublicBusiness) service; } @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub } } }
两种启动方法混合使用
我们在听音乐的时候,音乐是一个服务进程 ,startService(intent); 但是我们需要在前台见面activity中调用音乐的方法,比如播放音乐,暂停音乐。这个时候,使用服务进程的时候,我们无法访问到音乐服务中的方法,要想使用音乐服务中的方法,只能使用绑定进程,但是绑定进程,在activity销毁的时候也死了,这个时候就可以使用两种启动方法混合使用
Intent intent = new Intent(this, MusicService.class); //混合调用 //为了把服务所在进程变成服务进程 startService(intent); //为了拿到中间人对象 bindService(intent, new MusicServiceConn(), BIND_AUTO_CREATE);
先start,再bind,销毁时先unbind,在destory
使用服务注册广播接收者
Android四大组件都要在清单文件中注册
使用代码配置广播接收者
可以使用清单文件注册
广播一旦发出,系统就会去所有清单文件中寻找,哪个广播接收者的action和广播的action是匹配的,如果找到了,就把该广播接收者的进程启动起来
可以使用代码注册
需要使用广播接收者时,执行注册的代码,不需要时,执行解除注册的代码,有些广播不需要一直去一直接受的
特殊的广播接收者
安卓中有一些广播接收者,必须使用代码注册,清单文件注册是无效的
1. 屏幕锁屏和解锁
2. 电量改变
广播接收者比较特殊,既可以在清单文件中注册,也可以直接使用代码注册
有的广播接收者,必须代码注册
电量改变
屏幕锁屏和解锁
代码体现,通过服务来注册广播接收者
做两个button 分别开启服务 和关闭 开启服务的时候注册广播接受者, 关闭服务的手执行接触注册
第一步先写一个广播接受者,这个时候不需要再清单文件中注册,我们在代码中注册
public class screenReceive extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if(Intent.ACTION_SCREEN_ON.equals(action)) { System.out.println("pingmukaiqi"); }else if(Intent.ACTION_SCREEN_OFF.equals(action)){ System.out.println("pingmuguanbi"); } } }
第二步 使用服务来注册广播接收者,
public class RegisterService extends Service { private ScreenReceiver receiver; @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } @Override public void onCreate() { super.onCreate(); //1.创建广播接收者对象 receiver = new ScreenReceiver(); //2.创建intent-filter对象 IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); //3.注册广播接收者 registerReceiver(receiver, filter); } @Override public void onDestroy() { super.onDestroy(); //解除注册 unregisterReceiver(receiver); } }
第三步 在MainActivity中放两个button 分别开启和关闭服务 以此来做到,执行广播接受者和关闭广播接受者
public class MainActivity extends Activity { private Intent intent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); intent = new Intent(this,RegisterService.class); } public void click_start(View v) { startService(intent); } public void click_stop(View v) { stopService(intent); } }
本地服务和远程服务的区别
本地服务:服务和启动它的组件在同一个进程
远程服务:服务和启动它的组件不在同一个进程
远程服务只能隐式启动,类似隐式启动Activity,在清单文件中配置Service标签时,必须配置intent-filter子节点,并指定action子节点
代码体现 我们在一个应用中注册一个服务 在清单文件中配置Service标签时,必须配置intent-filter子节点,并指定action子节点
<service android:name="com.xiaochen.remoteservice.RemoteService"> <intent-filter > <action android:name="com.xiaochen.remote"/> </intent-filter> </service>
在另一个应用中启动
public void click(View v){ //启动远程服务 Intent intent = new Intent(); intent.setAction("com.xiaochen.remote"); startService(intent); }
AIDL
Android interface definition language
安卓接口定义语言
作用:跨进程通信
应用场景:远程服务中的中间人对象,其他应用是拿不到的,那么在通过绑定服务获取中间人对象时,就无法强制转换,使用aidl,就可以在其他应用中拿到中间人类所实现的接口
使用步骤
1. 把远程服务的方法抽取成一个单独的接口java文件
2. 把接口java文件的后缀名改成aidl
3. 它会在自动生成的java文件,在java文件中有一个静态抽象类Stub,它已经继承了binder类,实现了我们抽取的方法接口,这个类就是新的中间人
4. 把aidl文件复制粘贴到B项目,粘贴的时候注意,aidl文件所在的包名必须跟A项目中aidl所在的包名一致
5. 在B项目中,强转中间人对象时,直接使用Stub.asInterface()
我们通过模拟一个支付案例来实现跨进程通信
我们有一个支付宝,支付宝里面有一个支付服务,在支付服务中,有一个支付方法。
现在我们有一个应用,需要使用支付宝的支付方法,这个属于跨进程通信,所以使用AIDL
具体代码
支付宝应用
public class PayService extends Service { @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return new PayPangZhi(); } //中间人对象 class PayPangZhi extends Stub{ @Override public void pay() throws RemoteException { //通信异常 // 调用服务的pay方法 PayService.this.pay(); } } public void pay(){ System.out.println("支付"); } }
//在清单文件中注册服务。在另一程序中可以隐式启动
<service android:name="com.xiaochen.alipay.PayService">
<intent-filter >
<action android:name="com.xiaochen.zhifubao"/>
</intent-filter>
</service>
//写一个接口
//抽取出来的接口 ,里面定义远程需要访问的方法,这个时候把接口的后缀名改成aidl 这个时候会自动生产一个PayInterface 的java类 在java文件中有一个静态抽象类Stub,它已经继承了binder类,实现了PayInterface 接口,这个类就是新的中间人
interface PayInterface {
void pay();
}
需要支付方法的应用
第一步 我们首先将支付宝应用中的aidl文件复制粘贴到B项目,粘贴的时候注意,aidl文件所在的包名必须跟A项目中aidl所在的包名一致
第二步 启动远程服务,调用远程服务的方法
public class MainActivity extends Activity { PayInterface pi; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //隐式启动远程服务 Intent intent = new Intent(); intent.setAction("com.xiaochen.zhifubao");
//使用中间人对象,所以启动绑定服务 bindService(intent, new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub } @Override public void onServiceConnected(ComponentName name, IBinder service) { // 使用aidl中自动生成的方法来强转 pi = Stub.asInterface(service); } }, BIND_AUTO_CREATE); } //点击调用远程服务的支付方法 public void click(View v){ try { pi.pay(); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
进程优先级的补充:
以下是Androidapi对5种进程的描述
- 1. 前台进程
- 用户当前操作所必须的进程。满足以下任一条件时,进程被视作处于前台:
-
- 其中运行着正与用户交互的Activity(Activity对象的onResume()]方法已被调用)。
- 其中运行着被正与用户交互的activity绑定的服务。
- 其中运行着“前台”服务——服务以[startForeground()方式被调用。
- 其中运行着正在执行生命周期回调方法(onCreate()、onStart()或onDestroy())的服务。
- 其中运行着正在执行onReceive()方法的BroadcastReceiver。
-
- 一般而言,任何时刻前台进程都是为数不多的,只有迫不得已——当内存不足以维持它们同时运行时——才会被终止。通常,设备这时候已经到了使用虚拟内存的地步,终止一些前台进程是为了保证用户界面的及时响应。
- 可见进程被认为是非常重要的进程,除非无法维持所有前台进程同时运行了,它们是不会被终止的。
- 3. 服务进程
- 此进程运行着由startService()方法启动的服务,它不会升级为上述两级别。尽管服务进程不直接和用户所见内容关联,但他们通常在执行一些用户关心的操作(比如在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台、可见进程同时运行,系统会保持服务进程的运行。
- 5. 空进程
- 不含任何活动应用程序组件的进程。保留这种进程的唯一目的就是用作缓存,以改善下次在此进程中运行组件的启动时间。为了在进程缓存和内核缓存间平衡系统整体资源,系统经常会终止这种进程。
样式和主体
我们在开发中有些组件的样式可能相同,这个时候我们可以使用样式表style="@style/styleDemo" 在组件中通过style指定资源id,就可以实现组件的样式。
定义样式,可以方便开发,对相同样式的组件统一管理和统一修改
我们可以再res/values下的style.xml中定义样式
<style name="fuStyle"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:textSize">22sp</item> <item name="android:textColor">#00ff00</item> </style> <style name="zi" parent="fuStyle"> //这里可以引用父样式,在父样式的基础上修改或添加 <item name="android:textSize">30sp</item> </style> <style name="zi.sui" > //这里使用.来实现继承父样式 <item name="android:textColor">#0000ff</item> </style> <style name="myTheme"> //在这里我们定义主体 主体一般是使用在清单文件的application节点中的 <item name="android:background">#ff0000</item> </style>
样式使用
<TextView
android:text="@string/hello_world"
style="@style/fuStyle"
/>
主体使用
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/myTheme" > //这里是使用我们自己定义的主体
国际化
在开发中如果想用做一些国际化的产品,安卓系统使用不同的语言,对应程序显示不同的语言或图片 ,我们可以做文字国际化和图片国际化,是通过文件约定的后缀,里面放上对应的文件来实现的