《第一行代碼:Android篇》學習筆記(十一)

来源:https://www.cnblogs.com/1693977889zz/archive/2022/05/11/16256367.html
-Advertisement-
Play Games

本文和接下來的幾篇文章為閱讀郭霖先生所著《第一行代碼:Android(篇第2版)》的學習筆記,按照書中的內容順序進行記錄,書中的Demo本人全部都做過了。 每一章節本人都做了詳細的記錄,以下是我學習記錄(包含大量書中內容的整理和自己在學習中遇到的各種bug及解決方案),方便以後閱讀和查閱。最後,非常 ...


本文和接下來的幾篇文章為閱讀郭霖先生所著《第一行代碼:Android(篇第2版)》的學習筆記,按照書中的內容順序進行記錄,書中的Demo本人全部都做過了。
每一章節本人都做了詳細的記錄,以下是我學習記錄(包含大量書中內容的整理和自己在學習中遇到的各種bug及解決方案),方便以後閱讀和查閱。最後,非常感激郭霖先生提供這麼好的書籍。

第11章 Android特色開發——基於位置的服務

本章中,將要學習一些全新的Android技術,這些技術有別於傳統的PC或Web領域的應用技術,是只有在移動設備上才能實現的。

基於位置的服務(Location Based Service)。由於移動設備相比於電腦可以隨身攜帶,我們通過地理定位的技術就可以隨時得知自己所在的位置,從而圍繞這一點開發出很多有意思的應用。

11.1 基於位置的服務簡介

基於位置的服務簡稱LBS,其實它本身並不是什麼時髦的技術,主要的工作原理就是利用無線電通訊網路或GPS等定位方式來確定出移動設備所在的位置,而這種定位技術早在很多年前就已經出現了。

在過去移動設備的功能極其有限,即使定位到了設備所在的位置,也就僅僅只是定位到了而已,我們並不能在位置的基礎上進行一些其他的操作。而現在就大大不同了,有了Android系統作為載體,可以利用定位出的位置進行許多豐富多彩的操作。比如說天氣預報程式可以根據用戶所在的位置自動選擇城市,發微博的時候我們可以向朋友們曬一下自己在哪裡,不認識路的時候隨時打開地圖就可以查詢路線,等等。

首先要清楚,基於位置的服務所圍繞的核心就是要先確定出用戶所在的位置。

通常有兩種技術方式可以實現,一種是通過GPS定位,一種是通過網路定位:

  • GPS定位的工作原理是基於手機內置的GPS硬體直接和衛星交互來獲取當前的經緯度信息,這種定位方式精確度非常高,但缺點是只能在室外使用,室內基本無法接收到衛星的信號。
  • 網路定位的工作原理是根據手機當前網路附近的三個基站進行測速,以此計算出手機和每個基站之間的距離,再通過三角定位確定出一個大概的位置,這種定位方式精確度一般,但優點是在室內室外都可以使用。

Android對這兩種定位方式都提供了相應的API支持,但是由於一些特殊原因,Google的網路服務在中國不可訪問,從而導致網路定位方式的API失效。而GPS定位雖然不需要網路,但是必須要在室外才可以使用,因此你在室內開發的時候很有可能會遇到不管使用哪種定位方式都無法成功定位的情況。

基於以上原因,本書中不再講解Android原生定位API的用法了,而是使用一些國內第三方公司的SDK。目前國內在這一領域做得比較好的一個是百度,一個是高德,本章我們就來學習一下百度在LBS方面提供的豐富多彩的功能。

11.2 申請成為開發者

下麵內容與書中記錄的不一樣了,書中的方式我註冊不了...現在記錄自己是如何申請成為百度開發者的流程:

要想在自己的應用程式里使用百度的LBS功能,你得擁有一個百度賬號才能進行申請。然後,登錄網住:http://lbsyun.baidu.com/apiconsole/key

image

填入真實信息,進行郵箱驗證等,註冊的賬號:

image

結果在進行人臉識別認證的時候,我的始終無法識別。最後,申請了高德開發者,步驟如下:

1.網址:https://console.amap.com/dev/index

2.進行認證,支付寶、郵箱就可以了:

image

image

image

image

所以,以後Demo 書上是百度,我用的是高德了。

首先,創建新應用:

image

其次,新應用旁邊有一個添加按鈕,點擊添加:

image

這個發佈版安全碼SHA1,開發模式(debug)和發佈模式(release)下的 SHA1 值是不同的,發佈 apk時 需要根據發佈apk對應的keystore重新配置Key,獲取發佈模式下的SHA1的方法(可點擊如何獲取,學習獲取步驟),通過Android Studio獲取測試版SHA1:

1、打開Android Studio的Terminal工具。

2、輸入命令:keytool -v -list -keystore keystore文件路徑。

3、輸入Keystore密碼。

image

image

其中,91:16:04:30:C0:8B:6E:53:92:47:57:E6:FB:10:EF:08:1B:73:E6:3E就是所需的SHA1指紋了,當然你的Android Studio中顯示的指紋和我的肯定是不一樣的。

另外需要註意,目前我們使用的是debug.keystore文件所生成的指紋,這是Android自動生成的一個用於測試的簽名文件。而當你的應用程式發佈時還需要創建一個正式的簽名文件,如果要得到它的指紋,可以在cmd中輸入如下命令:

keytool -list -v -keystore <簽名文件路徑>

然後輸入正確的密碼就可以了。創建簽名文件的方法我們將在第15章中學習。

現在得到的這個SHA1指紋實際上是一個開發版的SHA1指紋,不過因為暫時我們還沒有一個發佈版的SHA1指紋,因此這兩個值都填成一樣的就可以了。

上面這句話是個大坑!如果SHA1的發佈版/測試版值都填入一樣的,在最後很有可能出現:坐標一直為4.9E-324(這是導致定位坐標這樣的原因之一,我之後在測試中,也改了不少。)

image

