Android 四大組件之再論service

来源:http://www.cnblogs.com/deman/archive/2016/10/16/5951236.html
-Advertisement-
Play Games

service常見的有2種方式,本地service以及remote service。 這2種的生命周期,同activity的通信方式等,都不相同。 關於這2種service如何使用,這裡不做介紹,只是介紹一些被遺漏的地方 1.遠程Service(AIDL方式) package com.joyfulm ...


service常見的有2種方式,本地service以及remote service。

這2種的生命周期,同activity的通信方式等,都不相同。

關於這2種service如何使用,這裡不做介紹,只是介紹一些被遺漏的地方

1.遠程Service(AIDL方式)

package com.joyfulmath.samples.basecontrol;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;

import com.joyfulmath.samples.R;
import com.joyfulmath.samples.TraceLog;

import org.androidannotations.annotations.Click;
import org.androidannotations.annotations.EActivity;

/**
 * Created by Administrator on 2016/10/11 0011.
 * service connect activity samples
 */
@EActivity(R.layout.activity_connect_service)
public class ServiceConActivity extends Activity {
    private ISamplesAidlInterface binder;
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            TraceLog.i();
            binder = ISamplesAidlInterface.Stub.asInterface(service);
            if(binder!=null)
            {
                try {
                    binder.registerCallBack(mCallBack);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            TraceLog.i();
            binder = null;
        }
    };

    private ICallBack.Stub mCallBack = new ICallBack.Stub() {
        @Override
        public void onServiceStateChanged(int s) throws RemoteException {
            TraceLog.i(String.valueOf(s));
        }
    };

    public void bindSamplesService()
    {
        TraceLog.i();
        Intent intent = new Intent(this,ServiceSamples.class);
//        intent.setAction("com.joyfulmath.service.samples");
        bindService(intent,connection,BIND_AUTO_CREATE);
    }

    public void unBindSamplesService()
    {
        TraceLog.i();
        if(binder!=null)
        {
            try {
                binder.unRegisterCallBack(mCallBack);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(connection);
    }

    @Click(R.id.btn_connect)
    void connectClick()
    {
        TraceLog.i();
        bindSamplesService();
    }

    @Click(R.id.btn_unconnect)
    void unConnectClick()
    {
        TraceLog.i();
        unBindSamplesService();
    }

    @Click(R.id.btn_do)
    void doAction()
    {
        if(binder!=null)
        {
            try {
                int r = binder.doBackground("action");
                TraceLog.i(String.valueOf(r));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        TraceLog.i();
        unBindSamplesService();
    }
}
ServiceConActivity
package com.joyfulmath.samples.basecontrol;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import com.joyfulmath.samples.TraceLog;

/**
 * Created by Administrator on 2016/10/11 0011.
 */
public class ServiceSamples extends Service {

    private SamplesBinder samplesBinder = null;
    private RemoteCallbackList<ICallBack> mCallbacks = new RemoteCallbackList<>();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        TraceLog.i();
        return samplesBinder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        TraceLog.i();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public boolean onUnbind(Intent intent) {
        TraceLog.i();
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        TraceLog.i();
        mCallbacks.kill();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        samplesBinder = new SamplesBinder();
        TraceLog.i();
    }

    public class SamplesBinder extends ISamplesAidlInterface.Stub{

        @Override
        public int doBackground(String action) throws RemoteException {
            TraceLog.i();
            return -1;
        }

        @Override
        public void findPerson(PersonCall p) throws RemoteException {
            notifyFindPerson();
        }

        @Override
        public void registerCallBack(ICallBack cb) throws RemoteException {
            mCallbacks.register(cb);
        }

        @Override
        public void unRegisterCallBack(ICallBack cb) throws RemoteException {
            mCallbacks.unregister(cb);
        }
    }

    private void notifyFindPerson() throws RemoteException {
        try{
            synchronized (this){
                int n = mCallbacks.beginBroadcast();
                for(int i=0;i<n;i++){
                    mCallbacks.getBroadcastItem(i).onServiceStateChanged(0x11);
                }
                mCallbacks.finishBroadcast();
            }
        }catch (RemoteException e)
        {
            TraceLog.i(e.getMessage());
        }

    }
}
ServiceSamples

這是簡單的service & activity交互的代碼。

在看關鍵的AIDL代碼:

// ISamplesAidlInterface.aidl
package com.joyfulmath.samples.basecontrol;
import com.joyfulmath.samples.basecontrol.PersonCall;
import com.joyfulmath.samples.basecontrol.ICallBack;
// Declare any non-default types here with import statements

interface ISamplesAidlInterface {

    int doBackground(in String action);
    void findPerson(in PersonCall p);
    void registerCallBack(ICallBack cb);
    void unRegisterCallBack(ICallBack cb);
}
// PersonCall.aidl
package com.joyfulmath.samples.basecontrol;
parcelable PersonCall;
// ICallBack.aidl
package com.joyfulmath.samples.basecontrol;

// Declare any non-default types here with import statements

interface ICallBack {
    void onServiceStateChanged(int s);
}

這裡有3個問題,我們從頭往下看,就能明白。

1)為什麼在其他APK調用該service的時候,aidl的文件包必須一致

2)為什麼要自定義PersonCall.aidl

3) ICallBack是什麼玩意。

4)多個APK連接同一個service,該service會產生多個實例嗎。怎麼保證不衝突呢?

