service

Service分为本地服务(LocalService)和远程服务(RemoteService),本地服务就是运行在同一个进程,记住service运行的是主线程,所以在开service里做耗时任务一定要线程。远程服务就是跟app不同个进程,app关了,服务进程还开着,需要另外关闭服务进程。

首先先讲下service与线程区别。
service与线程没有半毛钱关系,很多时候,你可能会问,为什么要用 Service,因为service是独立的,也只有一个实例,如果你开线程,你的活动关了,其他活动就很难控制线程去事情,其实服务最重要一点就是它方便与很多活动去控制它,且它只有一个,所有活动控制的对象都是一个实例。
而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。

service的使用有两种

第一种 ,用startService和stopService

public class MyService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("zjs", "onCreate: ");
        
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("zjs", "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("zjs", "onDestroy: ");
//释放掉资源,防止内存泄漏
    }
    
 public void startDownload() {
    }
}

创建个类去继承服务,再重写方法去实现逻辑,然后再需要的地方去开启停止活动。

Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);

非常注意,以第一种去开始服务,一旦服务开启就跟调用者(开启者)没有任何关系了,开启者退出了,开启者挂了,服务还在后台长期的运行,也就是活动一旦开启,着服务就一起运行,除非有人去调用它停止,不然活动销毁了,它也会一直运行,当然,你把app关了,服务肯定停止,因为它是本地服务,不是远程服务。还有,以第一种去开启服务,开启者不能调用服务里面的方法,即你在活动开启服务,那么上面的startDownload() 你是不可以调用的,你不可以说获得上面MyService 实例然后去调用MyService 实例的startDownload(),记住,不管是第一还是第二种,service是不存在实例的,因为就只有一个服务。
第一次使用startService(startIntent)后的生命周期是,

servier: onCreate()>onStartCommand,之后而不管再什么活动什么地方,重复了多少次开启服务, onCreate()方法都不会被调用,只会调用onStartComman方法。,所有活动所有地方都是只有一个同样的服务,当stopService(stopIntent);调用后,就会调用 onDestroy()方法。

第二种方法

原理就是,活动们想要跟Service连接,然后拿到Service,(注意,在第二张service也是只有一个的。)去调用Service里面的方法。上面那种做法就是不可以调用Service里面的方法

我们知道进程间通信是通过Binder,而四大组件的之间的通信也就是 四大组件与 AMS 之间的通信 其实也就是用binder
那么当 activity 去绑定这个server 的时候能够 获取到 这个服务的binder 不就可以在 activity 中利用这个 binder 与 server通信。
那么 server要做的就是提供一个binder 给绑定它的人


public class MyService extends Service {


    private MyBinder mBinder = new MyBinder();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("zjs", "onCreate: ");

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("zjs", "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("zjs", "onDestroy: ");
//释放掉资源,防止内存泄漏
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d("zjs", "onBind: ");
        return mBinder;//提供Binder 给活动们,让他们可以与server 通信
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d("zjs", "onUnbind: ");
        return super.onUnbind(intent);
    }

    class MyBinder extends Binder { //这里创建了一个binder,里面包含着当前server实例
        public MyService getService() {
            return MyService.this;
        }

    }

    //服务的方法,活动们要调用服务的方法
    public void startDownload() {
    }
}
public class MainActivity extends Activity implements OnClickListener {
 
	private Button startService;
 
	private Button stopService;
 
	private Button bindService;
 
	private Button unbindService;
 
  private MyService myService= null;
 
	private ServiceConnection connection = new ServiceConnection() {
 
		@Override
		public void onServiceDisconnected(ComponentName name) {
		}
 
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			MyService .MyBinder myBinder = (MyService .MyBinder) binder;//获取server提供的binder
            myService= myBinder.getService();//拿到了服务实例
            myService.startDownload()
	};
 
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		startService = (Button) findViewById(R.id.start_service);
		stopService = (Button) findViewById(R.id.stop_service);
		bindService = (Button) findViewById(R.id.bind_service);
		unbindService = (Button) findViewById(R.id.unbind_service);
		startService.setOnClickListener(this);
		stopService.setOnClickListener(this);
		bindService.setOnClickListener(this);
		unbindService.setOnClickListener(this);
	}
 
	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.start_service:
			Intent startIntent = new Intent(this, MyService.class);
			startService(startIntent);
			break;
		case R.id.stop_service:
			Intent stopIntent = new Intent(this, MyService.class);
			stopService(stopIntent);
			break;
		case R.id.bind_service:
			Intent bindIntent = new Intent(this, MyService.class);
			bindService(bindIntent, connection, BIND_AUTO_CREATE); //绑定一个服务,需要有 ServiceConnection 
			break;
		case R.id.unbind_service:
			unbindService(connection);
			break;
		default:
			break;
		}
	}
 
}