上面的代碼,與原書不一致,都是經過查詢網上資料改動過的,參考博客連接:

  1. 包名不一致(我的不是這個原因)https://www.cnblogs.com/hoyong/articles/11918642.html
  2. 室內用不了GPS....(我用的WIFI/移動4G)
  3. 網路不好,聯網失敗(我的網非常好,所以不是這個原因)
  4. Android的模擬器是不能定位的,所以要使用真機(我的不是這個原因,但是這個確實是真的,在我排查全部bug後,手機上測試OK,到Android的模擬器上運行,為上圖:坐標一直為4.9E-324,不顯示定位方式)
  5. 許可權錯誤/so庫載入錯誤(我的不是這個原因)https://www.jianshu.com/p/b394de3d989c

然而,在是上面3中的博客里,最後一條就是SHA1指紋發佈版和測試版不能一樣!!!我測試了,對我來說是正確的解決辦法。

下麵,記錄一下如何申請並找到SHA1指紋發佈版步驟:

  1. 找到apk簽名路徑,可通過AS中build-generate signed bundle/apk -apk 進入如圖界面:

image

image

  1. 檢查一下該目錄(E:\androidKey)中是否存在zhouzhou.jks,沒有的話,查看文件是否被隱藏了:

image

其中key store path 為簽名路徑。

  1. 操作和獲取開發版SHA1 基本一致,只需將debug.keystore換成簽名路徑即可,如圖:

image

  1. 最後,將在開發平臺更換髮布版SHA1重新提交。註:項目最好clean一下。

下麵開始,繼續記錄書上的內容,

最後還剩下一個包名選項,雖然目前我們的應用程式還不存在,但可以先將包名預定下來,比如就叫com.zhouzhou. lbstest,這樣所有的內容就都填寫完整了,如圖為百度地圖開放平臺創建的應用信息:

(註:第二天百度的人臉識別認證我整成功了,為了和書上保持一致,我接下來選擇用百度地圖開放平臺做Demo。事實上,高德平臺步驟也差不多,以後有機會會嘗試的)

image

接下來點擊提交,應用就應該創建成功了,如圖:

image

其中,i6VD2fHKM3msMfZtIOXAhFSzDiYGFIwL(訪問應用(AK))就是申請到的API Key,有了它就可以進行後續的LBS開發工作了,那麼我們馬上開始吧。

11.3 使用百度定位

新建一個LBSTest項目,包名應該就會自動被命名為com.zhouzhou.lbstest。另外需要註意,本章中所寫的代碼建議都在手機上運行,雖然模擬器中也提供了模擬地理位置的功能,但在手機上可以得到真實的位置數據,你的感受會更加深刻。

11.3.1 準備LBS SDK

在開始編碼之前,我們還需要先將百度LBS開放平臺的SDK準備好,目前百度地圖Android地圖SDK境內服務對非商業目的使用的開發者不收取任何費用,開發者可自行下載:http://lbsyun.baidu.com/index.php?title=androidsdk/sdkandev-download放心使用。

image

本章中,我們會用到基礎地圖和定位功能這兩個SDK,將它們勾選上,然後點擊“開發包”下載按鈕即可,如圖:

image

下載完成後對該壓縮包解壓,其中會有一個libs目錄,這裡面的內容就是我們所需要的一切了,如圖:

image

libs目錄下的內容又分為兩部分,BaiduLBS_Android.jar這個文件是Java層要使用到的,其他子目錄下的so文件是Native層要用到的。so文件是用C/C++語言進行編寫,然後再用NDK編譯出來的。當然這裡我們並不需要去編寫C/C++的代碼,因為百度都已經做好了封裝,但是我們需要將libs目錄下的每一個文件都放置到正確的位置。

首先,觀察一下當前的項目結構,會發現app模塊下麵有一個libs目錄,這裡就是用來存放所有的Jar包的,我們將BaiduLBS_Android.jar複製到這裡,如圖:

image

接下來展開src/main目錄,右擊該目錄→New→Directory,再創建一個名為jniLibs的目錄,這裡就是專門用來存放so文件的,然後把壓縮包里的其他所有目錄直接複製到這裡,如圖:

image

另外,雖然所有新創建的項目中,app/build.gradle文件都會預設配置以下這段聲明(不整這個也行,上面BaiduLBS_Android.jar複製之後,直接Add As Library...即可,dependencies中會被自動添加一行implementation files('libs\\BaiduLBS_Android.jar'),註意,Add As Library...和下麵的implementation fileTree(include: ['*.jar'], dir: 'libs')方式要二選一,不要都寫上,會報衝突!!!):

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
}

這表示會將libs目錄下所有以.jar結尾的文件添加到當前項目的引用中。(但是由於我們是直接將Jar包複製到libs目錄下的,並沒有修改gradle文件,因此不會彈出我們平時熟悉的Sync Now提示。這個時候必須手動點擊一下AndroidStudio頂部工具欄中的Sync按鈕,不然項目將無法引用到Jar包中提供的任何介面。)

點擊Sync按鈕之後,libs目錄下的jar文件就會多出一個向右的箭頭,這就表示項目已經能引用到這些Jar包了,如圖:

image

這樣我們就把LBS的SDK都準備好了,接下來開始編碼吧。

11.3.2 確定自己位置的經緯度

首先修改activity_main.xml中的代碼,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/position_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

然後,修改AndroidManifest.xml文件中的代碼,如下所示:

<?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.zhouzhou.lbstest">
    <!-- 這個許可權用於進行網路定位-->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <!-- 這個許可權用於訪問GPS定位-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <!-- 用於訪問wifi網路信息,wifi信息會用於進行網路定位-->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <!-- 獲取運營商信息,用於支持提供運營商信息相關的介面-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!-- 這個許可權用於獲取wifi的獲取許可權,wifi信息會用來進行網路定位-->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <!-- 寫入擴展存儲,向擴展卡寫入數據,用於寫入離線定位數據-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!-- 訪問網路,網路定位需要上網-->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <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.LBSTest">

        <!-- meta-data中android:value=在11.2節申請到的API Key -->
        <meta-data
            android:name="com.baidu.lbsapi.API_KEY"
            android:value="LGvkANF1s4zZK1MkK0XL6fYiL2TkV4L4" />
        <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>
        <service
            android:name="com.baidu.location.f"
            android:enabled="true"
            android:process=":remote">
        </service>
    </application>

