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

     


     

     国际化

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

  • 相关阅读:
    函数式宏定义与普通函数
    linux之sort用法
    HDU 4390 Number Sequence 容斥原理
    HDU 4407 Sum 容斥原理
    HDU 4059 The Boss on Mars 容斥原理
    UVA12653 Buses
    UVA 12651 Triangles
    UVA 10892
    HDU 4292 Food
    HDU 4288 Coder
  • 原文地址:https://www.cnblogs.com/jiaowoxiaochen/p/4951746.html
Copyright © 2011-2022 走看看