热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:Android服务Service使用总结

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Android服务Service使用总结相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Android服务Service使用总结相关的知识,希望对你有一定的参考价值。



一.Service简介

       Service是android 系统中的四大组件之一(Activity、Service、BroadcastReceiver、 ContentProvider),它跟Activity的级别差不多,但不能页面显示只能后台运行,并且可以和其他组件进行交互。service可以在很多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务总是藏在后台的,例如,一个service可能处理网络 事物,播放音乐,执行文件I/O,或与一个内容提供者交互,所有这些都在后台进行。
       我们一定要知道的是这里Service的后台运行并不是子线程。Service的运行是在主线程中进行的,只是它没有界面显示而已,它的耗时操作同样需要开启子线程,否者会跟Activity一样出现ANR(application not response–程序没有响应)。
       我们要知道的是主线程的内容包括UI和后台。只要程序中的UI或后台其中一个在跑,程序都算是在运行状态。


(一)Service的创建和注册


1.Service服务的创建

必须要实现重写其中的onBind方法,可以在里面做各种操作,也可以接收传递过来的Intent的数据做处理。

public class MyService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
System.out.println("MyService.onBind");
return null;
}
}

2.Service服务的注册,在AndroidManifest中注册

<service android:name&#61;".MyService" />

       服务的注册是四大组件中最简单的一个&#xff0c;一般只要设置name属性就可以了。但是如果有其他需求还是要设置其他的属性值的。

       对Service服务做好创建和注册后&#xff0c;就可以操作服务了。


&#xff08;二&#xff09;Service两种启动模式



Service的启动有两种方式&#xff1a;Context.startService() 和 Context.bindService()。这里的Context是上下文的意思。



1.startService()方式启动时的生命周期回调方法

&#xff08;1&#xff09;启动服务startService &#xff1a; –>onCreate()–> onStart()
&#xff08;2&#xff09;停止服务stopService &#xff1a; –>onDestroy()
       如果调用者直接退出而没有停止Service&#xff0c;则Service 会一直在后台运行。这里的退走只是关闭了UI界面。
       startService()方法启动服务&#xff0c;在服务未被创建时&#xff0c;系统会先调用服务的onCreate()方 法&#xff0c;接着调用onStart()方法。如果调用startService()方法前服务已经被创建&#xff0c;多次调用 startService()方法并不会导致多次创建服务&#xff0c;但会导致多次调用onStart()方法。采用 startService()方法启动的服务&#xff0c;只能调用stopService()方法结束服务&#xff0c;服务结束时 会调用生命周期的onDestroy()方法。


2.bindService()方式启动时的生命周期回调方法

&#xff08;1&#xff09;绑定bindService &#xff1a; –> onCreate() –> onBind()
&#xff08;2&#xff09;解绑unbindService&#xff1a; –>onUnbind()
&#xff08;3&#xff09;正常停止程序服务的方法是先解绑unbindService&#xff0c;再停止服务stopService。
&#xff08;4&#xff09;如果绑定后调用stopService 方法&#xff0c;这时是不能停止服务的&#xff0c;如果这时再调用解绑unbindService&#xff0c;程序后先解绑&#xff0c;后停止服务。
       用bindService()方法启动服务&#xff0c;在服务未被创建时&#xff0c;系统会先调用服务的onCreate() 方法&#xff0c;接着调用onBind()方法。这个时候调用者和服务绑定在一起&#xff0c;调用者退出了&#xff0c;系统就会
先调用服务的onUnbind()方法&#xff0c;接着调用onDestroy()方法。如果调用       bindService()方法前服务已经被绑定&#xff0c;多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说 onCreate()和onBind()方法并不会被多次调用)。如果调用者希望与正在绑定的服务解除绑 定&#xff0c;可以调用unbindService()方法&#xff0c;调用该方法也会导致系统调用服务的onUnbind()->onDestroy()方法。



