Android组件之服务的详解

目录
  • 一、服务的概念
  • 二、Android的多线程编程
    • 2.1 线程的基本用法
    • 2.2 在子线程中更新UI
      • 更新方式一
      • 更新方式二
    • 2.3 解析异步消息处理机制
    • 2.4 使用AsyncTask
  • 三、服务的基本用法
    • 3.1 首先定义一个服务
    • 3.2 MyService类里重写几个方法
    • 3.3 在注册文件中完成对服务的注册
    • 3.4 启动和停止服务
    • 3.5 活动和服务进行通信
  • 四、服务的生命周期
  • 五、服务的更多技巧
    • 5.1 使用前台服务
    • 5.2 服务中的多线程问题&IntentService

一、服务的概念

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组件服务内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • android组件SwipeRefreshLayout下拉小球式刷新效果

    swiperefreshlayout实现下拉小球式的刷新,供大家参考,具体内容如下 布局文件: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools&quo

  • Android组件ListView列表简单使用

    ListView是一种非常常见的一个组件,以垂直列表的形式显示列表项.而完成一个简单的ListView只需要这么三个步骤: 1.在布局文件XML中添加ListView并配置相应的属性 2.在资源文件XML中添加列表项 3.将布局文件应用上 举例: 1.在布局文件中加ListView并配置相应属性,如下配置了宽度.高度.分割线高度.是否显示头部分割线.列表项 <?xml version="1.0" encoding="utf-8"?> <Linear

  • Android组件实现列表选择框功能

    android提供的列表选择框(Spinner)相当于web端用户注册时的选择下拉框,比如注册候选择省份城市等.如下图便是一个列表选择框 下拉列表的列表选择项能够通过xml文件的android:entries属性指定,或是在java代码中导入,属性android:prompt是列表项的标题. 一    列表项数据: 实际运用当中,很多下拉列表项的数据实际是可知的,可以放在xml资源文件中.这时,开发者可以通过xml属性进行指定数据. 除了资源文件之外,开发者还能够使用适配器适配数据源.(适配器:

  • Android组件popupwindow使用方法详解

    先看效果: 现在很多的应用效果都需要做的炫些,像UC,以及天天静听,效果很炫的,源码已经对外开放了,有兴趣的可以去研究下的 上源码 main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fi

  • Android组件ViewStub基本使用方法详解

    ViewStub可以在运行时动态的添加布局.帮助文档给定的定义是: "A ViewStub is an invisible, zero-sized View that can be used to lazily inflate layout resources at runtime. When a ViewStub is made visible, or when inflate() is invoked, the layout resource is inflated. The ViewStu

  • Android组件之间交互核心Intent用法分析

    本文实例讲述了Android组件之间交互核心Intent用法.分享给大家供大家参考,具体如下: 从一个Activity启动到另一个Activity可以使用startActivity()方法或者是startActivityForResult()方法 第一种:直接启动一个Activity Intent intent = new Intent(Main.this, SecondActivity.class); startActivity(intent); 第二种:启动另一个Activity并返回结果

  • Android组件之服务的详解

    目录 一.服务的概念 二.Android的多线程编程 2.1 线程的基本用法 2.2 在子线程中更新UI 更新方式一 更新方式二 2.3 解析异步消息处理机制 2.4 使用AsyncTask 三.服务的基本用法 3.1 首先定义一个服务 3.2 MyService类里重写几个方法 3.3 在注册文件中完成对服务的注册 3.4 启动和停止服务 3.5 活动和服务进行通信 四.服务的生命周期 五.服务的更多技巧 5.1 使用前台服务 5.2 服务中的多线程问题&IntentService 一.服务的

  • android studio后台服务使用详解

    Service 是 Android 系统的服务组件,适用于开发没有用户界面且长时间在后台运行的功能.通过本次试验了解后台服务的基本原理,掌握本地服务的使用方法. 1.创建一个Service服务用来完成简单的求和和比较大小的数学运算.2.创建Activity并调用该数学Service activity_main.xml <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.

  • Android 组件样式定制方法详解

    前言 android中有很多现成的组件可以使用,但是android上面的程序很多时候用系统自带的组件都不太合适,主要是样式可能不是我们想要的.这个时候我们就需要定制一些样式.本文将讲解如何修改组件的样式. 1.默认样式. 在修改组件的样式之前,我们还是先了解一下组件默认的样式,如下: 当然还有很多的组件,这里就简单的列举一下就可以了.从上面的默认组件可以看出这些组件的样式和你的app的风格不一致.那么这个时候你可能需要做的就是更改组件的样式.(本人觉得默认样式基本上都比较丑,基本上每个app中都

  • Android四大组件之广播BroadcastReceiver详解

    定义 BroadcastReceiver,"广播接收者"的意思,顾名思义,它就是用来接收来自系统和应用中的广播.在Android系统中,广播体现在方方面面,例如当开机完成后系统会产生一条广播,接收到这条广播就能实现开机启动服务的功能:当网络状态改变时系统会产生一条广播,接收到这条广播就能及时地做出提示和保存数据等操作:当电池电量改变时,系统会产生一条广播,接收到这条广播就能在电量低时告知用户及时保存进度等等.Android中的广播机制设计的非常出色,很多事情原本需要开发者亲自操作的,现

  • Android Fragment滑动组件ViewPager的实例详解

    Android Fragment滑动组件ViewPager的实例详解 1适配器FragmentPagerAdapter的实现 对于FragmentPagerAdapter的派生类,只需要重写getItem(int)和getCount()就可以了. public class MyFragmentPagerAdapter extends FragmentPagerAdapter { private List<Fragment> list; public MyFragmentPagerAdapter

  • Android架构组件Room的使用详解

    Room其实就是一个orm,抽象了SQLite的使用,但是它作为Android的亲儿子orm,并且原生支持LiveData和Rxjava嵌套使用,学习一下还是不错的. Room有3个主要组件 Database :数据库 Entity : 代表数据库一个表结构 Dao : 包含访问数据库的方法 简单使用 添加Google Maven仓库 allprojects { repositories { jcenter() google() } } 添加依赖 dependencies { // Room i

  • vue服务端渲染页面缓存和组件缓存的实例详解

    vue缓存分为页面缓存.组建缓存.接口缓存,这里我主要说到了页面缓存和组建缓存 页面缓存: 在server.js中设置 const LRU = require('lru-cache') const microCache = LRU({ max: 100, // 最大缓存的数目 maxAge: 1000 // 重要提示:条目在 1 秒后过期. }) const isCacheable = req => { //判断是否需要页面缓存 if (req.url && req.url ===

  • Android开发Jetpack组件WorkManager用例详解

    目录 一.简介 二.导入 三.基本使用 3.1 定义后台任务 3.2 配置任务运行条件 3.2.1 只需执行一次的任务 3.2.2 周期性执行的任务 3.3 将任务传给 WorkManager 四.高级配置 4.1 设置任务延迟执行 4.2 给任务添加标签 4.3 取消任务 4.3.1 根据标签取消任务 4.3.2 根据 request 的 id 取消任务 4.3.3 取消所有任务 4.4 任务重试 4.5 监听任务结果 4.6 传递数据 4.7 链式任务 一.简介 WorkManager 用于

  • Android开发Jetpack组件DataBinding用例详解

    目录 简介 使用方式 1. build.gradle 中添加 kapt,并启用dataBinding 2.修改布局文件,添加 layout 和 data 标签 3.使用 DataBindingUtil 绑定布局 4.布局的 data 标签中添加数据变量,并使用其参数 5.BindingAdapter的使用 简介 DataBinding 是 Jetpack 组件之一,适用于 MVVM 模式开发,也是Google官方推荐使用的组件之一.使用DataBinding可以很容易的达到视图与逻辑分离,直接在

随机推荐