zoukankan      html  css  js  c++  java
  • 【转】 Pro Android学习笔记(七七):服务(2):Local Service

    目录(?)[-]

    1. Local service代码
    2. 调用Local ServiceLocal Service client代码
    3. AndroidManifestxml定义Serviceacitivty的launchMode
    4. 一些思考

    文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处:http://blog.csdn.net/flowingflying/

    Local Service的目的是更容易实现后台任务。下面是一个简单的小例子,通过一个activity请求服务和关闭服务。实际上应用中任何的component都可以使用服务,如activity,service,普通的java类。

    Client是一个activity,UI很简单,由两个button组成,按“Start Service”将触发Local Service,按“Stop Service”将强制终止Local Service。Local Service创建后,会在通知栏显示信息,Service被终止时,通知栏信息消失,我们可以据此观察local service是否正在运行。下拉通知栏,我们可以看到具体的通知信息。点击通知,通常会打开某个相关的activity,本例打开client。Local Service每收到请求(通过intent来传递),都会开启一个线程执行相应的任务,本例的任务采用Sleep()来模拟。

    Local service代码

    在给出代码之前,先看看执行的流程,以更好地了解local service的生命周期。

    //【1】自定义的LocalBackgroundService是android.app.Service的继承
    public class LocalBackgroundService extends Service{
        private static final String TAG = "LocalBackgroundService";
        public static final String COUNTER_STR = "counter"; 
        private NotificationManager notifyMgr = null;
        private ThreadGroup myThreads = new ThreadGroup(TAG);
        private static final int MY_NOFIGY_ID = 100; 
        
        @Override //【2】Service创建时回调函数,用于service的初始化,本例将在通知栏中给出信息
        public void onCreate()
     {  
            super.onCreate(); 
            Log.v(TAG,"onCreate() is called. service is " + this.toString());
            //在通知栏显示信息,相关代码也可参考Android学习笔记(五四):通知Notification(上)。service通常(包括本例)会在后台开启线程,在无UI情况下运行,如果用户需要干预运行,例如打开邮件,又例如关闭音乐播放,可通过通知的方式,用户点击通知,创建某activity来实现。
            notifyMgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
            displayNotificationMessage("Backgound service is running...");  
        }  
         
        @Override //【3】当收到client的intent时,触发onStartCommand()。 每次client的请求都有一个对应的startId,对应的可以用stopSelfResult(int startId),在实际应用中,可以利用startId进行一一识别和数据保存及对应,当然在本例中也可以用intent传递过来的counter作为识别码。当一个请求触发了onStartCommand(),正在运行之际,第二个请求来了,会在队列中,等待前面请求的代码运行完。本例在onStartCommand()中开启后台线程,可以触发产生多个后台线程。 
        public int onStartCommand(Intent intent, int flags, int startId) { 
            super.onStartCommand(intent, flags, startId);
            //【3.1】通过intent读取client传递的信息,本例简单,仅进行验证
            int counter = intent.getExtras().getInt(COUNTER_STR); 
            Log.v(TAG,"onStartCommand() is called. counter = " + counter + " startId = " + startId);
            //【3.2】Service运行在主线程,如果我们要进行后台处理,必须创建自己的后台线程,在本例中,我们使用了使用java.lang.Thread.Thread(ThreadGroup group, Runnable runnable, String threadName) ,采用ThreadGroup方式,是为了在Service结束,即onDestroy()中通过group.interrupt(),终止所有正在运行的线程。
            new Thread(myThreads,new ServiceWorker(counter),TAG+counter).start(); 
            return START_STICKY; 
        } 

       //【3.3】线程代码,本例简单地sleep十秒 
        private class ServiceWorker implements Runnable{ 
            private int counter = -1;    
             
            public ServiceWorker(int counter){ 
                this.counter = counter; 
            } 
            
            @Override 
            public void run() {  
                final String tag = "ServiceWorker-" + Thread.currentThread().getId();
                try{ 
                    Log.v(tag,"sleep after 10s, counter = " + counter);
                    Thread.sleep(10000); 
                    Log.v(tag,"waking up, counter = " + counter);
                }catch (InterruptedException  e)//当线程被中断,出现InterruptedException异常
                    Log.v(tag,"sleep Interrupted... counter = " + counter);
                }catch (Exception ex){ 
                    Log.e(tag,ex.toString()); 
                } 
            } 
            
        }     
         
        @Override //【4】在Service结束时调用,进行cleanup。根据本例设计,此前要终止所有线程,清除通知栏的显示。在实际运行中,我们可能不会这样简单粗暴的中断,例如正在发送的邮件,我们仍希望将邮件成功发送出去。 
        public void onDestroy()
     {  
            Log.v(TAG,"onDestroy() is called... Interrupt threads and cannceling notifications");
            myThreads.interrupt();  //对于Thread发送interrupt请求,本例是在sleep,因此会抛出InterruptedExcetpion异常,如果在进行I/O处理,会收到ClosedByInterruptException异常, 
            notifyMgr.cancelAll();   //清除所有的通知栏显示
            super.onDestroy();         
        }  

       @Override //【1】作为Local Service,client通过startService()来进行触发,不需要通过bindService(),但是onBind()是Service类的abstract函数必须重写,看简单地return null即可。
        public IBinder onBind(Intent arg0) {  
            Log.v(TAG,"onBind() is called."); 
            return null; 
        }  
          
        //【2】相关代码的理解,可以参考Android学习笔记(五四):通知Notification(上),本例采用Notification.Builder().build(),要求sdk的最小版本是16 
        private void displayNotificationMessage(String message){ 
            PendingIntent contentIntent = PendingIntent.getActivity
                                        (this, 0, new Intent(this,LocalServiceClientActivity.class), 0);//点击通知将创建client activity,注意这是一个新的activity,而不是返回原来的activity。
            Notification notify = new Notification.Builder(this) 
                                    .setSmallIcon(R.drawable.emo_im_winking) 
                                    .setTicker(message) 
                                    .setContentText(message)  
                                    .setContentIntent(contentIntent) 
                                    .build(); 
            notify.flags |= Notification.FLAG_NO_CLEAR; //本例,我们希望只要service存在,通知就存在,即不能人手清除该通知的显示,所以设置FLAG_NO_CLEAR。
            notifyMgr.notify(MY_NOFIGY_ID,notify);
        } 
    }

    调用Local Service:Local Service client代码

    public class LocalServiceClientActivity extends Activity{ 
        private static final String TAG="LocalServiceClientActivity"; 
        private int counter = 1; 
       @Override 
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState); 
            setContentView(R.layout.localservice_client); 
            Log.v(TAG,"client: onCreate() is called. client is " + this.toString());
        }      
         
        @Override 
        protected void onDestroy() { 
            // 本例在activity退出时关闭local service,当然在实际案例中,不一定要如此,可能是基于app 
            stopLocalService(); 
            super.onDestroy(); 
        } 

       // 用户按button触发,对应layout xml文件的<Button … android:onClient="onClickAct" …/>
        public void onClickAct(View v){ 
            switch(v.getId()){ 
            case R.id.startButton: 
                startLocalService(); 
                break; 
                
            case R.id.stopButton: 
                stopLocalService(); 
                break; 
                
            default: 
                break; 
            } 
        } 

        //请求local service,通过startService()请求本地服务 
        private void startLocalService(){ 
            Log.v(TAG,"client: Start service... counter = " + counter);
            Intent intent = new Intent(this,LocalBackgroundService.class);  
            intent.putExtra(LocalBackgroundService.COUNTER_STR, counter ++ );//添加传递的数据
            startService(intent);  //请求本地服务      
        } 

        //终止local service,通过stopService()请求本地服务,注意stopService()和startService()并不是一一对应的,可以请求多次本地服务,而一旦stopService()就会关闭本地服务,本例中所有的线程将被终止。
        private void stopLocalService(){ 
           Log.v(TAG,"client: Stop service... "); 
            Intent intent = new Intent(this,LocalBackgroundService.class); 
            if(stopService(intent)){ //如果service存在并关闭,即触发service的onDestroy(),返回true,如果不存在,返回false
                Log.v(TAG,"client: stopService was successful");
            }else{ 
                Log.v(TAG,"client: stopService was unsuccessful");
            } 
        } 

    AndroidManifest.xml:定义Service、acitivty的launchMode

    和acitivty一样,service也需要在AndroidManifest.xml中定义。

    <?xml version="1.0" encoding="utf-8"?> 
    <manifest … > 
        <application ... ... >  
            … … 
          <!-- 我们在按了Start Service的button后,按Home键返回系统主界面,这时如果我们下拉通知栏,按通知,将会开启一个新的activity。但是原有的activity只是看不见,仍然存在,我们希望显示的仍是原来的activity,通过设置launchMode为signleTop解决这个问题,将invisible的acitivity放在最前显示。  -->
           <activity android:name=".LocalServiceClientActivity" android:label="@string/demo_local_service"            android:launchMode="singleTop"/>
            ... ...             
            <!-- 本例是local service,只需要给出类名作为name属性即可  -->
            <service android:name="
    LocalBackgroundService" /> 
        </application> 
        … … 
    </manifest>

    一些思考

    有时我们只希望运行一个且是唯一的后台服务线程(如播放音乐),可以考虑在onCreate()中启动线程,但这种方式不能获得通过intent中传递的数据,如果那样只好在onStartService()中进行逻辑判断:是否已经有后台线程在运行。

    本笔记涉及的例子代码,可以在Pro Android学习:Android service小例子中下载。

    相关链接: 我的Android开发相关文章

  • 相关阅读:
    iOS开发UI篇—UIScrollView控件介绍
    iOS开发UI篇—iOS开发中三种简单的动画设置
    iOS开发UI篇—iOS开发中Xcode的一些使用技巧
    iOS开发UI篇—在ImageView中添加按钮以及Tag的参数说明
    iOS开发UI篇—KVC简单介绍
    iOS开发UI篇—从代码的逐步优化看MVC
    iOS开发UI篇—xib的简单使用
    iOS开发UI篇—字典转模型
    iOS开发UI篇—九宫格坐标计算
    A1005. Spell It Right
  • 原文地址:https://www.cnblogs.com/blongfree/p/5048061.html
Copyright © 2011-2022 走看看