非常注意:

以第二种方式,bind的方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。不过这肯定是只有一个绑定服务的情况,多个绑定就只有调用者挂了,服务依旧运行着,只有最后一个绑定服务的挂了,服务才挂,第二种是可以调用服务里面的方法,即上面的startDownload,而第一种是不可以的,但是不管是第一还是第二种,service 永远只有一个,第二种不过是利用了ibinder去让多个连接着共同享有一个service,然后去调用service的方法,并不是创建了n了service实例。当然,比如你再服务的方法这么做

 //服务的方法,活动们要调用服务的方法
    public void startDownload() {
        Log.d("zjs", "startDownload: ");
         Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
              
            }
        });
       thread.start();

    }

那么肯定是开启了n个线程。

当只且第一次第一个活动点击绑定服务:server:oncread>onbind.,
活动们就会各自执行ServiceConnection 的onServiceConnected()方法。之后任何包括第一个绑定服务的活动的绑定服务,,只有活动们就会各自执行ServiceConnection 的onServiceConnected(),server的oncread和onbind.不会再调用

调用unbindService之后发生的事情,client与Service解除绑定连接状态,注意调用unbindService后,活动们里面的ServiceConnection 的onServiceConnected() 方法是不会被调用的,onServiceDisconnected() 在正常关闭的情况下是不会被调用的.该方法只在Service 被破坏了或者被杀死的时候调用. 例如, 系统资源不足, 要关闭一些Services, 刚好连接绑定的 Service 是被关闭者之一, 这个时候onServiceDisconnected() 就会被调用,也就是正常操作就不会调用,要是出现异常或者系统资源不足才会。
如果多个连接service,那么当其中一个调用unbindService,service的onUnbind()和onDestroy()是不会执行的,只有最后一个unbindService,service的onUnbind()和onDestroy()才会执行的。

注意:使用bindService开启服务以后,与activity存在关联,退出activity时必须调用unbindService方法,否则会报ServiceConnection泄漏的错误。

通过上面我们知道开启一个服务就两种方式,不需要开启服务再去绑定服务,他们是独立的,看的是生命周期的方法。当第一第二种结合的时候:
注意,同一个服务可以用两种方式一同开启,没有先后顺序的要求,MyService的onCreate只会执行一次。
关闭服务需要stopService和unbindService都被调用,也没有先后顺序的影响,MyService的onDestroy也只执行一次。但是如果只用一种方式关闭服务,不论是哪种关闭方式,onDestroy都不会被执行,服务也不会被关闭。这一点需要注意。

接下来讲intentserver
IntentService是Service的子类,比普通的Service增加了额外的功能。先看Service本身存在两个问题:

Service不会自己开启一个线程去,而是需要人为的开启;

如果需要让服务运行的代码运行结束后,自动关闭服务我们得需要:

   @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
//逻辑代码
                stopSelf();//用这句话去停止服务
            }
        });
        thread.start();
        return super.onStartCommand(intent, flags, startId);
    }

而IntentService解决了上面的问题,它会自己内部开启一个线程 ,运行完自动结束。

