Android基础知识——探究服务

1.服务是什么

服务是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且还要求长期运行的任务。

服务并不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的应用程序进程。

服务并不会自动开启线程,所有代码默认运行在主线程中。

2.Android多线程编程

当我们执行一些耗时操作,如发起一条网络请求时,考虑到网速等其他原因,服务器未必会立刻响应我们的请求,若不将这类操作放在子线程中运行,会导致主线程被阻塞,从而影响软件的使用。下面我们就来学习Android中的多线程编程。

2.1线程的基本用法

新建一个类继承Thread,重写run()方法

示例:

//定义一个线程
class MyThread extends Thread{
    @Override
    public void run() {
        //处理具体逻辑
    }
}   
//启动该线程
new MyThread().start();

新建一个类实现Runnable接口,重写run()方法

示例:

//定义一个线程
class MyThread implements Runnable{
    @Override
    public void run() {
        //处理具体逻辑
    }
}
//启动该线程
MyThread thread=new MyThread();
new Thread(thread).start();

使用匿名类的方式去实现Runnable接口,重写run()方法

示例:

public void onClick(View view) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            //处理具体的逻辑
        }
    }).start();
}

2.2在子线程中更改UI

Android 的 UI 是线程不安全的,若想要更新应用程序的 UI 元素,必须在主线程中进行,否则会出现异常。

解决方法一:使用Handler

示例:

public class MainActivity extends AppCompatActivity {
    TextView textView;
    //定义一个整型常量用于表示更新TextView这个动作
    public final int CHANGE_TEXT=1;

    Handler handler=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case CHANGE_TEXT:
                	//在这里可以进行更新UI的操作
                    textView.setText("asdasd");
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button=(Button) findViewById(R.id.button);
        textView=(TextView) findViewById(R.id.text_View);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                    	//创建Message对象,并将它的what字段指定为CHANGE_TEXT
                        Message message=new Message();
                        message.what=CHANGE_TEXT;
                        handler.sendMessage(message);//将Message对象发送出去
                    }
                }).start();
            }
        });
    }
}

解决方法二:使用 runOnUiThread

示例:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button=(Button) findViewById(R.id.button);
        final TextView textView=(TextView) findViewById(R.id.text_View);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        MainActivity.this.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                            	//在这里进行更新UI的操作
                                textView.setText("asdsad");
                            }
                        });
                    }
                }).start();
            }
        });
    }
}

2.3解析异步处理机制

Android 中的异步消息处理主要由4个部份组成:Message、Handler、MessageQueue和Looper。

  • Message:包含描述和任意数据对象的消息,用于发送给Handler。

  • Handler:主要是用于发送和处理消息的。发送消息一般用它的 sendMessage() 方法,发送的消息经过一系列地辗转处理后,最终传递到它的 handleMessage() 方法中。

  • MessageQueue:顾名思义,消息队列。内部存储着一组消息。对外提供了插入和删除的操作。MessageQueue内部是以单链表的数据结构来存储消息列表的。每个线程中只会有一个MessageQueue对象。

  • Looper:Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的hanleMessage()方法中,每个线程中也只会有一个Looper对象。

了解了Message、Handler、MessageQueue和Looper的基本概念后,我们再来把异步消息处理的整个流程梳理一遍。首先需要在主线程中创建一个Handler对象,并重写handleMessage()方法。然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handler将这条消息发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handleMessage()方法中。由于Handler是在主线程中创建的,所以此时handleMessage()方法中的代码也会在主线程中运行,于是我们可以安心地进行UI操作了。

事实上刚才我们讲的两种方法的原理都是异步处理机制。

2.4使用AsyncTask

Andorid提供的在子线程中对UI进行操作的工具,实现原理也是基于异步消息处理机制的。

由于AsyncTask是一个抽象类,所以如果我们想使用它,就必须要创建一个子类去继承它。在继承时我们可以为AsyncTask类指定3个泛型参数,这3个参数的用途如下。

  • Params
    在执行 AsyncTask 时传入的参数,用于后台任务中使用

  • Progress
    后台任务执行时,若需在界面显示当前进度,则使用这里指定的泛型作为进度单位

  • Result
    当任务执行完毕后,若需对结果进行返回,则使用这里指定的泛型作为返回值类型

