天天看點

Android學習-四大元件(Service)

今天複習Service,包括如何啟動并結束一個服務、自定義服務、IntentService、活動與服務之間的通信、前台服務、異步處理以及書上寫的一個無線循環的服務實踐。

根據四大元件的特性,每定義一個新的服務,都要在AndroidManifest裡面進行注冊

<service android:name=".MyService"/>

自定義服務

自定義服務繼承Service,然後可以重寫裡面的方法來實作邏輯

public class MyService extends Service{
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        Log.d("myservice", "onBind執行");
        return null;
    }
}
           

onBind()是父類中的抽象方法,必須實作。之後就是常見的onCreate,onStartCommand,onDestroy了

onCreate可以執行一些初始化的操作,隻有在活動被建立的時候調用

onStartCommand執行主要的邏輯

onDestroy用來收尾

@Override
public void onCreate() {
    // TODO Auto-generated method stub
    Log.d("myservice", "onCreate執行");
    super.onCreate();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // TODO Auto-generated method stub
    Log.d("myservice", "onStartCommand執行");
    return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
    // TODO Auto-generated method stub
    Log.d("myservice", "onDestory執行");
    super.onDestroy();
}
           

啟動和停止服務

在MainActivity中實作按鈕操作即可

case R.id.start_service:
            Intent startService=new Intent(this,MyService.class);
            startService(startService);
            break;
case R.id.stop_service:
            Intent stopService=new Intent(this,MyService.class);
            stopService(stopService);
            break;
           

IntentService

IntentService可以在活動執行一次後自動結束,建立一個IntentService,并在MainActivity中綁定一個按鈕,按鈕點選事件同時列印MainActivity的線程id

public class MyIntentService extends IntentService{

    public MyIntentService() {
        super("MyIntentService");
        // TODO Auto-generated constructor stub
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        // TODO Auto-generated method stub
        Log.d("myservice", "this thread id"+Thread.currentThread().getId());
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        Log.d("myservice", "intentService destroy");
        super.onDestroy();
    }
}
           

MainActivity中的點選事件

case R.id.start_intentservice:
            Intent intentService=new Intent(this,MyIntentService.class);
            Log.d("myservice", "main thread id"+Thread.currentThread().getId());
            startService(intentService);
            break;
           

log

Android學習-四大元件(Service)

服務和活動之間的通信

利用之前建立自定義活動的時候實作的onBind()函數,可以讓服務與活動間進行通信

private MyBinder binder=new MyBinder();//建立MyBinder的對象

    class MyBinder extends Binder{//自定義内部類繼承Binder,在裡面用來實作自己需要的邏輯,比如這裡的下載下傳和傳回下載下傳進度

        public void startDownload() {
            Log.d("myservice", "startDownload");
        }

        public int downloadProgress() {
            Log.d("myservice", "downloadProgress");
            return ;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        Log.d("myservice", "onBind執行");
        return binder;//傳回自定義類對象
    }
           

在活動中,利用ServiceConnection類來執行之前在Service中的自定義類裡的方法

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

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {//綁定後會執行裡面的操作
            // TODO Auto-generated method stub
            mybinder=(MyService.MyBinder)service;//向下轉型
            mybinder.startDownload();
            int i=mybinder.downloadProgress();
            Log.d("myservice", ""+i);
        }
    };
           

然後在按鈕事件中通過bindService和unbindService來綁定和取消綁定服務

case R.id.bind_service:
            Intent bindService=new Intent(this,MyService.class);
            bindService(bindService, connection, BIND_AUTO_CREATE);//标志位,傳入BIND_AUTO_CREATE表示在活動和服務進行綁定後自動建立服務
            break;
        case R.id.unbind_service:
            unbindService(connection);
            break;
           

前台服務

比背景服務多一個正在運作的圖示在狀态欄裡面,可以直接在onCreate裡面寫入一個Notification