IntentService也是只有一个,其实就是里面自己用了HandlerThread,然后,实现的效果就是,只弄了个线程去执行耗时任务,把所有的intent以队列形式一个个执行。执行完一个才会继续执行下一个。在IntentService中,onCreate() 方法只会调用一次,所以只会创建一个工作线程,多次调用 startService(Intent) 时(onStartCommand也会调用多次)其实并不会创建新的工作线程,只是把消息即各种Intent加入消息队列中等待执行,所以,多次启动 IntentService 会按顺序执行事件,在IntentService中,不要用bindservier方法去开启,因为在源码里面的onBind()是默认返回null的,而采用bindService() 启动 IntentService的生命周期是:onCreate() —>onBind()—>onunbind()—>onDestory()
并不会调用onstart()或者onstartcommand()方法,所以不会将消息发送到消息队列,那么onHandleIntent()将不会回调。

IntentService使用:

在Manifest.xml中注册服务

 <service android:name="com.example.administrator.module.MyIntentService" />

public class MyIntentService extends IntentService {

    public MyIntentService() {
        super("MyIntentService");
    }//必须这么做传给父类不然报错

//在onHandleIntent做耗时io操作
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //复写onHandleIntent方法
        //实现耗时任务操作
        //打印当前的线程id
        String taskName = intent.getExtras().getString("taskName");
        Log.d("MyIntentService", "   Thread Id is : " + Thread.currentThread()
                .getId());
        switch (taskName) {
            case "task1":
                Log.i("MyIntentService", "do task1");
                break;
            case "task2":
                Log.i("MyIntentService", "do task2");
                break;
            default:
                break;
        }
    }

    @Override
    public void onCreate() {
        Log.i("MyIntentService", "onCreate");
        super.onCreate();
    }

    /*复写onStartCommand()方法*/
    //默认调用了父类的onStartCommand方法去把每个intent加入队列中
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("MyIntentService", "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("MyIntentService", "onDestroy executed");
    }
}
public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button= findViewById(R.id.aa);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(getApplicationContext(),MyIntentService.class);
                Bundle bundle = new Bundle();
                bundle.putString("taskName", "task1");
                i.putExtras(bundle);
                startService(i);
            }
        });
        Button button2= findViewById(R.id.bb);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i2 = new Intent(getApplicationContext(),MyIntentService.class);
                Bundle bundle2 = new Bundle();
                bundle2.putString("taskName", "task2");
                i2.putExtras(bundle2);
                startService(i2);
            }
        });

    }
}

此外还要注意的是,IntentService 中除了 onHandleIntent 方法其他都是运行在主线程的。

接下来讲前台服务

讲前台服务我们先讲通知的内容:

        NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        Intent intent = new Intent(MyService.this,MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);//点击通知要跳转的界面
        Notification notification=null;
        //Android8.0以上必须添加 渠道 才能显示通知栏
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //创建渠道
            String id = "my_channel_01";
            String name="渠道名字";
            NotificationChannel mChannel = new NotificationChannel(id, name, NotificationManager.IMPORTANCE_LOW);
            notificationManager.createNotificationChannel(mChannel);
            //设置图片,通知标题,发送时间,提示方式等属性
            NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), id);
            builder.setChannelId(id)//设置渠道
                    .setContentTitle("北极熊也有微笑")  //标题
                    .setContentText("北极熊是本人认为最棒的动物,没有之一")   //内容
                    .setSubText("--北极熊很Happy~")     //内容下面的一小段文字
                    .setTicker("收到北极熊也有微笑发来的的消息~")      //收到信息后状态栏显示的文字信息
                    .setWhen(System.currentTimeMillis())    //系统显示时间
                    .setSmallIcon(R.mipmap.ic_launcher)     //收到信息后状态栏显示的小图标
                    .setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE)    //设置默认的三色灯与振动器
                    .setDefaults(Notification.DEFAULT_SOUND)    //设置系统的提示音
                    .setAutoCancel(true);       //设置点击后取消Notification
            builder.setContentIntent(pendingIntent);    //绑定PendingIntent对象
            notification = builder.build();
        } else {
            //设置图片,通知标题,发送时间,提示方式等属性
            Notification.Builder builder = new Notification.Builder(getApplicationContext());
            builder.setContentTitle("北极熊也有微笑")  //标题
                    .setContentText("北极熊是本人认为最棒的动物,没有之一")   //内容
                    .setSubText("--北极熊很Happy~")     //内容下面的一小段文字
                    .setTicker("收到北极熊也有微笑发来的的消息~")      //收到信息后状态栏显示的文字信息
                    .setWhen(System.currentTimeMillis())    //系统显示时间
                    .setSmallIcon(R.mipmap.ic_launcher)     //收到信息后状态栏显示的小图标
                    .setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE)    //设置默认的三色灯与振动器
                    .setDefaults(Notification.DEFAULT_SOUND)    //设置系统的提示音
                    .setAutoCancel(true);       //设置点击后取消Notification
            builder.setContentIntent(pendingIntent);    //绑定PendingIntent对象
            notification = builder.build();
        }
        notificationManager.notify(1, notification);