绑定Service方法&#xff1a;bindService(intent, conn, Service.BIND_AUTO_CREATE);
三个参数的说明&#xff1a;
第一个&#xff1a;Intent对象
第二个&#xff1a;ServiceConnection对象&#xff0c;创建该对象要实现它的onServiceConnected()和 on ServiceDisconnected()来判断连接成功或者是断开连接
第三个&#xff1a;创建Service的模式&#xff0c;一般指定绑定的时候自动创建



&#xff08;三&#xff09;Service的五个生命周期的回调方法



1.周期命名


&#xff08;1&#xff09;onCreate&#xff08;&#xff09;
&#xff08;2&#xff09;onStart&#xff08;&#xff09;
&#xff08;3&#xff09;onBind&#xff08;&#xff09;
&#xff08;4&#xff09;onUnBind&#xff08;&#xff09;
&#xff08;5&#xff09;onDestroy&#xff08;&#xff09;



&#xff12;.生命周期图解

sheng
上面展示的是没有绑定服务和有绑定服务的生命周期的不同情况的过程。


&#xff13;.关于几个方法的说明

&#xff08;1&#xff09;onCreate&#xff08;&#xff09;说明服务第一次被创建
&#xff08;2&#xff09;onStartComand&#xff08;&#xff09;说明服务开始工作
&#xff08;3&#xff09;onBind&#xff08;&#xff09;说明服务已经绑定
&#xff08;4&#xff09;onUnBind&#xff08;&#xff09;说明服务已经解绑
&#xff08;5&#xff09;onDestroy&#xff08;&#xff09;说明服务已经停止
       正如上面说的启动服务有两种方式&#xff0c;一个是使用startService&#xff0c;另一个方法是使用bindService方法&#xff1b;使用bindService方法没有回调到startCommand方法&#xff1b;
       也可以先启动服务用startService&#xff0c;再绑定服务用bindService&#xff0c;这时的Service的回调方法的顺序是&#xff1a;
       –>onCreate()–>onStartCommand()–>onBind()


二&#xff0e;IntentService

       普通的Service要创建一个线程去完成耗时操作&#xff0c;因为其运行在主线程&#xff0c;並且要手動停止IntentService是继承于Service并处理异步请求的一个类&#xff0c;在IntentService内有一个工作线程 来处理耗时操作&#xff0c;启动IntentService的方式和启动传统Service一样&#xff0c;同时&#xff0c;当任务执行完 后&#xff0c;IntentService会自动停止&#xff0c;而不需要我们去手动控制。
       另外&#xff0c;可以启动IntentService多次&#xff0c;而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行&#xff0c;并且&#xff0c;每次只会执行一个工作线程&#xff0c;执行完第一个再执行第二个&#xff0c;以此类推。 而且&#xff0c;所有请求都在一个单线程中&#xff0c;不会阻塞应用程序的主线程&#xff08;UI Thread&#xff09;&#xff0c;同一时间只处理一个请求。
       那么&#xff0c;用IntentService有什么好处呢&#xff1f;
       首先&#xff0c;我们省去了在Service中手动开线程的麻烦&#xff0c;
       第二&#xff0c;当操作完成时&#xff0c;我们不用手动停止Service IntentService&#xff0c;一个方便我们处理业务流程的类&#xff0c;它是一个Service&#xff0c;但是比Service更智能


三&#xff0e;查看Service的生命周期回调方法的简单示例

本示例只是用来看看Service在服务开启时&#xff0c;停止时&#xff0c;绑定时&#xff0c;解绑时&#xff0c;生命周期方法的回调情况加深对Service生命周期的印象。


&#xff08;一&#xff09;创建&#xff2d;yService类&#xff08;继承Service&#xff09;

package com.lwz.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
/**
* 服务的创建&#xff0c;
* 测试生命周期的过程和先后
* 五个生命周期&#xff1a;
* onCreate
* onStartCommand
* onDestroy
* onBind
* onUnBind
*/

public class MyService extends Service {
&#64;Nullable
&#64;Override
public IBinder onBind(Intent intent) {
System.out.println("MyService.onBind");
return null;
}
&#64;Override
public void onCreate() {
System.out.println("MyService.onCreate");
super.onCreate();
}
&#64;Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("MyService.onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
&#64;Override
public void onDestroy() {
System.out.println("MyService.onDestroy");
super.onDestroy();
}
&#64;Override
public boolean onUnbind(Intent intent) {
System.out.println("MyService.onUnbind");
return super.onUnbind(intent);
}
}