</manifest>

AndroidManifest.xml文件改動比較多。可以看到,這裡首先添加了很多行許可權聲明,每一個許可權都是百度LBS SDK內部要用到的。然後,在<application>標簽的內部添加了一個<meta-data>標簽,這個標簽的android:name部分是固定的,必須填com.baidu. lbsapi.API_KEY,android:value部分則應該填入我們在11.2節申請到的API Key。最後,還需要再註冊一個LBS SDK中的服務,不用對這個服務的名字感到疑惑,因為百度LBS SDK中的代碼都是混淆過的。

接下來修改MainActivity中的代碼,如下所示:

package com.zhouzhou.lbstest;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    public LocationClient mLocationClient;
    private TextView positionText;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //沒有它,會報錯:Please recheck the setAgreePrivacy interface
        mLocationClient.setAgreePrivacy(true);
        try {
            //首先創建了一個LocationClient的實例,LocationClient的構建函數接收一個Context參數,
            //這裡調用getApplicationContext()方法來獲取一個全局的Context參數並傳入。
            mLocationClient = new LocationClient(getApplicationContext());
        } catch (Exception e) {
                e.printStackTrace();
            }
            //然後調用LocationClient的registerLocationListener()方法來註冊一個定位監聽器,當獲取到位置信息的時候,就會回調這個定位監聽器。
            mLocationClient.registerLocationListener(new MyLocationListener());
            setContentView(R.layout.activity_main);
            positionText = (TextView) findViewById(R.id.position_text_view);
            //怎樣才能在運行時一次性申請3個許可權呢?
            //創建一個空的List集合,然後依次判斷這3個許可權有沒有被授權,
            //如果沒被授權就添加到List集合中,最後將List轉換成數組,
            //再調用ActivityCompat.requestPermissions()方法一次性申請。
            List<String> permissionList = new ArrayList<>();
            if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
            }
            if (ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
                permissionList.add(Manifest.permission.READ_PHONE_STATE);
            }
            if (ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
            }
            if (! permissionList.isEmpty()) {
                String [] permissions = permissionList.toArray(new String[permissionList.size()]);
                ActivityCompat.requestPermissions(MainActivity.this,permissions,1);
            } else {
                requestLocation();
            }
    }
    private void requestLocation() {
        mLocationClient.start();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0) {
                    for (int result : grantResults) {
                        if (result != PackageManager.PERMISSION_GRANTED) {
                            Toast.makeText(this,"必須同意所有許可權才能使用本程式",Toast.LENGTH_SHORT).show();
                            finish();
                            return;
                        }
                    }
                    requestLocation();
                } else {
                    Toast.makeText(this,"發生未知錯誤",Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
        }
    }

    public class MyLocationListener implements BDLocationListener {
        /**
         * MyLocationListener的onReceiveLocation()方法中:
         * 通過BDLocation的getLatitude()方法獲取當前位置的緯度,
         * 通過getLongitude()方法獲取當前位置的經度,
         * 通過getLocType()方法獲取當前的定位方式,最終將結果組裝成一個字元串,顯示到TextView上面。
         */
        @Override
        public void onReceiveLocation(BDLocation bdLocation) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    StringBuilder currentPosition = new StringBuilder();
                    currentPosition.append("緯度:").append(bdLocation.getLatitude()).append("\n");
                    currentPosition.append("經度:").append(bdLocation.getLongitude()).append("\n");
                    currentPosition.append("定位方式:");
                    if (bdLocation.getLocType() == BDLocation.TypeGpsLocation) {
                        currentPosition.append("GPS");
                    } else if (bdLocation.getLocType() == BDLocation.TypeNetWorkLocation) {
                        currentPosition.append("網路");
                    }
                    positionText.setText(currentPosition);
                }
            });
        }
    }
}

註意:上面代碼中多了一行:

mLocationClient.setAgreePrivacy(true);

如果沒有這一行,報錯:...Please recheck the setAgreePrivacy interface,忘記截圖了。

  1. 問題描述: 根據官方文檔配置完後,地圖無法顯示,也不顯示網格圖層,會報location調用隱私政策介面異常。
  2. 解決辦法: 除了在Application.java中正常初始化context信息之外,在調用地圖的activity中的onCreat()方法中,添加一句LocationClient.setAgreePrivacy(true);
  3. 參考博客:解決百度地圖sdk v7.5.0版本,地圖不能正常顯示問題(隱私政策)https://blog.csdn.net/qq_43962511/article/details/123840858

運行時許可權的用法,由於我們在AndroidManifest.xml中聲明瞭很多許可權,參考一下7.2.1小節中的危險許可權表格可以發現,其中ACCESS_COARSE_LOCATION、ACCESS_FINE_LOCATION、READ_PHONE_STATE、WRITE_EXTERNAL_STORAGE這4個許可權是需要進行運行時許可權處理的,不過由於ACCESS_COARSE_LOCATION和ACCESS_FINE_LOCATION都屬於同一個許可權組,因此兩者只要申請其一就可以了。

那麼怎樣才能在運行時一次性申請3個許可權呢?

這裡我們使用了一種新的用法,首先創建一個空的List集合,然後依次判斷這3個許可權有沒有被授權,如果沒被授權就添加到List集合中,最後將List轉換成數組,再調用ActivityCompat. requestPermissions()方法一次性申請。

除此之外,onRequestPermissionsResult()方法中對許可權申請結果的邏輯處理也和之前有所不同,這次我們通過一個迴圈將申請的每個許可權都進行了判斷,如果有任何一個許可權被拒絕,那麼就直接調用finish()方法關閉當前程式,只有當所有許可權都被用戶同意了,才會調用requestLocation()方法開始地理位置定位。

requestLocation()方法中的代碼比較簡單,只是調用了一下LocationClient的start()方法就能開始定位了。定位的結果會回調到我們前面註冊的監聽器當中,也就是MyLocationListener。

