Android面試,與Service交互方式

来源:http://www.cnblogs.com/shouce/archive/2016/04/06/5357736.html
-Advertisement-
Play Games

五種交互方式,分別是:通過廣播交互、通過共用文件交互、通過Messenger(信使)交互、通過自定義介面交互、通過AIDL交互。(可能更多) Service與Thread的區別 Thread:Thread 是程式執行的最小單元,可以用 Thread 來執行一些非同步的操作。 Service:Servi ...


五種交互方式,分別是:通過廣播交互、通過共用文件交互、通過Messenger(信使)交互、通過自定義介面交互、通過AIDL交互。(可能更多)

Service與Thread的區別                                                              

Thread:Thread 是程式執行的最小單元,可以用 Thread 來執行一些非同步的操作。

Service:Service 是android的一種機制,當它運行的時候如果是Local Service,那麼對應的 Service 是運行在主進程的 main 線程上的。如果是Remote Service,那麼對應的 Service 則是運行在獨立進程的 main 線程上。

 

      Thread 的運行是獨立的,也就是說當一個 Activity 被 finish 之後,如果沒有主動停止 Thread 或者 Thread 里的 run 方法沒有執行完畢的話,Thread 也會一直執行。因此這裡會出現一個問題:當 Activity 被 finish 之後,不再持有該 Thread 的引用,也就是不能再控制該Thread。另一方面,沒有辦法在不同的 Activity 中對同一 Thread 進行控制。
      例如:如果 一個Thread 需要每隔一段時間連接伺服器校驗數據,該Thread需要在後臺一直運行。這時候如果創建該Thread的Activity被結束了而該Thread沒有停止,那麼將沒有辦法再控制該Thread,除非kill掉該程式的進程。這時候如果創建並啟動一個 Service ,在 Service 裡面創建、運行並控制該 Thread,這樣便解決了該問題(因為任何 Activity 都可以控制同一個Service,而系統也只會創建一個對應 Service 的實例)。
      因此可以把 Service 想象成一種消息服務,可以在任何有 Context 的地方調用 Context.startService、Context.stopService、Context.bindService、Context.unbindService來控制它,也可以在 Service 里註冊 BroadcastReceiver,通過發送 broadcast 來達到控制的目的,這些都是 Thread 做不到的。

Service的生命周期                                                                   

1.    被啟動的服務(startService())的生命周期。
             如果一個Service被某個Activity 調用Context.startService() 方法啟動,那麼不管是否有Activity使用bindService()綁定或unbindService()解除綁定到該Service,該Service都在後臺運行。如果一個Service被多次執行startService(),它的onCreate()方法只會調用一次,也就是說該Service只會創建一個實例,而它的onStartCommand()將會被調用多次(對應調用startService()的次數)。該Service將會一直在後臺運行,直到被調用stopService(),或自身的stopSelf方法。當然如果系統資源不足,系統也可能結束服務。
2.    被綁定的服務(bindService())的生命周期。
             如果一個Service被調用 Context.bindService ()方法綁定啟動,不管調用bindService()調用幾次,onCreate()方法都只會調用一次,而onStartCommand()方法始終不會被調用,這時會調用onBind()方法。當連接建立之後,Service將會一直運行,除非調用Context.unbindService() 斷開連接或者之前調用bindService() 的 Context 不存在了(如該Activity被finish),系統將會自動停止Service,對應onDestroy()將被調用。
3.    被啟動又被綁定的服務的生命周期。
             如果一個Service又被啟動又被綁定,則該Service將會一直在後臺運行。調用unbindService()將不會停止Service,而必須調用stopService()或Service的stopSelf()方法來停止服務。
4.   當服務被停止時清除服務。
            當一個Service被終止時,Service的onDestroy()方法將會被調用,在這裡應當做一些清除工作,如停止在Service中創建並運行的線程等。

Process的生命周期                                                                   

