我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android组件之服务的详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android组件之服务的详解

一、服务的概念

1、服务是实现程序后台运行的解决方案,主要执行那些不需要和用户交互而且还要求长期运行的任务

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

3、服务的后台≠创建子线程,需要在服务的内部手动创建子线程,并且在这里执行具体的任务,否则可能会出现主线程被堵塞的情况

二、Android的多线程编程

2.1 线程的基本用法

  • 线程声明方式一:

	//定义方式
	class myThread extends Thread{
		  @Override
		  public void run(){
		  		//具体的处理逻辑
		  }
	}
	
	//启动方式
	new myThread().start();
  • 线程声明方式二:

	//定义方式
	class myThread implements Runnable{
		 @Override
		  public void run(){
		  		//具体的处理逻辑
		  }
	}
	
	//启动方式
		myThread my = new myThread();
		new Thread(my).start();
  • 线程声明方式三:

	new Thread(new Runnable() {
            @Override
            public void run() {

			}
	}).start();

2.2 在子线程中更新UI

更新方式一

利用runOnUiThread()方法,该方法可以要执行的操作回调到主线程中进行操作,使用方法:

以更新TextView为例:


	@Override
    protected void onCreate(Bundle savedInstanceState) {
        .....
        change.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        changeUI("next to meet you");
                    }
                }).start();
            }
        });
    }
    public void changeUI(String date){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                text.setText(date);
            }
        });
    }

1、我们在子线程中调用changeUI()方法,并将更改后的数据传过去
2、在changeUI()方法中,调用runOnUiThread()方法将进程调回到主线程中,然后调用setText()方法进行更新UI操作

具体使用方法,博客:理解 Activity.runOnUiThread

更新方式二

利用Android自带的异步消息处理机制


public class MainActivity extends AppCompatActivity {
    public static final int UPDATE_TEXT = 1;
    private TextView text;

    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
    protected void onCreate(Bundle savedInstanceState) {
        ....
        change.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        handler.sendMessage(message);
                    }
                }).start();
            }
        });
    }
}

1、首先定义一个字段UPDATE_TEXT表示更新的具体动作
2、创建Handler的对象,并重写父类的handleMessage()方法,在该方法中对Message进行处理
3、判断Message的what的值,进行相应的UI操作
4、回到子线程,首先获取一个Message 的对象,对what字段赋值
5、调用Handler对象的sendMessage()将这个Message 发送出去
6、在Handler收到这个消息以后就会调用handleMessage()对消息进行处理,且现在已经是在主线程中处理不会出现问题

2.3 解析异步消息处理机制

1、处理机制组成:Message、Handler、MessageQueue、Looper

2、Message:

  • 1)作用:在线程之间传递信息
  • 2)可以在内部携带少量信息,利用其字段,如what,arg1,rg2,obj字段存储,用于在不同线程之间交换数据

3、Handler:

  • 1)作用:主要用于发送和处理消息
  • 2)发送消息使用Handler的sendMessage()方法
  • 3)发出的消息经过处理以后,传递到Handler的handleMessage()方法中

4、MessageQueue:

  • 1)作用:主要用于存放所有通过Handler发送的消息。每个线程中只有一个MessageQueue对象

5、Looper:

  • 1)作用:每个线程中MessageQueue的管家
  • 2)在调用其loop()方法后,进入无限循环,在循环中每发现MessageQueue中存在一条消息就将其取出,传递到Handler的handleMessage()方法中进行处理
  • 3)每个线程中只有一个Looper对象

6、整个异步流程:

在这里插入图片描述

  • 1)首先主线程中创建一个Handler的对象,并重写父类的handleMessage()方法
  • 2)在子线程要进行UI操作时,创建一个Message对象,通过Handler的sendMessage()方法出去
  • 3)这条消息会被添加到MessageQueue队列中等待
  • 4)Looper从MessageQueue中取出一条消息,传递到Handler的handleMessage()方法中进行处理

2.4 使用AsyncTask

1、AsyncTask的实现原理是基于异步消息处理机制的
2、AsyncTask是一个抽象类,在子类继承这个类时指定3个泛型参数:

  • Params:在执行AsyncTask时需传入的参数,可用于在后台任务中使用
  • Progress:后台任务执行时,若需要在界面上显示当前进度,这里就指定的泛型作为进度单位
  • Result:当任务执行完毕,若需要对结果进行返回,则使用这里指定的泛型作为返回值类型
  • 如:

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

