热门标签 | 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;



推荐阅读
  • CEPH LIO iSCSI Gateway及其使用参考文档
    本文介绍了CEPH LIO iSCSI Gateway以及使用该网关的参考文档,包括Ceph Block Device、CEPH ISCSI GATEWAY、USING AN ISCSI GATEWAY等。同时提供了多个参考链接,详细介绍了CEPH LIO iSCSI Gateway的配置和使用方法。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 本文介绍了MVP架构模式及其在国庆技术博客中的应用。MVP架构模式是一种演变自MVC架构的新模式,其中View和Model之间的通信通过Presenter进行。相比MVC架构,MVP架构将交互逻辑放在Presenter内部,而View直接从Model中读取数据而不是通过Controller。本文还探讨了MVP架构在国庆技术博客中的具体应用。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
  • 本文介绍了一款名为TimeSelector的Android日期时间选择器,采用了Material Design风格,可以在Android Studio中通过gradle添加依赖来使用,也可以在Eclipse中下载源码使用。文章详细介绍了TimeSelector的构造方法和参数说明,以及如何使用回调函数来处理选取时间后的操作。同时还提供了示例代码和可选的起始时间和结束时间设置。 ... [详细]
  • 使用eclipse创建一个Java项目的步骤
    本文介绍了使用eclipse创建一个Java项目的步骤,包括启动eclipse、选择New Project命令、在对话框中输入项目名称等。同时还介绍了Java Settings对话框中的一些选项,以及如何修改Java程序的输出目录。 ... [详细]
  • 本文详细介绍了Android中的坐标系以及与View相关的方法。首先介绍了Android坐标系和视图坐标系的概念,并通过图示进行了解释。接着提到了View的大小可以超过手机屏幕,并且只有在手机屏幕内才能看到。最后,作者表示将在后续文章中继续探讨与View相关的内容。 ... [详细]
  • 带添加按钮的GridView,item的删除事件
    先上图片效果;gridView无数据时显示添加按钮,有数据时,第一格显示添加按钮,后面显示数据:布局文件:addr_manage.xml<?xmlve ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
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社区 版权所有