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存在的問題
- 不會專門啓動一個單獨的進程,Service與他所在應用位於同一個進程中。
- 不是一條新的線程,因此不應該在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" />