安卓笔记:Service与BroadcastReveiver

Service是四大组件之一。与Activity相似,但Service一直在后台运行,没有用户界面,具有自己的生命周期。

BroadcastReveiver是四大组件之一,就像一个全局事件的事件监听器,用于监听系统发出的Broadcast。通过使用BroadcastReceiver可在不同应用程序之间通信。

1.Service

创建配置Service

1.定义一个继承Service的子类

2.在AndroidManifest.xml中配置该Service

Service 与Activity都是从Context派生出来,都可以调用Context里定义的getResources()\getContentResolver()等方法。

Service中也定义了一些列生命周期。

  • IBinder onBind(Intent intent):该方法是Service子类必须实现的方法。个i啊方法返回一个Ibinder对象,应用程序可通过该对象与Service组件通信。
  • void onCreate():在该Service第一次被创建后将立即回调该方法
  • void onDestory():再改Service被关闭之前会回调该方法
  • void onStartCommand(Intent intent,int flags,int startId):该方法的早期版本是void onStart(Intent intent,Int StartId),每次客户端调用startService(Intent)方法启动该Service时都会调用该方法
  • boolean onUnbind(Intent intent):当该Service上绑定的所有客户端都断开连接时将会返回该方法

配置Service使用<service../>元素可指定如下常用属性

  • name:指定该Service的实现类类名
  • exported:指定该Service是否能被其他APP启动。如果在配置该Service时指定了<intent-filter../>子元素,则该属性默认为true.
  • permission:指定启动该Service所需的权限
  • process:指定该Service所处的进程,该Service组件默认处于该App所在的进程中。

例子:

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">


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


        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

启动和停止Service

代码:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button start = findViewById(R.id.button);
        Button close = findViewById(R.id.button2);
        Intent intent = new Intent(this, FirstService.class);

        //java8支持lamdba
        start.setOnClickListener(view -> startService(intent));
        close.setOnClickListener(view ->stopService(intent));

    }
}

注意:从安卓5.0开始,谷歌要气必须使用显式Intent启动Service组件。

绑定本地Service与之通信(实例)

如果Service和访问者之间需要进行方法调用或数据交换,则应该使用bindService()和unbindService()方法启动、关闭Service。

  • bindService(Intent service,ServiceConnnection conn,int flags)

conn:该参数是一个ServiceConnection对象,用于监听访问者与Service之间的连接情况。

flags:制定绑定是否创建Service(如果Service未创建)。该参数可指定0(不自动创建)或BIND_AUTO_CREATE(自动创建)

实例:

 

Service生命周期

Service的生命周期随Service的启动方式。

 

IntentService

是Service子类。

Service存在的问题

  1. 不会专门启动一个单独的进程,Service与他所在应用位于同一个进程中。
  2. 不是一条新的线程,因此不应该在Service中直接处理耗时的任务。

IntentService弥补了Service的这两个不足,IntentService使用队列来管理请求Intent,每当客户端代码通过Intent请求启动IntentService时,IntentService会将该Intent假如队列中,然后开启一条worker线程来处理该Intent。对于异步的startService()请求,IntentService会按次序依次处理队列中的Intent,该线程保证同一时刻只处理一个Intent。

IntentService具有如下特征:

  • 会创建单独的worker线程来处理所有的Intent请求
  • 会创建单独的worker线程来处理onHandleintent()方法实现的代码,开发者无需处理多线程问题
  • 当所有请求处理完成后,IntentService会自动停止,开发者无须调用stopSelf()方法来停止该Service
  • 为Service的onBind()方法提供了默认实现,默认实现的onBind()方法返回null
  • 为Service的onStartCommand()方法提供了默认实现,该实现会将请求Intent添加到队列中

实例

2.跨进程调用Service(AIDL Service)

简介

为了实现跨进程通信(Interprocess Communication,简称IPC),Android提供了AIDL Service.

AIDL Service与Java中的RMI(远程方法调用)存在一定的相似之处,都是先定义一个远程调用接口,然后为该接口提供一个实现类。

不同点:客户端访问Service时,Android并不是直接返回Service对象给客户端,Service将他的代理对象(IBinder对象)通过onBind()方法返回给客户端。本地Service的onBind()方法会直接把IBinder对象本身传给客户端的ServiceConnection的onServiceConnected方法的第二个参数;远程Service的onBind()方法只是将IBinder对象的代理传给客户端的ServiceConnection的onServiceConnection方法的第二个参数。

当客户端获取了远程Service的IBinder对象的代理之后,接下来可以通过该IBinder对象来回调远程Service的属性和方法了。

