四大组件:Service学习

四大组件学习:服务Service

服务的定义

​ Service是Android中实现后台运行的解决方案,他非常适合执行那些不需要和用户交互且需要长期运行的任务。服务的运行不依赖任何的用户界面,即使程序被切换到后台,或者用户打开另外的应用程序,服务仍可以保持运行。

​ 不过需要注意的是,服务并不是运行在一个独立的进程中的,二十依赖创建服务时所在的应用程序进程。当某个应用程序进程被杀死,所有依赖该进程的服务都会停止。

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

Android多线程编程

### 1.线程用法	

1.定义线程

class MyThread extends Thread{
    @Override
    public void run(){
        //具体工作
    }
}//定义线程,1.继承Thread 2.重写run方法

启动线程

new Mythread().start();

new 出实例,调用start方法,run就会在子线程当中运行

2.更多的时候都不是使用继承的方法,而是选择使用Runnable接口来定义一个线程

class MyThread implements Runnable{
	@Override
	public void run(){
	 //具体工作
	}
}

启动线程

MyThread myThread = new MyThread();
new Thread(myThread).start();

Thread的给v偶早函数接受一个Runnable 参数,而我们new出的MyThread正式一个实现了Runnable接口的对象,所以就可以直接将他方法Thread的构造函数里面。调用Thread的start方法就可以启动

3.使用匿名类的方法,这种方法更加常见

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

2.在子线程中更新Ui

因为Android应用当中,UI是线程不安全的,也就是说要想更新应用程序的UI内容,就必须在主线程中进行。

但是有时需要在子线程里去执行一些耗时任务,然后根据任务的接过来更新对应的UI,Android提供了一套异步消息处理机制,完美解决了在子线程中执行UI操作的问题。

异步消息处理的使用方法

package com.example.androidthreadtest;

import android.os.Handler;
import android.os.Message;

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

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    public final int changeText = R.id.change_text;
    public static final int UPDATE_TEXT = 1;
    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);
    }
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPDATE_TEXT:
                    // 在这里可以进行 UI 操作
                    text.setText("Nice to meet you");
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.change_text) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Message message = new Message();
                    message.what = UPDATE_TEXT;
                    handler.sendMessage(message);
                }
            }).start();
        }
    }
}

​ 先定义一个整型常量UPDATE_TEXT,用来表示更新TextView这个动作,然后新增一个Handler对象,并重写父类的handleMessage()方法,在这里对具体的Message进行处理。如果发现Message的what字段的值等于UPDATE_TEXT,就将TextView的显示内容更改。

​ 下面再来看一下Change Text按钮的点击事件中的代码。可以看到并没有在子线程中直接进行UI操作,而是创建一个Message(android.os.Message)对象,并将它的what设置为UPDATE_TEXT,然后调用Handler的sendMessage方法将这条Message发送出去。很快,Handler就收到这条Message,并在handleMessage中对他处理,此时handleMessage方法中的代码就是在主线程中运行的了,所以可以放心在这里进行UI操作。
在这里插入图片描述

3.解析异步消息处理机制

一、异步消息处理组成部分

Android中的异步消息处理主要有4各部分组成。

  1. Message:在线程间传递的消息,可携带少量信息(whatarg1arg2 整型字段,obj 携带 Object 对象 ),用于不同线程交换数据。
  2. Handler:用于发送和处理消息。通过 sendMessage() 发消息,消息经处理后传递到 handleMessage() 处理 。
  3. MessageQueue:消息队列,存放所有经 Handler 发送的消息,等待被处理,每个线程仅有一个 MessageQueue 对象。
  4. Looper:每个线程中 MessageQueue 的管家,调用 loop() 进入无限循环,从 MessageQueue 取出消息并传递到 HandlerhandleMessage(),每个线程仅有一个 Looper 对象。

二、异步消息处理流程

  1. 主线程创建 Handler 并复写 handleMessage()
  2. 子线程需 UI 操作时,创建 Message,经 Handler 发送到 MessageQueue 等待处理。
  3. LooperMessageQueue 取消息分发回 HandlerhandleMessage(),因 Handler 主线程创建,handleMessage() 代码在主线程运行,可安全进行 UI 操作 。
    在这里插入图片描述

