1. 程式人生 > 其它 >ios如何快速轉型安卓開發-專題3

ios如何快速轉型安卓開發-專題3

1.Fragment

可以嵌入活動當中的UI片段。

1.碎片的通訊

FragmentManager提供一個finViewById,專門從佈局檔案中獲取碎片的例項

2.碎片的生命週期

1.狀態
(1)執行狀態:可見
(2)暫停狀態:被覆蓋但部分可見
(3)停止狀態:完全不可見
(4)銷燬狀態:被移除
2.回撥
(1)onAttach()。碎片和活動建立關聯。
(2)onCreateView()。碎片建立檢視時呼叫。
(3)onActivityCreated()。確保與碎片相關聯的活動一定已經建立完畢的時候呼叫。
(4)onDestory()。當與碎片關聯的檢視被移除的時候呼叫。
(5)onDetach()。當碎片與活動解除關聯的時候呼叫。

3.動態載入佈局

使用限定符匹配對應的平板和手機情況。

4.系統通知
點選檢視程式碼
package com.example.notificationtest;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

  final String CHANNEL_ID = "channel_id_1";
  final String CHANNEL_NAME = "channel_name_1";
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button sendNotice = (Button) findViewById(R.id.send_notice);
    sendNotice.setOnClickListener(new View.OnClickListener() {
      @RequiresApi(api = Build.VERSION_CODES.O)
      @Override
      public void onClick(View v) {
        Context context = getBaseContext();
        Intent intent = new Intent(MainActivity.this,MainActivity.class);
        PendingIntent pi = PendingIntent.getActivity(MainActivity.this,0,intent,0);
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        //Notification notification = new Notification();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_0_1) {
          NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID,CHANNEL_NAME,NotificationManager.IMPORTANCE_HIGH);
          manager.createNotificationChannel(notificationChannel);
        }
        Notification notification;
        notification = new NotificationCompat.Builder(MainActivity.this,CHANNEL_ID)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setPriority(NotificationCompat.PRIORITY_MAX)
            .setContentTitle("通知標題")
            .setAutoCancel(true)
            .setContentText("This is a notification This is a notificationThis is a notificationThis is a notificationThis is a notificationThis is a notificationThis is a notificationThis is a notificationThis is a notificationThis is a notificationThis is a notificationThis is a notificationThis is a notification")
            .setContentIntent(pi)
            .setVibrate(new long[]{0,1000,1000,1000})
            .setWhen(System.currentTimeMillis())
            .build();
        manager.notify(0,notification);
      }
    });
  }
}


	<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.example.notificationtest">
  <uses-permission android:name="android.permission.VIBRATE" />
  <application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.NotificationTest">
    <activity
      android:name=".MainActivity"
      android:exported="true">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
  </application>

</manifest>
注意點:高版本安卓,需要傳入channel,設定對應的channeld,才能彈出對應的系統通知。 android:requestLegacyExternalStorage="true" 外部儲存操作的時候,需設定該函式

2.Service

1.服務的生命週期

生命週期回撥方法:
(1)onCreate()
(2)onStartCommand()
(3)onBind()
(4)onDestory
呼叫startService()方法後,對應服務就會啟動,並回調onStartCommand()方法,服務沒有建立,onCreate()就會先於onStartCommand()方法執行,呼叫stopService,unBindService方法,執行onDestory方法。

2.前後臺服務
點選檢視程式碼
package com.example.servicetest;

import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
import androidx.annotation.Nullable;

public class MyIntentService extends IntentService {

  /**
   * Creates an IntentService.  Invoked by your subclass's constructor.
   *
   * @param name Used to name the worker thread, important only for debugging.
   */
  public MyIntentService(String name) {
    super(name);
  }
  public MyIntentService(){
    super(null);
  }

  @Override
  protected void onHandleIntent(@Nullable Intent intent) {
    Log.d("MyIntentService", "Thread id is" + Thread.currentThread().getId());
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    Log.d("MyIntentService", "onDestroy executed");
  }
}
package com.example.servicetest;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;