@SuppressLint("NewApi")
    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        Notification.Builder nBuilder=new Notification.Builder(this);
        Intent intent=new Intent(this,Activity02.class);
        PendingIntent pIntent=PendingIntent.getActivity(this, , intent, );
        nBuilder.setTicker("前台通知來了")
                .setContentTitle("前台通知")
                .setContentText("這是一個前台通知")
                .setContentIntent(pIntent)
                .setSmallIcon(R.drawable.ic_launcher);
        startForeground(, nBuilder.build());
        Log.d("myservice", "onCreate執行");
        super.onCreate();
    }
           

一個Notification必須具備以下幾個元素,不然不能顯示

1.setSmallIcon

2.setContentText

3.setContentTitle

這樣,服務啟動的時候就可以自動建立一個前台通知一直在通知欄裡

異步消息處理

Android不允許在子線程中進行UI的操作,是以可以通過異步消息處理來進行UI的操作

建立一個CHANGE參數用來識别操作,建立一個Handler用來操作UI

private Handler handler=new Handler(){

        public void handleMessage(Message msg) {
            switch (msg.what) {
            case CHANGE:
                textView.setText("Text02");
                break;

            default:
                break;
            }
        }
    };
           

因為這個是在主線程中的,是以可以對UI進行操作,然後用子線程通過發送Message來讓handler執行對應的操作,進而起到改變UI的效果

case R.id.change_text:
            new Thread(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    Message message=new Message();
                    message.what=CHANGE;
                    handler.sendMessage(message);
                }
            }).start();//别忘了start
            break;
           
start在最後面,有時候容易忽略而忘記啟動線程,這裡要注意一下

背景定時循環操作

整體的實作思路是

自定義服務-onStartCommand裡執行需要的操作(比如列印此時的時間),然後利用AlarmManager來執行定時操作,操作中利用廣播在廣播接收器中再次打開服務

public class AlarmBroadcast extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub
        Intent intent2=new Intent(context,AlarmService.class);
        context.startService(intent2);
    }
}
           
注意這裡是Context

AlarmManager使用方法

1.建立AlarmManager類

2.定義執行的時間

3.AlarmManager.set()執行操作

public class AlarmService extends Service{

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        Log.d("myservice", "Alarm onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                Log.d("myservice", "Time is "+new Date().toString());
            }
        }).start();//記得start
        AlarmManager alarmManager=(AlarmManager) getSystemService(ALARM_SERVICE);
        long triggerTime=SystemClock.elapsedRealtime()+*;//擷取開機以來的毫秒數+30秒
        Intent intent2=new Intent(this,AlarmBroadcast.class);
        PendingIntent pIntent=PendingIntent.getBroadcast(this, , intent2, );//此處應調用getBroadcast
        /*
         * 下面第一個參數表示類型,此處的表示任務執行時間為從開機時間算起之後多少多少毫秒
         * 這裡就是上面的triggerTime
         * 并且喚醒CPU
        */
        alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, pIntent);
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        Log.d("myservice", "Alarm onDestroy");
        super.onDestroy();
    }

}
           

AlarmManager.set()中第一個參數表示執行操作的模式,一共有4中,分别是ELAPSED_REALTIME、ELAPSED_REALTIME_WAKEUP、RTC和RTC_WAKEUP,他們的作用如下

參數名 作用
ELAPSED_REALTIME 定時任務的觸發時間從系統開機開始算起,但不會喚醒CPU
ELAPSED_REALTIME_WAKEUP 定時任務的觸發時間從系統開機開始算起,會喚醒CPU
RTC 定時任務的觸發時間從1970年1月1日0點開始算起,但不會喚醒CPU
RTC_WAKEUP 定時任務的觸發時間從1970年1月1日0點開始算起,會喚醒CPU

第二個參數表示以第一個參數為參考,操作執行的延遲時間,比如這裡第一個參數是ELAPSED_REALTIME_WAKEUP,延遲時間為SystemClock.elapsedRealtime()+30*1000毫秒,SystemClock.elapsedRealtime()用來擷取開機以後到現在為止的毫秒數,是以這裡的意思就是說,以開機時間為起點,操作在(已開機的時間+30秒)後執行

第三個參數就是PendingIntent,即要執行的操作,這裡因為是要調用廣播接收器,是以用的是getBroadcast而不是getActivity

執行結果

Android學習-四大元件(Service)