三、补充说明

runOnUiThread() 是异步消息处理机制的接口封装,原理与上述流程一致,简化了使用 。

使用AsyncTask

Android还提供了另外一些好用的工具AsyncTask。

​ **AsyncTask的基本用法:**由于AsyncTask是一个抽象类,在继承时我们可以为AsyncTask指定三个泛型参数,这三个参数的用途如下。

​ **1.Params。**在执行AsyncTask是需要传入的参数,可以用于在后台任务中使用

​ **2.Progress。**后台执行任务时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为返回值类型

​ **3.Result。**当任务执行完成后,如果需要对结果进行返回,就是用这里指定的反省作为返回值类型。

一个最简单的自定义AsyncTask

class DownloadTask extends AsyncTask<Void,Integer,Bolean>{

}

这里把AsyncTask的第一个泛型指定成Void,表示在执行AsyncTask的时候不要传入参数给后台任务,第二个Integer用来使用整形数据来显示进度。第三个Boolean表示使用布尔值来反馈执行结果。

下面需要对DownloadTask来重写AsyncTask的几个方法就可以完成对任务的定制。

1.onPreExecute()

此方法会在后台任务开始执行之前调用,可以做一些UI初始化,比如显示一个进度条对话框。

2。doInBackground(Params …)

这个方法的所有代码都会在子线程中运行,所以应该在这里取处理所有的好事任务,任务一旦通过就可以return语句来将任务执行的结果返回。如果第三个参数是Void就可以不反悔。

**注意:**这个方法不可以进行UI操作,如果需要更新UI,如返回当前任务进度,可以调用publishProgress(Progress…)完成

3.onProgressUpdate(Progress …)

当后台调用return进行返回时,这个方法就会被调用,返回的数据作为参数传递到此方法中,可以利用来进行一些UI操作,如提示结果,以及显示关闭进度条对话框。

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

    // 假设这些成员已在合适地方初始化,实际使用需根据情况处理
    private ProgressDialog progressDialog; 
    private Context context; 

    @Override
    protected void onPreExecute() {
        progressDialog.show(); // 显示进度对话框
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        try {
            while (true) {
                int downloadPercent = doDownload(); // 虚构的计算下载进度方法
                publishProgress(downloadPercent);
                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 succeeded", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(context, "Download failed", Toast.LENGTH_SHORT).show();
        }
    }

    // 虚构的计算下载进度方法,需实际实现或替换逻辑
    private int doDownload() {
        // 这里应编写真实计算下载进度的代码,比如结合网络请求、文件读写等
        // 此处简单模拟,实际需完善
        return 0; 
    }
}

上述代码定义了一个继承自 AsyncTaskDownloadTask,用于模拟下载任务,涵盖了 AsyncTask 核心的四个回调方法:onPreExecute(准备工作,如显示进度框 )、doInBackground(后台执行耗时任务 )、onProgressUpdate(更新进度,可进行 UI 操作 )、onPostExecute(任务结束,处理结果、收尾,可进行 UI 操作 ) 。不过代码里 progressDialogcontext 等成员需在合适地方(像构造方法、外部传入等 )正确初始化,doDownload 方法也需按实际下载逻辑完善 。

如果要启动这个任务只需要

new DownloadTask().execute();

服务的基本用法

1.定义一个服务

在这里插入图片描述

  1. 服务名为MyServiceExported属性控制是否允许当前程序外其他程序访问,Enabled属性控制服务是否启用,创建时可勾选这两个属性。

service的代码

package com.example.servicetest;

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

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

MyService继承自Service类,onBind()Service中唯一抽象方法,需在子类实现,当前可暂忽略 。

