zoukankan      html  css  js  c++  java
  • service的进一步学习和总结(aidl binder机制和进程间通讯)

     一、服务的一些分类:

      android service主要分系统服务应用程序服务,系统服务,framwork提供的,而应用程序服务是用户自己定义的继承Service的服务。

      应用程序服务又分为2类:本地服务远程服务,他们的区别在于:创建的服务的客户端与所创建的服务是否在同一个进程中

      本地服务:利用startService或者bindService创建出的服务,若该服务与创建服务的Activity在同一个进程中则称为本地服务。本地服务只在创建该服务的进程内部使用,该应用程序终止服务也跟着终止。

      远程服务:远程服务与创建者不在同一个进程中,因此主程序终止后服务仍然可以运行

    二、本地服务的介绍:

      用做个音乐播放器为例,为了让音乐在后台播放,我们一般创建一个播放音乐的服务,在activity里调用服务即可。

    主要步骤:1、创建service,在里面实现音乐的播放、暂停等功能。

         2、实现onbind返回的类,它继承binder类,主要是向activity抛出服务对象,供activity使用。

         3、在activity里面实现ServiceConnection接口类,然后bindservice(intent,new MusicServiceConnection(), Context.BIND_AUTO_CREATE) 注意第三个参数是自动生成本地服务的标记,当待绑定的服务不存在时使用。当待绑定的服务还没运行起来,绑定前先由framwork生成服务,再进行绑定。

         4、在onServiceConnected 初始化实现onbind返回的类,利用该对象可以利用getService获取绑定的服务对象来控制音乐的播放。

         5、利用广播来更新点击通知栏的播放按钮后activity的控件的状态。

    下面给出相应的demo:是音乐播放器的实现,在通知栏中点击播放暂停音乐与在activity中点击时候它们的控件状态都是一致的。

     

    public class MyService extends Service {
        public static MediaPlayer mediaPlayer;
        public String path = "/data/data/com.example.user.myservicedemo/files/she-say.mp3";
        private Notification notification;
        private MediaSession mSession;
        @Override
        public IBinder onBind(Intent intent) {
            // TODO: Return the communication channel to the service.'
            init();
            return  new MyBinder();
        }
        public  static final int  STATE_STOP = 0;
        public  static  final int  STATE_PAUSE = 1;
        public  static  final int  STATE_PLAY = 2;
        public static   int musicPlayerState = STATE_STOP;
    
        public void init()
        {
            startNotification();
            rigisterNotificationListener();
        }
        public void startMusic()
        {
            if(musicPlayerState == STATE_STOP) {
                mediaPlayer = new MediaPlayer();
                Uri myUri = Uri.parse(path);
                mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                try {
                    mediaPlayer.setDataSource(getApplicationContext(), myUri);
                    mediaPlayer.prepare();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
                mediaPlayer.start();
                 musicPlayerState = STATE_PLAY;
                startNotification();
    
        }
    
        public void pauseMusic()
        {
            if(mediaPlayer!=null&&mediaPlayer.isPlaying())
                mediaPlayer.pause();
            musicPlayerState = STATE_PAUSE;
            startNotification();
        }
    
         class MyBinder extends Binder
        {
            MyService getService()
           {
               return MyService.this;
           }
        }
    
    
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public  void startNotification()
        {
            mSession = new MediaSession(getApplicationContext(), this.getClass().getName());
            int play = musicPlayerState == STATE_PLAY?R.drawable.pause:R.drawable.play;
            notification = new Notification.Builder(this)
                    .setContentTitle("Music")
                    .setContentText("她说")
                    .setSmallIcon(R.drawable.ab)
                    .addAction(play, "",PendingIntent.getBroadcast(this, 0,
                            new Intent().setAction("android.intent.action.paly"),0))
                   .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.a))
                    .setStyle(new Notification.MediaStyle()
                            .setShowActionsInCompactView(0)
                            .setMediaSession(mSession.getSessionToken()))
                    .build();
            startForeground(101,notification);
        }
    
        public void rigisterNotificationListener()
        {
            BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    switch (intent.getAction())
                    {
                        case "android.intent.action.paly":
                                if(musicPlayerState == STATE_PLAY) {
                                    pauseMusic();
                                    startNotification();
                                }
                                    else
                                {
                                    startMusic();
                                    startNotification();
                                }
                            break;
                        default:
                            break;
                    }
                }
            };
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction("android.intent.action.paly");
            registerReceiver(broadcastReceiver ,intentFilter);
        }
    
    }
    

      

      

    public class MainActivity extends AppCompatActivity {
        
        private Button button;
        private MyService myService;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            init();
        }
    
    public void init()
    {
        Intent intent = new Intent(this ,MyService.class);
        bindService(intent,new MusicServiceConnection(), Context.BIND_AUTO_CREATE);
        button = (Button) findViewById(R.id.button);
        rigistMusicListner();
    }
    
       class MusicServiceConnection  implements  ServiceConnection
       {
           @Override
           public void onServiceConnected(ComponentName name, IBinder service) {
               myService = ((MyService.MyBinder) service).getService();
    
           }
    
           @Override
           public void onServiceDisconnected(ComponentName name) {
    
           }
       }
    
    
        public void start(View view) {
        //当然这里的MyService.musicPlayerState 可以利用bind来抛出
        if (MyService.musicPlayerState == MyService.STATE_STOP||MyService.musicPlayerState == MyService.STATE_PAUSE)
        {
            myService.startMusic();
        }
        else
        {
            myService.pauseMusic();
        }
        updateButton(button);
    }
    
        public void updateButton(View view)
        {
            if(view.getId() == R.id.button)
            {
                //button.getText() 是为play,但是下面用button.getText()=="play" ,第一次会返回false,==是判断2个字符串是否为同一对象,第一次的play是button初始化的时候就有这个对象,而第二次setText
                //是在字符串常量区。
                if (button.getText().equals("play"))
                  button.setText("pause");
                else
                    button.setText("play");
            }
        }
        //点击通知栏时候收到的广播在activity里更新控件状态
        public  void  rigistMusicListner()
        {
            BroadcastReceiver receiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    switch (intent.getAction())
                    {
                        case "android.intent.action.paly":
                           updateButton(button);
                            break;
                        default:
                            break;
                    }
                }
            };
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction("android.intent.action.paly");
            registerReceiver(receiver,intentFilter);
        }
        
    }
    

      

     三、远程服务的介绍:

     调用远程服务,我和原来坐过的一样:

    1、写远程服务并在mainfest内配置,并在该服务的进程包里添加一个aidl文件RemoteService,申明一些服务抛出的方法。(android studio 下点击project ,在main目录下)。

    2、clean(或者 rebuild)下工程后(会在目录下生成相应的RemoteService.java文件(可以在服务里写Mybinder extends Stub 就会让你导包,然后选择本项目目录,然后在打开文件的地方点击邮件可找到生的java文件了),里面是java的接口RemoteService和子类Stub,通过Stub子类的asasInterface(binder)可以获取RemoteService的实例对象),在服务里写如onbind方法返回的类,该类继承RemoteServce.Stub。

    3、将aidl文件拷贝到调用服务的activity的对应的project - main 目录下,aidl下的包名与远程服务所在的包名得一致。

    4、在bind时候回调函数中利用asasInterface(service)获得实现接口的实例对象,利用该对象就可以调用服务里的方法对服务进行操作了。

    其中遇到了个问题,在bindService时候i启动服务总是失败,经查资料发现android 5.0 后不能隐式的启动服务了,即在清单里定义intentfilter 里添加action,再利用Intent intent = new intent("") 这种形式绑定服务后就会曝出:

    Service Intent must be explicit: Intent { act=com.example.user.myservicedemo.IMyAidlInterface } 的异常。

    5、android 6.0中提到了新的方法,来实现进程间通讯,这个以后有时间也总结一下。

     解决办法就是将Intent转化为显示的:

    方法一:相对笔记麻烦,但是里面的一些方法值得学习下

    在activity里面定义一个函数:

     public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {  
            // Retrieve all services that can match the given intent  
            PackageManager pm = context.getPackageManager();  //获得所有的应用
            List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);  //所有应用中查询服务,存入resolveInfo
       
            // Make sure only one match was found  
            if (resolveInfo == null || resolveInfo.size() != 1) {  
                return null;  
            }  
       
            // Get component info and create ComponentName  
            ResolveInfo serviceInfo = resolveInfo.get(0);  
            String packageName = serviceInfo.serviceInfo.packageName;  //获取包
            String className = serviceInfo.serviceInfo.name;  //获取类名(全路径)
            ComponentName component = new ComponentName(packageName, className);  
       
            // Create a new intent. Use the old one for extras and such reuse  
            Intent explicitIntent = new Intent(implicitIntent);  
       
            // Set the component to be explicit  
            explicitIntent.setComponent(component);  
       
            return explicitIntent;  
        }  
    

      然后绑定服务的时候:

    final Intent intent = new Intent();  
    intent.setAction("com.example.user.firstapp.FIRST_SERVICE");  
    final Intent eintent = new Intent(createExplicitFromImplicitIntent(this,intent));  
    bindService(eintent,conn, Service.BIND_AUTO_CREATE);  
    

     分析了下,其实不用那么麻烦三行代码就搞定了:

    方法二:

     Intent intent  = new Intent("android.intent.action.remote");
            ComponentName componentName =new ComponentName("com.example.user.myservicedemo" , "com.example.user.myservicedemo.RemoteService");
            intent.setComponent(componentName);
    

      这里需要注意的是componentName的构造函数的2个参数一个是包名、一个是类名。必须是全路径的。

     

      

      

      

  • 相关阅读:
    计算机网络--Socket
    计算机网络-p2p
    ubuntu安装redis
    解决beego在ubuntu下连接mysql与重置mysql密码
    二叉树的结点计算题
    极限之无穷小的比阶
    数据结构上机实验(7)
    线代中两个列向量的小知识
    中值定理结合行列式计算
    n阶行列式计算
  • 原文地址:https://www.cnblogs.com/bokeofzp/p/6044595.html
Copyright © 2011-2022 走看看