&#xff08;二&#xff09;在AndroidManifest中注册服务

<service android:name&#61;".MyService" />

&#xff08;三&#xff09;设计控制服务开启、停止、绑定、解绑状态的代码

package com.lwz.service;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
/**
* 服务的创建和使用
* 注意这里的服务不依赖于Activity页面&#xff0c;即使页面关闭了&#xff0c;服务没有主动去停止&#xff0c;是不会关闭的
* Service也是在主线程中执行任务的&#xff0c;但是为什么不会造成主线程阻塞&#xff1f;&#xff1f;
* 因为做的不是耗时操作&#xff0c;如果做耗时操作一样会造成ANR。。。
* 这里点击绑定服务后&#xff0c;点击停止服务按钮是无效的&#xff0c;要先解绑后&#xff0c;才能停止服务。
* 正常情况下&#xff0c;从绑定状态到解绑状态是不会停止服务的。只是一种状态改变而已。
* 这里点击绑定服务后&#xff0c;点击停止服务按钮是无效的&#xff0c;但是解绑后&#xff0c;会马上停止服务。
*/

public class MainActivity extends AppCompatActivity {
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//开启服务
public void startService(View view) {
//开启服务需要Intent对象,和Activity跳转类似
startService(new Intent(this, MyService.class));
}
//停止服务
public void stopService(View view) {
//停止服务的方法
stopService(new Intent(this, MyService.class));
}
//绑定服务
public void bindService(View view) {
//绑定服务
bindService(new Intent(this, MyService.class), conn, flags);
}
//解绑服务
public void unBindService(View view) {
//防止在没有绑定的情况下&#xff0c;去解除绑定&#xff0c;抛出异常
try {
//解除绑定
unbindService(conn);
} catch (Exception e) {
System.out.println("MainActivity.unBindService" &#43; e);
}
}
//服务绑定的连接对象
private ServiceConnection conn &#61; new ServiceConnection() {
&#64;Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
&#64;Override
public void onServiceDisconnected(ComponentName name) {
}
};
//服务绑定的标识
//BIND_AUTO_CREATE 绑定的同时&#xff0c;启动Service
private int flags &#61; Service.BIND_AUTO_CREATE;
}

上面是使用四个按钮来实现服务的几种状态的改变。


&#xff08;四&#xff09;布局文件的设计


<LinearLayout xmlns:android&#61;"http://schemas.android.com/apk/res/android"
android:id&#61;"&#64;&#43;id/activity_main"
android:layout_width&#61;"match_parent"
android:layout_height&#61;"match_parent"
android:orientation&#61;"vertical">

<TextView
android:layout_width&#61;"wrap_content"
android:layout_height&#61;"wrap_content"
android:layout_gravity&#61;"center_horizontal"
android:text&#61;"Service" />

<Button
android:layout_width&#61;"wrap_content"
android:layout_height&#61;"wrap_content"
android:onClick&#61;"startService"
android:text&#61;"启动服务" />

<Button
android:layout_width&#61;"wrap_content"
android:layout_height&#61;"wrap_content"
android:onClick&#61;"stopService"
android:text&#61;"停止服务" />

<Button
android:layout_width&#61;"wrap_content"
android:layout_height&#61;"wrap_content"
android:onClick&#61;"bindService"
android:text&#61;"绑定服务" />

<Button
android:layout_width&#61;"wrap_content"
android:layout_height&#61;"wrap_content"
android:onClick&#61;"unBindService"
android:text&#61;"解绑服务" />

LinearLayout>

上面布局文件代码比较简单的&#xff0c;只是用四个按钮搞定&#xff01;

程序运行后显示的界面&#xff1a;
s1

点击“启动服务”按钮后的Log信息&#xff1a;
s2
这时有两个回调方法执行。onCreate和onStartCommand.

