Android四大组件Servier(上)

来源:互联网 发布:vue.js a标签跳转 编辑:程序博客网 时间:2024/05/29 18:41

此文赠与龙哥,希望对他有帮助
准备自己复习下,哈哈


1.定义一个服务
首先创建一个服务

public class MyService extends Service {    public MyService() {    }    @Override    public IBinder onBind(Intent intent) {        throw new UnsupportedOperationException("Not yet implemented");    }}

可以看到,MyService是继承自Serview的,说明这是一个服务,目前MyService中可以算是空空如也,但是有一个onBind()方法特别醒目。这个方法是Service中唯一一个抽象方法,所以必须在子类中实现,我们后边会使用到onBind(),目前可以先忽略掉
既然是定义一个服务,自然应该在服务中心去处理一些事情,那处理事情的逻辑应该写在哪里,我们重写Service中的另外一些方法,方法如下

 @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();    }

可以看到,我们重写了onCreate(),onStartCommand()和onDestory()这个三个方法,他们是每个服务中最长用到3个方法 ,其中
onCreate()在服务创建的时候调用
onStartCommand()方法,在服务每次启用的时候调用,
onDestory()方法在,服务销毁的时候调用
通常情况下,如果我们希望服务一旦启动就立刻去执行摸个动作,就可以吧逻辑写在onStartCommand之中,,当服务销毁时,我们又应该在onDestory方法中取回收那些不再使用的资源
另外需要注意,每一个服务都需要在配置文件中注册才能生效

  <service            android:name=".MyService"            android:enabled="true"            android:exported="true">        </service>

2.启动和停止服务
定义好了服务之后,接下来就应该考虑如何去启动和停止这个服务,启动和停止的方法当然你也不会陌生,主要是借助Intent来实现的,下面我们,就尝试在项目中启动和停止MyServicer这个服务

首先我们在布局中,生成两个按钮,一个是开启服务,一个是停止服务

public class MainActivity extends AppCompatActivity implements View.OnClickListener{    private Button startService,stopService;    @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_serevice);        startService.setOnClickListener(this);        stopService.setOnClickListener(this);    }    @Override    public void onClick(View view) {        switch (view.getId()){            case R.id.start_service:                Intent startIntent=new Intent(this,MyService.class);                startService(startIntent);                break;            case R.id.   stop_serevice:                Intent stopIntent=new Intent(this,MyService.class);                stopService(stopIntent);                break;            default:                break;        }    }}

可以看到,我们定义了2个按钮,和他们的点击事件,我们构造出一个Intent对象,并调用startService方法来启动MyService这个服务,另外一个按钮同理
startService()和stopService()的方法都定义在context类中,所以,我们可以直接调用这两个方法。注意,这里完全是由活动来决定服务合适停止的,如果没有点击停止按钮,服务就一直处于运行状态,
那么接下来,我们给MyService的几个方法中加入打印日志