第一个泛型参数指定为Void,表示在执行AsyncTask时不需要传入参数给后台任务
第二个参数指定为Integer,表示使用整型数据来作为进度显示单位
第三个参数指定为Boolean,表示用boolean类型数据来反馈执行结果

3、在继承后,还需重写如下方法

  • onPreExecute() :在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,如显示一个进度条对话框等
  • doInBackground(Params… ) :该方法中所有的代码都会在子线程中运行。后台任务一旦完成通过return语句将任务的执行结果返回,若AsyncTask第三个参数指定为Void,则可以不返回任务执行结果。若需要更新UI元素,则调用publishProgress(Progress…)方法来完成
  • onProgressUpdate(Progress… ):当在后台任务中调用publishProgress(Progress…)方法,该方法就会被调用,该方法所携带的参数就是在后台任务中传递过来的,在这个方法中利用参数中的数值可以对UI进行操作
  • onPostExecute(Result) :后台任务执行完毕并返回return语句进行返回时,这个方法就会被调用,返回的数据作为参数会传递到此方法中,可以利用返回的数据来进行一些UI操作,如提醒任务执行的结果,或关闭进度条对话框等具体框架:

		public class DownloadTask extends AsyncTask<Void,Integer,Boolean> {
		    @Override
		    protected void onPreExecute() {
		        progressDialog.show();//显示进度对话框
		    }
		
		    @Override
		    protected Boolean doInBackground(Void... voids) {//在该方法里执行下载任务
		        try{
		            while(true){
		                int percent = doDownload();//虚构一个方法,返回一个值表示下载进度
		                publishProgress(percent);//调用publishProgress方法,并将进度数值传进去
		                if(percent >= 100)
		                    break;
		            }
		        }catch (Exception e){
		            e.printStackTrace();
		        }
		        return true;
		    }
		
		    @Override
		    protected void onProgressUpdate(Integer... values) {//该方法所带的参数就是publishProgress()方法的进度数值
		        progressDialog.setMessage("Downloaded" + values[0] + "%");
		    }
		
		    @Override
		    protected void onPostExecute(Boolean aBoolean) {//后台任务完成后该方法就会被调用
		        progressDialog.dismiss();//关闭进度对话框
		        //提示下载结果
		        if (aBoolean){
		            Toast.makeText(context,"Succeeded",Toast.LENGTH_SHORT).show();
		        }else {
		            Toast.makeText(context,"Failed",Toast.LENGTH_SHORT).show();
		        }
		    }
		}

4、启动方法:


	new DownloadTask().execute();

三、服务的基本用法

3.1 首先定义一个服务

在这里插入图片描述

在上述界面中:Exported表示是否允许除了当前程序之外的其他程序访问这个服务;Enabled表示是否启动这个服务

3.2 MyService类里重写几个方法


	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();
	    }//服务被创建时调用
	
	    @Override
	    public int onStartCommand(Intent intent, int flags, int startId) {
	        return super.onStartCommand(intent, flags, startId);
	    }//每次服务启动时调用,若需要服务一旦启动就立刻执行某个动作,就可以将逻辑卸载这个方法里面
	
	    @Override
	    public void onDestroy() {
	        super.onDestroy();
	    }//服务被销毁时调用
	}

1、onCreate()方法在服务被创建时调用
2、onStartCommand()方法在每次服务启动时调用,若需要服务一旦启动就立刻执行某个动作,就可以将逻辑卸载这个方法里面
3、onDestroy()方法在服务被销毁时调用
4、onCreate()方法在服务第一次创建时调用,onStartCommand()方法是在每次启动服务时调用(若在一次服务中调用了onDestroy()方法,那么这个服务就无了,再点开始按钮就会调用onCreate()方法)

3.3 在注册文件中完成对服务的注册


	<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	    package="com.example.testservices" >
	    <application
	        .....
	        <service
	            android:name=".MyService"
	            android:enabled="true"
	            android:exported="true" >
	        </service>
	        .....
	    </application>
	</manifest>

3.4 启动和停止服务

