Android第一行代码--Service(四大组件之一)

Posted 钢铁-程序猿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android第一行代码--Service(四大组件之一)相关的知识,希望对你有一定的参考价值。

文章目录

android第一行代码-Service

Android支持后台功能,这使得应用程序即使在关闭的情况下仍然可以在后台继续运行。

服务是什么

非常适合执行那些不需要和用户交互而且需要长时间运行的任务。

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

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

Android多线程编程

线程的基本用法

  • 1、继承Thread类,实现run方法
  • 2、实现Runnable接口,实现里面的run方法。

在子线程中更新UI

Android的UI和其他许多GUI库一样,也是线程不安全的。有就是说,如果想要更新应用程序里的UI元素,则必须在主线程中进行,否则就会出现异常。

package com.example.androidthreadtest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener 

    private TextView text;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView) findViewById(R.id.text);
        Button button = (Button) findViewById(R.id.change_text);
        button.setOnClickListener(this);
    

    @Override
    public void onClick(View v) 
        switch (v.getId())
            case R.id.change_text:
                new Thread(new Runnable() 
                    @Override
                    public void run() 
                        text.setText("Nice to meet you");
                    
                ).start();
                break;
            default:
                break;
        
    

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
当运行上面的代码时会报错,这是因为在子线程中更新UI导致的。

但是有时候必须要在子线程中执行一些耗时任务,然后根据任务的执行结果来更新相应的UI控件,这该怎么解决呢?

对于这种情况,Android提供了一套异步消息处理机制,完美解决了在子线程中进行UI操作的问题。

通过异步消息处理机制解决在子线程中更新UI导致的报错问题

package com.example.androidthreadtest;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener 

    private TextView text;
    public static final int UPDATE_TEXT = 1;

    private Handler handler = new Handler()
        @Override
        public void handleMessage(@NonNull Message msg) 
            switch (msg.what)
                case UPDATE_TEXT:
                    text.setText("Nice to meet you");
                    break;
                default:
                    break;
            
        
    ;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView)findViewById(R.id.text);
        Button changeText = (Button)findViewById(R.id.change_text);
        changeText.setOnClickListener(this);

    

    @Override
    public void onClick(View v) 
        switch (v.getId())
            case R.id.change_text:
                new Thread(new Runnable() 
                    @Override
                    public void run() 
                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        handler.sendMessage(message);
                    
                ).start();
                break;
            default:
                break;
        
    

解析异步消息处理机制

Android中的异步消息处理主要分为四个部分:

  • 1、Message
  • 2、Handler
  • 3、MessageQueue
  • 4、Looper

1、Message(传递消息)

用于在线程间传递消息,可以携带少量的信息,用于在不同线程之间交换数据。除此之外,Message还有arg1和arg2字段来携带一些整型数据,obj属性来携带一些Object对象。

2、Handler(处理者,用于发送和处理消息,发出的消息最终会传递到Handler的handleMessage方法中)

用于发送和处理消息。发送消息一般是使用sendMessage方法,而发出的消息经过一系列辗转处理后,最终会传递到Handler的handleMessage方法中。

3、MessageQueue(消息队列)

用于存放Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理,每个线程只会有一个MessageQueue对象。

4、Looper(MessageQueue的管家,MessageQueue中的消息取出传递给handleMessage方法中)

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

消息处理过程

  • 1、创建Handler对象,重写handleMessage方法
  • 2、当子线程需要进行UI操作时,创建Message对象,通过Handler将消息传递出去。
  • 3、将消息添加到MessageQueue
  • 4、Looper不断尝试从MessageQueue取出待处理消息,最后发回Handler的handleMessage方法中。

由于Handler是主线程中创建的,所以此时handleMessage方法的代码也会在主线程中运行,于是就可以安心进行UI操作。

AsyncTask

通过AsyncTask,即使你对异步消息处理机制完全不了解,也可以十分简单从子线程切换到主线程。AsyncTask背后的实现原理也是基于异步消息处理机制的,知识Android帮我们做了很好的封装而已。

使用AsyncTask(抽象类)

如果要使用AsyncTask,就需要创建一个子类去继承它,在继承时,我们就可以为AsyncTask类指定三个泛型参数。

  • 1、Params:doInBackground函数的参数类型,在执行AsyncTask时需要传入的参数,可用于在后台任务中使用
  • 2、Progress:onProgressUpdate函数的参数类型,后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位
  • 3、Result:onPostExecute函数的参数类型,当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
class DownloadTask extends AsyncTask<Void,Integer,Boolean>

  • Void表示不需要传入参数给后台任务
  • Integer表示使用整型数据作为进度显示单位
  • Boolean表示使用Boolean数据来反馈执行结果

需要重写AsyncTask中的几个方法才能完成对任务的定制,经常需要去重写的方法有4个:

  • 1、onPreExecute():这个方法在任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框。
  • 2、doInBackground(Params…):这个方法中的所有代码都会在子线程中运行,我们需要在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果返回。如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress…)方法来完成。
  • 3、onProgressUpdate(Progress…):当在后台任务中调用了publishProgress(Progress…)方法后,onProgressUpdate(Progress…)方法就会很快被调用,该方法中携带的参数就是在后台任务中传递过来的,在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新
  • 4、onPostExecute(Result):当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中。可以利用返回的数据进行一些UI操作,比如说提醒任务执行的结果以及关闭进度条对话框等。