public class MyService extends Service {
  public MyService() {
  }
  private DownloadBinder mBinder = new DownloadBinder();

  class DownloadBinder extends Binder {
    public void startDownload() {
      Log.d("MyService", "startDownload executed");
    }

    public int getProgress() {
      Log.d("MyService", "getProgress executed");
      return 0;
    }
  }
  @RequiresApi(api = Build.VERSION_CODES.O)
  @Override
  public void onCreate() {
    super.onCreate();
    Log.d("MyService","onCreate executed");
    Intent intent = new Intent(MyService.this, MainActivity.class);
    PendingIntent pi = PendingIntent.getActivity(this,0, intent, 0);
    NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    NotificationChannel notificationChannel = new NotificationChannel("channel_id","channel_name",NotificationManager.IMPORTANCE_HIGH);
    manager.createNotificationChannel(notificationChannel);
    Notification notification = new NotificationCompat.Builder(MyService.this,"channel_id")
        .setContentTitle("This is content title")
        .setContentText("This is content text")
        .setWhen(System.currentTimeMillis())
        .setSmallIcon(R.mipmap.ic_launcher)
        .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
        .setContentIntent(pi)
        .build();
    startForeground(1, notification);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d("MyService","onStartCommand executed");
    return super.onStartCommand(intent, flags, startId);
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    Log.d("MyService","onDestroy executed");
  }

  @Override
  public IBinder onBind(Intent intent) {
    // TODO: Return the communication channel to the service.
    return mBinder;
  }
}
package com.example.servicetest;

import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

  private MyService.DownloadBinder mDownloadBinder;

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

    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button startService = (Button) findViewById(R.id.startService);
    Button stopService = (Button) findViewById(R.id.stopService);
    Button bindService = (Button) findViewById(R.id.bindService);
    Button unbindService = (Button) findViewById(R.id.unbindService);
    Button startIntentService = (Button) findViewById(R.id.startIntentService);
    startService.setOnClickListener(this);
    stopService.setOnClickListener(this);
    bindService.setOnClickListener(this);
    unbindService.setOnClickListener(this);
    startIntentService.setOnClickListener(this);
  }

  @Override
  public void onClick(View v) {
    switch (v.getId()) {
      case R.id.startService:
        Intent startIntent = new Intent(this,MyService.class);
        startService(startIntent);
        break;
      case R.id.stopService:
        Intent stopIntent = new Intent(this, MyService.class);
        stopService(stopIntent);
        break;
      case R.id.bindService:
        Intent bindIntent = new Intent(this, MyService.class);
        bindService(bindIntent, mConnection, BIND_AUTO_CREATE);
        break;
      case R.id.unbindService:
        unbindService(mConnection);
        break;
      case R.id.startIntentService:
        Log.d("MainActivity","Thread id is" + Thread.currentThread().getId());
        Intent intentService = new Intent(this, MyIntentService.class);
        startService(intentService);
        break;
      default:
        break;
    }
  }
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  package="com.example.servicetest">
  <uses-permission android:name="android.permission.VIBRATE" />
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

  <application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.ServiceTest">
    <service
      android:name=".MyService"
      android:enabled="true"
      android:exported="true"></service>
    <service android:name=".MyIntentService" />

    <activity
      android:name=".MainActivity"
      android:exported="true">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
  </application>

</manifest>
	

3.多執行緒

1.非同步訊息處理機制

非同步訊息處理:由四個部分組成,Message、Hanlder、MessageQueue、Looper。
(1)Message:執行緒之間傳遞訊息,用於在不同執行緒之間交換資料。
(2)Handler:用於傳送和處理訊息,sendMessgae(),經過一系列的處理之後,傳遞到HandleMessgae中。
(3)MessageQueue:用於存放所有通過Handler傳送的訊息,每個執行緒只會有一個MessageQueue物件。
(4)Looper:每個執行緒中的MessageQueue的關機,呼叫Looper中的loop()方法後,會進入到一個無限迴圈當中,發現MessageQueue中存在一條訊息,就會將其取出,傳遞到HanleMessage方法中。每個執行緒中也只會有一個looper物件。

