zoukankan      html  css  js  c++  java
  • Android四大组件之service

    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" > //这里是使用我们自己定义的主体

     


     

     国际化

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

  • 相关阅读:
    省选知识点
    寒假练习
    水题欢乐赛-套路
    2019年12月(2)
    洛谷P1347 排序
    Aizu
    2019年12月(1)
    【CSP2019】
    联系博主
    UVA1420 Priest John's Busiest Day【贪心】
  • 原文地址:https://www.cnblogs.com/jiaowoxiaochen/p/4951746.html
Copyright © 2011-2022 走看看