現在運行一下程式了。毫無疑問,打開程式首先就會彈出運行時許可權的申請對話框,註意,與書中不同,我的Demo手機上是一項一項選擇的是否同意(3次授權):

image

然後就會立刻開始定位了,結果如圖:

image

可以看到,設備當前的經緯度信息已經成功定位出來了。

不過,在預設情況下,調用LocationClient的start()方法只會定位一次,如果我們正在快速移動中,怎樣才能實時更新當前的位置呢?

為此,百度LBS SDK提供了一系列的設置方法,來允許我們更改預設的行為,修改MainActivity中的代碼,如下所示:

public class MainActivity extends AppCompatActivity {
    ...
    private void requestLocation() {
        initLocation();
        mLocationClient.start();
    }
    private void initLocation() {
        LocationClientOption option = new LocationClientOption();
        // 表示每5秒鐘會更新一下當前的位置
        option.setScanSpan(5000);
        mLocationClient.setLocOption(option);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mLocationClient.stop();
    }
    ...
}

這裡增加了一個initLocation()方法,在initLocation()方法中我們創建了一個LocationClientOption對象,然後調用它的setScanSpan()方法來設置更新的間隔。

最後要記得,在活動被銷毀的時候一定要調用LocationClient的stop()方法來停止定位,不然程式會持續在後臺不停地進行定位,從而嚴重消耗手機的電量。

重新運行程式,拿著手機隨處移動,界面上的經緯度信息也會跟著一起變化。(測試,有效!!!)

11.3.3 選擇定位模式

Android中主要有兩種:一種是通過GPS定位,一種是通過網路定位。

上一小節中的例子中,一直是使用的網路定位。那麼如何才能切換到精確度更高的GPS定位呢?

本小節我們就來學習一下。首先,GPS定位功能必須要由用戶主動去啟用才行,不然任何應用程式都無法使用GPS獲取到手機當前的位置信息。進入手機的設置→位置信息(我的手機沒有位置信息選項,下圖是我Android的模擬器上的截取的),如圖:

image

可以通過頂部的開關來控制定位功能是開啟還是關閉,另外,點擊“模式”可以選擇具體的定位模式,我的手機與書上的不一樣,我下拉手機菜單欄,看到GPS,手指長按GPS跳到GPS設置界面,如下圖所示:

image

其中,

  • 高精確度模式,表示允許使用GPS、無線網路、藍牙或移動網路來進行定位;
  • 節電模式,表示僅允許使用無線網路、藍牙或移動網路來進行定位;
  • 僅限設備模式,表示僅允許使用GPS來進行定位。

也就是說,如果我們想要使用GPS定位功能,這裡必須要選擇高精確度模式,或者僅限設備模式。

當然,你並不需要擔心一旦啟用GPS定位功能後,手機的電量就會直線下滑,這隻是表明你已經同意讓應用程式來對你的手機進行GPS定位了,但只有當定位操作真正開始的時候,才會影響到手機的電量。

開啟了GPS定位功能之後,再回來看一下代碼。我們可以在initLocation()方法中對百度LBS SDK的定位模式進行指定,一共有3種模式可選:Hight_Accuracy、Battery_Saving和Device_Sensors。

  • Hight_Accuracy 表示高精確度模式,會在GPS信號正常的情況下優先使用GPS定位,在無法接收GPS信號的時候使用網路定位。
  • Battery_Saving 表示節電模式,只會使用網路進行定位。
  • Device_Sensors 表示感測器模式,只會使用GPS進行定位。

Hight_Accuracy是預設的模式,也就是說,我們即使不修改任何代碼,只要拿著手機走到室外去,讓手機可以接收到GPS信號,就會自動切換到GPS定位模式了。

當然我們也可以強制指定只使用GPS進行定位,修改MainActivity中的代碼,如下所示:

public class MainActivity extends AppCompatActivity {
   ...
    private void initLocation() {
        LocationClientOption option = new LocationClientOption();
        // 表示每5秒鐘會更新一下當前的位置
        option.setScanSpan(5000);
       // 調用了setLocationMode()方法來將定位模式指定成感測器模式,也就是說只能使用GPS進行定位
        option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);
        mLocationClient.setLocOption(option);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mLocationClient.stop();
    }
	...
}

這裡調用了setLocationMode()方法來將定位模式指定成感測器模式,也就是說只能使用GPS進行定位。重新運行一下程式,然後拿著你的手機走到室外去,切記:如果想要使用GPS定位功能,手機上的GPS模式,必須要選擇高精確度模式,或者僅限設備模式 ,結果如圖:

image

11.3.4 看得懂的位置信息

雖然成功獲取到了設備當前位置的經緯度信息,但這種經緯度的值一般人是根本看不懂。

為了能夠更加直觀地閱讀,我們還需要學習一下如何獲取看得懂的位置信息。百度LBS SDK在這方面提供了非常好的支持,我們只需要進行一些簡單的介面調用就能得到當前位置各種豐富的地址信息。

修改MainActivity中的代碼,如下所示:

public class MainActivity extends AppCompatActivity {
    ...
    private void initLocation() {
        LocationClientOption option = new LocationClientOption();
        // 表示每5秒鐘會更新一下當前的位置
        option.setScanSpan(5000);
        // 調用了LocationClientOption的setIsNeedAddress()方法,並傳入true,表示需要獲取當前位置詳細的地址信息。
        option.setIsNeedAddress(true);
        // 調用了setLocationMode()方法來將定位模式指定成感測器模式,也就是說只能使用GPS進行定位
        option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);
        mLocationClient.setLocOption(option);
    }
    ...
    public class MyLocationListener implements BDLocationListener {
        @Override
        public void onReceiveLocation(BDLocation bdLocation) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    StringBuilder currentPosition = new StringBuilder();
                    currentPosition.append("緯度:").append(bdLocation.getLatitude()).append("\n");
                    currentPosition.append("經度:").append(bdLocation.getLongitude()).append("\n");
                    currentPosition.append("國家:").append(bdLocation.getCountry()).append("\n");
                    currentPosition.append("省:").append(bdLocation.getProvince()).append("\n");
                    currentPosition.append("市:").append(bdLocation.getCity()).append("\n");
                    currentPosition.append("區:").append(bdLocation.getDistrict()).append("\n");
                    currentPosition.append("街道:").append(bdLocation.getStreet()).append("\n");
                    currentPosition.append("定位方式:");
                    if (bdLocation.getLocType() == BDLocation.TypeGpsLocation) {
                        currentPosition.append("GPS");
                    } else if (bdLocation.getLocType() == BDLocation.TypeNetWorkLocation) {
                        currentPosition.append("網路");
                    }
                    positionText.setText(currentPosition);
                }
            });
        }
    }
}