當Service運行在低記憶體的環境時,系統會kill掉一些進程。因此進程的優先順序將會狠重要:
      1.    如果Service當前正在執行onCreate()、onStartCommand()、onDestroy()方法,那麼此時主進程將會成為前臺進程來保證代碼可以執行完成而避免被kill。
      2.    如果Service已經啟動,那麼主進程將會比其他可見的進程的重要性低,但比其他看不見的進程高。這裡說的可見指的是對用戶來講,可見的進程優先順序永遠是最高的,用戶至上嘛。但只有少部分進程始終是用戶可見的,因此除非系統處於極度低記憶體的時候,不然 service是不會被kill的。
      3.    如果有Client端連到Service,那麼Service永遠比Client端重要。
      4.    Service可以使用startForeground()將Service放到前臺狀態。這樣在低記憶體時被kill的幾率更低,但如果在極低記憶體的情況下,該Service理論上還是會被kill掉。但這個情況基本不用考慮。

廣播交互                                                                              

1提到Activity與Service的交互,可能狠多人首先想到的就是BroadCast——廣播。在Android中,廣播是系統提供的一種很好的交互方式。比如:在電池電量過低,開機完成等情況下,系統都會發出相應的系統廣播,我們的應用程式只需要註冊相應的廣播接收器,就可以接收到這些系統的廣播。同時,我們也可以定義自己的廣播,這樣在不同的Activity、Service以及應用程式之間,就可以通過廣播來實現交互。我們通過模擬應用程式後臺下載的情況來分析Service與Activity的交互方式。

當我們點擊StartService按鈕之後,界面上的進度條將會每隔一秒加1。因為是模擬下載,因此下載動作我們在Service中通過一個Timer定時器來實現,在Timer中對一個整型數據i進行自加(i++),然後Client端獲取Server端的i值並顯示在界面上,從而達到模擬的目的。

1.1. 實現原理

        Server端將目前的下載進度,通過廣播的方式發送出來,Client端註冊此廣播的監聽器,當獲取到該廣播後,將廣播中當前的下載進度解析出來並更新到界面上。

1.2. 實現步驟

        1.2.1 在Client端中通過startService()啟動Service。

if(v == startBtn){  
    Log.i(TAG, "start button clicked...pid: "+Process.myPid());  
    mIntent.setClass(BroadCastService.this, DownLoadService.class);  
    startService(mIntent);  
}

這裡的mIntent = new Intent();Process.myPid()方法可以獲取當前進程的ID號。
       1.2.2 DownLoadService接到啟動的命令之後,執行onCreate()方法,併在其中開啟timer計數模擬下載。

複製代碼
@Override  
public void onCreate() {  
    super.onCreate();  
    Log.i(TAG, "DownLoadService.onCreate()...pid: "+Process.myPid());  
    intent = new Intent("com.seven.broadcast");  
    mTimer = new Timer();  
    mTimer.schedule(new MyTimerTask(), 0 , TIME * 1000);  
}
複製代碼

這裡的intent是Server端向Client端傳送數據用的,使用的action是”com.seven.broadcast”,Client端只有註冊了相應action才能夠接收到Server端的廣播,並解析其中的內容。Process.myPid()是獲取當前進程的ID。
        1.2.3 在Server端的timer計數其中發送廣播,告知Client端目前下載進度。

複製代碼
class MyTimerTask extends TimerTask{  
    @Override  
    public void run() {  
        if(i==100){  
            i=0;  
        }  
        intent.putExtra("CurrentLoading", i);  
        sendBroadcast(intent);  
        i++;  
        Log.e(TAG, "i= "+i);  
    }  
}
複製代碼

通過intent.putExtra(key,value);設置intent的值,然後通過sendBroadcast(intent)方法,將廣播發送出去。

        1.2.4 在Client端通過匿名內部類的方式實例化BroadcastReceiver並覆寫其中的onReceive()方法。

複製代碼
BroadcastReceiver receiver = new BroadcastReceiver() {  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        if(MYACTION.equals(intent.getAction())){  
            Log.i(TAG, "get the broadcast from DownLoadService...");  
            curLoad = intent.getIntExtra("CurrentLoading", ERROR);  
            mHandler.sendMessage(mHandler.obtainMessage());  
        }  
    }  
};
複製代碼