创建AIDL文件

Android需要AIDL(Android Interface Definition Language,Android接口定义语言)来定义远程接口。

AIDL定义接口的源代码必须以.aidl结尾

在AIDL接口中用到的数据类型,除基本类型、Spring、List、Map、CharSequence之外,其它类型的全都需要导包(即使在同一个包中也需要导包)

x.aidl

package xxxx.xxxx.xxxx;
interface Icat
{
    Stinrg getColor();
    String getWeight();
}

将接口暴露给客户端

public class AidlService extends Service
{
	private CatBinder catBinder;
	private Timer timer = new Timer();
	private String[] colors = new String[]{"红色", "黄色", "黑色"};
	private double[] weights = new double[]{2.3, 3.1, 1.58};
	private String color;
	private double weight;
	// 继承Stub,也就是实现了ICat接口,并实现了IBinder接口
	class CatBinder extends ICat.Stub
	{
		@Override
		public String getColor()
		{
			return AidlService.this.color;
		}

		@Override
		public double getWeight()
		{
			return AidlService.this.weight;
		}
	}

	@Override public void onCreate()
	{
		super.onCreate();
		catBinder = new CatBinder();
		timer.schedule(new TimerTask()
		{
			@Override public void run()
			{
				// 随机改变Service组件内color、weight属性的值
				int rand = (int) (Math.random() * 3);
				color = colors[rand];
				weight = weights[rand];
			}
		},0, 800);
	}

	@Override public IBinder onBind(Intent intent)
	{
		/* 返回catBinder对象
		 * 在绑定本地Service的情况下,该catBinder对象会直接
		 * 传给客户端的ServiceConnection对象
		 * 的onServiceConnected方法的第二个参数
		 * 在绑定远程Service的情况下,只将catBinder对象的代理
		 * 传给客户端的ServiceConnection对象
		 * 的onServiceConnected方法的第二个参数
		 */
		return catBinder; // ①
	}
	@Override public void onDestroy()
	{
		timer.cancel();
	}
}

配置

		<!-- 定义一个Service组件 -->
		<service android:name=".AidlService" >
			<intent-filter>
				<action android:name="org.crazyit.aidl.action.AIDL_SERVICE" />
			</intent-filter>
		</service>

客户端访问AIDL Service

1.创建SerivceConnection对象

5.以ServiceConnection对象作为参数,调用Context的bindService()方法绑定原创Service

public class MainActivity extends Activity
{
	private ICat catService;
	private ServiceConnection conn = new ServiceConnection()
	{
		@Override public void onServiceConnected(ComponentName name, IBinder service)
		{
			// 获取远程Service的onBind方法返回的对象的代理
			catService = ICat.Stub.asInterface(service);
		}
		@Override public void onServiceDisconnected(ComponentName name)
		{
			catService = null;
		}
	};
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		Button getBn = findViewById(R.id.get);
		TextView colorTv = findViewById(R.id.color);
		TextView weightTv = findViewById(R.id.weight);
		// 创建所需绑定的Service的Intent
		Intent intent = new Intent();
		intent.setAction("org.crazyit.aidl.action.AIDL_SERVICE");
		// 设置要启动的Service所在包,也就是将该Intent变成所谓的显式Intent
		intent.setPackage("org.crazyit.service");
		// 绑定远程Service
		bindService(intent, conn, Service.BIND_AUTO_CREATE);
		getBn.setOnClickListener(view -> {
			// 获取并显示远程Service的状态
			try {
				colorTv.setText("名字:" + catService.getColor());
				weightTv.setText("重量:" + catService.getWeight());
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		});
	}
	@Override public void onDestroy()
	{
		super.onDestroy();
		// 解除绑定
		this.unbindService(conn);
	}
}

3.电话管理器(TelephonyManager)

4.短信管理器(SmsManager)

5.音频管理器(AudioManager)

6.震动器(Vibrator)

vibrate(VibrationEffect vibe):控制手机按VibrationEffect效果执行震动

vibrate(VibrationEffect vibe,AudioAttributes attributes):控制手机按VibrationEffect效果执行震动,并执行AudioAttributes指定的声音效果

cancel():关闭手机震动

7.手机闹钟服务(AlarmManager)

8.广播接收器

简介:

四大组件之一。用于监听系统全局的广播消息。Android8要求启动BroadcastReceiver的Intent必须是显式Intent。

程序启动BroadcastReceiver需要两步

1.创建需要启动的BroadcastReceiver的Intent