public class MyService extends Service {    public MyService() {    }    @Override    public IBinder onBind(Intent intent) {        throw new UnsupportedOperationException("Not yet implemented");    }    @Override    public void onCreate() {        super.onCreate();        Log.i("MyService","onCreate");    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.i("MyService","onStartCommand");        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onDestroy() {        super.onDestroy();        Log.i("MyService","onDestroy");    }}

来进行测试

这里写图片描述

可以看到,打印日志和我们预计的结果是一样的
其中onCreate()方法是在服务第一次创建的时候调用的,而onSteatCommand()方法这是在每次启动服务中都会调用,

3.活动和服务进行通信
上一小节中我们学习了如何开启和停止服务的放大,虽然服务实在活动中启动的,但是启动服务之后,活动和服务肌肤就没什么关系了,确实如此,我们在活动中调用startService()方法来启动MyService这个服务,然后MyService的onCreat()和onStartCommand()方法机会执行,之后服务会,一直处于运行状态,但具体运行的是什么逻辑,活动是控制不了的。这就类似于活动通知服务一下:‘你可以启动了’,然后服务就去忙自己的事情了,但是活动并不知道服务到底去做什么事情,以及完成的如何。
那么有没有什么办法,能让获得和服务的关系变得更紧密一点,例如活动中指挥服务去做什么,服务就去做什么。当然可以,这就需要借助刚才忽略掉onBind();
比如说,我们希望在MyService里提供一个下载功能,然后在活动中可以决定何时开始下载,以及可以查看下载进度。实现这个功能的思路是创建一个专门的binder对象对下载功能进行管理,修改MyService中的代码,

public class MyService extends Service {    private DownloadBinder mBinder=new DownloadBinder();    class DownloadBinder extends Binder{        public void  startDownload(){         Log.d("MyService","startDownload executed");        }        public int getPregress(){            Log.d("MyService","getPregress executed");            return 0;        }    }    public MyService() {    }    @Override    public IBinder onBind(Intent intent) {        return mBinder;    }    @Override    public void onCreate() {        super.onCreate();        Log.i("MyService","onCreate");    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.i("MyService","onStartCommand");        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onDestroy() {        super.onDestroy();        Log.i("MyService","onDestroy");    }}

可以看到,这里我们新建一个DownloadBinder类,并让他继承自Binder,然后给他的内部提供了,开始下载以及查看下载进度的方法,方然,只是模拟方法,并没有真正实现功能,我们在两个方法中分别打印了日志
接着,在MyService中创建了DownloadBinder的实例,然后onBind()方法中返回了这个实例,这样MyService中的功能结束了,
在布局中我们添加两个按钮

<Button        android:id="@+id/bind_service"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="bind_service"        />    <Button        android:id="@+id/unbind_service"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="unbind_service"        />

两个按钮的作用分别是绑定服务和取消绑定服务,
当一个活动和服务绑定后,就可以调用该服务的binder提供的方法了,修改MainAcitivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener{    private Button startService,stopService,bindService,unbindService;    private MyService.DownloadBinder downloadBinder;    private ServiceConnection connection=new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {            downloadBinder=(MyService.DownloadBinder)iBinder;            downloadBinder.getPregress();            downloadBinder.startDownload();        }        @Override        public void onServiceDisconnected(ComponentName componentName) {        }    };    @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_serevice);        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 view) {        switch (view.getId()){            case R.id.start_service:                Intent startIntent=new Intent(this,MyService.class);                startService(startIntent);                break;            case R.id.   stop_serevice:                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);  //绑定服务                stopService(bindIntent);                break;            case R.id.   unbind_service:                unbindService(connection);//解绑服务                break;            default:                break;        }    }}

可以看到,这里我们首先创建了一个ServiceConnection的匿名类,在里边重写了onServiceConnected()和onServicerDisconnected()方法,这两个方法,分别会在服务和活动绑定成功以及接触绑定的时候调用,在onServiceConnected()方法中,我们向下转型得到了DownloadBinder的实例,有了这个实例,我们就可以调用public方法了,在onServiceConnected()方法中,调用DownloadBinder 的startDownload()和getProgress()方法
,我们运行一下,看下打印
这里写图片描述

可以看到,。首先,我们的MyService 的onCreate方法得到执行,,然后startDownload和getProgress方法得到执行,说明我们确实成调用了服务中的方法了,
任何一个服务在整个应用程序范围内都是通用的,即MyService不仅可以和MainActivity绑定,还可以和任何一个其他的活动进行绑定,而且在绑定完成后,他们都可以获取到想通的DownloadBinder实例

4.服务的生命周期
一旦在项目中的任何位置,调用了Context的startService()方法,相应的服务就会启动起来,并且回调onStartCommand()方法,如果这个服务没有创建过,onCreate()方法会先于onStartCommand()方法执行。服务启动以后会一直保持运行状态知道stopService()或者stopself()方法被调用。注意,虽然每次调用startService()方法,onStartCommand就会执行一次,但实际上每个服务都会只存在一个实例。所以不管你调用了多少次startService()方法,只需要调用一次stopService()或者stopSelf()fangfa ,服务就会停下来。

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

当调用了startService()方法以后,又去调stopService()方法,这时服务中的onDestory()方法就会执行,表示服务已经摧毁。类似的,当调用bindService()方法后,又去调用unbindService方法,onDestory()方法也会执行,这两种都很好理解,但是,
如果一个服务既调用了startService()有调用了,bindService(),这种情况下如何销毁呢
这种情况下,要同事调用stopService()和unbinderService()方法,onDestory()方法才会执行


5.服务的更多技巧
5.1使用前台服务
服务几乎都是在后台运行的,一直以来他都是默默的工作,但是服务的系统优先级还是比较低的。当系统内存不足的情况是,就有可能回收桥正在后台运行的服务。如果你希望服务可以一直保持运行状况,而不会由于系统内存不足的原因导致被回收,就可以考虑使用前台服务。前台服务和普通服务的最大的区别就在于,他会一直有一个正在运行的图标在系统的状态栏显示,下拉状态后可以看到更加详细的信息,非常类似于通知的效果。当然有时候,你也可能不仅仅是为了防止服务被回收掉才使用前台服务,有些项目由于特殊的需求会要求必须使用前台服务,比如天气预报,他的服务就是在后台更细腻天气数据的同时,还会在系统状态栏一直显示当前的天气信息
那么我们来写一个前台服务吧,其实并不复杂,修改MyService中的代码,如下所示

 @Override    public void onCreate() {        super.onCreate();        Log.i("MyService","onCreate");        Intent intent=new Intent(this,MainActivity.class);        PendingIntent pi=PendingIntent.getActivity(this,0,intent,0);        Notification notification=new NotificationCompat.Builder(this)                .setContentTitle("This is content title")                .setContentText("This is content text")                .setWhen(System.currentTimeMillis())                .setSmallIcon(R.mipmap.ic_launcher)                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))                .setContentIntent(pi)                .build();        startForeground(1,notification);    }