首先要创建一个活动和两个用于启动服务的按钮,创建完成后通过Intent实现活动和服务之间的“桥梁”搭建


		Button start = findViewById(R.id.start_service);
        Button stop = findViewById(R.id.stop_service);
        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent startIntent = new Intent(MainActivity2.this,MyService.class);
                startService(startIntent);//启动服务
            }
        });
        stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent stopIntent = new Intent(MainActivity2.this,MyService.class);
                stopService(stopIntent);//停止服务
            }
        });

关于让服务停止,如果不点击停止按钮服务会一直处于运行状态。可以通过在MyService中指定的地方调用 stopSelf()方法 实现服务的自动停止

然后在MyService里定义一些日志


		public class MyService extends Service {
		    final static String TAG = "MyService";
		   	....
		    @Override
		    public void onCreate() {
		        super.onCreate();
		        Log.d(TAG,"onCreate executed");
		    }//服务被创建时调用
		
		    @Override
		    public int onStartCommand(Intent intent, int flags, int startId) {
		        Log.d(TAG,"onStartCommand executed");
		        return super.onStartCommand(intent, flags, startId);
		    }//每次服务启动时调用,若需要服务一旦启动就立刻执行某个动作,就可以将逻辑卸载这个方法里面
		
		    @Override
		    public void onDestroy() {
		        super.onDestroy();
		        Log.d(TAG,"onDestroy executed");
		    }//服务被销毁时调用
		}

运行结果:

在这里插入图片描述

3.5 活动和服务进行通信

以下载功能为例:在服务中提供一个下载功能,在活动中决定何时开始下载以及随时查看下载进度

1、首先在MyService中创建一个Binder的子类内部类,对下载功能进行管理:


		 class DownloadBinder extends Binder{
		        public void startDownload(){
		            Log.d(TAG,"startDownload executed");
		        }//开始下载的模拟方法
		        public int getProgress(){
		            Log.d(TAG,"getProgress executed");
		            return 0;
		        }//查看下载进度的模拟方法
		    }

2、然后同样地,在MyService里获取到DownloadBinder 的实例,并且通过 onBind()方法 返回这个实例(最关键的一步)


 	private DownloadBinder binder = new DownloadBinder();
	.....
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return binder;
    }

3、在活动中定义两个按钮,分别用于绑定服务和服务解绑

4、在活动中创建一个ServiceConnection的匿名类,并重写onServiceConnected()、onServiceDisconnected()方法,这两个方法会在活动与服务成功绑定和断开时调用


public class MainActivity2 extends AppCompatActivity {
    private MyService.DownloadBinder binder;
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            binder = (MyService.DownloadBinder) iBinder;
            binder.startDownload();
            binder.getProgress();
            //在这里通过转型获取到Binder实例,通过这个实例,就可以在活动中根据具体的场景来调用DownloadBinder中任何public的方法
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        .....
    }
}

5、在按钮事件里完成活动与服务之间的绑定和解绑


 		bind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent bindIntent = new Intent(MainActivity2.this,MyService.class);
                bindService(bindIntent,connection,BIND_AUTO_CREATE);//绑定服务
                //BIND_AUTO_CREATE是一个标志位,该标志位表示在活动和服务进行绑定后自动创建服务,即MyService里的onCreate()方法会得到执行,onStartCommand()方法不会得到执行
            }
        });
        unbind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                unbindService(connection);//解除绑定
            }
        });

四、服务的生命周期

在这里插入图片描述

在这里插入图片描述

五、服务的更多技巧

5.1 使用前台服务

1、前台服务的作用:可以使服务一直保持运行状态,而不会由于系统内存不足的原因导致被回收
2、跟普通服务的区别:一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更详细的信息
3、具体实现:


public class MyService extends Service {
    .....
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG,"onCreate executed");
        Intent intent = new Intent(this,MainActivity2.class);
        PendingIntent pi = PendingIntent.getActivity(this,0,intent,0);
        NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        Notification notification = new NotificationCompat.Builder(this,"YonC")
                .setContentTitle("This is content title")
                .setContentText("his is content text")
                .setWhen(System.currentTimeMillis())
                .setContentIntent(pi)
                .build();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("YonC", "YonC",NotificationManager.IMPORTANCE_HIGH);
            manager.createNotificationChannel(channel);
        }
        manager.notify(1,notification);
        startForeground(1,notification);
    }//服务被创建时调用
	.....
}