点击“停止服务”按钮后的Log信息&#xff1a;
s3
执行了一个回调方法&#xff1a;onDestroy&#xff1b;这时服务已经停止&#xff0c;相当于程序刚运行的状态&#xff01;

点击“绑定服务”按钮后的Log信息&#xff1a;
s4
这里执行了两个回调方法&#xff0c;先启动服务&#xff0c;再绑定服务&#xff01;
在绑定服务的情况下是不能停止服务的&#xff0c;要解绑服务才能停止服务。
在程序的服务启动/绑定了的情况下&#xff0c;再点击启动服务&#xff0c;只会回调onStartCommand方法&#xff0c;也就是说一个服务在一个生命周期内只会回调一次onCreate方法。

点击“解绑服务”按钮后显示的Log信息&#xff1a;
s5
执行了两个回调方法&#xff1a;onUnBind和onDestroy

       如果是用服务做简单 的事情&#xff0c;使用第一种方法来启动服务就可以了&#xff0c;但是如果需要涉及到比较复杂的数据处理和操作就用绑定服务的方法来启动服务。


四.IntentService的使用示例

程序设计&#xff1a;查找手机SD卡中的所有图片显示在UI界面上。

&#xff08;一&#xff09;布局文件activity_main.xml文件实际

<RelativeLayout xmlns:android&#61;"http://schemas.android.com/apk/res/android"
android:id&#61;"&#64;&#43;id/activity_main"
android:layout_width&#61;"match_parent"
android:layout_height&#61;"match_parent">

<ListView
android:id&#61;"&#64;&#43;id/main_lv"
android:layout_width&#61;"match_parent"
android:layout_height&#61;"match_parent">
ListView>
RelativeLayout>

非常简单的布局设计&#xff0c;使用List View来显示所有的图片


&#xff08;二&#xff09;遍历文件的工具类的设计

package com.lwz.intentservice;
import android.os.Environment;
import android.os.StatFs;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* SD卡的路径&#xff1a;Environment.getExternalStorageDirectory()
*/