在MyLocationListener的onReceiveLocation()方法就可以獲取到各種豐富的地址信息了,調用getCountry()方法可以得到當前所在國家,調用getProvince()方法可以得到當前所在省份,以此類推。

需要註意,由於獲取地址信息一定需要用到網路,因此即使我們將定位模式指定成了Device_Sensors,也會自動開啟網路定位功能。現在重新運行一下程式,結果如圖:

image

可以看到,手機當前位置的地址信息已經成功顯示出來了。如果你帶著手機移動了較遠的距離,界面上顯示的位置也會跟著一起變化的。

11.4 使用百度地圖

手機地圖能夠隨時隨地進行查看,並且輕鬆構建出行路線,使用起來明顯更加地方便。應用程式里也是可以加入地圖功能的,比如優步中使用的就是百度地圖。本節我們就來學習一下這方面的知識。

11.4.1 讓地圖顯示出來

讓地圖顯示出來,這個例子我決定我像書中介紹的再LBSTest應用中做了,想再自己做一遍使用百度地圖的流程,所以又新建了一個LBSTest2應用,用這個來記錄一下。

  • 第一步:Android Studio 中:

    • 新建LBSTest2應用

    • 下載SDK本地依賴

    • 將開發包拷貝至工程

      • 添加jar文件(BaiduLBS_Android.jar),Add As Library...

      • 添加so文件(在src/main/目錄下新建jniLibs目錄(如果您的項目中已經包含該目錄不用重覆創建),在下載的開發包中拷貝項目中需要的CPU架構對應的so文件文件夾到jniLibs目錄)

        註意:Jar文件和so文件的版本號必須一致,並且保證Jar文件與so文件是同一版本包取出的。

image

(註:第一步主要參考官方文檔地址的地址:https://lbsyun.baidu.com/index.php?title=androidsdk/guide/create-project/androidstudio

image

  • 第三步:配置AndroidManifest.xml文件
<application>  
    <meta-data  
        android:name="com.baidu.lbsapi.API_KEY"  
        android:value="開發者 key" />  
</application>
  • 第四步: 在<application/>外部添加如下許可權聲明:
<!-- 訪問網路,進行地圖相關業務數據請求,包括地圖數據,路線規劃,POI檢索等 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 獲取網路狀態,根據網路狀態切換進行數據請求網路轉換 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<!-- 讀取外置存儲。如果開發者使用了so動態載入功能並且把so文件放在了外置存儲區域,則需要申請該許可權,否則不需要 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 寫外置存儲。如果開發者使用了離線地圖,並且數據寫在外置存儲區域,則需要申請該許可權 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

註:自Android6.0起部分許可權的使用需要開發者在代碼中動態申請。

參考博客:解決百度地圖sdk v7.5.0版本,地圖不能正常顯示問題,https://blog.csdn.net/qq_43962511/article/details/123840858

// 是否同意隱私政策,預設為false
SDKInitializer.setAgreePrivacy(getApplicationContext(), true);
  • 第五步: 在佈局文件中添加地圖容器

MapView是View的一個子類,用於在Android View中放置地圖。MapView的使用方法與Android提供的其他View一樣。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.baidu.mapapi.map.MapView
        android:id="@+id/bmapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clickable="true" />
</LinearLayout>
  • 第六步:地圖初始化

註意:在SDK各功能組件使用之前都需要調用“SDKInitializer.initialize(getApplicationContext())”,因此建議在應用創建時初始化SDK引用的Context為全局變數。

新建一個自定義的Application——DemoApplication,在其onCreate方法中完成SDK的初始化。示例代碼如下:

(其中,“是否同意隱私政策”是之前沒有添加,出來bug後,上網查到的。try/catch 是網上查到的)

package com.zhouzhou.lbstest2;

import android.app.Application;

import com.baidu.mapapi.CoordType;
import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.common.BaiduMapSDKException;

public class DemoApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        // 是否同意隱私政策,預設為false
        SDKInitializer.setAgreePrivacy(getApplicationContext(), true);
        try {
            //在使用SDK各組件之前初始化context信息,傳入ApplicationContext
            SDKInitializer.initialize(getApplicationContext());
            //自4.3.0起,百度地圖SDK所有介面均支持百度坐標和國測局坐標,用此方法設置您使用的坐標類型.
            //包括BD09LL和GCJ02兩種坐標,預設是BD09LL坐標。
            SDKInitializer.setCoordType(CoordType.BD09LL);

        } catch (BaiduMapSDKException e) {
            e.printStackTrace();
        }
    }
}
  • 第七步:在AndroidManifest.xml文件中聲明該Application
<application
       android:name=".DemoApplication"

image

  • 第八步:創建地圖Activity,管理MapView生命周期

註意:在項目中使用地圖的時候要特別註意合理地管理地圖生命周期,這非常重要。

以下示例代碼簡述對地圖生命周期的管理:

package com.zhouzhou.lbstest2;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import com.baidu.mapapi.map.MapView;

public class MainActivity extends AppCompatActivity {
    private MapView mMapView = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //獲取地圖控制項引用
        mMapView = (MapView) findViewById(R.id.bmapView);
    }
    @Override
    protected void onResume() {
        super.onResume();
        //在activity執行onResume時執行mMapView. onResume (),實現地圖生命周期管理
        mMapView.onResume();
    }
    @Override
    protected void onPause() {
        super.onPause();
        //在activity執行onPause時執行mMapView. onPause (),實現地圖生命周期管理
        mMapView.onPause();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //在activity執行onDestroy時執行mMapView.onDestroy(),實現地圖生命周期管理
        mMapView.onDestroy();
    }
}