Service 核心重写方法及作用

  • onCreate():服务创建时调用,可做初始化操作,重写需调用super.onCreate()

  • onStartCommand():每次服务启动时调用,若服务启动后要立即执行动作,逻辑写在此,重写需调用super.onStartCommand(intent, flags, startId)

  • onDestroy():服务销毁时调用,用于回收不再使用的资源,重写需调用super.onDestroy()

    public class MyService extends 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();
        }
    }
    
  • Service 生效条件:需在AndroidManifest.xml中注册,Android Studio 一般会自动完成。注册时通过``标签配置,如android:name指定服务类名,android:enabled控制是否启用,android:exported控制是否允许其他程序访问 。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.servicetest">

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

</manifest>

这样就完全定义好一个服务了

2.启动和停止

<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>

主活动布局加两个按钮启动和停止

MainActivity

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(startIntent); // 启动服务
                break;
            case R.id.stop_service:
                Intent stopIntent = new Intent(this, MyService.class);
                stopService(stopIntent); // 停止服务
                break;
            default:
                break;
        }
    }
}

MainActivity 实现 View.OnClickListener 接口,在 onCreate 中获取按钮实例并设置点击监听器;onClick 方法里通过 switch 语句,依据按钮 id 分别构建 Intent,调用 startService(启动服务 )和 stopService(停止服务 )方法 。

注意 startServicestopService 方法定义在 Context 类中,活动可直接调用;服务也可通过 stopSelf 方法自行停止 。

package com.example.servicetest;

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

import androidx.annotation.Nullable;

public class MyService extends Service {

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

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

修改service
MyServiceonCreate()onStartCommand()onDestroy() 等生命周期方法中加入日志打印(如 Log.d ),通过观察日志判断服务是否成功启动、停止 。

在这里插入图片描述

3.服务和活动的通信

  1. 服务与活动的关系问题:启动服务后,活动与服务基本脱离关联,活动无法控制服务具体逻辑及知晓执行情况,想让关系更紧密需用 onBind() 方法。
  2. onBind() 方法作用:可让活动指挥服务执行任务,通过创建继承 Binder 的类(如 DownloadBinder ),在服务中返回该类实例,活动绑定服务后可调用其方法。
  3. 实现步骤
    • 服务端:创建 DownloadBinder 类,含业务方法(如模拟下载的 startDownloadgetProgress );在服务的 onBind 方法返回 DownloadBinder 实例。
    • 活动端:布局添加绑定(bind_service )、解绑(unbind_service )按钮;活动中通过 ServiceConnection 建立与服务的连接,连接成功后获取 DownloadBinder 实例,进而调用服务内方法 。

服务端代码MyService.java

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;
    }
}

布局文件代码activity_main.xml

<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>

活动端代码MainActivity.java

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 = findViewById(R.id.bind_service);
        Button unbindService = 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;
        }
    }
}

服务的生命周期

一、服务生命周期相关方法

服务生命周期涉及的关键回调方法有 onCreate()onStartCommand()onBind()onDestroy() ,这些方法在服务不同阶段被调用 。

二、startService() 相关流程

  1. 调用 ContextstartService() 方法会启动服务,若服务未创建,先执行 onCreate() ,再执行 onStartCommand() ;若已创建,直接执行 onStartCommand() ,且每次调用 startService() 都会触发 onStartCommand() ,但服务仅一个实例 。
  2. 服务启动后持续运行,直到调用 stopService()(外部调用 )或 stopSelf()(服务自身调用 ),此时会执行 onDestroy() 销毁服务 。

三、bindService() 相关流程

  1. 调用 ContextbindService() 可获取服务持久连接,若服务未创建,先执行 onCreate() ,再执行 onBind() ;已创建则直接执行 onBind() ,调用方可获取 onBind() 返回的 IBinder 对象与服务通信 。
  2. 只要连接未断开(未调用 unbindService() ),服务就保持运行,调用 unbindService() 会触发 onDestroy() (若同时满足停止条件 )。

四、混合调用(startService() + bindService() )的销毁条件

若服务既通过 startService() 启动,又通过 bindService() 绑定,需同时调用 stopService()unbindService() ,让服务的启动和绑定条件都不满足,才会执行 onDestroy() 销毁服务 。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值