public class FileUtils {
/**
* 获得指定目录下的所有的图片
*/

public static final ArrayList getAllPicture(File dir) {
ArrayList files &#61; getAllFile(dir);
ArrayList imgList &#61; new ArrayList<>();
for (File file : files) {
if (file.getName().endsWith(".png") || file.getName().endsWith(".jpg"))
imgList.add(file);
}
return imgList;
}
/**
* 递归遍历文件夹的方法
*/

public static final void getFileFromDir(File dir, List fileList) {
File[] files &#61; dir.listFiles();
if (files &#61;&#61; null)
return;
for (File file : files) {
if (file.isDirectory())
getFileFromDir(file, fileList);
fileList.add(file);
}
}
/**
* 获得根目录下的所有图片
*/

public static final ArrayList getAllPicture() {
return getAllPicture(Environment.getExternalStorageDirectory());
}
}

&#xff08;三&#xff09;简化BaseAdapter的一个工具类

package com.lwz.intentservice;
import android.content.Context;
import android.widget.BaseAdapter;
import java.util.ArrayList;
import java.util.List;
/**
* 这是一个简化BaseAdapter适配器的工具类
* 这是使用的是定义一个泛型T&#xff0c;使用时传入什么数据&#xff0c;T就是什么数据
* 实际设计中除了getVIew方法外&#xff0c;其他的方法基本是差不多的
* 所以继承这个工具类后只要重写getView方法&#xff0c;就可以使用BaseAdapter了
*/

public abstract class ListItemAdapter<T> extends BaseAdapter {
List list &#61; new ArrayList<>();
Context context;
ListItemAdapter(Context context, List list) {
this.context &#61; context;
this.list &#61; list;
}
ListItemAdapter(Context context, T[] list) {
this.context &#61; context;
for (T t : list) {
this.list.add(t);
}
}
&#64;Override
public int getCount() {
return list &#61;&#61; null ? 0 : list.size();
}
&#64;Override
public T getItem(int position) {
return list &#61;&#61; null ? null : list.get(position);
}
&#64;Override
public long getItemId(int position) {
return position;
}
}

&#xff08;四&#xff09;MyIntentService的创建

package com.lwz.intentservice;
import android.app.IntentService;
import android.content.Intent;
import android.os.Message;
import android.util.Log;
import java.io.File;
import java.util.ArrayList;
/**
* IntentService的使用
* IntentService是Service的子类&#xff0c;也需要在xml中注册
* 它有自定义的子线程的方法
* 这里主要需要解决的问题是资源文件得到后怎么把数据传递给UI线程的Activity
*/

public class MyIntentService extends IntentService {
/**
* 通过构造方法&#xff0c;传入子线程的名字
* 但是这里必须要创建一个无参的构造方法
*/

public MyIntentService() {
super("myService");
}
/**
* 这是在子线程中的执行操作
*/

&#64;Override
protected void onHandleIntent(Intent intent) {
Log.e("TAG", "子线程开始工作");
//遍历文件夹获取图片
ArrayList list &#61; FileUtils.getAllPicture();
//使用handler发送信息
Message msg &#61; Message.obtain();
//这里给handler对象传递一个对象
msg.obj &#61; list;
//发送广播来传递数据
Intent intent1 &#61; new Intent("filefinish");
intent1.putExtra("file", list);
sendBroadcast(intent1);
}
&#64;Override
public void onCreate() {
super.onCreate();
Log.e("TAG", "onCreate");
}
&#64;Override
public void onDestroy() {
super.onDestroy();
Log.e("TAG", "onDestroy");
}
}

这里的Intent Service的注册和Intent的注册是一样的。


&#xff08;五&#xff09;MainActivity中的代码设计

package com.lwz.intentservice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ListView;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* 这里使用服务来IntentService来遍历文件夹
* 在程序创建的使用就要启动服务
* 在页面销毁的时候就停止服务
但是Service执行完任务后还有传递数据给MainActivity
在MainActivity中才能进行UI界面的更新
这就涉及到Service和Activity的数据传递问题了
这里使用的是用广播来传递数据
*/

public class MainActivity extends AppCompatActivity {
//定义布局内的控件
ListView listView;
//定义适配器的数据的集合
//一定要static&#xff1f;&#xff1f;&#xff1f;
static ArrayList fileList;
static MyBaseAdapter adapter;
MyBroadcastReceiver mbcr;
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mbcr &#61; new MyBroadcastReceiver();
//动态注册一个广播
IntentFilter filter &#61; new IntentFilter();
filter.addAction("filefinish");
registerReceiver(mbcr, filter);// 注册
//创建适配器的对象
adapter &#61; new MyBaseAdapter(this, fileList);
//实例化布局内的控件
listView &#61; (ListView) findViewById(R.id.main_lv);
//给listView设置适配器
listView.setAdapter(adapter);
//启动服务
startService(new Intent(this, MyIntentService.class));
}
//创建适配器的类
class MyBaseAdapter extends ListItemAdapter<File> {
MyBaseAdapter(Context context, List list) {
super(context, list);
}
&#64;Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView image &#61; null;
if (convertView &#61;&#61; null) {
image &#61; new ImageView(getBaseContext());
convertView &#61; image;
} else {
image &#61; (ImageView) convertView;
}
//设置图片资源和属性
image.setImageURI(Uri.fromFile(fileList.get(position)));
image.setScaleType(ImageView.ScaleType.FIT_XY);
image.setAdjustViewBounds(true);
return image;
}
}
//停止服务
public void stop() {
stopService(new Intent(MainActivity.this, MyIntentService.class));
}
&#64;Override
protected void onDestroy() {
super.onDestroy();
//即使之前停止了服务&#xff0c;再次停止服务也是不会报错的
stop();
//解除广播
unregisterReceiver(mbcr);
}
//动态创建广播接收者
class MyBroadcastReceiver extends BroadcastReceiver {
&#64;Override
public void onReceive(Context context, Intent intent) {
//对接收到的广播进行处理&#xff0c;intent里面包含数据
fileList &#61; (ArrayList) intent.getSerializableExtra("file");
//刷新适配器
adapter.notifyDataSetChanged();
//停止服务,它的子线程也会停止
stop();
}
}
}

      程序运行前还记得加上SD卡的访问权限&#xff1b;
      上面程序功能还是有点问题&#xff01;遍历完文件后。页面没有马上更新&#xff1f;退出程序再进来&#xff0c;页面上马上显示SD卡的图片。
      一般的说遍历文件夹也不算是耗时操作&#xff0c;这里只是简单示范。
      一般的耗时操作是从网络下载数据&#xff0c;或本地移动大文件等等。