完成以上工作即可在您的應用中顯示地圖:

image

11.4.2 移動到我的位置

這是一張預設的地圖,顯示的是北京市中心的位置,而你可能希望看到更加精細的地圖信息,比如說自己所在位置的周邊環境。顯然,通過縮放和移動的方式來慢慢找到自己的位置是一種很愚蠢的做法。那麼本小節我們就來學習一下,如何才能在地圖中快速移動到自己的位置。

百度LBS SDK的API中提供了一個BaiduMap類,它是地圖的總控制器,調用MapView的getMap()方法就能獲取到BaiduMap的實例,如下所示:

BaiduMap baiduMap = mapView.getMap();

有了BaiduMap後,我們就能對地圖進行各種各樣的操作了,比如設置地圖的縮放級別以及將地圖移動到某一個經緯度上。

百度地圖將縮放級別的取值範圍限定在3到19之間,其中小數點位的值也是可以取的,值越大,地圖顯示的信息就越精細。比如我們想要將縮放級別設置成12.5,就可以這樣寫:

MapStatusUpdate update = MapStatusUpdateFactory.zoomTo(12.5f);
baiduMap.animateMapStatus(update);

其中MapStatusUpdateFactory的zoomTo()方法接收一個float型的參數,就是用於設置縮放級別的,這裡我們傳入12.5f。zoomTo()方法返回一個MapStatusUpdate對象,我們把這個對象傳入BaiduMap的animateMapStatus()方法當中即可完成縮放功能。

那麼怎樣才能讓地圖移動到某一個經緯度上呢?

這就需要藉助LatLng類。其實LatLng並沒有什麼太多的用法,主要就是用於存放經緯度值的,它的構造方法接收兩個參數,第一個參數是緯度值,第二個參數是經度值。之後調用MapStatusUpdateFactory的newLatLng()方法將LatLng對象傳入,newLatLng()方法返回的也是一個MapStatusUpdate對象,我們再把這個對象傳入BaiduMap的animateMapStatus()方法當中,就可以將地圖移動到指定的經緯度上了,寫法如下:

LatLng ll = new LatLng(39.915,116.404);
MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
baiduMap.animateMapStatus(update);

上述代碼就實現了將地圖移動到北緯39.915度、東經116.404度這個位置的功能。

瞭解了這些知識之後,接下來再去實現將地圖快速移動到自己位置的功能就變得非常簡單了。首先我們可以利用在11.3節中所學的定位技術來獲得自己當前位置的經緯度,之後再按照上述的方法來將地圖移動到指定的位置就可以了。

那麼下麵我們就來繼續完善LBSTest這個項目,加入“移動到我的位置”這個功能。修改MainActivity中的代碼,如下所示:

package com.zhouzhou.lbstest2;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.Toast;

import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.mapapi.map.BaiduMap;
import com.baidu.mapapi.map.MapStatusUpdate;
import com.baidu.mapapi.map.MapStatusUpdateFactory;
import com.baidu.mapapi.map.MapView;
import com.baidu.mapapi.model.LatLng;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    public LocationClient mLocationClient;
    private MapView mMapView = null;
    private BaiduMap baiduMap;
    private boolean isFirstLocate = true;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //沒有它,會報錯:Please recheck the setAgreePrivacy interface
        mLocationClient.setAgreePrivacy(true);
        try {
            mLocationClient = new LocationClient(getApplicationContext());
        } catch (Exception e) {
            e.printStackTrace();
        }
        mLocationClient.registerLocationListener(new MyLocationListener());
        setContentView(R.layout.activity_main);
        //獲取地圖控制項引用
        mMapView = (MapView) findViewById(R.id.bmapView);
        baiduMap = mMapView.getMap();
        List<String> permissionList = new ArrayList<>();
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
        }
        if (ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.READ_PHONE_STATE);
        }
        if (ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        if (! permissionList.isEmpty()) {
            String [] permissions = permissionList.toArray(new String[permissionList.size()]);
            ActivityCompat.requestPermissions(MainActivity.this,permissions,1);
        } else {
            requestLocation();
        }
    }
    private void requestLocation() {
        mLocationClient.start();
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0) {
                    for (int result : grantResults) {
                        if (result != PackageManager.PERMISSION_GRANTED) {
                            Toast.makeText(this,"必須同意所有許可權才能使用本程式",Toast.LENGTH_SHORT).show();
                            finish();
                            return;
                        }
                    }
                    requestLocation();
                } else {
                    Toast.makeText(this,"發生未知錯誤",Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
        }
    }
    private void navigateTo(BDLocation location) {
        if (isFirstLocate) {
            LatLng ll = new LatLng(location.getLatitude(),location.getLongitude());
            MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
            baiduMap.animateMapStatus(update);
            update = MapStatusUpdateFactory.zoomTo(16f);
            baiduMap.animateMapStatus(update);
            isFirstLocate = false;
        }
    }
    public class MyLocationListener implements BDLocationListener {

        @Override
        public void onReceiveLocation(BDLocation bdLocation) {
            if (bdLocation.getLocType() == BDLocation.TypeGpsLocation || bdLocation.getLocType() == BDLocation.TypeNetWorkLocation) {
                navigateTo(bdLocation);
            }
        }
    }
    @Override
    protected void onResume() {
        super.onResume();
        //在activity執行onResume時執行mMapView. onResume (),實現地圖生命周期管理
        mMapView.onResume();
    }
    @Override
    protected void onPause() {
        super.onPause();
        //在activity執行onPause時執行mMapView. onPause (),實現地圖生命周期管理
        mMapView.onPause();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mLocationClient.stop();
        //在activity執行onDestroy時執行mMapView.onDestroy(),實現地圖生命周期管理
        mMapView.onDestroy();
    }
}