在onReceive()方法中,判斷是否為Server端發送的廣播,如果是則對廣播中攜帶的intent數據進行解包處理。這裡也可以單獨寫一個類繼承自BroadcastReceiver,在其中覆寫onReceive()方法,在Client端中實例化其對象,同樣可以達到相應的效果,這樣做可以為後面實現靜態註冊廣播。
        1.2.5 更新主介面下載進度。

複製代碼
Handler mHandler = new Handler(){  
    @Override  
    public void handleMessage(Message msg) {  
        super.handleMessage(msg);  
        Log.i(TAG, "current loading: "+curLoad);  
        if(curLoad<0||curLoad>100){  
            Log.e(TAG, "ERROR: "+curLoad);  
            return;  
        }  
        mProgressBar.setProgress(curLoad);  
        mTextView.setText(curLoad+"%");  
    }  
};
複製代碼

這裡對獲取到的進度進行了一次判斷,如果獲取到的值沒有異常,那麼將會顯示到界面,並更新進度條的進度,如果異常則返回。
         1.2.6 一定要對Broadcast進行註冊和取消註冊。只有註冊之後相應的broadcast之後才能接收到廣播註冊方法有兩種。
動態註冊/取消註冊:

複製代碼
@Override  
protected void onResume() {  
    super.onResume();  
    Log.i(TAG, "register the broadcast receiver...");  
    IntentFilter filter = new IntentFilter();  
    filter.addAction(MYACTION);  
    registerReceiver(receiver, filter);  
}  
@Override  
protected void onDestroy() {  
    super.onDestroy();  
    Log.i(TAG, "unregister the broadcast receiver...");  
    unregisterReceiver(receiver);  
}
複製代碼

動態註冊可以隨時註冊隨時取消。

靜態註冊:

<receiver android:name="MyBroadcastReceiver">  
    <intent-filter>  
        <action android:name="com.seven.broadcast" />  
    </intent-filter>  
</receiver>

註:這裡的MyBroadcastReceiver是一個繼承自BroadcastReceiver的類。靜態註冊只要註冊了一次那麼只要該程式沒有被卸載那麼該廣播將一直有效。

最後貼出整個AndroidManifest.xml文件

複製代碼
<application android:icon="@drawable/icon" android:label="@string/app_name">  
    <activity android:name=".BroadCastService"  
              android:label="@string/app_name">  
        <intent-filter>  
            <action android:name="android.intent.action.MAIN" />  
            <category android:name="android.intent.category.LAUNCHER" />  
        </intent-filter>  
    </activity>  
    <service android:name="DownLoadService" android:process=":remote"/>  
</application>
複製代碼

這裡的android:process =”:remote”可以使該Service運行在單獨進程中,從而可以模擬跨進程通信。

         1.3 小結

通過廣播的方式實現Activity與Service的交互操作簡單且容易實現,可以勝任簡單級的應用。但缺點也十分明顯,發送廣播受到系統制約。系統會優先發送系統級廣播,在某些特定的情況下,我們自定義的廣播可能會延遲。同時在廣播接收器中不能處理長耗時操作,否則系統會出現ANR即應用程式無響應。

共用文件交互                                                                         

2這裡提到的共用文件指的是Activity和Service使用同一個文件來達到傳遞數據的目的。我們使用SharedPreferences來實現共用,當然也可以使用其它IO方法實現,通過這種方式實現交互時需要註意,對於文件的讀寫的時候,同一時間只能一方讀一方寫,不能兩方同時寫。

2.1 實現原理

         Server端將當前下載進度寫入共用文件中,Client端通過讀取共用文件中的下載進度,並更新到主界面上。

2.2 實現步驟

         2.2.1 在Client端通過startService()啟動Service。

複製代碼
if(startSerBtn==v){  
    Log.i(TAG, "Start Button Clicked.");  
    if(intent!=null){  
    startService(intent);  
    timer.schedule(new MyTimerTask(), 0, TIME * 1000);  
    }  
}
複製代碼

        2.2.2 Server端收到啟動intent之後執行onCreate()方法,並開啟timer,模擬下載,以及初始化SharedPreferences對象preferences。

