AndroidAndroid开发程序员

10探究服务-服务的用法

2018-04-01  本文已影响60人  何惧l

定义一个服务

  1. 新建一个项目,右击com.example.servicetest --> New --> Service -->Service,会弹出一个窗口,如下所示:


    创建服务的窗口.png
public class MyService extends Service {
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}
  1. 这个时候Service中还是空空如也,所以就得重写Service中的另外一些方法了,如下所示:
public class MyService extends Service {
    public MyService() {
    }

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

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

}

  1. 要注意的是每一个服务要在AndroidManifest.xml文件中进行注册,但是android Studio已经帮我们注册好了
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.md.server">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
      ...
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">  
        </service>
    </application>
</manifest>

启动和停止服务

启动和停止一个服务还要借助Intent来实现,在项目中实战一下

  1. 修改activity_main.xml中的代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
>
    
    <Button
        android:id="@+id/start_Service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="start_service"/>

    <Button
        android:id="@+id/stop_Service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="stop_service"/>
</LinearLayout>

  1. 修改MainActivity中的代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button start_Service = (Button)findViewById(R.id.start_Service);
        Button stop_Service = (Button)findViewById(R.id.stop_Service);
        start_Service.setOnClickListener(this);
        stop_Service.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_Service:
                Intent stopIntent = new Intent(this,MyService.class);
                // 停止服务
                stopService(stopIntent);
                break;
            default:
                break;
        }
    }
}
  1. 怎么才能证实服务启动了呢,这个时候只要在MyService的方法中添加打印日志就可以了,如下
public class MyService extends Service {
    public MyService() {
    }

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

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

    @Override
    public void onDestroy() {
        
        super.onDestroy();
        Log.d("MyService","onDestroy executed");
    }

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

服务和活动进行通信

虽然服务是在活动里的,但是启动服务后,活动于服务基本就没有什么关系了,因为服务里的方法得到执行,之后服务就会一直处于运行的状态,但是具体运行逻辑的时候,活动控制不了服务了,有没有办法让活动这服务关联起来?就是在活动中指挥服务,让服务干什么,服务就干什么,这个时候就得借助onBind()方法了

  1. 比如希望在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 getProgress(){
            Log.d("MyService","getProgress executed");
            return 0;
        }
    }
    ...

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}
  1. 在活动中如何调用服务里的这些方法,首先在布局文件中新增两个按钮,修改activity.xml中的代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
>
    ...
    <Button
        android:id="@+id/bind_Service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="bind_Service"/>

    <Button
        android:id="@+id/unbind_Service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="unbind_Service"/>
</LinearLayout>
  1. 当一个活动和服务绑定了之后,就可以调用该服务中的Bunder提供的方法,修改MainActivity中的代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private MyService.DownloadBinder downloadBinder;
    // 创建一个匿名类
    private ServiceConnection connection = new ServiceConnection() {
        // 绑定成功调用
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //通过向下转型得到了DownloadBinder的实例
            downloadBinder = (MyService.DownloadBinder) iBinder;
            // 调用方法
            downloadBinder.startDownload();
            downloadBinder.getProgress();
        }
        // 解除绑定调用
        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button start_Service = (Button)findViewById(R.id.start_Service);
        Button stop_Service = (Button)findViewById(R.id.stop_Service);
        start_Service.setOnClickListener(this);
        stop_Service.setOnClickListener(this);
        Button bind_Service = (Button)findViewById(R.id.bind_Service);
        Button unbind_Service = (Button)findViewById(R.id.unbind_Service);
        bind_Service.setOnClickListener(this);
        unbind_Service.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_Service:
                Intent stopIntent = new Intent(this,MyService.class);
                // 停止服务
                stopService(stopIntent);
                break;
            case R.id.bind_Service:
                Intent bindIntent = new Intent(this,MyService.class);
                // 绑定服务,第二个参数是前面创建ServiceConnection的实例
                bindService(bindIntent,connection,BIND_AUTO_CREATE)
                break;
            case R.id.unbind_Service:
                // 解除绑定
                unbindService(connection);
                break;
            default:
                break;
        }
    }
}