2.调用Context的sendBroadcast()或sendOrderedBroadcast()方法来启动制定的BroadcastReceiver

BroadcastReceiver属于系统级的监听器,拥有自己的进程,只要存在与之匹配的Intent被广播出来,BroadcastReceiver就会被激发。

实现BroadcastReceiver:重写BroadcastReceiver的onReceive(Context context,Intent intent)方法

接着指定该BroadcastReceiver能匹配的Intent

1.使用代码进行指定

Intent filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED")
IncomingSMSReceiver receiver = new IncomingSMSReceiver()
registerReceiver(receiver, filter)

2.按AndroidManifest.xml文件中配置

		<receiver android:name=".MyReceiver" android:enabled="true"
			android:exported="false">
			<intent-filter>
				<!-- 指定该BroadcastReceiver所响应的Intent的Action -->
				<action android:name="org.crazyit.action.CRAZY_BROADCAST" />
			</intent-filter>
		</receiver>

在配置<reveiver.../>元素时可指定如下常用属性

  • name
  • exported
  • label
  • permission
  • process

每次系统Broadcast事件发生后,系统都会创建对应的BroadcastReceiver实例,并自动触发他的onReceiver()方法,执行完该方法,BroadcastReceiver实例就会被销毁。

如果BroadcastReceiver的onReceiver()方法不能再10秒内完成,Android会认为该程序无响应,弹出ANR(Application No Response)对话框。

如果需要根据Broadcast完成比较耗时的操作,可以考虑通过Intent启动一个Service来完成,

如果BroadcastReceiver所在的进程结束了,虽然该进程内还有用户启动的新线程,但由于进程内部包含任何活动组件,系统可能在内存紧张时优先结束该进程,导致BroadcastReceiver启动的子线程无法完成,

发送广播

public class MainActivity extends Activity
{
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		// 获取程序界面中的按钮
		Button sendBbn = findViewById(R.id.send);
		sendBbn.setOnClickListener(view -> {
			// 创建Intent对象
			Intent intent = new Intent();
			// 设置Intent的Action属性
			intent.setAction("org.crazyit.action.CRAZY_BROADCAST");
			intent.setPackage("org.crazyit.broadcast");
			intent.putExtra("msg", "简单的消息");
			// 发送广播
			sendBroadcast(intent);
		});
	}
}
public class MyReceiver extends BroadcastReceiver
{
	@Override
	public void onReceive(Context context, Intent intent)
	{
		Toast.makeText(context, "接收到的Intent的Action为:" + intent.getAction()
			+ "\n消息内容是:" + intent.getStringExtra("msg"),
			Toast.LENGTH_LONG).show();
	}
}

配置 

		<receiver android:name=".MyReceiver" android:enabled="true"
			android:exported="false">
			<intent-filter>
				<!-- 指定该BroadcastReceiver所响应的Intent的Action -->
				<action android:name="org.crazyit.action.CRAZY_BROADCAST" />
			</intent-filter>
		</receiver>

有序广播

Broadcast分为Normal Broadcast(普通广播)Ordered Broadcast(有序广播)

Normal Broadcast(普通广播)

是完全异步的,可以在同一时刻(逻辑上)被所有接收者收到,消息的传递效率比较高。但接收者不能将处理结果传递给下一个接收者,并且无法终止Broadcast Intent的传播

Ordered Broadcast(有序广播)

Ordered Broadcast的接收者将按照预先声明的优先级依次接收Broadcast。优先接收到Broadcast的接收者可以通过setResultExtras(Bundle)方法将处理结果存入Broadcast中,然后传给下一个接收者,下一个接收者通过Bundle bundle = getResultExtras(true)获取上一个接收者存入的数据。优先接收到Broadcast的接收者可以调用BroadcastReceiver的abortBroadcast()方法终于Broadcast。后面的广播接收者无法收到。