可以看到,这里只是修改了onCreate()方法中的代码
构建Notification之后,调用startForeground()方法。这里接受2个参数,第一个是通知的id,第二个参数书构建出来的notification对象,调用startForeground方法后就会让MyService变成一个前台服务,并在系统状态栏显示出来。
现在重新运行下程序,启动服务,MyService就会启动一个前台服务,如图
这里写图片描述

5.2使用IntentService
话说回来,在本章一开始,我们就知道,服务的代码试运行在主线程中的,如果直接在服务中去处理一些耗时操作,就很容易出现ANR
所以这个时候,就需要使用android 的多线程的技术了,我们应该在服务的每个具体的方法中开启一个子线程,然后在这里去处理耐心额耗时的逻辑。因此,一个比较标准的服务就可以写成

 @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.i("MyService","onStartCommand");        new Thread(new Runnable() {            @Override            public void run() {                //处理具体逻辑                stopSelf();            }        }).start();        return super.onStartCommand(intent, flags, startId);    }

虽然这样写并不复杂,但是总会有一些程序员忘记开启线程,或者忘记结束服务,为了可以简单的创建一个一步的会自动停止的服务,android专门提供了一个IntentService类,这个类就可以很好的解决前面提到的2中尴尬,
新建一个MyIntentService继承自IntentService,代码如下所示:

public class MyIntentService extends IntentService {    public MyIntentService(String name) {        super(name);//调用父类的有璨构造函数    }    @Override    protected void onHandleIntent(@Nullable Intent intent) {//打印当前线程ID        Log.d(" MyIntentService","Thread id is "+Thread.currentThread().getId());    }    @Override    public void onDestroy() {        super.onDestroy();        Log.d("MyIntentService","onDestory executed");    }}

首先提供一个无参构造函数,并且必须在其内部调用父类的有参构造函数。然后要在子类中实现onHandleIntent()这个抽象方法,在这个方法中可以去处理一些具体逻辑,而且完全不用担心anr的问题
因为这个方法已经在子线程中运行了,这里为了证实一下,我们在onHandleIntent方法中打印当前线程id
另外更具IntentService的特性,这个服务在运行结束后会自动停止,我们写了打印日志,来验证下

public class MainActivity extends AppCompatActivity implements View.OnClickListener{    private Button startService,stopService,bindService,unbindService,startIntentService;    private MyService.DownloadBinder downloadBinder;    private ServiceConnection connection=new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {            downloadBinder=(MyService.DownloadBinder)iBinder;            downloadBinder.getPregress();            downloadBinder.startDownload();        }        @Override        public void onServiceDisconnected(ComponentName componentName) {        }    };    @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_serevice);        bindService=(Button)findViewById(R.id.bind_service);        unbindService=(Button)findViewById(R.id.unbind_service);        startIntentService=(Button)findViewById(R.id.start_intent_service);        startService.setOnClickListener(this);        stopService.setOnClickListener(this);        bindService.setOnClickListener(this);        unbindService.setOnClickListener(this);        startIntentService.setOnClickListener(this);    }    @Override    public void onClick(View view) {        switch (view.getId()){            case R.id.start_service:                Intent startIntent=new Intent(this,MyService.class);                startService(startIntent);                break;            case R.id.   stop_serevice:                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);  //绑定服务                stopService(bindIntent);                break;            case R.id.   unbind_service:                unbindService(connection);//解绑服务                break;            case R.id.start_intent_service:                Log.i("MainActivity","Thread id is "+Thread.currentThread().getId());                Intent intent=new Intent(this,MyIntentService2.class);                startService(intent);                break;            default:                break;        }    }}

可以看到,我们在StartIntentService 按钮的点击时间里面去启动,MyIntentService这个服务,并在这里打印一线主线程,
记得在配置文件中注册下这个服务
打一下日志
这里写图片描述

可以看到,不仅MyIntentService和MainActivity所在的线程id不一样,并且onDestory()方法也得到执行,索命,MyIntentServicezai 运行完毕后会自动停止。集成了开启线程和自动停止于一身,IntentService还是博得了不少程序员的喜爱。
Servier(上)到此结束

阅读全文
0 0
原创粉丝点击