複製代碼
@Override  
public void onCreate() {  
    super.onCreate();  
    Log.i(TAG, "DownLoadService.onCreate()...");  
    preferences = getSharedPreferences("CurrentLoading_SharedPs", 0);  
    timer = new Timer();  
    timer.schedule(new MyTimerTask(), 0, TIME*1000);  
}
複製代碼

       2.2.3 開始計數並將下載進度寫入shared_prefs文件夾下的xml文件中,內容以鍵值對的方式保存。

複製代碼
class MyTimerTask extends TimerTask{  
    @Override  
    public void run() {  
        setCurrentLoading();  
        if(100==i){  
            i=0;  
        }  
        i++;  
    }         
}     
private void setCurrentLoading() {  
    preferences.edit().putInt("CurrentLoading", i).commit();  
}
複製代碼

對於SharedPreferences的使用需要註意一下幾點:

        首先,使用sharedPreferences前需要獲取文件引用。

preferences = getSharedPreferences("CurrentLoading_SharedPs", 0);

        其次,使用sharedpreferences寫數據方式。

preferences.edit().putInt("CurrentLoading", i).commit();

        最後,讀取數據的方式。

int couLoad = preferences.getInt("CurrentLoading", 0);

        2.2.4 Client端通過讀取/data/data/com.seven.servicetestdemo/shared_prefs文件夾下的xml文件,並取得裡面的鍵值對,從而獲取到當前的下載進度,並更新到主界面上。

複製代碼
Handler mHandler = new Handler(){  
    @Override  
    public void handleMessage(Message msg) {  
        super.handleMessage(msg);  
        int couLoad = preferences.getInt("CurrentLoading", 0);  
        mProgressBar.setProgress(couLoad);  
        currentTv.setText(couLoad+"%");  
    }  
 };
複製代碼

   2.3 小結

        因為方法簡單,因此就不貼出AndroidManifest.xml文件了。對於這種方式實現Activity與Service的交互,可以說很方便,就像使用管道,一個往裡寫,一個往外讀。但這種方式也有缺陷,寫入數據較為複雜以及數據量較大時,就有可能導致寫入與讀數據出不一致的錯誤。同時因為經過了一個中轉站,這種操作將更耗時。

Messenger交互(信使交互)                                                           

3Messenger翻譯過來指的是信使,它引用了一個Handler對象,別人能夠向它發送消息(使用mMessenger.send(Message msg)方法)。該類允許跨進程間基於Message通信,在服務端使用Handler創建一個 Messenger,客戶端只要獲得這個服務端的Messenger對象就可以與服務端通信了。也就是說我們可以把Messenger當做Client端與Server端的傳話筒,這樣就可以溝通交流了。

3.1 實現原理

        在Server端與Client端之間通過一個Messenger對象來傳遞消息,該對象類似於信息中轉站,所有信息通過該對象攜帶。

3.2 Messenger的一般用法

        (1). 在Server端創建信使對象。

               mMessenger = new Messenger(mHandler)

        (2). Client端使用bindService()綁定Server端。

        (3). Server端的onBind()方法返回一個binder對象。

               return mMessenger.getBinder();

        (4). Client端使用返回的binder對象得到Server端信使。

public void onServiceConnected(ComponentName name, IBinder service) {    
              rMessenger = new Messenger(service);        
             ......  
 }

這裡雖然是new了一個Messenger,但我們查看它的實現

public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target);  }