public class MainActivity extends Activity
{
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		// 获取程序中的send按钮
		Button sendBn = findViewById(R.id.send);
		sendBn.setOnClickListener(view -> {
			// 创建Intent对象
			Intent intent = new Intent();
			intent.setAction("org.crazyit.action.CRAZY_BROADCAST");
			intent.setPackage("org.crazyit.broadcast");
			intent.putExtra("msg", "简单的消息");
			// 发送有序广播
			sendOrderedBroadcast(intent, null);
		});
	}
}
public class MyReceiver1 extends BroadcastReceiver
{
	@Override
	public void onReceive(Context context, Intent intent)
	{
		Toast.makeText(context, "接收到的Intent的Action为:" +
			intent.getAction() + "\n消息内容是:" + intent.getStringExtra("msg"),
			Toast.LENGTH_SHORT).show();
		// 创建一个Bundle对象,并存入数据
		Bundle bundle = new Bundle();
		bundle.putString("first", "第一个BroadcastReceiver存入的消息");
		// 将bundle放入结果中
		setResultExtras(bundle);
		// 取消Broadcast的继续传播
		abortBroadcast(); // ①
	}
}
public class MyReceiver2 extends BroadcastReceiver
{
	@Override
	public void onReceive(Context context, Intent intent)
	{
		Bundle bundle = getResultExtras(true);
		// 解析前一个BroadcastReceiver所存入的key为first的消息
		String first = bundle.getString("first");
		Toast.makeText(context, "第一个Broadcast存入的消息为:"
				+ first, Toast.LENGTH_LONG).show();
	}
}

        <receiver android:name=".MyReceiver1">
			<intent-filter android:priority="20">
				<action android:name="org.crazyit.action.CRAZY_BROADCAST" />
			</intent-filter>
		</receiver>
		<receiver android:name=".MyReceiver2">
			<intent-filter android:priority="0">
				<action android:name="org.crazyit.action.CRAZY_BROADCAST" />
			</intent-filter>
		</receiver>

 

9.接收系统广播消息

Android常见的广播Action常量(参考Android API 关于Intent的说明)

  • ACTION_AIRPLANE_MODE_CHANGED//关闭或打开飞行模式时的广播
  • ACTION_BATTERY_CHANGED//充电状态,或者电池的电量发生变化
  • ACTION_BATTERY_LOW//表示电池电量低
  • ACTION_BATTERY_OKAY//表示电池电量充足,即从电池电量低变化到饱满时会发出广播
  • ACTION_BOOT_COMPLETED//在系统启动完成后,这个动作被广播一次(只有一次)。
  • ACTION_SHUTDOWN//系统被关闭

通过使用BroadcastReceiver来监听特殊的广播,即可让应用随系统执行特定的操作。

实例:开机自动运行的Activity

 

public class LaunchReceiver extends BroadcastReceiver
{
	@Override
	public void onReceive(Context context, Intent intent)
	{
		Intent tIntent = new Intent(context
				, MainActivity.class);
		tIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		// 启动指定Activity
		context.startActivity(tIntent);
	}
}

配置BroadcastReceiver 

		<!-- 定义一个BroadcastReceiver,监听系统开机广播  -->
		<receiver android:name=".LaunchReceiver">
			<intent-filter>
				<action android:name="android.intent.action.BOOT_COMPLETED" />
			</intent-filter>
		</receiver>

增加权限

	<!-- 授予应用程序访问系统开机事件的权限 -->
	<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

实例:手机电量提示

手机电量发生改变时,系统会对外发送Intent的Ation为ACTION_BATTERY_CHANGED常量的广播;电量过低,发送Intent的Ation为ACTION_BATTERY_LOW常量的广播。通过开发对应Intent的BroadcastReceiver,可让系统对手机点亮进行提示。

public class BatteryReceiver extends BroadcastReceiver
{
	@Override
	public void onReceive(Context context, Intent intent)
	{
		Bundle bundle = intent.getExtras();
		// 获取当前电量
		int current = bundle.getInt("level");
		// 获取总电量
		int total = bundle.getInt("scale");
		// 如果当前电量小于总电量的15%
		if (current * 1.0 / total < 0.15)
		{
			Toast.makeText(context, "电量过低,请尽快充电!", Toast.LENGTH_LONG).show();
		}
	}
}

主: 

		IntentFilter batteryfilter = new IntentFilter();
		// 设置该Intent的Action属性
		batteryfilter.addAction(Intent.ACTION_BATTERY_CHANGED);
		// 注册BatteryReceiver
		registerReceiver(new BatteryReceiver(), batteryfilter);

		BatteryManager bm = (BatteryManager) getSystemService(Context.BATTERY_SERVICE);
		// 获取电池的状态
		int st = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS);
		// 获取电池的剩下电量(剩下的百分比)
		int a = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
		// 获取电池的剩下的电量(以纳瓦时为单位)
        //int a = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER);
		// 获取电池的平均电流(以毫安为单位),正值表示正在充电,负值表示正在放电
		int b = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE);
		// 获取电池的瞬时电流(以毫安为单位),正值表示正在充电,负值表示正在放电
		int c = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_NOW);

权限: 

	<!-- 授权应用读取电量信息 -->
	<uses-permission android:name="android.permission.BATTERY_STATS" />

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值