android Service詳解(下)

延續上一篇

二、本地Service:

(一)、Started Service啓動MediaPlayer播放音樂:
1、操作步驟:
1、寫xml佈局文件;
2、寫MainActivity文件,通過按鈕點擊事件啓動Service;
3、寫繼承於Service的StartService類文件:重寫onCreate()/onStartCommand()/onDestroy()回調方法。
2、XML佈局文件核心代碼:

<Button
    android:id="@+id/button_main_play"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="clickButton"
    android:text="播放音樂" />

<Button
    android:id="@+id/button_main_pause"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="clickButton"
    android:text="暫停音樂"  />

<Button
    android:id="@+id/button_main_stop"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="clickButton"
    android:text="停止音樂" />

<Button
    android:id="@+id/button_main_exit"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="clickButton"
    android:text="關閉當前窗體" />

<Button
    android:id="@+id/button_main_stopservice"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="clickButton"
    android:text="停止服務" />

`

3、MainActivty核心代碼:

public class MainActivity extends Activity {
       private static final String TAG = "MainActivity";
       private Intent intent = null;


   @Override
   protected void onCreate(Bundle savedInstanceState) {
           Log.i (TAG, "==onCreate執行");
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
           intent = new Intent(this, StartServicePlayMusic.class);
   }

   @Override
   protected void onDestroy() {
           Log.i ("MainActivty", "==onDestroy()");
           super.onDestroy();
           if (intent != null) {
                // 停止服務可以通過stopService(intent)來停止,也可以intent到Service程序中,通過stopSelf()來停止。
                stopService(intent);
           }
   }

   public void clickButton(View view) {
           int type = 0;
           switch (view.getId()) {
              case R.id.button_main_play:
                   type = 1;
                    break;
              case R.id.button_main_pause:
                   type = 2;
                    break;
              case R.id.button_main_stop:
                   type = 3;
                    break;
              case R.id.button_main_exit:
                   finish();
                    break;
              case R.id.button_main_stopservice:
                    // 停止服務可以通過stopService(intent)來停止,也可以intent到Service程序中,通過stopSelf()來停止
                    // stopService(intent);
                    // finish();
                   type = 4;
                    break;
           }


           Bundle bundle = new Bundle();
           bundle.putInt("type", type);
           intent.putExtras(bundle);
           startService(intent);
   }

}

4、StartServicePlayMusic核心代碼:

public class StartServicePlayMusic extends Service {
       private static final String TAG = "StartServicePlayMusic";
       private MediaPlayer mediaPlayer;

   @Override
   public IBinder onBind(Intent intent) {
           Log.i (TAG, "==onBind執行");
           return null;
   }

   @Override
   public void onCreate() {
           Log.i (TAG, "==onCreate執行");
           super.onCreate();
           if (mediaPlayer == null) {
                   mediaPlayer = MediaPlayer.create(this, R.raw.hitta);
                   mediaPlayer.setLooping(false);
           }
   }

   @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
           Log.i (TAG, "==onStartCommand執行");
           if (intent != null) {
                   Bundle bundle = intent.getExtras();
                   int type = bundle.getInt("type");
                   switch (type) {
                   case 1:
                           play();
                           break;
                   case 2:
                           pause();
                           break;
                   case 3:
                           stop();
                           break;
                   case 4:
                           stopSelf();
                           break;
                   }
           }
              return START_STICKY;
      }

   @Override
   public void onDestroy() {
             Log.i (TAG, "==onDestroy執行");
           super.onDestroy();
           if (mediaPlayer != null) {
                   mediaPlayer.stop();
                   mediaPlayer.release();
           }
   }

   public void play() {
           if (!mediaPlayer.isPlaying()) {
                   mediaPlayer.start();
           }
   }

   public void pause() {
           if (mediaPlayer.isPlaying()) {
                   mediaPlayer.pause();
           }
   }

   public void stop() {
           if (mediaPlayer != null) {
                   mediaPlayer.stop();
           }
   }


}

5、Manifest清單配置文件核心代碼:

<service 

            android:name=".StartServicePlayMusic">

            <intent-filter>

                <action android:name="com.steven.startservice.playmusic"/>

            </intent-filter>

        </service>
            android:name=".StartServicePlayMusic">
            <intent-filter>
                <action android:name="com.steven.startservice.playmusic"/>
            </intent-filter>
        </service>

6、Started Service總結:

  • Activity頁面中需要startService(intent) 和 stopService(intent)兩個方法來啓動Service和停止Service;
  • 繼承於Service類的自定義子類——MyStartService類中,生命週期回調方法有:onCreate() 、onStartCommand() 、onDestroy();
  • 如果停止服務,可以在Activity中調用stopService(intent),也可以intent到Service中執行stopSelf()方法;
  • 執行停止服務方法,會回調Service生命週期中的onDestroy()方法;
  • 如果希望關閉Activity窗體,服務也停止,那麼在Activity的onDestroy()方法中執行stopService()方法。如果希望關閉窗體後,服務還繼續,那麼Activity的onDestroy()中不執行停止服務即可;
  • 在StartService中不會回調onBind()方法;
  • 在停止服務後,如果再次點擊“播放”,可以重新啓動StartService。

7、IntentService與Service:

不管是何種Service,它默認都是在應用程序的主線程(亦即UI線程)中運行的。所以,如果你的Service將要運行非常耗時或者可能被阻塞的操作時,你的應用程序將會被掛起,甚至會出現ANR錯誤。爲了避免這一問題,你應該在Service中重新啓動一個新的線程來進行這些操作。現有兩種方法大家參考:
① 直接在Service的onStartCommand()方法中新建一個線程來執行;

② Android SDK 中爲我們提供了一個現成的Service類來實現這個功能,它就是IntentService,它主要負責以下幾個方面:
Creates a default worker thread that executes all intents delivered to onStartCommand() separate from your application’s main thread.
生成一個默認的且與主線程互相獨立的工作者線程來執行所有傳送至 onStartCommand() 方法的Intetnt
Creates a work queue that passes one intent at a time to your onHandleIntent() implementation, so you never have to worry about multi-threading.
生成一個工作隊列來傳送Intent對象給你的onHandleIntent()方法,同一時刻只傳送一個Intent對象,這樣一來,你就不必擔心多線程的問題。
Stops the service after all start requests have been handled, so you never have to call stopSelf().
在所有的請求(Intent)都被執行完以後會自動停止服務,所以,你不需要自己去調用stopSelf()方法來停止該服務
Provides default implementation of onBind() that returns null.
提供了一個onBind()方法的默認實現,它返回null
Provides a default implementation of onStartCommand() that sends the intent to the work queue and then to your onHandleIntent() implementation
提供了一個onStartCommand()方法的默認實現,它將Intent先傳送至工作隊列,然後從工作隊列中每次取出一個傳送至onHandleIntent()方法,在該方法中對Intent對相應的處理
IntentService使用隊列的方式將請求的Intent加入隊列,然後開啓一個worker thread(線程)來處理隊列中的Intent,對於異步的startService請求,IntentService會處理完成一個之後再處理第二個,每一個請求都會在一個單獨的worker thread中處理,不會阻塞應用程序的主線程。
這裏就給我們提供了一個思路,如果有耗時的操作可以在Service裏面開啓新線程,也可以使用IntentService來處理耗時操作。 但你若是想在Service中讓多個線程併發的話,就得使用第一種方法,在Service內部起多個線程,但是這樣的話,你可要處理好線程的同步。

(1)、Service實現加載圖片的核心代碼:

public class DownloadService extends IntentService {
        private static final String TAG = "DownloadService";
        private String urlString = "https://www.google.com.hk/images/srpr/logo11w.png";
        private NotificationCompat.Builder builder = null;
        private NotificationManager manager = null;

     public DownloadService() {
             super("");
     }

    @Override
     protected void onHandleIntent(Intent intent) {
             manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
             builder = new NotificationCompat.Builder(getApplicationContext());
             builder.setSmallIcon(R.drawable.ic_launcher);
             builder.setContentTitle("提示:");
             builder.setContentText("圖片加載完成,請點擊查看!");
             builder.setTicker("圖片加載完成");
             builder.setAutoCancel(true);


             Intent intent2 = new Intent(getApplicationContext(), MainActivity.class);
             intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
             PendingIntent pIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent2,
                             PendingIntent.FLAG_ONE_SHOT);
             builder.setContentIntent(pIntent);

             byte[] data = HttpClientHelper.loadByteFromURL(urlString);
             boolean flag = SDCardHelper.saveFileToSDCard(data, "Download", "logo11w.png");
            
   if (flag) {
                     manager.notify(1, builder.build());
             }
     }


}

(2)、IntentService實現加載圖片的核心代碼:

public class DownloadService extends Service {


        private static final String TAG = "DownloadService";
        private String urlString = "https://www.google.com.hk/images/srpr/logo11w.png";
        private NotificationCompat.Builder builder = null;
        private NotificationManager manager = null;


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

     @Override
     public void onCreate() {
             super.onCreate();
             manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
             builder = new NotificationCompat.Builder(getApplicationContext());
             builder.setSmallIcon(R.drawable.ic_launcher);
             builder.setContentTitle("提示:");
             builder.setContentText("圖片加載完成,請點擊查看!");
             builder.setTicker("圖片加載完成");
             builder.setAutoCancel(true);


             Intent intent = new Intent(getApplicationContext(), MainActivity.class);
             intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
             PendingIntent pIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent,
             PendingIntent.FLAG_ONE_SHOT);


             builder.setContentIntent(pIntent);
     }

     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
             new Thread(new Runnable() {
                    @Override
                     public void run() {
                             byte[] data = HttpClientHelper.loadByteFromURL(urlString);
                             boolean flag = SDCardHelper.saveFileToSDCard(data, "Download","logo11w.png");


                             if (flag) {
                                     manager.notify(1, builder.build());
                                     stopSelf();
                             }
                     }


             }).start();


             return START_STICKY;
     }

     @Override
     public void onDestroy() {
             super.onDestroy();
     }


}

(二)、Bound Service啓動MediaPlayer播放音樂:
1、操作步驟:
1、寫xml佈局文件;
2、寫MainActivity文件,構建ServiceConnection對象,重寫其中的抽象方法onServiceDisconnected()和onServiceConnected();
3、寫繼承於Service的BindService類文件,定義繼承於Binder的內部類MyBinder,在其中定義方法getService();
4、BindService類中重寫onCreate()方法、重寫onBind()回調方法,onBind()方法返回MyBinder對象,重寫onDestroy()方法;

2、XML佈局文件:同上

3、MainActivty核心代碼:

public class MainActivity extends Activity {
  private static final String TAG = "MainActivity";
  private Intent intent;
  private ServiceConnection conn = null;
  private BindServicePlayMusic musicService;

 @Override
  protected void onCreate(Bundle savedInstanceState) {
          Log.i (TAG, "==onCreate執行");
          super.onCreate(savedInstanceState);

          setContentView(R.layout.activity_main);
          // 啓動服務有多種寫法:
          // 可以通過new Intent(action字符串)來實現;
          // intent = new Intent("com.steven.bindservice.playmusic");
          intent = new Intent(this, BindServicePlayMusic.class);
          conn = new ServiceConnection() {
 @Override
                  public void onServiceDisconnected(ComponentName name) {
                            musicService = null;
                  }

                 @Override
                  public void onServiceConnected(ComponentName name, IBinder service) {
                          musicService = ((BindServicePlayMusic.MyBinder) service).getService();
                          if (musicService != null) {
                                  musicService.play();
                          }
                  }
          };
  }

  public void clickButton(View view) {
          switch (view.getId()) {
            case R.id.button_main_play:
                  if (musicService == null) {
                           bindService(intent, conn, Context.BIND_AUTO_CREATE);
                   } else {
                           musicService.play();
                   }
                  break;
            case R.id.button_main_pause:
                  if (musicService != null) {
                          musicService.pause();
                  }
                  break;
            case R.id.button_main_stop:
                  if (musicService != null) {
                          musicService.stop();
                  }
                  break;
            case R.id.button_main_exit:
                  finish();
                  break;
            case R.id.button_main_stopservice:
                  // BindService中stopService(intent)不起作用,要通過undbindService來停止服務
                  // stopService(intent);
                  // musicService = null的目的是如果停止服務後,再次”播放“,可以正常執行。
                  // 如果不將musicService設置爲null,再次播放時,將直接執行musicService.play(),
 //而不執行bindService(),那麼會導致異常
                      musicService = null;
                      unbindService(conn);
                  break;
          }
  }
}

4、BindServicePlayMusic核心代碼:

public class BindServicePlayMusic extends Service {


       private static final String TAG = "BindServicePlayMusic";
       private MediaPlayer mediaPlayer;
       private IBinder binder = null;

     @Override
      public void onCreate() {
              Log.i (TAG, "==onCreate執行");
              super.onCreate();
              if (mediaPlayer == null) {
                      mediaPlayer = MediaPlayer.create(this, R.raw.heavencity);
                      mediaPlayer.setLooping(false);
              }
              binder = new MyBinder();
      }

      class MyBinder extends Binder {
              public BindServicePlayMusic getService() {
                      return BindServicePlayMusic.this;
              }
      };

     @Override
      public IBinder onBind(Intent intent) {
              Log.i (TAG, "==onBind執行");
              play();
              return binder;
      }

     @Override
      public boolean onUnbind(Intent intent) {
              Log.i (TAG, "==onUnbind執行");
              return super.onUnbind(intent);
      }

     @Override
      public void onDestroy() {
              Log.i (TAG, "==onDestroy執行");
              super.onDestroy();
              if (mediaPlayer != null) {
                      mediaPlayer.stop();
                      mediaPlayer.release();
              }
      }

      public void play() {
              if (!mediaPlayer.isPlaying()) {
                      mediaPlayer.start();
              }
      }

      public void pause() {
              if (mediaPlayer.isPlaying()) {
                      mediaPlayer.pause();
              }
      }

      public void stop() {
              if (mediaPlayer != null) {
                      mediaPlayer.stop();
              }
      }
}

5、Manifest清單配置文件核心代碼:

 <service 

            android:name=".BindServicePlayMusic">

            <intent-filter>

                <action android:name=“com.steven.bindservice.playmusic"/>

            </intent-filter>

 </service>


           <service  android:name=".BindServicePlayMusic">
            <intent-filter>

                <action android:name=“com.steven.bindservice.playmusic"/>

            </intent-filter>

        </service>



           <service  android:name=".BindServicePlayMusic">
            <intent-filter>
                <action android:name=“com.steven.bindservice.playmusic"/>
            </intent-filter>
        </service>

三、 拓展知識(進程和生命週期):

Android操作系統嘗試儘可能長時間保持應用的進程,但當可用內存很低時要移走一部分進程。哪些程序可以運行,哪些要被銷燬?答案是:重要級別低的進程可能被淘汰。
按照重要性排列,一共可以分成5級:

1、前臺運行進程:
用戶此時需要處理和顯示的進程。符合下列條件任何一個,這個進程就被認爲是前臺運行進程。
與用戶正發生交互;
它控制一個與用戶交互的必須的基本的服務;
一個正在調用生命週期回調方法的ervice(如onCreate()、onStar()、onDestroy());
一個正在運行onReceive()方法的廣播接收對象。
銷燬前臺運行進程是系統萬不得已的、最後的選擇——當內存不夠系統繼續運行下去時,殺掉一些前臺進程來保證能夠響應用戶的需求。
2、可用進程:
一個可用進程沒有任何前臺組件,但它仍然可以影響到用戶的界面。下面情況發生時,可以稱該進程爲可用進程。
它是一個非前臺的activity,但對用戶仍然可用(onPause()方法已經被調用)。例如:前臺的activity是一個允許上一個activity可見的對話框。也就是說當前activity中是一個對話框,對話框之外的地方能看到前一個activity的界面。

3、服務進程:
服務進程是一個通過調用startService()方法啓動的服務,並且不屬於前兩種情況。儘管服務進程沒有直接被用戶看到,但他們確實是用戶所關心的,比如後臺播放音樂或網絡下載數據,所以系統保證他們的運行。

4、後臺進程:
一個後臺進程就是非當前正在運行的activity(activity的onStop()方法已經被調用),他們不會對用戶體驗造成直接的影響,當沒有足夠內存來運行前臺可見程序時,他們將會被終止。
通常,後臺進程會有很多個在運行,LRU最近使用程序列表來保證經常運行的activity能最後一個被終止。

5、空線程:
一個空線程沒有運行任何可用應用程序,保留他們的唯一原因是爲了設立一個緩存機制,來加快組件啓動的時間。系統經常殺死這些內存來平衡系統的整個系統的資源,進程緩存和基本核心緩存之間的資源。

四、AIDL實現掛斷電話:

(一)、核心代碼:

public class InCallReceiver extends BroadcastReceiver {
       private TelephonyManager manager = null;

       @Override
       public void onReceive(Context context, Intent intent) {
               manager = (TelephonyManager) context .getSystemService(Service.TELEPHONY_SERVICE);
              
       switch (manager.getCallState()) {
                case TelephonyManager.CALL_STATE_RINGING:
                       String incomingNumber = intent.getStringExtra("incoming_number");
                       if ("12345678".equals(incomingNumber)) {
                               try {
                                    // 獲得TelephonyManager的class對象
                                       Class<TelephonyManager> telephonyManagerClass = TelephonyManager.class;
                                    // 獲得TelephonyManager.getITelephony方法的Method對象
                                       Method telephonyMethod = telephonyManagerClass
                                                       .getDeclaredMethod("getITelephony", (Class[]) null);
                                    // 允許訪問私有的方法
                                       telephonyMethod.setAccessible(true);
                                    // 調用getITelephony()方法返回ITelephony對象
                        ITelephony telephony = (com.android.internal.telephony.ITelephony) telephonyMethod
                                                       .invoke(manager, (Object[]) null);
                                    // 掛斷電話
                                       telephony.endCall();
                               } catch (Exception e) {
                                       Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
                               }
                       }
                    break;
               }
       }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章