示例:

public class DownloadTask extends AsyncTask<Void,Integer,Boolean> {

	//在后台任务执行前调用,用于一些界面上的初始化操作
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

	//在子线程中运行,在这处理所有耗时操作
    //注意:不可以进行UI操作,若需要可调用publishProgress(Progress...)方法来完成
    @Override
    protected Boolean doInBackground(Void... voids) {
        while(true){
            int downloadPercent=56;
            publishProgress(downloadPercent);
            if(downloadPercent>=100){
                break;
            }
        }
        return null;
    }

    //当后台任务中调用了publishProgress(Progress...)方法后调用
    //返回的数据会作为参数传递到此方法中,可利用返回的数据进行一些UI操作
    @Override
    protected void onProgressUpdate(Integer... values) {
        Toast.makeText(context,values[0]+"%",Toast.LENGTH_SHORT).show();
        super.onProgressUpdate(values);
    }

	//当后台任务执行完毕并通过return语句进行返回时调用
    @Override
    protected void onPostExecute(Boolean aBoolean) {
        super.onPostExecute(aBoolean);
    }
}

3.服务的基本用法

3.1定义服务

File -> New -> Service -> Service 这样,AS将会在AndroidManifest.xml中自动注册Service。

示例:

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.e("MyService","onCreate" );
    }

	//该方法会在服务启动时调用
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e("MyService","onStartCommand" );
        return super.onStartCommand(intent, flags, startId);
    }

	//该方法会在服务销毁时调用
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("MyService", "onDestroy");
    }
}

3.2启动和停止服务

启动服务步骤:

1.获取Intent实例,在构造方法中传入Context和Service类。
2.调用startService(intent)方法启动服务。

停止服务步骤:

1.获取Intent实例,在构造方法中传入Context和Service类。
2.调用stopService(intent)方法停止服务。

示例:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startButton=(Button) findViewById(R.id.start_button);
        startButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
            	//启动服务
                Intent intent=new Intent(MainActivity.this,MyService.class);
                startService(intent);
            }
        });
        Button stopButton=(Button) findViewById(R.id.stop_button);
        stopButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
            	//停止服务
                Intent intent=new Intent(MainActivity.this,MyService.class);
                stopService(intent);
            }
        });
    }
}

事实上我们不仅可以在活动中停止服务,我们也可以在服务中让他自己停下来。我们只需在服务中的任意位置调用stopSelf()方法即可。

注意: onCreate()方法只会在服务第一次创建时调用,而onStartCommand()方法在每次启动服务时都会调用,在上面的例子中第一次点击启动服务按钮时两个方法都会执行,之后再点击启动服务按钮就只有onStartCommant()方法执行了,同时这也说明了Service只会存在一个实例。

3.3活动和服务进行通信

借助onBind()方法比如说,目前MyService中需要提供一个下载功能。

服务中代码示例:

public class MyService extends Service {

    private DownloadBinder mBinder=new DownloadBinder();
    
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

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

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

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("MyService", "onDestroy");
    }
    
    class DownloadBinder extends Binder{
        //我们可以在其中定义任何你想在活动中调用的方法。
        public void startService(){
            Log.e("MyService", "startService");
        }
        
        public void stopService(){
            Log.e("MyService", "stopService");
        }
    }
}

活动中代码示例:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    ServiceConnection connection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //当服务与活动绑定时调用
            MyService.DownloadBinder binder= (MyService.DownloadBinder) iBinder;
            binder.startService();
            binder.stopService();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            //当服务与活动解绑时调用
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startButton=(Button) findViewById(R.id.start_button);
        startButton.setOnClickListener(this);
        Button stopButton=(Button) findViewById(R.id.stop_button);
        stopButton.setOnClickListener(this);
        Button bindButton=(Button) findViewById(R.id.bind_button);
        bindButton.setOnClickListener(this);
        Button unBindButton=(Button) findViewById(R.id.stop_button);
        unBindButton.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.stop_button:
                Intent stopIntent=new Intent(MainActivity.this,MyService.class);
                stopService(stopIntent);
                break;
            case R.id.start_button:
                Intent startIntent=new Intent(MainActivity.this,MyService.class);
                startService(startIntent);
                break;
            case R.id.bind_button:
            	//活动绑定服务
                Intent intent=new Intent(MainActivity.this,MyService.class);
                bindService(intent,connection,BIND_AUTO_CREATE);
                break;
            case R.id.unBind_button:
            	//活动解绑服务
                unbindService(connection);
                break;
            default:
                break;
        }
    }
}