其實1) & 2)的問題是一樣的,都是基於java的classloader原理。

同一個類,必須在同一個包內,並且由同一個classloader載入,才能表示他們是同一個類。

所以AIDL在拷貝的時候,必須保證是同一個包名(AIDL在打包的時候會生成java文件。)

並且自定義的參數class,必須有AIDL定義,才能讓其他APK可以理解該類。當然為了傳輸,需要繼承自pracacle

3)關於service回調的工作,是由RemoteCallbackList 專門用來回調通知client端。

首先在client端定義的listener,遠端是沒有實體對象的,所以在作為參數傳入到遠端的時候,會複製一份,並且與binder綁定。

下麵來看看真正的乾貨,第4個問題:

我們分成幾個小問題來解答。

I,如果service和activity不在同一個app,那麼activity可以通過startservice or bindservice的方式啟動該service嗎?如果不行,怎麼啟動該service。

經測試,可以通過bindservice的方式啟動。

II,如果2個client同時對同一個service做bind操作,會有什麼結果?

binderservice都會返回成功操作,並且前一個client,沒有收到disconnect的通知。

此時service的操作,會返回對後面一個client傳遞的參數的操作,也就是只有一份service實例,會同時binder2個client,but只會處理後面一個client的行為。

所以此時,service應該阻止由其他client端輸入的請求,並且可以提供介面給到client,由他決定是否關閉這個binder。

下麵是bindservice的flag參數說明:

常量名含義
BIND_ABOVE_CLIENT 8 如果當綁定服務期間遇到OOM需要殺死進程,客戶進程會先於服務進程被殺死。
BIND_ADJUST_WITH_ACTIVITY 128 允許客戶進程提升被綁定服務進程的優先順序
BIND_ALLOW_OOM_MANAGEMENT 16 如果綁定服務期間遇到OOM需要殺死進程,被綁定的服務進程會被OOM列入獵殺對象中。
BIND_AUTO_CREATE 1 若綁定服務時服務未啟動,則會自動啟動服務。 註意,這種情況下服務的onStartCommand仍然未被調用(它只會在顯式調用startService時才會被調用)。
BIND_DEBUG_UNBIND 2 使用此標誌綁定服務之後的unBindService方法會無效。 這種方法會引起記憶體泄露,只能在調試時使用。
BIND_IMPORTANT 64 被綁定的服務進程優先順序會被提到FOREGROUND級別
BIND_NOT_FOREGROUND 4 被綁定的服務進程優先順序不允許被提到FOREGROUND級別
BIND_WAIVE_PRIORITY 32 被綁定的服務進程不會被OOM列入獵殺對象中。

可以看到,他們是可以組合使用的。

 

如果在第三方APP 使用service

第一步:在java同級目錄下,創建aidl文件夾

第二步:把AIDL文件copy該目錄下,註意保持包名一致。

第三步:把自定義的class,copy到java目錄下,包名一致。

第四步:啟動service需要用顯示的定義(android5.0開始):

    public void bindSamplesService()
    {
        TraceLog.i();
//        ComponentName name = new ComponentName("com.joyfulmath.samples","ServiceSamples");
        Intent intent = new Intent();
//        intent.setComponent(name);
        intent.setAction("com.joyfulmath.service.samples");
        intent.setPackage("com.joyfulmath.samples");
        intent.putExtra("cookie","third");
        bindService(intent,connection,BIND_AUTO_CREATE|BIND_ADJUST_WITH_ACTIVITY);
    }

android 5.1上,親測,該方式可行,使用componentName不行,需進一步研究。

2.startservice

startservice可以跨進程調用,也就是調用其他app的service。

    public void bindSamplesService()
    {
        TraceLog.i();
        Intent intent = new Intent();
        intent.setAction("com.joyfulmath.service.samples");
        intent.setPackage("com.joyfulmath.samples");
        intent.putExtra("cookie","third");
//        bindService(intent,connection,BIND_AUTO_CREATE|BIND_ADJUST_WITH_ACTIVITY);
        startService(intent);
    }