五&#xff0e;同一个程序中Service和Activity通信的一些方式&#xff0c;这里展示主要代码。

这里组件不要忘记在AndroidManifest中注册


&#xff08;一&#xff09;使用Intent来传递数据


1.MainActivity中的代码&#xff0c;发送数据

Intent intent &#61; new Intent(this, MyService.class);
intent.putExtra("msg", "activity向service传递一个hello service");
startService(intent);

2.MyService中的代码&#xff0c;接收数据

      这里要使用onStartCommand的方法来接收Intent的数据&#xff0c;如果上面使用的是bind的方法来启动服务&#xff0c;这里可以在onBind方法中接收数据。

&#64;Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("onStartCommand", intent.getStringExtra(msg));
return super.onStartCommand(intent, flags, startId);
}

&#xff08;二&#xff09;使用单例模式来传递数据

      这个方法算是有点麻烦的吧&#xff01;这里要在MyService类中先做好单例&#xff0c;然后在Activity中调用MyService对象的方法


1.MyService中的代码

//定义一个静态的类变量&#xff0c;单例的使用准备
private static MyService instance;
//静态方法&#xff0c;返回的是一个本类对象
//为了能让另一边的类调用Myservice的方法
public static MyService getInstance() {
return instance;
}
&#64;Override
public void onCreate() {
super.onCreate();
//单例模式变量赋值
instance &#61; this;
}
public void print(String msg) {
Log.e("service", msg);
}

      其中print方法是在Activity中调用的&#xff0c;可以达到传送数据给MyService&#xff0c;但是这里要先启动过服务后才能使用单例&#xff0c;因为这里是在MyService的onCreate方法中把对象赋值给instance&#xff0c;之后才能实现单例。


2.MainActivity中的代码&#xff1a;

/**
* 单例模式传参
*MyService这里通过一个静态方法&#xff0c;来获得MyService的对象
这里通过MyService.getInstance()方法来获得MyService对象
*/

//必须保证Myservice对象不能为null
//静态的变量&#xff0c;最后释放&#xff08;不用的时候&#xff0c;手动将static变量&#61;null&#xff09;
if (MyService.getInstance() !&#61; null) {
MyService.getInstance().print("使用单例从activity中调用service的方法");
}

&#xff08;三&#xff09;广播传参传数据

      弄两个广播接收者相互传数据。
      这里要在MyService和MyService中分别动态的创建广播接收者和动态注册广播接收者&#xff0c;然后在MainActivity中发送广播&#xff0c;在MyService中接收到广播传来递数据后&#xff0c;在发送广播&#xff0c;让MainActivity接收广播数据&#xff01;


1.MyService中的代码&#xff1a;

&#64;Override
public void onCreate() {
super.onCreate();
//动态注册广播接收者,要定义好接收的action属性值
IntentFilter filter &#61; new IntentFilter("service");
registerReceiver(serviceReceiver, filter);
}
//定义一个广播接收者BroadcastReceiver
BroadcastReceiver serviceReceiver &#61; new BroadcastReceiver() {
&#64;Override
public void onReceive(Context context, Intent intent) {
Log.e("service", "接收到了activity发送的广播:" &#43; intent.getStringExtra("msg"));
//发送广播给MainActivity
sendBroadcast(new Intent("activity").putExtra("msg", "发送给activity的消息"));
}
};

2.MainActivity中的代码&#xff1a;

&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注册本类内的广播,定义好action的属性值
IntentFilter filter &#61; new IntentFilter("activity");
registerReceiver(activityReceiver, filter);
}
/**
* 通过广播来传递数据
*/

