Android 服务Service 学习笔记

学习《第一行代码》笔记

最下面注意:防止踩android8.0的坑,第一行代码第二版例子太老了。

服务(Service)是 Android中实现程序后台运行的解决方案。服务的运行不依赖于任何用户界面,即使 当程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行。 

服务并不是运行在一个独立的进程当中的,而是依赖于创建服务 时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停 止运行。 

实际上服务并不会自动开启线程,所有的代码 都是默认运行在主线程当中的。也就是说,我们需要在服务的内部手动创建子线程,并在这 里执行具体的任务,否则就有可能出现主线程被阻塞住的情况。

public class MyService 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(); 
    }
}
//需要在AndroidManifest中注册才可以使用
<service android:name=".MyService" > </service> 

启动和停止服务:

Intent startIntent = new Intent(this, MyService.class); 
startService(startIntent); // 启动服务
Intent stopIntent = new Intent(this, MyService.class); 
stopService(stopIntent);   //停止服务

在Service类中也可以调用 stopSelf()停止服务

onCreate()方法是在服务第一次创建的时候调用的,而 onStartCommand()方法则在 每次启动服务的时候都会调用,由于刚才我们是第一次点击 Start Service按钮,服务此时还 未创建过,所以两个方法都会执行,之后如果你再连续多点击几次 Start Service按钮,你就 会发现只有 onStartCommand()方法可以得到执行。

 

 活动和服务进行通信:

可以在Activity中使用服务去下载数据,然后服务会把下载进度返回给Activity。实现异步线程的效果

public class MainActivity extends Activity implements OnClickListener { 
    private Button startService; 
    private Button stopService; 
    private Button bindService; 
    private Button unbindService; 
    private MyService.DownloadBinder downloadBinder; 
    private ServiceConnection connection = new ServiceConnection() { 
        @Override 
        public void onServiceDisconnected(ComponentName name) { }
        @Override 
        public void onServiceConnected(ComponentName name, IBinder service) { 
            downloadBinder = (MyService.DownloadBinder) service; 
            downloadBinder.startDownload();
            downloadBinder.getProgress(); 
        }
    };

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main);
         …… 
        bindService = (Button) findViewById(R.id.bind_service); 
        unbindService = (Button) findViewById(R.id.unbind_service); 
        bindService.setOnClickListener(this); 
        unbindService.setOnClickListener(this); 
    } 
    @Override 
    public void onClick(View v) { 
        switch (v.getId()) {
        …… 
        case R.id.bind_service: 
            Intent bindIntent = new Intent(this, MyService.class); 
            bindService(bindIntent, connection, BIND_AUTO_CREATE); // 绑定服务
            break; 
        case R.id.unbind_service: 
            unbindService(connection); // 解绑服务 
            break; 
        default: 
            break; 
        }
    }
} 
public class MyService extends Service { 
    private DownloadBinder mBinder = new DownloadBinder(); 
    class DownloadBinder extends Binder {
        public void startDownload() { 
            Log.d("MyService", "startDownload executed");
         } 
        public int getProgress() { 
            Log.d("MyService", "getProgress executed");
            return 0; 
        } 
    }
    @Override 
    public IBinder onBind(Intent intent) { 
        return mBinder; 
    }
    ……
} 

 

调用 Context的 bindService()来获取一个服务的持久连接,这时就会回调 服务中的 onBind()方法。类似地,如果这个服务之前还没有创建过,onCreate()方法会先于 onBind()方法执行。之后,调用方可以获取到 onBind()方法里返回的 IBinder对象的实例,这 样就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保 持运行状态。

当调用了 startService()方法后,又去调用 stopService()方法,这时服务中的 onDestroy() 方法就会执行,表示服务已经销毁了。类似地,当调用了 bindService()方法后,又去调用 unbindService()方法,onDestroy()方法也会执行,这两种情况都很好理解。但是需要注意, 我们是完全有可能对一个服务既调用了 startService()方法,又调用了 bindService()方法的, 这种情况下该如何才能让服务销毁掉呢?根据 Android系统的机制,一个服务只要被启动或 者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被 销毁。所以,这种情况下要同时调用 stopService()和 unbindService()方法,onDestroy()方法才 会执行。 

 

由于服务的优先级比较低,在内存不够的时候,容易被服务回收,因此可以将服务前台化,这样服务可以不被回收。

在学习前台服务的时候,由于第一行代码第二版例子太老了,例子中的android版本小于android 8.0 ,因此会出现android8.0的坑

在startForeground的时候会报错StartForeground Bad Notification Error 。解决方法为:https://blog.csdn.net/LJW874362735/article/details/100863117

需要添加的代码是两个if

Intent intent = new Intent(this, TestActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
 
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher));
...
...  
// 【适配Android8.0】设置Notification的Channel_ID,否则不能正常显示
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    builder.setChannelId("notification_id");
}
...
...
 
 
// 额外添加:
// 【适配Android8.0】给NotificationManager对象设置NotificationChannel
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
    NotificationChannel channel = new NotificationChannel("notification_id", "notification_name", NotificationManager.IMPORTANCE_LOW);
    notificationManager.createNotificationChannel(channel);
}
 