關於startservice,你所不知道的內容如下:

public abstract ComponentName startService(Intent service);

該方法還會返回一個ComponentName ,這個name就是表示package+name,因為classname會重覆。

    * @return If the service is being started or is already running, the
     * {@link ComponentName} of the actual service that was started is
     * returned; else if the service does not exist null is returned.

註釋說的很清楚。

    * <p>This function will throw {@link SecurityException} if you do not
     * have permission to start the given service.

沒有許可權,就會報安全異常。

跨進程啟動service的流程:

如果考慮到進程,那麼我們就應該暫時撇開四大組件的概念。

從操作系統,進程線程的本質來考慮問題。

Activity是生存在一個ActivityThread。它就是一個app(一般對應一個進程)的主線程。

那麼service在哪裡,也在主線程中。可以通過tracelong來認證這個結論。

所以說,service雖然是有獨立生命周期的一大組件,但是它預設還是在主線程中。所以也會ANR。

既然要跨進程,必然也需要binder機制,可能我們看不到而已。

大致流程如下:

從主進程調用到AMS進程(SystemServer進程),創建新的進程。這個過程需要用到binder通信。

從新進程回調AMS,獲取新進程的一些信息。關鍵是這些信息是從源進程傳遞過來+manifest註冊的。

從AMS回到新進程,直到新進程啟動(同時包括service啟動)

這三步都是跨進程啟動service的過程,都需要binder機制來通信。

具體詳細流程,後續會繼續分析。

3.process lifecycle

關於service對應的lifecycle已經在activity那篇里說明瞭。

Android 四大組件之Activity(續2)

4.binder機制

關於這塊之前以及有相關博文,接下來打算再詳細分析下。binder機制是android最重要的基石。

 server 會在通過servermanger註冊它,然後提供遠程調用的句柄,通過binder機制

client獲取servermanger不需要通過binder,應為servermanger是預設的句柄為0,可以直接獲取到。

所以說,servermanager是在等待client端發送請求,然後它去尋找以及註冊的server,得到它的遠程對象,進行通信。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近找工作發現有些公司要求會ReactNative,決定入坑。 搭建環境:官網詳細的教程附鏈接。 坑一:翻牆,建議整個搭建過程中翻牆。第一步:安裝Chocolatey,管理員運行cmd,輸入指令: Chocolatey安裝官網指令:@powershell -NoProfile -ExecutionP ...
  • O.REILLY@ key-> vertical-align: top; 我喜歡折角,我是斷背山? 我真他媽折角了。我槽 key->background-image: linear-gradient(to bottom right, gray 0, gray 90%, transparent 91% ...
  • JavaScript獲取客戶端電腦硬體及系統等信息的方法 JavaScript 獲取客戶端電腦硬體及系統信息 通過WMI來實現獲取客戶端電腦硬體及系統信息: ...
  • JS中總共有六種繼承模式,包括原型鏈、借用構造函數、組合繼承、原型式繼承寄生式繼承和寄生組合式繼承。為了便於理解記憶,我遐想了一個過程,對6中模式進行了簡單的闡述。 很長的一個故事,姑且起個名字叫女媧造人吧。 創建對象 女媧一個一個的捏人(創建對象),這樣太慢,於是設計了一種機器(函數),想造什麼樣 ...
  • 由於課程需要和自身需求以及廣大的LLer的需求,這個學期我將做一個造福全世界LLer的安卓app,他的名字是——還沒想好(喂),總之是個LL SIF的練習器。什麼?你問我LL SIF是什麼?看來你不是真正的LLer看來我需要說明一下。 LL SIF全稱LoveLive School Idol Fes ...
  • UIColor有一個方法叫做+ (UIColor *)colorWithPatternImage:(UIImage *)image;。返回的是一個UIColor,但沒有明確的RGB值,所以叫做pattern color。 [color getRed:&a green:&g blue:&b alpha... ...
  • 首先是要打開Wex5 (這是廢話,下麵進入正題..) 1.第一步,找到界面中的 ”視窗” 點擊打開,你會看到一個 “ 首選項 ”按按照流程也要打開 (囧),,,,,,,,看圖為重 2.當你打開了 “首選項” 會展示出一列樹節,我們要找到樹節中的 “Studio配置” 屬性,然後展開 3.展開 “St ...
  • 在Xcode中使用Git進行源碼版本控制 在應用程式開發過程中,很重要的一部分工作就是如何進行源碼的版本控制。當代碼出現問題時,我們就需要將代碼恢復到原先正常的版本。如果是多個人共同開發一個項目,那麼代碼的控制就會非常複雜。幸運的是,開發者不需要自己控制這些,因為有專門的軟體來負責,叫做版本控制系統 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...