序文
サービス(サービス)は、Androidでプログラムのバックグラウンド操作を実現するためのソリューションであり、ユーザーとの対話を必要としない長時間実行タスクの実行に非常に適しています。
キーポイント
- 別のプロセスで実行されるのではなく、サービスが作成されるアプリケーションプロセスに依存します。
- 時間のかかる操作では、サービス内に子スレッドを手動で作成し、ここで特定のタスクを実行する必要があります。そうしないと、メインスレッドがブロックされる可能性があります。
ライフサイクル
サービスには、サービスを開始する3つの方法があります。
- サービスを開始します:startService()はサービスを開き、stopService()はサービスを閉じます
startService()コールバックonCreate-> onStartCommand
stopService()コールバックonDestroy
- サービスのバインド:bindService()はサービスをバインドし、unbindService()はサービスのバインドを解除します。クライアントは、IBinderインターフェースを介してサービスと通信します
bindService()コールバックonCreate-> onBind
unbindService()コールバックonUnBind-> onDestory
- ハイブリッド方式:最初にサービスを開始し、次にサービスをバインドします
startService()コールバックonCreate-> onStartCommand; bindService()コールバックonBind、unbindService()コールバックonUnBind; stopService()コールバックonDestroy
キーポイント:
- サービスが何度開かれたとしても、onCreateメソッドは1回だけ呼び出され、システムはサービスのインスタンスを1つだけ作成します。
- ライフサイクルメソッド全体で、onStartCommandメソッドのみを複数回呼び出すことができ、他のメソッドは1回しか呼び出すことができません。
- onStartCommand()呼び出し= startService()呼び出し
- 複数のクライアントを同じサービスにバインドできます。すべてのクライアントがバインド解除されると、システムはサービスを破棄します
概要:
- startServiceはサービスを開始しますが、サービスを操作できません
- bindServiceはサービスをバインドし、サービスを操作することもできます
サービス分類
- 分割によって作動する場所のローカルサービスとリモートサービス:
ローカルサービス
アプリケーションプログラムで使用され、時間のかかるタスク、最も一般的で一般的に使用されるバックグラウンドサービスサービスを実装します。
インスタンス
MyServiceクラス
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
System.out.println("执行了onCreat()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("执行了onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("执行了onDestory()");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private MyBinder mBinder = new MyBinder();
class MyBinder extends Binder {
public String printServiceInfo() {
return "Activity 和 Service 建立通信,传递数据";
}
}
}
ActivityでIntentオブジェクトを作成し、startService()を呼び出してサービスを開始し、stopServiceを呼び出してサービスを停止し、bindServiceを呼び出してサービスをバインドし、unbindServiceを呼び出してサービスをバインド解除します。
@Override
public void onClick(View view) {
Intent i = new Intent(MainActivity.this, MyService.class);
switch (view.getId()) {
case R.id.btn_start_service:
startService(i);
break;
case R.id.btn_stop_service:
stopService(i);
break;
case R.id.btn_bind_service:
bindService(i, connection, BIND_AUTO_CREATE);
break;
case R.id.btn_unbind_service:
unbindService(connection);
break;
}
}
private MyService.MyBinder myBinder;
//创建ServiceConnection的匿名类
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//实例化Service的内部类myBinder
//通过向下转型得到了MyBinder的实例,Binder实现了IBinder 接口
myBinder = (MyService.MyBinder) iBinder;
//在Activity调用Service类的方法
String info = myBinder.printServiceInfo();
System.out.println("---------->" + info);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
AndroidManifest.xmlにサービスを登録する
<service android:name=".MyService" />
操作の結果は次のとおりです。
リモートサービス
Androidシステム内のアプリケーション間で使用され、天気予報サービスなどの他のアプリケーションで再利用できます。他のアプリケーションは、そのようなサービスを作成する必要はなく、既存のサービスを呼び出すだけです。インターフェイスを定義して、他のアプリケーションが動作できるように公開できます。クライアントはサービスオブジェクトへの接続を確立し、その接続を介してサービスを呼び出します。
ステップ
サーバー側:
- 新しい定義AIDLファイルを作成し、サービスがクライアントに提供する必要のあるインターフェースを宣言します
- AIDLで定義されているインターフェイスメソッドをServiceサブクラスに実装し、ライフサイクルメソッドを定義します
- AndroidMainfest.xmlにサービスを登録し、リモートサービスとして宣言します
サーバー側インスタンス
新しいAIDLファイルを作成し、サービスがアクティビティと通信するために必要なコンテンツ(メソッド)を新しいAIDLファイルで定義し、コンパイルします(プロジェクトの作成)
interface AIDLService {
/**
* //AIDL中支持以下的数据类型
//1. 基本数据类型
//2. String 和CharSequence
//3. List 和 Map ,List和Map 对象的元素必须是AIDL支持的数据类型;
//4. AIDL自动生成的接口(需要导入-import)
//5. 实现android.os.Parcelable 接口的类(需要导入-import)
*/
void aidlService();
}
AIDLで定義されているインターフェイスメソッドをServiceサブクラスに実装し、ライフサイクルメソッドを定義します
public class ServiceDemo extends Service {
public ServiceDemo() {
}
@Override
public IBinder onBind(Intent intent) {
System.out.println("-------->onBind");
return mBinder;
}
AIDLService.Stub mBinder = new AIDLService.Stub() {
@Override
public void aidlService() throws RemoteException {
System.out.println("客户端通过AIDL与远程后台成功通信");
}
};
}
AndroidMainfest.xmlにサービスを登録し、リモートサービスとして宣言します
<service
android:name=".service.ServiceDemo"
android:exported="true" //设置可被其他进程调用
android:process=":remote">//将本地服务设置成远程服务
<intent-filter>
// 此处Intent的action必须写成“服务器端包名.aidl文件名”
<action android:name="com.xf.AIDLService"/>
</intent-filter>
</service>
クライアント:
- サーバーのAIDLファイルをディレクトリにコピーします
- Stub.asInterfaceインターフェイスを使用してサーバーのバインダーを取得し、必要に応じてサービスによって提供されるインターフェイスメソッドを呼び出します
- インテントを介してサーバーのサービス名とパッケージを指定し、リモートサービスをバインドします
クライアントインスタンス
サーバーのAIDLファイルが配置されているパッケージをクライアントディレクトリ(Project / app / src / main)に
コピーし、アクティビティでコンパイルし、Stub.asInterfaceインターフェイスを使用してサーバーのバインダーを取得します。サービスを指定します。インテントパッケージを介したサーバーの名前と場所、サービスバインディングを実行し、必要に応じてサービスによって提供されるインターフェイスメソッドを呼び出します。
// 开启服务
case R.id.btn_remote_service:
Intent intent = new Intent("com.xf.AIDLService");
intent.setPackage("com.xf");
bindService(intent, reConn, BIND_AUTO_CREATE);
break;
// 连接 远程服务
private AIDLService mAidlService;
private ServiceConnection reConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//使用asInterface()方法获取服务器端返回的IBinder对象
//将IBinder对象传换成了AIDLService接口对象
mAidlService = AIDLService.Stub.asInterface(iBinder);
try {
mAidlService.aidlService();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
テスト結果:
上記のテスト結果からわかるように、クライアントはサーバーサービスのメソッドを呼び出しました。つまり、クライアントとサーバーはクロスプロセス通信を実行しました。
ローカルサービスとリモートサービスの比較
- 操作の種類に応じて、フォアグラウンドサービスとバックグラウンドサービスに分けられます
受信
フォアグラウンドサービスとは、ユーザーが頻繁にフォローするサービスを指します。そのため、メモリが少なすぎると、強制終了の対象にはなりません。フォアグラウンドサービスはステータスバー通知を提供する必要があり、「進行中」グループの下に配置されます。つまり、通知は、サービスが終了するか、フォアグラウンドから削除された後にのみ閉じることができます。
インスタンス
ServiceクラスのonCreateメソッドでフォアグラウンドサービスを開きます
@Override
public void onCreate() {
super.onCreate();
System.out.println("------->onCreate");
// 前台服务
String channelID = "com.xf.serviceclientdemo";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelID, "前台服务", NotificationManager.IMPORTANCE_HIGH);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
}
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, channelID)
.setContentTitle("前台服务通知的标题")
.setContentText("前台服务通知的内容")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);//让Service变成前台Service,并在系统的状态栏显示出来
}
フォアグラウンドからサービスを削除するには、stopForeground()メソッドを呼び出します。このメソッドは、ステータスバー通知を同時に削除するかどうかを示すブールパラメータを受け入れます。
試験結果
フロントエンドサービスとバックエンドサービスの比較
サービス関連の知識
- bindService()がサービスバインディングを実行する場合、そのフラグは次のとおりです。
- Context.BIND_AUTO_CREATE
は、バインディングリクエストを受信すると、サービスが作成されていない場合はすぐに作成されることを意味します。システムメモリが不足している場合は、メモリを解放するために優先コンポーネントを最初に破棄する必要があります。サービスに存在するものは破壊されたオブジェクトになり、サービスは破壊されました。 - Context.BIND_DEBUG_UNBIND
は通常、バインドされたサービスが正しいかどうかを判断するためのデバッグシナリオで使用されますが、メモリリークが発生しやすいため、デバッグ以外の目的で使用することはお勧めしません。 - Context.BIND_NOT_FOREGROUND
は、システムがサービスに存在するプロセスがフォアグラウンドの優先順位を持ち、バックグラウンドでのみ実行されるのを防ぐことを示します
- Service要素の属性:
- android:name:サービスクラス名
- android:label:サービスの名前。この項目が設定されていない場合、デフォルトで表示されるサービス名はクラス名です。
- android:icon:サービスのアイコン
- android:permission:このサービスの権限を確認します。つまり、この権限を提供するアプリケーションのみがこのサービスを制御または接続できます
- android:process:サービスが別のプロセスで実行されているかどうかを示します。これが設定されている場合、この文字列がパッケージ名に追加され、別のプロセスの名前が示されます。
- android:enabled:この項目がtrueに設定されている場合、サービスはデフォルトでシステムによって開始され、設定されていない場合、デフォルトの項目はfalseになります。
- android:exported:サービスを他のアプリケーションで制御または接続できるかどうかを示します。設定されていない場合、デフォルトの項目はfalseです。
- onStartCommand()メソッドは整数を返す必要があります。この整数は、サービスを強制終了した後もシステムを実行し続ける方法を示します
- START_STICKY
onStartCommand()が戻った後にシステムがサービスを強制終了すると、サービスが再構築されてonStartCommand()が呼び出されますが、前のインテントは再度送信されませんが、nullインテントでonStartCommand()が呼び出されます。送信されていないサービスを開始するインテントがまだない限り、残りのインテントは引き続き送信されます。これは、コマンドを実行しないが、常に実行中でスタンバイ状態である必要があるメディアプレーヤー(または同様のサービス)に適用されます。
- START_NOT_STICKY
onStartCommand()が戻った後にシステムがサービスを強制終了した場合、未送信のインテントがまだない限り、システムはサービスを再構築しません。サービスが不要になり、アプリケーションが未完了の作業を再開できる場合、これはサービスの実行を回避するための最も安全なオプションです。
- START_REDELIVER_INTENT
onStartCommand()が戻った後にシステムがサービスを強制終了すると、システムはサービスを再構築し、最後に送信されたインテントでonStartCommand()を呼び出します。未送信のインテントも順番に送信されます。これは、ファイルのダウンロードなど、作業をすぐに再開する必要があるアクティブなサービスに適用されます。