天天看點

Android學習筆記重點筆記------探索服務(第一行代碼)一、服務

一、服務

1、服務

服務是Android實作程式背景運作的方案,其不依賴于任何使用者界面

2、Android多線程

(1)線程的啟動方法一:

A:

class MyThread extends Thread{
      public void run(){
        // 這裡輸入處理事件的邏輯
          ··············
       }
 }
//啟動該線程
new MyThread().start();
           

(2)線程啟動的方法二:

B:

class MyThread implements Runnable{
         public void run(){
             // 這裡輸入處理事件的邏輯
             ··············
         }
}
//啟動線程
MyThread myThread = new MyThread();
new Thread(myThread) .start();
           

(3)線程啟動的方法三(常用):

C:

new Thread(new Runnable(){
    public void run(){
    // // 這裡輸入處理事件的邏輯
          ··············
    }).start;
}
           

注意:UI元素必須在主線程中進行,不能在子線程中進行。

這裡我們建立一個工程為AndroidThreadTest的工程來學習:

具體思想為建立一個按鈕和文本顯示内容,在點選事件中建立子線程,用該線程實作文本内容的變化,具體代碼如下:

activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <Button
        android:id="@+id/chang_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="改變文本内容"/>


    <TextView
        android:id="@+id/text"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:text="Hello World!"
        />

</RelativeLayout>
           

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView text;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button changText = (Button)findViewById(R.id.chang_text);
        text = (TextView)findViewById(R.id.text);
        changText.setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.chang_text:
                //開始建立子線程
                new Thread(new Runnable() {
                    @Override
                    //執行線程操作
                    public void run() {
                        Log.d("MainActivity","子線程建立成功,正在運作,下一步,執行UI操作");
                        text.setText("修改界面成功,歡迎看見你");
                    }
                }).start();
        }
    }
}
           

然後運作,點選按鈕後發現程式崩潰了。

Android學習筆記重點筆記------探索服務(第一行代碼)一、服務

檢視錯誤資訊可知,我們在子線程中進行UI操作這是不行的,UI操作應該切換至主線程中去完成。

那麼,我們可以采用異步消息處理機制去解決在子線程中的UI操作。

異步通信機制通過在主線程中運作的handleMessage()方法進行處理子線程的想要完成的UI操作,我們通過消息(data資料)來通知主線程的進行UI顯示。

在AndroidThreadTest的基礎上進行修改。

在MainActivity中,

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView text;
    //定義整形變量,用于更新TextView這個動作
    public static final int UPDATE_TEXT = 1;
    //定義一個Handler對象
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {  //重寫父類的handleMessage()方法
            /*如果接受字段等于UPDATE_TEXT,将修改UI内容*/
            switch (msg.what) {
                case UPDATE_TEXT:
                    Log.d("MainActivity","修改内容...");
                    text.setText("修改界面成功");
                    break;
                default:
                    break;
            }
        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button changText = (Button)findViewById(R.id.chang_text);
        text = (TextView)findViewById(R.id.text);
        changText.setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.chang_text:
                //開始建立子線程
                new Thread(new Runnable() {
                    @Override
                    //執行線程操作
                    public void run() {
                        Log.d("MainActivity","子線程建立成功");
                        //建立一個Message對象,
                        Message message = new Message();
                        //将對象的what字段設定為UPDATE_TEXT
                        message.what = UPDATE_TEXT;
                        //将資訊發送出去,就會調用handler方法去修改文本内容。
                        handler.sendMessage(message);
                        /*Log.d("MainActivity","子線程建立成功,正在運作,下一步,執行UI操作");
                        text.setText("修改界面成功,歡迎看見你");*/
                    }
                }).start();
                break;
                default:
                    break;
        }
    }
}
           

3:異步消息處理機制。

其處理過程主要有四部分組成:(1)Message、(2)Handler、(3)MessageQueue、(4)Looper。

下面我們就來具體介紹:

(1)Message

是線上程間傳遞的消息,其内部攜帶少量資訊,也可采 用 arg1和arg2攜帶整形資料,obj攜帶Object對象。

(2)Handler

用于發送和處理消息,發送消息一般采用Handler的 sendMessage()方法,而消息會最終到達 handleMessage()方法中。

(3)MessageQueue

為消息隊列,用于存放所有通過Handler發送的消息,每個線程隻有一個MessageQueue對象。

(4)Looper

為每個線程中的,隊列MessageQueue管理者。調用Looper的loop()方法後,就會進入一個無限循環,每發現MessageQueue隊列中存在消息,就會将其取出,并傳遞到handleMessage()方法中

4、AsyncTask

是一個需要建立子類去繼承的抽象類,繼承時需指定三個泛行參數,分别為(1)params:AsyncTask執行時需要傳入的參數,可在背景任務中使用。(2)Progress:進度機關,顯示任務進度。(3)Result:對任務結果進行傳回

如:

class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
  ···
}
           

而在該類中我們需要重寫AsyncTask的方法。常用的有四個:

(1)onPreExecute()

背景任務執行前調用,進行界面的初始化操作,比如顯示進度對話框

(2)onInBackground(Params…)