public void sendBroadcast(View view) {
//指明action属性值
Intent intent &#61; new Intent("service");
intent.putExtra("msg", "activity向广播传递一个hello broadcast");
sendBroadcast(intent);
}
//定义一个内部类的广播接收者&#xff0c;用于接收MyService传递过来的数据
BroadcastReceiver activityReceiver &#61; new BroadcastReceiver() {
&#64;Override
public void onReceive(Context context, Intent intent) {
Log.e("activity", intent.getStringExtra("msg"));
}
};

&#xff08;四&#xff09;MyService实例调用方法

      这个方法是最最麻烦的方法了&#xff01;涉及到一个Binder类的使用&#xff01;这里要被调用的方法其实不是MyService中的方法&#xff0c;而是里面的内部接口的抽象方法&#xff0c;需要在MainActivity中去实现这个方法&#xff01;但是&#xff0c;实际这个方法实在MyService中执行的。


1.MyService中的代码&#xff1a;

//定义一个接口
interface Callback {
//定义两个要实现的方法
void call();
void start();
}
//定义一个接口对象
Callback callback;
/**
* 创建Binder类&#xff0c;很多很多的Service就是通过Binder机制来和客户端通讯交互的。
*/

class Mybinder extends Binder {
public MyService getService() {
return MyService.this;
}
//设置回调方法
public void setCallback(Callback callback) {
MyService.this.callback &#61; callback;
}
}
//定义一个模拟开始音乐播放的方法
//需要重写start里面的方法来开始播放音乐
public void startMusic() {
//播放
Toast.makeText(this, "音乐开始播放", Toast.LENGTH_SHORT).show();
callback.start();
}
&#64;Nullable
&#64;Override
public IBinder onBind(Intent intent) {
//要传递一个MyBinder对象给MainActivity
return new MyBinder();
}
//定义一个模拟开始音乐播放的方法
//需要重写start里面的方法来开始播放音乐
public void startMusic() {
//播放
Toast.makeText(this, "音乐开始播放", Toast.LENGTH_SHORT).show();
//在MainActivity中实例化callback对象
callback.start();
}

      上面的代码中要开始播放音乐要调用startMusic方法&#xff0c;并且要实例化里面的callback对象&#xff0c;而要实例化callback对象必须要调用内部类Mybinder的set Callback方法&#xff0c;而实现这个方法又必须实现这个接口的方法&#xff01;


2.MainActivity中的代码&#xff1a;

/**
* 绑定服务Service
调用MyService的方法来调用内部类的接口方法
*/

