Android Serviceの概要

10959 ワード

なぜサービスを使うのか
ActivityのレビューServiceと言えば先に4人の卵胞兄弟を見に来たのですがContentProviderBroadcastReceiverとあまり関係がないので、卵胞兄弟Serviceというのがポイントですが、昔ながらのルールなのか、それともActivityというのか、コードはread the fucking source codeから.
public class MainActivity extends ActionBarActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...
        startActivity(new Intent(this, TabActivity.class));
    }
}

上のコードはこれ以上よく知られていないので、ついて行ってみましょう.
@Override
public void startActivity(Intent intent) {
    this.startActivity(intent, null);
}

続いていくとインターフェイスに入りますstartActivity
public interface IActivityManager extends IInterface {
    public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags,
            ProfilerInfo profilerInfo, Bundle options) throws RemoteException;
}

実装クラスに入るIActivityManager
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
        String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    data.writeString(callingPackage);
    intent.writeToParcel(data, 0);
    data.writeString(resolvedType);
    data.writeStrongBinder(resultTo);
    data.writeString(resultWho);
    data.writeInt(requestCode);
    data.writeInt(startFlags);
    if (profilerInfo != null) {
        data.writeInt(1);
        profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    } else {
        data.writeInt(0);
    }
    if (options != null) {
        data.writeInt(1);
        options.writeToParcel(data, 0);
    } else {
        data.writeInt(0);
    }
    mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
    reply.readException();
    int result = reply.readInt();
    reply.recycle();
    data.recycle();
    return result;
}

この方法は簡単に分析すると、L 27以前はデータがプロセスをまたぐ前にパッケージング処理を行う必要があり、その後L 27はtransactメソッドを呼び出し、NDKメソッドに入り、最後にreplayから戻り結果を取得する.
サービスに戻る
上にこんなに多くのコードが付いていて、最も重要なのはActivityコンポーネントのジャンプが完全にデカップリングされていることです.もっと直接的に言えば、Contextコンテキストだけでジャンプが完了することです.つまり、どんなシーンでもContextコンテキストがあれば、どのようにジャンプしたいのか、このような良い特性は、残りの4人の卵胞兄弟の中にもこのような特性があるのではないでしょうか.この疑問を持って続けてみましょうActivityManagerNative
public class MainActivity extends ActionBarActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...
        startService(new Intent(this, TabService.class));
    }
}

続いて、入るstartService
@Override
public ComponentName startService(Intent service) {
    return mBase.startService(service);
}

実装クラスに入るContextWrapperクラス
@Override
public ComponentName startService(Intent service) {
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, mUser);
}

1行目はwarning、2行目は続くContextImplエージェントクラス
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, int userId) throws RemoteException
{
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    service.writeToParcel(data, 0);
    data.writeString(resolvedType);
    data.writeInt(userId);
    mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
    reply.readException();
    ComponentName res = ComponentName.readFromParcel(reply);
    data.recycle();
    reply.recycle();
    return res;
}

この方法は全体的に見覚えがあるかどうかは,中の具体的な流れは後述しない.上のソースコードの対比を通じて、サービスとActivityがこのように想像していることを発見しました.では、サービスとActivityがこのように似ている以上、なぜこのように多くのサービスを出して何をしていますか.理由は簡単です.デカップリングのため、シーンをまたがることができます.そして、サービスを使用する理由がたくさんあります.例えば、次のようにします.
      ,                   ,     Service   。  Activity   ,       ,      ,Service       。        Service         Context     

次に、サービスを制御する方法について説明します.前述したように、Contextを取得すればサービスに対してやりたいことをすることができます.以下はインスタンスコードです.
public void onClick(View v) {
    switch (v.getId()) {
    case R.id.start_service:
        Intent startIntent = new Intent(this, MyService.class);
        startService(startIntent);
        break;
    case R.id.stop_service:
        Log.d("MyService", "click Stop Service button");
        Intent stopIntent = new Intent(this, MyService.class);
        stopService(stopIntent);
        break;
    case R.id.bind_service:
        Intent bindIntent = new Intent(this, MyService.class);
        bindService(bindIntent, connection, BIND_AUTO_CREATE);
        break;
    case R.id.unbind_service:
        Log.d("MyService", "click Unbind Service button");
        unbindService(connection);
        break;
    default:
        break;
    }
}