發現它的mTarget是通過AIDL得到的,實際上就是遠程創建的那個。

        (5). Client端可以使用這個Server端的信使對象向Server端發送消息。
               rMessenger.send(msg);

        這樣Server端的Handler對象就能收到消息了,然後可以在其handlerMessage(Message msg)方法中進行處理。經過這5個步驟之後只有Client端向Server端發送消息,這樣的消息傳遞是單向的,那麼如何實現消息的雙向傳遞呢?

        首先需要在第5步做修改,在send(msg)前通過msm.replyTo = mMessenger將Client端自己的信使設置到消息中,這樣Server端接收到消息時同時也得到了Client端的信使對象,然後Server端也可以通過使用得到的Client端的信使對象來項Client端發送消息 cMessenger = msg.replyTo2  cMessenger.send(message);

       這樣即完成了從Server端向Client端發送消息的功能,這樣Client端可以在自己的Handler對象的handlerMessage()方法中接收服務端發送來的message進行處理。

3.3 實現步驟

        3.3.1 創建並初始化Server端的信使對象。

複製代碼
private Handler mHandler = new Handler(){  
    @Override  
    public void handleMessage(Message msg) {  
        super.handleMessage(msg);  
        switch (msg.what) {  
        case TEST:  
            Log.e(TAG, "Get Message from MainActivity.");  
            cMessenger = msg.replyTo;  
            mTimer.schedule(new MyTimerTask(), 1000,TIME * 1000);  
            break;  
            default:  
                break;  
            }  
        }         
};  
//It's the messenger of server  
private Messenger mMessenger = new Messenger(mHandler);
複製代碼

        3.3.2 在Client端使用bindService()方法綁定Server端。

private void doBindService(){  
        Log.i(TAG, "doBindService()...");  
    mIsBind = bindService(intent, serConn, BIND_AUTO_CREATE);//if bind success return true  
        Log.e(TAG, "Is bind: "+mIsBind);  
}

        3.3.3 在Server端的onBind()方法中返回一個binder對象。

@Override  
public IBinder onBind(Intent intent) {  
    Log.i(TAG, "MessengerService.onBind()...");  
    return mMessenger.getBinder();  
}

這裡的mMessenger就是Server端的信使對象。

        3.3.4 Client端使用ServiceConnected()方法來獲取Server端的信使對象。

複製代碼
private ServiceConnection serConn = new ServiceConnection() {     
    @Override  
    public void onServiceDisconnected(ComponentName name) {  
        Log.i(TAG, "onServiceDisconnected()...");  
        rMessenger = null;  
    }         
    @Override  
    public void onServiceConnected(ComponentName name, IBinder service) {  
        Log.i(TAG, "onServiceConnected()...");  
    rMessenger = new Messenger(service);//get the object of remote service  
    mMessenger = new Messenger(mHandler);//initial the object of local service  
    sendMessage();  
    }  
};
複製代碼

獲取Server端的信使對象的同時,也初始化Client端的自己的信使對象,並且通過sendMessage()方法發送消息給Server端,表示可以開始下載了。

        3.3.5 Client端使用獲取到的rMessenger來發送消息給Server端,同時將Client端的信使封裝到消息中,一併發送給Server端。

複製代碼
private void sendMessage() {  
    Message msg = Message.obtain(null, MessengerService.TEST);//MessengerService.TEST=0  
    msg.replyTo = mMessenger;  
    try {  
        rMessenger.send(msg);  
    } catch (RemoteException e) {  
        e.printStackTrace();  
    }  
}
複製代碼

這裡的MessengerService.TEST為Server端里的一個靜態常量。Msg.replyTo=mMessenger;表示發送給Server端的信息里攜帶Client端的信使。

        3.3.6 Server端獲取Client端發送的消息並得到Client端的信使對象。

複製代碼
private Handler mHandler = new Handler(){  
    @Override  
    public void handleMessage(Message msg) {  
        super.handleMessage(msg);  
        switch (msg.what) {  
        case TEST:  
            Log.e(TAG, "Get Message from MainActivity.");  
            cMessenger = msg.replyTo;//get the messenger of client  
            mTimer.schedule(new MyTimerTask(), 1000,TIME * 1000);  
            break;  
        default:  
            break;  
        }  
    }  
};
複製代碼

在接收到Client端的信息之後,Server端開啟timer模擬下載,並接收Client端的信使對象。

        3.3.7 Server端向Client端發送數據。