//定义一个MyService对象
MyService myService;
public void bindService(View view) {
bindService(new Intent(this, MyService.class), conn, BIND_AUTO_CREATE);
myService.startMusic();
}
//创建ServiceConnection对象
ServiceConnection conn &#61; new ServiceConnection() {
&#64;Override
public void onServiceConnected(ComponentName name, IBinder service) {
//连接服务后&#xff0c;操作。。。
//获取IBinder的bind对象&#xff0c;从MyService的onBinder中传递过来的
MyService.Mybinder bind &#61; (MyService.Mybinder) service;
//通过bind对象获取Service对象
myService &#61; bind.getService();
//设置监听事件的回调方法&#xff0c;并实现里面的两个方法
//这里的回调方法不是MyService中的&#xff0c;而是内部类Mybinder中的
bind.setCallback(new MyService.Callback() {
&#64;Override
public void call() {
Log.e("activity", "Service回调Activity");
}
&#64;Override
public void start() {
//比如在后台播放音乐&#xff1b;开始播放音乐
Log.e("action", "正在播放音乐");
//关闭页面
finish();
}
});

      上面的关系确实是有点乱&#xff0c;我发现我有些注解还是有点问题的&#xff01;&#xff01;
      上面就是Service中的个方面的总结。
      Service还可以用来做进程间的数据传递&#xff0c;这里就涉及到AIDL&#xff08;Android Interface Definition Language&#xff0c;安卓接口定义语言&#xff09;进程通信。这个相对来说比较复杂&#xff0c;另作总结&#xff01;



推荐阅读
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
  • 本文介绍如何在 Android 中自定义加载对话框 CustomProgressDialog,包括自定义 View 类和 XML 布局文件的详细步骤。 ... [详细]
  • 本文介绍了一种自定义的Android圆形进度条视图,支持在进度条上显示数字,并在圆心位置展示文字内容。通过自定义绘图和组件组合的方式实现,详细展示了自定义View的开发流程和关键技术点。示例代码和效果展示将在文章末尾提供。 ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • [转]doc,ppt,xls文件格式转PDF格式http:blog.csdn.netlee353086articledetails7920355确实好用。需要注意的是#import ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 大类|电阻器_使用Requests、Etree、BeautifulSoup、Pandas和Path库进行数据抓取与处理 | 将指定区域内容保存为HTML和Excel格式
    大类|电阻器_使用Requests、Etree、BeautifulSoup、Pandas和Path库进行数据抓取与处理 | 将指定区域内容保存为HTML和Excel格式 ... [详细]
  • 在分析和解决 Keepalived VIP 漂移故障的过程中,我们发现主备节点配置如下:主节点 IP 为 172.16.30.31,备份节点 IP 为 172.16.30.32,虚拟 IP 为 172.16.30.10。故障表现为监控系统显示 Keepalived 主节点状态异常,导致 VIP 漂移到备份节点。通过详细检查配置文件和日志,我们发现主节点上的 Keepalived 进程未能正常运行,最终通过优化配置和重启服务解决了该问题。此外,我们还增加了健康检查机制,以提高系统的稳定性和可靠性。 ... [详细]
  • 本文详细解析了使用C++实现的键盘输入记录程序的源代码,该程序在Windows应用程序开发中具有很高的实用价值。键盘记录功能不仅在远程控制软件中广泛应用,还为开发者提供了强大的调试和监控工具。通过具体实例,本文深入探讨了C++键盘记录程序的设计与实现,适合需要相关技术的开发者参考。 ... [详细]
  • 【问题】在Android开发中,当为EditText添加TextWatcher并实现onTextChanged方法时,会遇到一个问题:即使只对EditText进行一次修改(例如使用删除键删除一个字符),该方法也会被频繁触发。这不仅影响性能,还可能导致逻辑错误。本文将探讨这一问题的原因,并提供有效的解决方案,包括使用Handler或计时器来限制方法的调用频率,以及通过自定义TextWatcher来优化事件处理,从而提高应用的稳定性和用户体验。 ... [详细]
  • 在Android平台中,播放音频的采样率通常固定为44.1kHz,而录音的采样率则固定为8kHz。为了确保音频设备的正常工作,底层驱动必须预先设定这些固定的采样率。当上层应用提供的采样率与这些预设值不匹配时,需要通过重采样(resample)技术来调整采样率,以保证音频数据的正确处理和传输。本文将详细探讨FFMpeg在音频处理中的基础理论及重采样技术的应用。 ... [详细]
  • 在处理 XML 数据时,如果需要解析 `` 标签的内容,可以采用 Pull 解析方法。Pull 解析是一种高效的 XML 解析方式,适用于流式数据处理。具体实现中,可以通过 Java 的 `XmlPullParser` 或其他类似的库来逐步读取和解析 XML 文档中的 `` 元素。这样不仅能够提高解析效率,还能减少内存占用。本文将详细介绍如何使用 Pull 解析方法来提取 `` 标签的内容,并提供一个示例代码,帮助开发者快速解决问题。 ... [详细]
  • 在Android开发中,实现多点触控功能需要使用`OnTouchListener`监听器来捕获触摸事件,并在`onTouch`方法中进行详细的事件处理。为了优化多点触控的交互体验,开发者可以通过识别不同的触摸手势(如缩放、旋转等)并进行相应的逻辑处理。此外,还可以结合`MotionEvent`类提供的方法,如`getPointerCount()`和`getPointerId()`,来精确控制每个触点的行为,从而提升用户操作的流畅性和响应性。 ... [详细]
  • Spring框架的核心组件与架构解析 ... [详细]
author-avatar
33今夜无眠44
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有