ここで注意しなければならないのは、制御が常にペアで現れていることです.例えば、第1の状況です.
startService stopService
2つ目のケース
bindService unbindService
3つ目のケース
startService bindService unbindService stopService
ServiceとThreadおよびProcessの違い
Android初心者の多くはこのような疑問を持っているかもしれませんが、ServiceとThreadはいったい何の関係があるのでしょうか.いつサービスを使うべきか、いつThreadを使うべきか.答えは少し驚くかもしれませんが、サービスとThreadの間には何の関係もありません.多くの人がそれらを結びつけるのは、主にサービスのバックグラウンド概念のためです.Threadは、サブスレッドを開くために使用されていることを知っています.ここで時間のかかる操作を実行すると、メインスレッドの実行がブロックされません.サービスが私たちが最初に理解したとき、バックグラウンドのタスクを処理するために使われていると思っていました.時間のかかる操作もここで実行できます.これは混同されます.しかし、サービスがメインスレッドで実行されていることを教えてあげたら、Threadと何の関係があると思いますか?
ここでは実験をしないで検証します.興味があれば、自分で検証することができます.だからはっきり言って、サービスはスレッドでもプロセスでもない.彼はAndroidが私に使いやすいコンポーネントを提供してくれただけだ.決して連想しないでください.
ローカルサービス
サービスは機能によってローカルサービスとリモートサービスに分けられます.もちろんフロントサービスとバックグラウンドサービスもありますが、バックグラウンドサービスはおかしくありません.フロントサービスは何ですか.ここでは例を挙げるだけで、深く入り込まないで、例えば墨跡の天気、デフォルトは通知欄の中に表示されて、それではどのようにすることができて、あなたはGoogleは列の子が多くて、私达はローカルとリモートのサービスに戻って、一部の人は連想することができて、実はこのローカルとリモートは相対的なプロセスで、同じプロセスの中でローカルのサービスで、同じプロセスの中でリモートのサービスではありません.異なるプロセス間通信と同じプロセス間通信には大きな違いがあることを知っています.最も率直な表現は複雑さの違いです.まず、ローカルサービスがどのように通信するかを見てみましょう.[参照コード]
public class MyService extends Service {

    public static final String TAG = "MyService";

    private MyBinder mBinder = new MyBinder();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate() executed");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand() executed");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy() executed");
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    class MyBinder extends Binder {

        public void startDownload() {
            Log.d("TAG", "startDownload() executed");
            //          
        }
    }
}

ここでMyBinderクラスはBinderクラスから継承され、startDownload()の公開メソッドがバックグラウンドでダウンロードタスクを実行するために追加されていることに注意してください.このクラスのインスタンスmBinderは、リロードメソッドonBindで戻り値として返されます.このインスタンスはActivityとServiceの間で最も密接な橋渡しです.
public class MainActivity extends Activity implements OnClickListener {
    private Button bindService;
    private Button unbindService;

    private MyService.MyBinder myBinder;
    private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = (MyService.MyBinder) service;
            myBinder.startDownload();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindService = (Button) findViewById(R.id.bind_service);
        unbindService = (Button) findViewById(R.id.unbind_service);
        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.bind_service:
            Intent bindIntent = new Intent(this, MyService.class);
            bindService(bindIntent, connection, BIND_AUTO_CREATE);
            break;
        case R.id.unbind_service:
            unbindService(connection);
            break;
        default:
            break;
        }
    }
}

[ここで参照]
ここではまず、ActivityとServiceの関連付けと関連付け解除の際に呼び出されるonServiceConnected()メソッドとonServiceDisconnected()メソッドを書き換えたServiceConnectionの匿名クラスを作成します.onServiceConnected()メソッドでは,MyBinderのインスタンスを下向きに変換することで得たが,このインスタンスがあればActivityとServiceの関係は非常に緊密になる.
Activityでは、MyBinderの任意のpublicメソッドを特定のシーンに基づいて呼び出すことができます.つまり、Activityがサービスを指揮してサービスを何にするかを指揮する機能を実現します.
もちろん、現在ActivityとServiceは関連付けられていませんが、この機能はBind Serviceボタンのクリックイベントで完成しています.ここでは、まだIntentオブジェクトを構築し、bindService()メソッドを呼び出してActivityとServiceをバインドしていることがわかります.BindService()メソッドは3つのパラメータを受信します.最初のパラメータは構築されたばかりのIntentオブジェクトで、2番目のパラメータは前に作成されたServiceConnectionのインスタンスで、3番目のパラメータはフラグビットで、ここでBIND_AUTO_CREATEは、ActivityとServiceが関連付けられた後に自動的にServiceが作成されることを示し、MyServiceのonCreate()メソッドは実行されますが、onStartCommand()メソッドは実行されません.
そしてどうやってActivityとServiceの関連を解除したいのでしょうか?unbindService()メソッドを呼び出せばいいのですが、これもUnbind Serviceボタンのクリックイベントで実現されるロジックです.
リモートサービス
まず、リモート・サービスの実装方法を比較します.



    ......
    
    
    



上記のプロファイルでは、サービスとAPPが異なるプロセスに属しているはずであり、複雑なのはプロセス間通信であることがわかります.
では、Activityをリモート・サービスに関連付けるにはどうすればいいのでしょうか.これはAIDLを用いてプロセス間通信(IPC)を行うことになる.AIDL(Android Interface Definition Language)は、Androidインタフェース定義言語の意味で、あるサービスと複数のアプリケーションコンポーネントとの間でプロセス間通信を行うことができ、複数のアプリケーションが同じサービスを共有する機能を実現することができる.
次にプロセス間通信の実現方法について詳しく説明するつもりはありません.Android SDKには多くの例が参考になるからです.また、AIDLについて少しも詳しくない場合は、Android Serviceで完全に解析することができます.サービスについて知っておくべきことはすべて(下)この文章で説明します.また、Androidのサービスを全面的にまとめたブログもあります.