四大组件:Service学习
四大组件学习:服务Service
文章目录
- 四大组件学习:服务Service
-
- 服务的定义
- Android多线程编程
-
- 2.在子线程中更新Ui
- 3.解析异步消息处理机制
- 服务的基本用法
-
- 1.定义一个服务
- 2.启动和停止
- 3.服务和活动的通信
- 服务的生命周期
服务的定义
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{@Overridepublic 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各部分组成。
- Message:在线程间传递的消息,可携带少量信息(
what
、arg1
、arg2
整型字段,obj
携带Object
对象 ),用于不同线程交换数据。 - Handler:用于发送和处理消息。通过
sendMessage()
发消息,消息经处理后传递到handleMessage()
处理 。 - MessageQueue:消息队列,存放所有经
Handler
发送的消息,等待被处理,每个线程仅有一个MessageQueue
对象。 - Looper:每个线程中
MessageQueue
的管家,调用loop()
进入无限循环,从MessageQueue
取出消息并传递到Handler
的handleMessage()
,每个线程仅有一个Looper
对象。
二、异步消息处理流程
- 主线程创建
Handler
并复写handleMessage()
。 - 子线程需 UI 操作时,创建
Message
,经Handler
发送到MessageQueue
等待处理。 Looper
从MessageQueue
取消息分发回Handler
的handleMessage()
,因Handler
主线程创建,handleMessage()
代码在主线程运行,可安全进行 UI 操作 。
三、补充说明
runOnUiThread()
是异步消息处理机制的接口封装,原理与上述流程一致,简化了使用 。
使用AsyncTask
Android还提供了另外一些好用的工具AsyncTask。
**AsyncTask的基本用法:**由于AsyncTask是一个抽象类,在继承时我们可以为AsyncTask指定三个泛型参数,这三个参数的用途如下。
**1.Params。**在执行AsyncTask是需要传入的参数,可以用于在后台任务中使用
**2.Progress。**后台执行任务时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为返回值类型
**3.Result。**当任务执行完成后,如果需要对结果进行返回,就是用这里指定的反省作为返回值类型。
一个最简单的自定义AsyncTask
class DownloadTask extends AsyncTask{}
这里把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; }}
上述代码定义了一个继承自 AsyncTask
的 DownloadTask
,用于模拟下载任务,涵盖了 AsyncTask
核心的四个回调方法:onPreExecute
(准备工作,如显示进度框 )、doInBackground
(后台执行耗时任务 )、onProgressUpdate
(更新进度,可进行 UI 操作 )、onPostExecute
(任务结束,处理结果、收尾,可进行 UI 操作 ) 。不过代码里 progressDialog
、context
等成员需在合适地方(像构造方法、外部传入等 )正确初始化,doDownload
方法也需按实际下载逻辑完善 。
如果要启动这个任务只需要
new DownloadTask().execute();
服务的基本用法
1.定义一个服务
- 服务名为
MyService
,Exported
属性控制是否允许当前程序外其他程序访问,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
控制是否允许其他程序访问 。
这样就完全定义好一个服务了
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
(停止服务 )方法 。
注意 startService
和 stopService
方法定义在 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
在 MyService
的 onCreate()
、onStartCommand()
、onDestroy()
等生命周期方法中加入日志打印(如 Log.d
),通过观察日志判断服务是否成功启动、停止 。
3.服务和活动的通信
- 服务与活动的关系问题:启动服务后,活动与服务基本脱离关联,活动无法控制服务具体逻辑及知晓执行情况,想让关系更紧密需用
onBind()
方法。 onBind()
方法作用:可让活动指挥服务执行任务,通过创建继承Binder
的类(如DownloadBinder
),在服务中返回该类实例,活动绑定服务后可调用其方法。- 实现步骤
- 服务端:创建
DownloadBinder
类,含业务方法(如模拟下载的startDownload
、getProgress
);在服务的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()
相关流程
- 调用
Context
的startService()
方法会启动服务,若服务未创建,先执行onCreate()
,再执行onStartCommand()
;若已创建,直接执行onStartCommand()
,且每次调用startService()
都会触发onStartCommand()
,但服务仅一个实例 。 - 服务启动后持续运行,直到调用
stopService()
(外部调用 )或stopSelf()
(服务自身调用 ),此时会执行onDestroy()
销毁服务 。
三、bindService()
相关流程
- 调用
Context
的bindService()
可获取服务持久连接,若服务未创建,先执行onCreate()
,再执行onBind()
;已创建则直接执行onBind()
,调用方可获取onBind()
返回的IBinder
对象与服务通信 。 - 只要连接未断开(未调用
unbindService()
),服务就保持运行,调用unbindService()
会触发onDestroy()
(若同时满足停止条件 )。
四、混合调用(startService()
+ bindService()
)的销毁条件
若服务既通过 startService()
启动,又通过 bindService()
绑定,需同时调用 stopService()
和 unbindService()
,让服务的启动和绑定条件都不满足,才会执行 onDestroy()
销毁服务 。