4.服务的生命周期

服务的生命周期如图所示:
在这里插入图片描述
注意: 当我们对一个服务既调用了startService()方法,又调用了bindService()方法时,要同时调用 stopService()和unbindService()方法,onDestroy()方法才会执行。

5.服务的更多技巧

5.1使用IntentService

服务中的代码默认都是运行在主线程当中的,如果直接在服务里去处理一些耗时的逻辑,就很容易出现ANR的情况。因此我们在使用服务时,其中的主要逻辑都需在子线程中进行。

示例:

public class MyService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.e("MyService","onStartCommand" );
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }

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

当服务开启后就会一直处于运行状态,必须调用stopService()或者stopSelf()才能停止服务,因此当我们要实现一个服务在执行完毕后自动停止,可以这样写:

示例:

public class MyService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

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

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

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

当然,为了可以更简单地创建一个异步地、会自动停止地服务,Android专门提供了一个 IntentService 类,可以很方便的帮我们实现这些功能。

示例:

public class MyService extends IntentService {
    public MyService(){
        super("MyService");//调用父类的有参构造器
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //在这里的逻辑代码,默认都是在子线程中进行的,并且当线程代码运行完毕时会走动调用stopSelf()
    }

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

注意: IntentService类型的服务,同样也需要在AndroidManifest.xml中进行注册。

6.扩展问题

6.1Service生命周期?

1.被启动的服务(startService())的生命周期:

答:如果一个Service被某个Activity 调用Context.startService() 方法启动,那么不管是否有Activity使用bindService()绑定或unbindService()解除绑定到该Service,该Service都在后台运行。如果一个Service被多次执行startService(),它的onCreate()方法只会调用一次,也就是说该Service只会创建一个实例,而它的onStartCommand()将会被调用多次(对应调用startService()的次数)。该Service将会一直在后台运行,直到被调用stopService(),或自身的stopSelf()方法。当然如果系统资源不足,系统也可能结束服务。

2.被绑定的服务(bindService())的生命周期:

答:如果一个Service被调用 Context.bindService ()方法绑定启动,不管调用bindService()调用几次,onCreate()方法都只会调用一次,而onStartCommand()方法始终不会被调用,这时会调用onBind()方法。当连接建立之后,Service将会一直运行,除非调用Context.unbindService() 断开连接或者之前调用bindService() 的 Context 不存在了(如该Activity被finish),系统将会自动停止Service,对应onDestroy()将被调用。

3.被启动又被绑定的服务的生命周期:

答:当我们对一个服务既调用了startService()方法,又调用了bindService()方法时,要同时调用 stopService()和unbindService()方法,onDestroy()方法才会执行。

4.当服务被停止时清除服务:

答:当一个Service被终止时,Service的onDestroy()方法将会被调用,在这里应当做一些清除工作,如停止在Service中创建并运行的线程等。

6.2Service的启动方式

  • 采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStartCommand()方法。采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。

  • 采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的 onCreate()方法,接着调用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方 法,接着调用onDestroy()方法。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法并不会导致 多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)。如果调用者希望与正在绑定的服务解除绑定,可以调用 unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()–>onDestroy()方法。

6.3利用Handler机制传递数据

Handler机制存在一些让我们操作Bundle对象的API,因此我们可以把Bundle当媒介来传递数据。

示例:

public class MainActivity extends AppCompatActivity {

    Handler handler=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    Bundle bundle=msg.getData();//取出bundle对象,以获取数据
                    String name=bundle.getString("name");
                    int age=bundle.getInt("age");
                    Toast.makeText(MainActivity.this,name,Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button=(Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message=new Message();
                        message.what=1;
                        Bundle bundle=new Bundle();//准备要传递的数据
                        bundle.putString("name","Tom");
                        bundle.putInt("age",18);
                        message.setData(bundle);//将bundle对象存储在message中
                        handler.sendMessage(message);
                    }
                }).start();
            }
        });
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值