活动的生命周期

  1. 一旦在项目中的任何位置调用了Context的startServive()方法,相应的服务就会得到执行,并且回调onStartCommand()方法,如果服务第一次创建,那么onCreate()方法就会先于onStartCommand()方法执行,
  2. 服务启动了就会一直保持运行状态,直到stopService()或者是stopSelf()方法被调用,注意虽然每一次都是调用startServive()方法,但是每个服务都只会存在一个实例,只要调用一次stopService()或者是stopSelf()方法,服务就会停止下来
  3. 另外,还可以调用Context的bindService()来获取一个服务的持久连接,这时就会回调服务中的onBind()方法,这时,如果服务第一次创建,那么onCreate()方法就会先于onBind()方法执行,之后就可以获取到onBind()方法里返回的IBinder对象实例,这样就能自由的和服务进行通信,只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态
  4. 当调用startService()方法之后,又去调用stopService()方法,这是服务中的onDestroy()方法就会得到执行,这个时候服务就销毁了,类似的,当调用了bindService()方法之后,调用了unbindService()方法,服务中的onDestroy()方法就会得到执行,这个时候服务就销毁了
  5. 要注意的是,对一个服务调用了startService(),又调用了bindService()方法之后,这个时候要调用stopService()和unbindService()方法,onDestroy()方法才会执行

服务的更多技巧

使用前台服务

服务一般都是在后台运行的,当系统出现内存不足的时候,就有可能回收掉正在后台运行的服务,如果想要服务一直保持运行状态,而不会由于系统内存不足的原因导致被回收,就可以考虑使用前台服务了

  1. 前台服务与普通服务最大的区别就是它会一直有一个正在运行的图标在系统的状态栏显示,下拉可以看到更加详细的信息,比如说天气预报这种应用,一般都会在系统栏一直显示当前的天气
  2. 创建一个前台的服务,修改MyService中的代码
public class MyService extends Service {
    ...
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("MyService","onCreate executed");
        Intent intent = new Intent(this,MainActivity.class);
        PendingIntent p1 = PendingIntent.getActivity(this,0,intent,0);
        Notification notification = new NotificationCompat.Builder(this)
                .setContentText("this is title")
                .setContentText("thsi is content text")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
                .setContentIntent(p1)
                .build();
        // 这里与之前不同
        startForeground(1,notification);
    }

    ...
}

使用IntentService

服务中的代码都是默认运行在主线程当中的,如果直接在服务中处理一些耗时的逻辑,就容易出现ANR的情况,所以这个时候就使用到了Android多线程编程技术了

  1. 我们应该在服务的每个具体的方法里开启一个子线程,然后在这里去处理一些耗时的逻辑,因此一个比较标准的服务就可以写成
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 处理具体的逻辑
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 处理具体的逻辑
                // 处理完逻辑,自动停止下来
                stopSelf();
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }

  1. 新建一个MyIntentServive类继承自IntentServive
public class MyIntentServive extends IntentService {
    public MyIntentServive() {
        // 调用父类有参的构造函数,
        super("MyIntentServive");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        // 具体的逻辑操作
        // 打印当前的线程的id
        Log.d("MyIntentService","Thread is "+Thread.currentThread().getId());
    }

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

}

  1. 修改activity_main.xml中的代码,加入一个MyIntentService这个服务的按钮
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
>

   ...
    <Button
        android:id="@+id/start_intent_Service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="MyIntentService"/>

</LinearLayout>
  1. 修改MainActivity中的代码

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
      ...
        Button startIntentService = (Button)findViewById(R.id.start_intent_Service);
        startIntentService.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
           ...
            case R.id.start_intent_Service:
            // 打印主线程id
                Log.d("MainActivity","Thread is "+ Thread.currentThread().getId());
                Intent intentService = new Intent(this,MyIntentServive.class);
                startService(intentService);
                break;
            default:
                break;
        }
    }
}

  1. 别忘了,还要在AndroidManifest.xml中进行注册

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.md.server">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
     ...
        <service android:name=".MyIntentServive" />

    </application>

</manifest>
上一篇下一篇

猜你喜欢

热点阅读