代码改变世界

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. 前台进程
用户当前操作所必须的进程。满足以下任一条件时,进程被视作处于前台:
一般而言,任何时刻前台进程都是为数不多的,只有迫不得已——当内存不足以维持它们同时运行时——才会被终止。通常,设备这时候已经到了使用虚拟内存的地步,终止一些前台进程是为了保证用户界面的及时响应。
2. 可见进程
没有前台组件、但仍会影响用户在屏幕上所见内容的进程。满足以下任一条件时,进程被认为是可见的:
  • 其中运行着不在前台的Activity,但用户仍然可见到此activity(onPause()方法被调用了)。比如以下场合就可能发生这种情况:前台activity打开了一个对话框,而之前的activity还允许显示在后面。
  • 其中运行着被可见(或前台)activity绑定的服务
可见进程被认为是非常重要的进程,除非无法维持所有前台进程同时运行了,它们是不会被终止的。
3. 服务进程
此进程运行着由startService()方法启动的服务,它不会升级为上述两级别。尽管服务进程不直接和用户所见内容关联,但他们通常在执行一些用户关心的操作(比如在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台、可见进程同时运行,系统会保持服务进程的运行。
4. 后台进程
包含目前用户不可见activity(Activity对象的onStop()方法已被调用)的进程。这些进程对用户体验没有直接的影响,系统可能在任意时间终止它们,以回收内存供前台进程、可见进程及服务进程使用。通常会有很多后台进程在运行,所以它们被保存在一个LRU(最近最少使用)列表中,以确保最近被用户使用的activity最后一个被终止。如果一个activity正确实现了生命周期方法,并保存了当前的状态,则终止此类进程不会对用户体验产生可见的影响。因为在用户返回时,activity会恢复所有可见的状态。关于保存和恢复状态的详细信息,请参阅Activity文档。
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" > //这里是使用我们自己定义的主体

 


 

 国际化

   在开发中如果想用做一些国际化的产品,安卓系统使用不同的语言,对应程序显示不同的语言或图片 ,我们可以做文字国际化和图片国际化,是通过文件约定的后缀,里面放上对应的文件来实现的