现在的位置: 首页 > 综合 > 正文

菜鸟笔记5-初识Service以及notification

2017年02月24日 ⁄ 综合 ⁄ 共 7231字 ⁄ 字号 评论关闭

学习android也有几个月了,到现在四大组件也只用过activity,实在惭愧,现在开始学习一下Service的使用。网上已经有很多很好地教程了,我这里整理一下,算是对自己上阶段学习的总结。

Service

我们都用过activity,activity是运行在前台界面的,有自己的生命周期,而service是运行在后台的代码,它没有界面,不过也有自己的生命周期。启动activity有两种方式,一种是调用startService(),另一种是bindService()。从下面的图可以看出来两种启动的方式是不一样的。

1.用startService()启动,首先会调用onCreate()方法,然后是onStartCommend(),这样service就启动了,你可以在onStartComment()里执行需要执行的代码。用这种方法启动service后这个service就会一直运行下去,即使启动这个service的组件被销毁了也对它没什么影响,直到调用stopService()方法或者stopSelf()才会被销毁。

2.用bindService()启动,也是先调用onCreate()方法,然后是onBind(),onBind()这个方法刚创建service的时候就会重写,使用的startService()的时候没有调用这个方法,可以return null.这样服务就与创建它的组件绑定了,一个service可以和多个组件绑定,所有跟它绑定的组件都被销毁,组件就会被销毁。



使用service也和activity一样要现在AndroidManifest里面先注册。

<service android:name="lee.org.servicedemo.MyService" >
</service>

创建Service类

public class MyService2 extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

然后就可以在里面实现逻辑了。

音乐播放器是最常见的例子,比如酷狗播放器,播放音乐后及时程序退到后台甚至关掉程序,还是可以继续播放音乐。

下面我们就来实现一个非常简单的可以在后台播放音乐的demo。

界面布局非常简单,就是放几个按钮,我这里就不贴代码了。


public class MainActivity extends Activity implements View.OnClickListener {

    private final static String TAG = "MainActivity";
   // private ImageButton btn1;上一首
    private ImageButton btn2;//播放
   // private ImageButton btn3;//下一首
    private MediaPlayer player;//用于播放音乐
   // private static final String path = "/kgmusic/download/sky.mp3";
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            MyService.MyBind myBind = (MyService.MyBind)service;//用于activity和绑定的service交互
            myBind.play();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.v(TAG,"解除绑定");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
//用于显示右上角的溢出菜单  默认是不显示的
        try {
            ViewConfiguration mconfig = ViewConfiguration.get(this);
            Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
            if(menuKeyField != null) {
                menuKeyField.setAccessible(true);
                menuKeyField.setBoolean(mconfig, false);
            }
        } catch (Exception ex) {

        }

        init();
    }

    //初始化控件
    private void init() {
/*
        String filePath = Environment.getExternalStorageDirectory().getPath() + path;//这里我就放了一首歌  把地址写死了
        Log.v(TAG, filePath);
        player = new MediaPlayer();
        player.setLooping(true);//设置单曲循环
        try {
            player.setDataSource(filePath);
            player.prepare();//准备  不调用这个方法下面调用play()的时候会出错
        } catch (IOException e) {
            e.printStackTrace();
        }
*/
        //btn1 = (ImageButton) findViewById(R.id.btn_previous);
        btn2 = (ImageButton) findViewById(R.id.btn_play);
        //btn3 = (ImageButton) findViewById(R.id.btn_next);

        //btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);
        //btn3.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Intent intent = new Intent(MainActivity.this,MyService.class);
        switch(v.getId()){

            case R.id.btn_play:
                play(intent);//用service启动
                //play();
                break;
            case R.id.btn_previous:
                previous();
                break;
            case R.id.btn_next:
                next();
                break;
        }
    }

    void play(Intent intent){
        startService(intent);
    }
//play()方法我们放在service中实现  如果在这里实现,我们退出之后就不能继续播放音乐。
/*
    void play(){

        if(player.isPlaying()){
            btn2.setImageResource(R.drawable.btn_play_style);
            pause();
        }

        else {
            Log.v(TAG, "start");
            player.start();
            btn2.setImageResource(R.drawable.btn_pause);

        }
    }

    void pause(){

        Log.v(TAG,"pause");
        player.pause();
    }
    void next(){

        Log.v(TAG,"next");
    }
    void previous(){

        Log.v(TAG,"previous");
    }
*/
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main,menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()){
            case R.id.item1:
                Intent intent = new Intent(this,MainActivity2.class);
                startActivity(intent);
                finish();
                break;
            case R.id.item2:
                Intent intent2 = new Intent(this,MyService.class);
                stopService(intent2);//停止服务
                break;
            case R.id.item3:
                Intent intent3 = new Intent(this,MyService.class);
                bindService(intent3,conn,BIND_AUTO_CREATE);//用bindservice方式启动service
                break;
            case R.id.item4:
                unbindService(conn);//解除绑定
                break;
        }
        return true;
    }
}

