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个参数一个是包名、一个是类名。必须是全路径的。

 

  

  

  

posted @ 2016-11-08 20:53  Lammy  阅读(361)  评论(0编辑  收藏  举报