// 启动前台服务通知
startForeground(1, builder.build());

 

实例:

MyService:

public class MyService extends Service {

    public static final String TAG = "MyService";

    private MyBinder myBinder = new MyBinder();

    public class MyBinder extends Binder{
        public void startDownload(){
            Log.d(TAG,"startDownload");
            Toast.makeText(getApplicationContext(),"startDownload",Toast.LENGTH_SHORT).show();
        }
        public void getProcess(){
            Log.d(TAG,"getProcess");
            Toast.makeText(getApplicationContext(),"getProcess",Toast.LENGTH_SHORT).show();
        }
    }


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {   //在ServiceConnection中返回IBinder
        return myBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //第一次创建服务的时候调用
        Log.d(TAG,"onCreate");
        Toast.makeText(getApplicationContext(),"onCreate",Toast.LENGTH_SHORT).show();
        Log.d("MyService", "onCreate executed");

    }

    public void startFrontService(){
        Notification.Builder builder = new Notification.Builder(this.getApplicationContext());;   //获取一个Notification构造器

        // 【适配Android8.0】设置Notification的Channel_ID,否则不能正常显示
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            builder.setChannelId(String.valueOf(1));
        }
        // 额外添加:
        // 【适配Android8.0】给NotificationManager对象设置NotificationChannel
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
            NotificationChannel channel = new NotificationChannel(String.valueOf(1), "notification_name", NotificationManager.IMPORTANCE_LOW);
            notificationManager.createNotificationChannel(channel);
        }

        Intent nfIntent = new Intent(this, ServiceDemoActivity.class);
        builder.setContentIntent(PendingIntent.getActivity(this, 0, nfIntent, 0));       // 设置PendingIntent
        builder.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.drawable.ic_launcher_background));// 设置下拉列表中的图标(大图标)
        builder.setContentTitle("下拉列表中的Title");  // 设置下拉列表里的标题
        builder.setSmallIcon(R.mipmap.ic_launcher);  // 设置状态栏内的小图标
        builder.setContentText("要显示的内容");       // 设置上下文内容
        builder.setWhen(System.currentTimeMillis()); // 设置该通知发生的时间

        Notification notification = builder.build(); // 获取构建好的Notification
        notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
        startForeground(1, notification);// 开始前台服务
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //每次startService的时候调用
        Log.d(TAG,"onStartCommand");
        Toast.makeText(getApplicationContext(),"onStartCommand",Toast.LENGTH_SHORT).show();

        if(intent.getBooleanExtra("create",false)){
            startFrontService();
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"onDestroy");
        Toast.makeText(getApplicationContext(),"onDestroy",Toast.LENGTH_SHORT).show();

        stopForeground(true);// 停止前台服务
    }
}

主Activity:

public class ServiceDemoActivity extends AppCompatActivity implements View.OnClickListener {

    private MyService.MyBinder myBinder;
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = (MyService.MyBinder) service;
            myBinder.startDownload();
            myBinder.getProcess();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_service_demo);


    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.startService:
                Intent intent = new Intent(this,MyService.class);
                startService(intent);
                break;
            case R.id.stopService:
                Intent intent1 = new Intent(this,MyService.class);
                stopService(intent1);
                break;
            case R.id.bindService:
                Intent intent2 = new Intent(this,MyService.class);
                bindService(intent2,serviceConnection,BIND_AUTO_CREATE);
                break;
            case R.id.unbindService:
                unbindService(serviceConnection);
                break;
            case R.id.FrontStart:
                Intent intent3 = new Intent(this,MyService.class);
                intent3.putExtra("create",true);
                startService(intent3);
                break;
            case R.id.FrontStop:
                Intent intent4 = new Intent(this,MyService.class);
                stopService(intent4);
                break;
            default:
                break;

        }
    }
}

 

IntentService,基于多线程的服务,因为服务需要处理比较耗时的任务,所以可以开启线程处理

Android 专门提供了一个 IntentService类:可以简单地创建一个异步的、会自动停止的服务。

IntentService 兼容new Thread(),stopSelf();于一身。

public class MyIntentService extends IntentService { 
    public MyIntentService() { 
        super("MyIntentService"); // 调用父类的有参构造函数 
    } 
    @Override 
    protected void onHandleIntent(Intent intent) { 
        // 打印当前线程的id 去处理一些具体的逻辑
        Log.d("MyIntentService", "Thread id is " + Thread.currentThread(). getId()); 
    } 
    @Override 
    public void onDestroy() { 
        super.onDestroy(); 
        Log.d("MyIntentService", "onDestroy executed"); 
    } 
}

 

第一行代码里面的骚操作:长期定时执行服务的程序逻辑:定义一个服务,服务中onStartCommand每隔一小时发一次广播,广播收到消息后启动服务,这样循环之行。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值