Service实现


public class MyService extends Service{

    private final static String TAG = "MyService";
    private MediaPlayer player;//用于播放音乐
    private static final String path = "/kgmusic/download/sky.mp3";
    private MyBind myBind = new MyBind();

    @Override
    public IBinder onBind(Intent intent) {
        Log.v(TAG,"bind");
        return myBind;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.v(TAG,"onCreate");
        String filePath = Environment.getExternalStorageDirectory().getPath() + path;
        Log.v(TAG, filePath);
        player = new MediaPlayer();
        player.setLooping(true);//设置单曲循环
        try {
            player.setDataSource(filePath);
            player.prepare(); //准备  不调用这个方法下面调用play()的时候会出错

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        Log.v(TAG,"onStartCommand");
        play();
        return super.onStartCommand(intent, flags, startId);
    }

    void play(){

        if(player.isPlaying()){
            pause();
        }

        else {
            player.start();
        }
    }

    void pause(){

        Log.v(TAG,"pause");
        player.pause();
    }

    @Override
    public void onDestroy() {
        player.stop();
        Log.v(TAG,"Destroy");
    }

    class MyBind extends Binder{
        public void play(){
            if(player.isPlaying()){
                pause();
            }
            else {
                player.start();
            }
        }
    }
}

这样一个非常简单的能在后台播放音乐的音乐播放器就完成了。当我们按home键退出的时候,音乐还是会继续播放,甚至我们把程序退出,音乐还是会继续播放。


但是,这样也会出现一些问题,在后台播放音乐的时候经常会自己停掉,过段时间又自己响起来。这是怎么回事呢?因为我测试用的手机比较差,内存很小,多开几个应用内存就不够用了,android系统会在系统内存不足的时候把那些优先级低的进程杀掉,在设置里面查看运行情况,发现内存不足的时候这个service的资源就会被释放掉,过段时间又会重启,所以造成音乐播放的断断续续,但是我手机上也装了酷狗音乐,但它就不会出现这种情况,凭什么啊?大家都是在后台运行的service,难道它运气就好点,每次杀进程的时候都杀掉别人?后来在万能的Google的帮助下,找到了一种可能性。酷狗音乐一打开的时候就会在通知栏显示一个通知。



难道在通知栏显示就不会被杀掉了???

下面就先来了解一下通知notification的基本情况吧。

Notification支持文字内容显示、震动三色灯铃声等多种提示形式,在默认情况下,Notification仅显示消息标题消息内容送达时间这3项内容。

关于notification的一些基本情况下面那两篇博客讲的非常详细,我也就不班门弄斧 了。

http://blog.csdn.net/vipzjyno1/article/details/25248021

http://blog.csdn.net/vipzjyno1/article/details/25248021

有些地方要注意一下,网上很多代码都是用了

Notification notification = new Notification(R.drawable.ic_launcher, "有通知到来", System.currentTimeMillis());
notification.setLatestEventInfo(this, "这是通知的标题", "这是通知的内容",pendingIntent);

这样错是没错,不过这些方法都已经被弃用了,现在Google推荐的是使用builder来代替,高版本的可以直接用

Notification.Builder builder = new Notification.Builder(this);

低版本的在引入v4包后可以使用

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);

下面我们就来创建一个简单的notification吧。

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.drawable.ic_launcher);//设置图标
builder.setContentText("contentText");//设置内容文本
builder.setContentTitle("title");//设置标题文本
builder.setWhen(System.currentTimeMillis());
builder.setTicker("you get a new ticker");//设置消息来的时候提示的文本
builder.setAutoCancel(true);
builder.setOngoing(true);//设置为true的时候在通知栏上不能通过滑动来使其消失,也就是说会一直停在通知栏上,金山那个通知大概就是这样做的
builder.setDefaults(Notification.DEFAULT_ALL);//通知的类型  有铃声  震动 和三色光,震动和灯光使用的时候要先添加permission 
Intent notificationIntent = new Intent(this, MainActivity2.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,notificationIntent, 0);
builder.setFullScreenIntent(pendingIntent,true);//全屏通知
 NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(1,builder.build());//启动通知
startForeground(1, builder.build());//置为前台

这样一个简单的通知栏就搞定了,这里只是列举部分属性,更多的属性可以参考以上两篇博客。

然后把这段代码加到刚才的service的代码中,这样进程被杀掉的可能性就大大降低, 至少我还没有碰到过资源被释放的情况。

为什么加个通知栏就行了呢?关键在最后一段代码,startForeground() 方法让service运行在前台,提高了优先级,系统会优先释放掉后台运行的进程来保持前台的进程,这样就能极大避免了进程资源被释放掉。不过这样似乎还不能完全避免service被杀掉,不过一般情况下是不会被销毁的。

好了,差不多就这样了,有什么讲的不对的地方欢迎大家批评指正,也欢迎大家跟我交流。

抱歉!评论已关闭.