該方法在子線程中完成,用于處理所有的耗時任務,任務完成可使用return語句将執行結果傳回,如果像将耗時任務進度回報給主線程,可調用publishProgress(Params…)方法來完成

(3)onProgressUpdate(Progress…)

當背景任務調用publishProgress()方法後,該方法就會被調用,,該方法中攜帶的參數是背景任務中傳遞出來的,該方法可以對UI進行操作,利用參數中的數值對目前界面元素進行更新。

(4)onPostExecute(Result)

背景任務執行完畢并通過returen傳回後,該方法就會被調用,可利用傳回的資料進行UI操作,比如提醒任務執行的結果以及關閉進度條對話框,

啟動該AsyncTask異步機制的方法為,new +子類+().execute();

二、服務的基本用法

建立服務的方法:

new --------Service ------Service

重寫父類的三個方法:

(1)onCreate():服務建立時調用

(2)onStartCommand():服務啟動時調用

(3)onDestroy():服務銷毀時調用

服務為四大組建需要在AndroidMainifest.xml中注冊。

我們建立一個ServiceTest項目,在java包中建立一個服務為MyService,在activty_main.xml中定義兩個按鈕,用于啟動和停止服務:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <Button
        android:id="@+id/start_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:hint="啟動服務"
         />
    <Button
        android:id="@+id/stop_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="停止服務"
        android:textSize="20dp"/>
</LinearLayout>
           

在主函數中處理兩個按鈕點選事件,通過Context類中的startService()和stopService()對服務進行啟動和停止,如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startService = findViewById(R.id.start_service);
        Button stopService = findViewById(R.id.stop_service);
        startService.setOnClickListener(this);
        stopService.setOnClickListener(this);
    }
    @Override
    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:
                Intent stopIntent = new Intent(this,MyService.class);
                stopService(stopIntent);   //停止服務
                break;
                default:
                    break;
        }
    }
}
           

我們可以在MyService的任意位置調用stopSelf()讓服務停下來。

那麼怎麼讓服務去做什麼呢?完成什麼樣的任務?

     我們需要onBinder()方法内建構我們的任務對象。例如,我們需要讓背景去下載下傳一個檔案,我們需要建構一個Binder對象來下載下傳,修改MyService中的内容:

·······································
private DownloadBinder mbinder = new DownloadBinder();
public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
       return mbinder;
    }    

class DownloadBinder extends Binder {
        public void startDownload(){
            Log.d("TAG","開始下載下傳中...");
        }
        public void stopDownload(){
            Log.d("TAG","停止下載下傳...");
        }
        public int getProgress(){
            Log.d("TAG"," 得到下載下傳進度...");
            return 0;
        }
    }
      ··································
           

當活動與服務綁定後,調用服務裡的Binder提供的方法,在MainActivity中實作:

······························
  private MyService.DownloadBinder downloadBinder;

  private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            downloadBinder = (MyService.DownloadBinder)service;
            downloadBinder.startDownload();
            downloadBinder.getProgress();
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };  

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startService = findViewById(R.id.start_service);
        Button stopService = findViewById(R.id.stop_service);
        Button unbindService = findViewById(R.id.unbind_service);
        Button onbindService = findViewById(R.id.onbind_service);
        startService.setOnClickListener(this);
        stopService.setOnClickListener(this);
        onbindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);
    }
Override
    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:
                Intent stopIntent = new Intent(this,MyService.class);
                stopService(stopIntent);   //停止服務
                break;
            case R.id.onbind_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;
        }
    }
    ·················
           

在oncreate()方法中添加一個背景通知,将MyService程式設計一個前台服務。如下所示:

//服務建立時調用
   public void onCreate(){
        super.onCreate();
        Log.d(TAG,"服務建立中...");
        Intent intent = new Intent(this,MainActivity.class);
        PendingIntent pi = PendingIntent.getActivity(this,0,intent,0);
        Notification notification = new NotificationCompat.Builder(this)
                .setContentTitle("This is content title")
                .setContentText("This is content")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
                .setContentIntent(pi)
                .build();
        startForeground(1,notification);
    }
           

對于耗時操作中需要用到開啟子線程和結束服務的方法,android提供了一個異步會自動停止的服務的類IntentService類。這裡我們建立一個MyIntentService類:

public class MyIntentService extends IntentService {
   public MyIntentService() {
       super("MyIntentService");  //調用父類的有參構造函數
   }
   @Override
   protected void onHandleIntent(Intent intent) {
       //列印目前線程的id
       Log.d("MyIntentService","Thread id is " +Thread.currentThread().getId());
   }
   @Override
   public void onDestroy() {
       super.onDestroy();
       Log.d("MyIntentService","銷毀執行中");
   }
}  
           

MainActivity中添加以下邏輯:

case R.id.start_intent_service:
              Log.d("MainActivity","Thread id is "+Thread.currentThread().getId());
              Intent intent = new Intent(this,MyIntentService.class);
              startService(intent);
              break;
           

運作後發現:

Android學習筆記重點筆記------探索服務(第一行代碼)一、服務

程式運作完成後,自動銷毀。

繼續閱讀