複製代碼
class MyTimerTask extends TimerTask {  
    @Override  
    public void run() {  
        if (i == 100) {  
            i = 0;  
        }  
        try {  
            //send the message to the client  
        Message message = Message.obtain(null, MessengerService.TEST,i, 0);  
            cMessenger.send(message);  
        } catch (RemoteException e) {  
                e.printStackTrace();  
        }  
            i++;  
    }  
}
複製代碼

直接使用接收到的Client端的信使對象來發送當前下載進度給Client端。

        3.3.8 Client端接收來自Server端的數據。

複製代碼
private Handler mHandler = new Handler(){  
    @Override  
    public void handleMessage(Message msg) {  
        super.handleMessage(msg);  
        switch (msg.what) {  
        case MessengerService.TEST:  
            Log.e(TAG, "Get Message From MessengerService. i= "+msg.arg1);  
            int curLoad = msg.arg1;  
            mTextView.setText(curLoad+"%");  
            mProgressBar.setProgress(curLoad);  
            break;  
        default:  
            break;  
        }  
    }  
};
複製代碼

Client端的接收和Server端的接收狠類似。接收到Server端傳過來的數據之後進行介面更新,以及下載進度更新。

        以下是AndroidManifest.xml文件:

複製代碼
<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
      package="com.seven.messengerservicedemo"  
      android:versionCode="1"  
      android:versionName="1.0">  
    <uses-sdk android:minSdkVersion="10" />  
    <application android:icon="@drawable/icon" android:label="@string/app_name">  
        <activity android:name=".MainActivity"  
                  android:label="@string/app_name">  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  
                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  
    <service android:name="MessengerService">  
        <intent-filter>  
    <action ndroid:name="com.seven.messagerservice.MessengerService" />  
        </intent-filter>  
    </service>  
</application>  
</manifest>
複製代碼

這裡在Service的註冊中加入了過濾動作,只有相匹配的action才能啟動相應的Service。

        3.4 小結

        通過Messenger來實現Activity和Service的交互,稍微深入一點我們就可以知道,其實Messenger也是通過AIDL來實現的。對於前兩種實現方式,Messenger方式總體上來講也是比較容易理解的,這就和平時使用Handler和Thread通信一個道理。

自定義介面交互                                                                       

4何謂自定義介面呢,其實就是我們自己通過介面的實現來達到Activity與Service交互的目的,我們通過在Activity和Service之間架設一座橋樑,從而達到數據交互的目的,而這種實現方式和AIDL非常類似(後文會說到)。

4.1 實現原理

        自定義一個介面,該介面中有一個獲取當前下載進度的空方法。Server端用一個類繼承自Binder並實現該介面,覆寫了其中獲取當前下載進度的方法。Client端通過ServiceConnection獲取到該類的對象,從而能夠使用該獲取當前下載進度的方法,最終實現實時交互。

4.2 實現步驟

        4.2.1 新建一個Interface,併在其中創建一個用於獲取當前下載進度的的空方法getCurrentLoad()。

public interface ICountService {  
    public int getCurrentLoad();  
}

    4.2.2 新建Server端DownService實現ICountService併在其中通過一個內部類ServiceBinder繼承自Binder並實現ICoutService介面。

複製代碼
public class DownLoadService extends Service implements ICountService{  
private ServiceBinder serviceBinder = new ServiceBinder();    
public class ServiceBinder extends Binder implements ICountService{  
    @Override  
    public int getCurrentLoad() {  
        Log.i(TAG, "ServiceBinder getCurrentLoad()... i=:"+i);  
        return i;  
    }     
}  
@Override  
public int getCurrentLoad() {  
    return 0;  
}  
}
複製代碼

在Server端中,實現獲取下載進度的空方法getCurrentLoad();這是Eclipse自動生成的,重點不在這裡。我們需要在ServiceBinder類中覆寫getCurrentLoad()方法,這裡我們返回當前的下載進度i。

       4.2.3 Client端使用bindService()綁定Server端。

