时间:2022-03-08 10:17:45 | 栏目:Android代码 | 点击:次
1、服务是实现程序后台运行的解决方案,主要执行那些不需要和用户交互而且还要求长期运行的任务
2、服务是依赖于创建服务时所在的应用程序进程而存在的,而不是运行在一个独立的进程当中
3、服务的后台≠创建子线程,需要在服务的内部手动创建子线程,并且在这里执行具体的任务,否则可能会出现主线程被堵塞的情况
//定义方式 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();
利用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()对消息进行处理,且现在已经是在主线程中处理不会出现问题
1、处理机制组成:Message、Handler、MessageQueue、Looper
2、Message:
3、Handler:
4、MessageQueue:
5、Looper:
6、整个异步流程:
1、AsyncTask的实现原理是基于异步消息处理机制的
2、AsyncTask是一个抽象类,在子类继承这个类时指定3个泛型参数:
public class DownloadTask extends AsyncTask<Void,Integer,Boolean> { .... }
第一个泛型参数指定为Void,表示在执行AsyncTask时不需要传入参数给后台任务
第二个参数指定为Integer,表示使用整型数据来作为进度显示单位
第三个参数指定为Boolean,表示用boolean类型数据来反馈执行结果
3、在继承后,还需重写如下方法
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();
在上述界面中:Exported表示是否允许除了当前程序之外的其他程序访问这个服务;Enabled表示是否启动这个服务
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()方法)
<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>
首先要创建一个活动和两个用于启动服务的按钮,创建完成后通过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"); }//服务被销毁时调用 }
运行结果:
以下载功能为例:在服务中提供一个下载功能,在活动中决定何时开始下载以及随时查看下载进度
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);//解除绑定 } });
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" />
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"/>
完成该服务的注册