通知的内容没什么好讲的,无非就是点击按钮或者需要的时候就弹出了一条通知,然后点击这条通知就跳转相应的画面。不懂得百度看一下就明白了,就是一堆设置功能属性而已,上面的代码就是开启一条通知的代码,你可以在里面修改自己需要的属性功能。

好继续讲前台服务

上面说的都是后台服务,后台服务在系统内存不足的时候,可能会被系统杀死,如果想要让服务尽量不被杀死,就可以使用前台服务。它与普通服务的区别就是,其实就是如果你把这个服务弄为前台服务,那么就会一直在状态栏显示一条通知,并且,这条通知不会去除,直到这个服务结束或者被杀死,这条通知一直放在那,使用前提服务,那么这个服务是被用户认可的,可以一直运行着,从而也不容易被系统杀死。

使用:

public class MyService extends Service {
    public MyService() {
    }


    @Override
    public void onCreate() {
        super.onCreate();
        NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        Intent intent = new Intent(MyService.this,MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);//点击通知要跳转的界面
        Notification notification=null;
        //Android8.0以上必须添加 渠道 才能显示通知栏
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //创建渠道
            String id = "my_channel_01";
            String name="渠道名字";
            NotificationChannel mChannel = new NotificationChannel(id, name, NotificationManager.IMPORTANCE_LOW);
            notificationManager.createNotificationChannel(mChannel);
            //设置图片,通知标题,发送时间,提示方式等属性
            NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), id);
            builder.setChannelId(id)//设置渠道
                    .setContentTitle("北极熊也有微笑")  //标题
                    .setContentText("北极熊是本人认为最棒的动物,没有之一")   //内容
                    .setSubText("--北极熊很Happy~")     //内容下面的一小段文字
                    .setTicker("收到北极熊也有微笑发来的的消息~")      //收到信息后状态栏显示的文字信息
                    .setWhen(System.currentTimeMillis())    //系统显示时间
                    .setSmallIcon(R.mipmap.ic_launcher)     //收到信息后状态栏显示的小图标
                    .setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE)    //设置默认的三色灯与振动器
                    .setDefaults(Notification.DEFAULT_SOUND)    //设置系统的提示音
                    .setAutoCancel(true);       //设置点击后取消Notification
            builder.setContentIntent(pendingIntent);    //绑定PendingIntent对象
            notification = builder.build();
        } else {
            //设置图片,通知标题,发送时间,提示方式等属性
            Notification.Builder builder = new Notification.Builder(getApplicationContext());
            builder.setContentTitle("北极熊也有微笑")  //标题
                    .setContentText("北极熊是本人认为最棒的动物,没有之一")   //内容
                    .setSubText("--北极熊很Happy~")     //内容下面的一小段文字
                    .setTicker("收到北极熊也有微笑发来的的消息~")      //收到信息后状态栏显示的文字信息
                    .setWhen(System.currentTimeMillis())    //系统显示时间
                    .setSmallIcon(R.mipmap.ic_launcher)     //收到信息后状态栏显示的小图标
                    .setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE)    //设置默认的三色灯与振动器
                    .setDefaults(Notification.DEFAULT_SOUND)    //设置系统的提示音
                    .setAutoCancel(true);       //设置点击后取消Notification
            builder.setContentIntent(pendingIntent);    //绑定PendingIntent对象
            notification = builder.build();
        }
        startForeground(1, notification);//把这个服务当前台服务。

    }
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

使用跟普通服务一样,就只不过在普通服务里加了个通知信息要长什么样子并且通过
startForeground(1, notification);把这个服务当前台服务。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值