1、这里利用PendingIntent 和Notification 创建一个通知的形式创建了一个前台活动
2、首先通过notify()方法将该通知显现出来
3、然后调用startForeground()方法让MyService变成一个前台服务,该方法接收两个参数,第一个参数是通知的id,
类似于notify()方法的第一个参数;第二个参数为构建出的Notification对象

4、注意还需要申请权限


	<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

5.2 服务中的多线程问题&IntentService

1、ANR:即Application Not Responding,直接在服务中处理一些耗时的逻辑就会出现此类情况
2、IntentService是服务的子类之一,在该类中的onHandleIntent()方法中的逻辑都是在子线程中执行,因此可以将服务中一些耗时的逻辑都放在这个方法中从而避免ANR问题
3、具体实现:


		public class MyIntentService extends IntentService {
		    public MyIntentService(){
		        super("MyIntentService");
		    }//这个无参的构造函数和调用父类有参构造函数是必须有的
		    @Override
		    protected void onHandleIntent(@Nullable Intent intent) {
		        //该方法里的逻辑都是在子线程中执行的
		    }
		}

	 son.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity2.this,MyIntentService.class);
                startService(intent);
            }
        });

在按钮注册事件中引用Intent “建桥”,再调用startService()方法开启服务


    <service android:name=".MyIntentService"/>

完成该服务的注册

到此这篇关于Android组件之服务的详解的文章就介绍到这了,更多相关Android组件服务内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android组件之服务的详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android四大组件之Service(服务)实例详解

本文实例讲述了Android四大组件之服务用法。分享给大家供大家参考,具体如下: 很多情况下,一些与用户很少需要产生交互的应用程序,我们一般让它们在后台运行就行了,而且在它们运行期间我们仍然能运行其他的应用。 为了处理这种后台进程,Andr
2022-06-06

Android四大组件之BroadcastReceiver详解

BroadcastReceiver是Android四大组件之一,用于接收和处理系统广播或者应用内发送的广播。广播是一种跨组件、跨应用的通信机制,可以用于在应用内部或者应用之间传递消息或者事件。BroadcastReceiver的主要作用是接
2023-08-09

详解Android业务组件化之URL Schema使用

前言: 最近公司业务发展迅速,单一的项目工程不再适合公司发展需要,所以开始推进公司APP业务组件化,很荣幸自己能够牵头做这件事,经过研究实现组件化的通信方案通过URL Schema,所以想着现在还是在预研阶段,很有必要先了解一下U
2022-06-06

Android组件必学之TabHost使用方法详解

一、TabHost用法 通常情况下我们会通过继承TabActivity,调用getTabHost()获取TabHost实例,下面是具体过程。 TabHostActivity.javapublic class TabHostActivity
2022-06-06

详解Android中BroadCastReceiver组件

BroadcastReceiver也就是“广播接收者”的意思,它是用来接收来自系统和应用中的广播。 在Android中,Broadcast是一种广泛运用的在应用程序之间传输信息的机制。而BroadcastReceiver是对发送出来的 Br
2022-06-06

Android中ContentProvider组件详解

ContentProvider是Android中的一种组件,用于管理应用程序之间共享的数据。它提供了一种标准化的接口,使得应用程序可以访问其他应用程序的数据,并支持对数据进行增删改查的操作。ContentProvider的主要作用是提供数据
2023-08-25

Android中 service组件详解

service组件跟activity组件及其类似,可以说service是没有界面的activity,当然service的生命周期和activity还是有一定的差别的。 service组件一般用在什么地方的,上面讲了service组件没
2022-06-06

Springcloud之Gateway组件详解

SpringCloudGateway是SpringCloud微服务生态下的网关组件。SpringCloudGateway是基于Spring5和SpringBoot2搭建的,本质上是一个SpringBoot应用。本文详细介绍了SpringCloud的网关组件Gateway,,需要的朋友可以参考下
2023-05-19

Android WebView组件用法详解

本文实例讲述了Android WebView组件用法。分享给大家供大家参考,具体如下: 如果想WebView能够访问网络,必须在AndroidManifest.xml里面添加权限2022-06-06

Android系统的四大组件详解

Android四大基本组件介绍与生命周期Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器。一:了解四大基本组件Activity :
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录