if (startSerBtn == v) {  
    Log.i(TAG, "Start Button Clicked.");  
    bindService(intent, serConn, BIND_AUTO_CREATE);  
    timer.schedule(new MyTimerTask(), 1000, TIME * 1000);//這裡一定要延遲一下再開始獲取數據,不然會報空指針異常  
}

在Client端綁定Server端的同時,延遲1s開始獲取下載進度。其中的intent = new Intent(“com.seven.test”);com.seven.test該字元串要與在AndroidManifest.xml中申明的一致

       4.2.4 Server端返回binder對象。

@Override  
public IBinder onBind(Intent intent) {  
    Log.i(TAG, "DownLoadService.onBind()...");  
    return serviceBinder;  
}

這裡的serviceBinder因為繼承了Binder因此也是Binder對象。

        4.2.5 Client端通過ServiceConnection來獲取Server端的binder對象。

複製代碼
private ServiceConnection serConn = new ServiceConnection() {  
@Override  
    public void onServiceDisconnected(ComponentName name) {  
        iCountService = null;  
    }         
    @Override  
    public void onServiceConnected(ComponentName name, IBinder service) {  
        Log.i(TAG, "onServiceConnected()...");  
        iCountService = (ICountService)service;  
    }  
};
複製代碼

獲取的過程是在bindService()過程中完成的,這裡的iCountService是介面ICountService的對象,在這裡得到實例化。

        4.2.6 在綁定完成之後,Server端會開啟下載,在實際情況中Server端會開啟獨立線程用於下載,這裡用i++來代替。

複製代碼
@Override  
public void onCreate() {  
    super.onCreate();  
    Log.i(TAG, "DownLoadService.onCreate()...");  
    timer = new Timer();  
    timer.schedule(new MyTimerTask(), 0, TIME*1000);  
}  
class MyTimerTask extends TimerTask{  
    @Override  
    public void run() {  
        if(

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

-Advertisement-
Play Games
更多相關文章
  • TabHost是Android中自帶的選項卡控制項,效果圖如下: 主佈局文件 核心代碼: 兩個子頁面很簡單,兩個Activity以及對應佈局。 ...
  • import java.util.Random; import javax.crypto.Cipher;import javax.crypto.SecretKey;import javax.crypto.SecretKeyFactory;import javax.crypto.spec.PBEKey ...
  • 之前有兩篇文章關於推送證書配置的,特別是對於新手很有用,可以去看看. http://www.cnblogs.com/wolfhous/p/5344871.html http://www.cnblogs.com/wolfhous/p/5344853.html 開始集成極光推送 1,官網下載sdk以及d ...
  • 沒羽@阿裡移動安全,更多安全類技術乾貨,請訪問阿裡聚安全博客 這是Android mediaserver的提權漏洞,利用CVE-2014-7920和CVE-2014-7921實現提權,從0許可權提到media許可權,其中CVE-2014-7921影響Android 4.0.3及以後的版本,CVE-201 ...
  • 申請加急網址:https://developer.apple.com/appstore/contact/appreviewteam/index.html 補充:加急審核說明是可以寫中文的 提交加急審核需要理由,一般涉及到銀行信息,或者崩潰打不開這種的比較容易通過。反正蘋果很苛刻,一般不給處理。如果處 ...
  • 效果 想必大家對 iPhone 的指紋解鎖功能已經相當的熟悉了。來看看效果吧! Local Authentication 概述 Local Authentication 框架提供了按照指定的安全策略請求用戶授權(本地授權)的工具。例如, 通過 Touch ID 請求用戶授權。 LAContext L ...
  • 一,效果圖。 二,工程圖。 三,代碼。 ViewController.m - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a ni ...
  • 說到遠程推送,應該用的也挺多的,今天就基於SEA的雲推送服務,做一個推送的小demo,來瞭解一下iOS中的遠程推送是怎麼一回事兒,首先你得有蘋果的開發者賬號,好鹹蛋也差不多了,主要內容走起。 一、準備階段 1.在蘋果開發官網上申請推送證書之前需要一個名字叫做certSigningRequest文件。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...