class DownloadTask extends AsyncTask<Void,Integer,Boolean> 
    @Override
    protected void onPreExecute() 
        progressDialog.show(); //显示进度对话框
    

    @Override
    protected Boolean doInBackground(Void... Params) 
        try
            while (true)
                int downloadPercent = doDownload(); //这是一个虚构的方法
                publishProgress(downloadPercent); //传入当前的下载进度,这样onProgressUpdate()方法就会很快被调用。
                if (downloadPercent >= 100)
                    break;
                
            
        catch (Exception e)
            return false;
        
        return true;
    

    @Override
    protected void onProgressUpdate(Integer... values) 
        progressDialog.setMessage("Downloaded"+values[0]+"%");
    

    @Override
    protected void onPostExecute(Boolean result) 
        progressDialog.dismiss(); //关闭进度条对话框
        if (result)
            Toast.makeText(context,"Download succeed",Toast.LENGTH_SHORT).show();
        else
            Toast.makeText(context,"Download failed",Toast.LENGTH_SHORT).show();
        
    

简单来说,使用AsyncTask的诀窍就是在DoInBackground()方法中执行具体的耗时任务,在onProgressUpdate()方法中进行UI操作,在onPostExecute()方法中执行一些任务的收尾工作。

如果想要调动这个任务,只需要编写以下代码即可,new DownloadTask().execute();

服务的基本用法

1、定义一个服务(继承Service类)

通过New–》Service–》Service可以定义一个服务。

在创建时Exported表示是否允许除了当前程序之外的其他程序访问这个服务。Enable表示是否启动这个服务。

既然是定义一个服务,自然应该在服务中去处理一些事情,那处理事情的逻辑应该写在哪里呢?需要重写Service中的一些方法。如下所示:

  • onCreate():在服务创建的时候调用
  • onStartCommand():在服务启动的时候调用
  • onDestroy():在服务销毁的时候调用

每个服务都需要在AndroidManifest.xml中进行注册才能生效,通过Android Studio创建Service会自动在AndroidManifest文件中进行注册。

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

启动和停止服务(主要借助Intent来实现)

完全是由活动决定服务何时停止。

例子:
MyService:

package com.example.servicetest;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

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");
    

    @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");
    

MainActivity:

package com.example.servicetest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startService = (Button)findViewById(R.id.start_service);
        Button stopService = (Button)findViewById(R.id.stop_service);
        startService.setOnClickListener(this);
        stopService.setOnClickListener(this);
    

    @Override
    public void onClick(View v) 
        switch (v.getId())
            case R.id.start_service:
                Intent startIntent = new Intent(this,MyService.class);
                //startService()和stopService()方法都是定义在Context类中的,所以我们在活动中可以直接调用这两个方法
                startService(startIntent); //启动服务
                break;
            case R.id.stop_service:
                Intent stopIntent = new Intent(this,MyService.class);
                stopService(stopIntent); //停止服务
                break;
            default:
                break;
        
    

服务在活动中启动的

服务如果启动成功,可以在Settings->Developer options->Running Service中找到它。

onCreate()和onStartCommand()方法的区别:onCreate()是在服务第一次创建的时候调用的,而onStartCommand()方法则在每次启动服务的时候都会调用。

活动和服务进行通信

有没有什么办法可以让活动和服务的关系更紧密呢?比如在活动中指挥服务去干什么,服务就去干什么。

  • 借助onBind()方法来实现

活动绑定服务,当一个活动和服务绑定了之后,就可以调用服务里的Binder提供的方法了。

MyService类

package com.example.servicetest;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

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;
        

    

    public MyService() 
    

    @Override
    public IBinder onBind(Intent intent) 
        // TODO: Return the communication channel to the service.
        return mbinder;
    

    @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");
    

MainActivity类

package com.example.servicetest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener 

    private MyService.DownloadBinder downloadBinder;
    private ServiceConnection connection = new ServiceConnection() 
    
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) 
            downloadBinder = (MyService.DownloadBinder)service;
            downloadBinder.startDownload();
            downloadBinder.getProgress();
        

        @Override
        public void onServiceDisconnected(ComponentName name) 

        
    ;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button bindService = (Button)findViewById(R.id.bind_service);
        Button 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);
                ///将MainActivity和MyService进行绑定
                //BIND_AUTO_CREATE表示活动和服务进行绑定后自动创建服务
                //这会使得MyService中的onCreate方法得到执行,onStartComma

以上是关于Android第一行代码--Service(四大组件之一)的主要内容,如果未能解决你的问题,请参考以下文章

Android第一行代码--Service(四大组件之一)

Android第一行代码--Service(四大组件之一)

Android四大组件

Android面试四大组件之服务Service

浅谈Android四大组建之一Service

Android四大组件——Service