2.AsyncTask使用

重新其中方法可完成對應的任務排程
(1)onProExecute():在後臺任務開始執行前呼叫,用於進行介面上的初始化操作。
(2)doInBackground(Params...):方法中所有程式碼都會在子執行緒中執行,不可以進行UI的操作
(3)onProgressUpdate(Progress...):後臺呼叫了publishProgress(Progress...)方法後,此方法就會呼叫,返回的資料作為引數傳遞,可以進行Ui操作。
(4)onPostExecute(Result):後臺任務執行完畢之後,並執行return 返回時,呼叫此方法,返回資料作為引數傳遞到此方法中,可以利用返回資料做UI操作。

4.接入三方SDK實現and ServiceTest

點選檢視程式碼
package com.example.servicebestpratice;

public interface DownloadListener {
  void onProgress(int progress);
  void onSuccess();
  void onFailed();
  void onPaused();
  void onCancled();
}
package com.example.servicebestpratice;

import java.io.File;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Binder;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;

public class DownloadService extends Service {
  private DownloadTask mDownloadTask;
  private String downloadUrl;
  private NotificationManager mManager;
  private DownloadListener mListener = new DownloadListener() {
    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public void onProgress(int progress) {
      getNotificationManager().notify(1,getNotification("Downloading...",progress));
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public void onSuccess() {
      mDownloadTask = null;
      stopForeground(true);
      getNotificationManager().notify(1,getNotification("Download Success", -1));
      Toast.makeText(DownloadService.this, "Download Success", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onFailed() {
      mDownloadTask = null;
      Toast.makeText(DownloadService.this, "Download Failed", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onPaused() {
      mDownloadTask = null;
      Toast.makeText(DownloadService.this, "Download Paused", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onCancled() {
      mDownloadTask = null;
      Toast.makeText(DownloadService.this, "Download Cancled", Toast.LENGTH_SHORT).show();
    }
  };
  public DownloadService() {
  }

  private DownloadBinder mBinder = new DownloadBinder();
  @Override
  public IBinder onBind(Intent intent) {
    return mBinder;
  }
  class DownloadBinder extends Binder {
    @RequiresApi(api = Build.VERSION_CODES.O)
    public void startDownload(String url) {
      if (mDownloadTask == null) {
        downloadUrl = url;
        mDownloadTask = new DownloadTask(mListener);
        mDownloadTask.execute(downloadUrl);
        mManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        NotificationChannel notificationChannel = new NotificationChannel("channelId","channelName",NotificationManager.IMPORTANCE_HIGH);
        mManager.createNotificationChannel(notificationChannel);
        startForeground(1,getNotification("Download...",0));
        Toast.makeText(DownloadService.this, "Downloading...",Toast.LENGTH_SHORT).show();
      }
    }
    public void pauseDownload() {
      if (mDownloadTask != null) {
        mDownloadTask.pauseDownload();
      }
    }
    @RequiresApi(api = Build.VERSION_CODES.O)
    public void cancelDownload() {
      if (mDownloadTask != null) {
        mDownloadTask.cancelDownload();
      } else {
        if (downloadUrl != null) {
          String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
          String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
          File file = new File(directory + fileName);
          if (file.exists()) {
            file.delete();
          }
          getNotificationManager().cancel(1);
          stopForeground(true);
          Toast.makeText(DownloadService.this, "Canceled", Toast.LENGTH_SHORT).show();

        }
      }
    }
  }
  @RequiresApi(api = Build.VERSION_CODES.O)
  private NotificationManager getNotificationManager() {
    return mManager;
  }
  private Notification getNotification(String title, int progress) {
    Intent intent = new Intent(this, MainActivity.class);
    PendingIntent pi = PendingIntent.getActivity(this,0,intent,0);
    NotificationCompat.Builder builder = new NotificationCompat.Builder(this,"channelId");
    builder.setSmallIcon(R.mipmap.ic_launcher);
    builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher));
    builder.setContentIntent(pi);
    builder.setContentTitle(title);
    if (progress > 0) {
      builder.setContentText(progress + "%");
      builder.setProgress(100,progress,false);
    }
    return builder.build();
  }
}
	
	package com.example.servicebestpratice;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;

import android.os.AsyncTask;
import android.os.Environment;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class DownloadTask extends AsyncTask<String, Integer, Integer> {
  public static final int TYPE_SUCCESS = 0;
  public static final int TYPE_FAILED = 1;
  public static final int TYPE_PAUSED = 2;
  public static final int TYPE_CANCELED = 3;
  private DownloadListener mListener;
  private boolean isCanceled = false;
  private boolean isPaused = false;
  private int lastProgress;
  public DownloadTask(DownloadListener listener) {
    this.mListener = listener;
  }
  @Override
  protected Integer doInBackground(String... strings) {
    InputStream is = null;
    RandomAccessFile savedFile = null;
    File file = null;
    try {
      long downloadedLength = 0;
      String downloadUrl = strings[0];
      String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
      String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
      file = new File(directory + fileName);
      if (file.exists()) {
        downloadedLength = file.length();
      }
      long contentLength = getContentLength(downloadUrl);
      if (contentLength == 0) {
        return TYPE_FAILED;
      } else if (contentLength == downloadedLength) {
        return TYPE_SUCCESS;
      }
      OkHttpClient client = new OkHttpClient();
      Request request = new Request.Builder()
          .addHeader("RANGE","bytes=" + downloadedLength + "-")
          .url(downloadUrl)
          .build();
      Response response = client.newCall(request).execute();
      if (response != null) {
        is = response.body().byteStream();
        savedFile = new RandomAccessFile(file, "rw");
        savedFile.seek(downloadedLength);
        byte[] b = new byte[1024];
        int total = 0;
        int len;
        while ((len = is.read(b))!=-1) {
          if (isCanceled) {
            return TYPE_CANCELED;
          } else if (isPaused) {
            return TYPE_PAUSED;
          } else {
            total += len;
            savedFile.write(b,0,len);
            int progress = (int)((total+downloadedLength) * 100 / contentLength);
            publishProgress(progress);
          }
        }
        response.close();
        return TYPE_SUCCESS;
      }
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        if (is != null) {
          is.close();
        }
        if (savedFile != null) {
          savedFile.close();
        }
        if (isCanceled && file != null) {
          file.delete();
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    return null;
  }

  @Override
  protected void onProgressUpdate(Integer... values) {
    super.onProgressUpdate(values);
    int progress = values[0];
    if (progress > lastProgress) {
      mListener.onProgress(progress);
      lastProgress = progress;
    }
  }

  @Override
  protected void onPostExecute(Integer integer) {
    super.onPostExecute(integer);
    switch (integer) {
      case TYPE_CANCELED:
        mListener.onCancled();
        break;
      case TYPE_SUCCESS:
        mListener.onSuccess();
        break;
      case TYPE_PAUSED:
        mListener.onPaused();
        break;
      case TYPE_FAILED:
        mListener.onFailed();
      default:
        break;
    }
  }

  private long getContentLength(String downloadUrl) throws IOException {
    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder()
        .url(downloadUrl)
        .build();
    Response response = client.newCall(request).execute();
    if (response != null && response.isSuccessful()) {
      long contentLength = response.body().contentLength();
      response.close();
      return contentLength;
    }
    return 0;
  }

  public void pauseDownload() {
    isPaused = true;
  }

  public void  cancelDownload() {
    isCanceled = true;
  }
}

	package com.example.servicebestpratice;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.baidu.location.BDAbstractLocationListener;
import com.baidu.location.BDLocation;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.mapapi.map.BaiduMap;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  private DownloadService.DownloadBinder mDownloadBinder;
  private LocationClient mLocationClient;
  private MyLocationListener mListener = new MyLocationListener();
  private TextView positionText;
  private final ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      mDownloadBinder = (DownloadService.DownloadBinder) service;
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }

  };

  public class MyLocationListener extends BDAbstractLocationListener {

    @Override
    public void onReceiveLocation(BDLocation bdLocation) {
      double latitude = bdLocation.getLatitude();
      double longitude = bdLocation.getLongitude();
      float radius = bdLocation.getRadius();
      String coorType = bdLocation.getCoorType();
      int errorCode = bdLocation.getLocType();
      positionText.setText(latitude + " ++ " +longitude + " ++ " +radius + " ++ " +coorType + " ++ ");
    }
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mLocationClient = new LocationClient(getApplicationContext());
    mLocationClient.registerLocationListener(mListener);
    LocationClientOption option = new LocationClientOption();
    option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
    option.setScanSpan(1000);
    mLocationClient.setLocOption(option);
    mLocationClient.start();
    Button startDownload = (Button) findViewById(R.id.start_download);
    Button pauseDownload = (Button) findViewById(R.id.pause_download);
    Button cancelDownload = (Button) findViewById(R.id.cancel_download);
    positionText = (TextView) findViewById(R.id.position_text_view);
    startDownload.setOnClickListener(this);
    pauseDownload.setOnClickListener(this);
    cancelDownload.setOnClickListener(this);
    Intent intent = new Intent(this, DownloadService.class);
    startService(intent);
    bindService(intent, mConnection, BIND_AUTO_CREATE);
    if (ContextCompat
        .checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
        PackageManager.PERMISSION_GRANTED) {
      ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
    }
  }

  @SuppressLint("NonConstantResourceId")
  @RequiresApi(api = Build.VERSION_CODES.O)
  @Override
  public void onClick(View v) {
    if (mDownloadBinder == null) {
      return;
    }
    switch (v.getId()) {
      case R.id.start_download:
        String url = "https://raw.githubusercontent.com/guolindev/eclipse/master/eclipse-inst-win64.exe";
        mDownloadBinder.startDownload(url);
        break;
      case R.id.pause_download:
        mDownloadBinder.pauseDownload();
        break;
      case R.id.cancel_download:
        mDownloadBinder.cancelDownload();
        break;
      default:
        break;
    }
  }

  @Override
  public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
      @NonNull int[] grantResults) {
    if (requestCode == 1) {
      if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
        Toast.makeText(this, "failed", Toast.LENGTH_SHORT).show();
        finish();
      }
    }
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    unbindService(mConnection);
  }
}
	
	<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".MainActivity">
  <Button
    android:id="@+id/start_download"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Start Downlaod" />
  <Button
    android:id="@+id/pause_download"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Pause Downlaod" />
  <Button
    android:id="@+id/cancel_download"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Cancel Downlaod" />
  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />
  <TextView
    android:id="@+id/position_text_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

</LinearLayout>
	
	plugins {
    id 'com.android.application'
}

android {
    compileSdk 31

    defaultConfig {
        ndk {
            // 設定支援的SO庫架構(開發者可以根據需要,選擇一個或多個平臺的so)
            abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86","x86_64"
        }
        applicationId "com.example.servicebestpratice"
        minSdk 21
        targetSdk 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(dir: 'libs', includes: ['*.jar'])
    implementation("com.squareup.okhttp3:okhttp:4.2.0")
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
    implementation 'com.baidu.lbsyun:BaiduMapSDK_Map:7.4.0'
    implementation 'com.baidu.lbsyun:BaiduMapSDK_Util:7.4.0'
    implementation 'com.baidu.lbsyun:BaiduMapSDK_Search:7.4.0'
    implementation 'com.baidu.lbsyun:BaiduMapSDK_Location:9.1.8'

    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}