主要是加入了一個navigateTo()方法。這個方法,先是將BDLocation對象中的地理位置信息取出並封裝到LatLng對象中,然後調用MapStatusUpdateFactory的newLatLng()方法並將LatLng對象傳入,接著將返回的MapStatus-Update對象作為參數傳入到BaiduMap的animateMapStatus()方法當中,和上面介紹的用法是一模一樣的。

並且,這裡為了讓地圖信息可以顯示得更加豐富一些,我們將縮放級別設置成了16。需要註意,上述代碼當中我們使用了一個isFirstLocate變數,這個變數的作用是為了防止多次調用animateMapStatus()方法,因為將地圖移動到我們當前的位置只需要在程式第一次定位的時候調用一次就可以了。

另外,當定位到設備當前位置的時候,我們在onReceiveLocation()方法中直接把BDLocation對象傳給navigateTo()方法,這樣就能夠讓地圖移動到設備所在的位置了。

註意:出現一些因為沒有添加許可權而產生的bug,所以,在AndroidManifest.xml中又添加了幾項許可權,整體如下:

<!-- 訪問網路,進行地圖相關業務數據請求,包括地圖數據,路線規劃,POI檢索等 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 獲取網路狀態,根據網路狀態切換進行數據請求網路轉換 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<!-- 讀取外置存儲。如果開發者使用了so動態載入功能並且把so文件放在了外置存儲區域,則需要申請該許可權,否則不需要 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 寫外置存儲。如果開發者使用了離線地圖,並且數據寫在外置存儲區域,則需要申請該許可權 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 這個許可權用於進行網路定位 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 這個許可權用於訪問GPS定位 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- WIFI -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<!-- PHONE -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>

現在重新運行一下程式,結果如圖:

image

11.4.3 讓“我”顯示在地圖上

已經可以讓地圖顯示我們周邊的環境了。通常情況下手機地圖上應該都會有一個小游標,用於顯示設備當前所在的位置,並且如果設備正在移動的話,那麼這個游標也會跟著一起移動。那麼我們現在就繼續對現有代碼進行擴展,讓“我”能夠顯示在地圖上。

百度LBS SDK當中提供了一個MyLocationData.Builder類,這個類是用來封裝設備當前所在位置的,我們只需將經緯度信息傳入到這個類的相應方法當中就可以了,如下所示:

MyLocationData.Builder locationBuilder = new MyLocationData.Builder();
locationBuilder.latitude(39.915);
locationBuilder.longitude(116.404);

MyLocationData.Builder類還提供了一個build()方法,當我們把要封裝的信息都設置完成之後,只需要調用它的build()方法,就會生成一個MyLocationData的實例,然後再將這個實例傳入到BaiduMap的setMyLocationData()方法當中,就可以讓設備當前的位置顯示在地圖上了,寫法如下:

MyLocationData locationData = locationBuilder.build();
baiduMap.setMyLocationData(locationData);

大體思路就是這個樣子,下麵我們開始來實現一下,修改MainActivity中的代碼,如下所示:

public class MainActivity extends AppCompatActivity {
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //沒有它,會報錯:Please recheck the setAgreePrivacy interface
        mLocationClient.setAgreePrivacy(true);
        try {
            mLocationClient = new LocationClient(getApplicationContext());
        } catch (Exception e) {
            e.printStackTrace();
        }
        mLocationClient.registerLocationListener(new MyLocationListener());
        setContentView(R.layout.activity_main);
        //獲取地圖控制項引用
        mMapView = (MapView) findViewById(R.id.bmapView);
        baiduMap = mMapView.getMap();
        //根據百度地圖的限制,想要使用這一功能,一定要事先調用BaiduMap的setMyLocationEnabled()方法將此功能開啟,否則設備的位置將無法在地圖上顯示。
        baiduMap.setMyLocationEnabled(true);
        ...
    private void navigateTo(BDLocation location) {
        if (isFirstLocate) {
            LatLng ll = new LatLng(location.getLatitude(),location.getLongitude());
            MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
            baiduMap.animateMapStatus(update);
            update = MapStatusUpdateFactory.zoomTo(16f);
            baiduMap.animateMapStatus(update);
            isFirstLocate = false;
        }
        MyLocationData.Builder locationBuilder = new MyLocationData.Builder();
        locationBuilder.latitude(location.getLatitude());
        locationBuilder.longitude(location.getLongitude());
        MyLocationData locationData = locationBuilder.build();
        baiduMap.setMyLocationData(locationData);
    }
    ...
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //在activity執行onDestroy時執行mMapView.onDestroy(),實現地圖生命周期管理
        mLocationClient.stop();
        mMapView.onDestroy();
        //在程式退出的時候,也要記得將BaiduMap的setMyLocationEnabled()方法功能給關閉掉
        baiduMap.setMyLocationEnabled(false);
    }
}

在navigateTo()方法中,添加了MyLocationData的構建邏輯,將Location中包含的經度和緯度分別封裝到了MyLocationData.Builder當中,最後把MyLocationData設置到了BaiduMap的setMyLocationData()方法當中。

註意:這段邏輯必須寫在isFirstLocate這個if條件語句的外面,因為讓地圖移動到我們當前的位置只需要在第一次定位的時候執行,但是設備在地圖上顯示的位置卻應該是隨著設備的移動而實時改變的。

image

想要更加深入地研究百度LBS的各種用法,可以到官方網站上面參考開髮指南,地址是:http://lbsyun.baidu.com。另外,百度LBS SDK的版本未來隨時都有可能更新,也許更新之後會導致書上的例子無法正常運行,因此除了照著圖書學習之外,根據官網的開髮指南來進行學習也是非常重要的

11.5 Git時間——版本控制工具的高級用法

打開Git Bash,併進入到LBSTest2這個項目的根目錄,然後執行提交操作:

git init
git add
git commit -m "First Commit"

這樣就將準備工作完成了,下麵就讓我們開始學習關於Git的高級用法。

11.5.1 分支的用法

分支是版本控制工具中比較高級且比較重要的一個概念,它主要的作用就是在現有代碼的基礎上開闢一個分叉口,使得代碼可以在主幹線和分支線上同時進行開發,且相互之間不會影響。分支的工作原理示意圖如圖:

image

為什麼需要建立分支呢?只在主幹線上進行開發不是挺好的嗎?

通常情況下,只在主幹線上進行開發是完全沒有問題的,不過一旦涉及出版本的情況,如果不建立分支的話,你就會非常地頭疼。舉個簡單的例子吧,比如說你們公司研發了一款不錯的軟體,最近剛剛完成,並推出了1.0版本。但是領導是不會讓你們閑著的,馬上提出了新的需求,讓你們投入到了1.1版本的開發工作當中。過了幾個星期,1.1版本的功能已完成了一半,但是這個時候有用戶反饋,之前上線的1.0版本發現了幾個重大的bug,嚴重影響軟體的正常使用。領導也相當重視這個問題,要求你們立刻修複這些bug,並重新發佈1.0版本,但這個時候你就非常為難了,你會發現根本沒法去修複這些bug。因為現在1.1版本已開發一半了,如果在現有代碼的基礎上修複這些bug,那麼更新的1.0版本將會帶有一半1.1版本的功能!

但是如果你使用了分支的話,就完全不會存在這個讓人頭疼的問題。你只需要在發佈1.0版本的時候建立一個分支,然後在主幹線上繼續開發1.1版本的功能。當1.0版本上發現任何bug的時候,就在分支線上進行修改,然後發佈新的1.0版本,並記得將修改後的代碼合併到主幹線上。這樣的話,不僅可以輕鬆解決掉1.0版本存在的bug,而且保證了主幹線上的代碼也已經修複了這些bug,當1.1版本發佈時就不會有同樣的bug存在了。

分支的英文名是branch,如果想要查看當前的版本庫當中有哪些分支,可以使用git branch這個命令,結果如圖:

image

由於目前LBSTest2項目中還沒有創建過任何分支,因此只有一個master分支存在,這也就是前面所說的主幹線。接下來我們嘗試去創建一個分支,命令如下:

git branch version1.0

這樣就創建了一個名為version1.0的分支,我們再次輸入git branch這個命令來檢查一下,結果如圖:

image

可以看到,果然有一個叫作version1.0的分支出現了。你會發現,master分支的前面有一個“*”號,說明目前我們的代碼還是在master分支上的,怎樣切換到version1.0這個分支上——使用checkout命令即可,如下所示:

git checkout version1.0

再次輸入git branch來進行檢查,結果如圖:

image

可以看到,我們已經把代碼成功切換到version1.0這個分支上了。

需要註意的是,在version1.0分支上修改並提交的代碼將不會影響到master分支。同樣的道理,在master分支上修改並提交的代碼也不會影響到version1.0分支。因此,如果我們在version1.0分支上修複了一個bug,在master分支上這個bug仍然是存在的

這時將修改的代碼一行行複製到master分支上顯然不是一種聰明的做法,最好的辦法就是使用merge命令來完成合併操作,如下所示:

git checkout master
git merge version1.0

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

-Advertisement-
Play Games
更多相關文章
  • 在數倉項目中,我們常常會選擇Apache Atlas進行數據的治理。本文結合筆者在生產環境中遇到的常見問題及解決方法,整合出完整的Atlas編譯、部署及使用過程。 ...
  • 1、高可用性的目的是什麼? 高可用性的目標是以最小的停機時間提供連續的服務(唯一真正具有 "零 "停機時間的設備是心臟起搏器和核武器中的安全裝置)。這意味著,如果一個組件發生故障,另一個組件可以立即接管其功能,而不會實質性地中斷對系統用戶的服務。高可用性還要求有能力檢測到一個或多個組件發生故障,然後 ...
  • 上一篇文章我們演示瞭如何《在 S3 備份恢復 RadonDB MySQL 集群數據》,本文將演示在 KubeSphere[1] 中使用 Prometheus[2] + Grafana[3] 構建 MySQL 監控平臺,開啟所需監控指標。 背景 Prometheus 基於文本的暴露格式,已經成為雲原生 ...
  • 聲明:全文來源《mysql SQL必知必會(第3版)》 第一章 瞭解SQL 1.1 資料庫基礎 資料庫(database)保存有組織的數據的容器 表(table)某種特定類型數據的結構化清單。資料庫中的每個表都有一個用來標識自己的名字。此名字是唯一的。 模式(schema)關於資料庫和表的佈局及特性 ...
  • 本文介紹什麼是通配符、如何使用通配符,以及怎樣使用 SQL LIKE 操作符進行通配搜索,以便對數據進行複雜過濾。 一、LIKE 操作符 前面介紹的所有操作符都是針對已知值進行過濾的。不管是匹配一個值還是多個值,檢驗大於還是小於已知值,或者檢查某個範圍的值,其共同點是過濾中使用的值都是已知的。 但是 ...
  • 本文和接下來的幾篇文章為閱讀郭霖先生所著《第一行代碼:Android(篇第2版)》的學習筆記,按照書中的內容順序進行記錄,書中的Demo本人全部都做過了。 每一章節本人都做了詳細的記錄,以下是我學習記錄(包含大量書中內容的整理和自己在學習中遇到的各種bug及解決方案),方便以後閱讀和查閱。最後,非常 ...
  • 本文和接下來的幾篇文章為閱讀郭霖先生所著《第一行代碼:Android(篇第2版)》的學習筆記,按照書中的內容順序進行記錄,書中的Demo本人全部都做過了。 每一章節本人都做了詳細的記錄,以下是我學習記錄(包含大量書中內容的整理和自己在學習中遇到的各種bug及解決方案),方便以後閱讀和查閱。最後,非常 ...
  • 本文和接下來的幾篇文章為閱讀郭霖先生所著《第一行代碼:Android(篇第2版)》的學習筆記,按照書中的內容順序進行記錄,書中的Demo本人全部都做過了。 每一章節本人都做了詳細的記錄,以下是我學習記錄(包含大量書中內容的整理和自己在學習中遇到的各種bug及解決方案),方便以後閱讀和查閱。最後,非常 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...