Adolph
Engineer
Engineer
  • UID623
  • Fans2
  • Follows1
  • Posts72
Reads:1137Replies:0

Developing Android Apps with the Go Language - A Quick Start with the Go Language

Created#
More Posted time:Sep 21, 2016 13:38 PM
Go at a glance
The following introduces some significant differences between Go and C/C++/Java to help you quickly gain an understanding of the Go language. Then, you can learn it further during practices.
Go supports GC
This exempts you from managing memory yourself.
However, if GC affects performance, you will have to optimize GC.
Variable types are specified after variables
Example:
Variable:
var i int = 10

Constant
const ClassFile string = FilePath + "Test.class"

Struct is specified after a user-defined type
Unlike the C language, the Go language requires that struct must be specified after a user-defined type. This requirement also applies to system types. The keyword type must be specified first.
Example:
type ELFFile struct {
    eiClass      int
    littleEndian bool
    osABI        string
}


Types can be inferred and multiple function parameters can be returned
Definitions and values can be combined in a function using the := operator. In this case, specifying types is not required, because types can be inferred based on the statement that follows.
Example:
buf, err := ioutil.ReadFile(elfFileName)

The preceding example shows that a function of the Go language can return multiple parameters. If some parameters are not important, they can be replaced by the special variable "_". Ignore such variables.
Unused variables and packages cause compilation failures
If a referenced package or defined variable is not used, no alarm will be generated. Instead, compilation fails.
A public variable or function has a capitalized initial, while a private variable or function has a non-capitalized initial
The Go language does not define qualifiers to distinguish between public and private variables and functions. If a variable or function has a capitalized initial, such as Println, it is a public variable or function. Otherwise, it is a private variable or function.
Array copying is performed during array value assignment
Note that when the values of one array are assigned to another, array copying is performed.
Parentheses are not required for flow control
The parentheses in the if and for statements can be omitted.
In the following example, the condition next to the if statement is not included in parentheses:
if err != nil {
        fmt.Println("Error reading ELF:", err)
    }


The switch statement includes a break by default
The switch statement of the Go language includes a break by default. Therefore, you do not need to specify a break for it. If the break is not required, use the fallthrough statement to cancel the break.
The Go language provides pointers
By default, values are transferred when copying is required. If references must be transferred to address copying requirements, use pointers.
The Go language provides the goto statement
Avoid frequent usage of this statement.
The Go language also supports the break and continue statements. These two statements support redirection with labels. You can reserve goto for the final redirection.
The Go languages supports only for as a loop statement
The Go language does not provide the while, do while, or do until loop statement. The for is the only loop statement.
The following provides an example of an infinite loop:
for ;; {
        ...
    }


main and init functions
The main function of the main package is the Go language entry point.
One init function can be created for each package and the function will be called automatically.
The Go language does not provide the this pointer
Objects must be explicitly specified. The this point is unavailable to implicitly specify objects.
The following provides an example of explicitly specifying objects:
func (elfFile *ELFFile) ParseEIClass_v2(value byte) {
    if value == 1 {
        elfFile.eiClass = 32
        fmt.Println("It is 32-bit")
    } else if value == 2 {
        elfFile.eiClass = 64
        fmt.Println("It is 64-bit")
    } else {
        elfFile.eiClass = 0
        fmt.Println("unknown format, neither 32-bit nor 64-bit")
    }
}


The Go language requires that target object declaration must be added before a common function definition.
Non-intrusive interface design
The Ducking Principle is applied, which means that if something walks the way a duck walks and sounds like a duck, you can deem that it is a duck.
This is the principle for designing the interfaces of the Go language.
The following provides an example, which involves three VMs that support the athrow method:
package main

type Hotspot struct {
}

type Dalvik struct {
}

type AndroidRuntime struct {
}

func (vm Hotspot) athrow() {

}

func (dalvik Dalvik) athrow() {
    dalvik.throw()
}

func (dalvik Dalvik) throw() {

}

func (art AndroidRuntime) athrow() {
    art.pDeliverException()

}

func (art AndroidRuntime) pDeliverException() {

}


The preceding three types of MVs are implemented in different ways. Hotspot directly uses the athrow method, Dalvik uses its own throw method, while ART calls pDeliverException.
However, it is declared that all of them support the athrow method.
Therefore, an interface named SupportException is declared to define the athrow method.
Then, variables of the SupportException type can be assigned for various JVM implementations.
The following provides an example:
type SupportException interface {
    athrow()
}

func throwException() {
    hotspot := Hotspot{}
    dalvik := Dalvik{}
    art := AndroidRuntime{}

    var jvm1 SupportException
    var jvm2 SupportException
    var jvm3 SupportException

    jvm1 = hotspot
    jvm1.athrow()

    jvm2 = dalvik
    jvm2.athrow()

    jvm3 = art
    jvm3.athrow()
}


This means that you can implement classes without considering interface definitions. After the classes are implemented, you can define an interface based on the class implementations.
defer mechanism
The defer is a function-level mechanism to defer execution. It serves as an equivalent of the function-level finally statement and will definitely be executed.
For example, after a file is opened successfully, a file closure or channel operation can be deferred.
func dex2oat(ch chan bool, dexFile string) {
    defer close(ch)
    ch <- dex2oatImpl(dexFile)
    fmt.Println("Dex2OAT finished!")
}


This mechanism helps ensure that such operation will be performed, especially when a function has much content.
Reference type
Slice
Arrays of the Go language are read-only.
Array length can be defined using either of the following methods:
• 1. Directly specify the length.
• 2. Allow the Go language to calculate the length.
You can use the following statement to directly specify the length:
magic := [4]byte{0x7f, 'E', 'L', 'F'}

If you are reluctant to calculate the length or it is difficult to calculate the length, you can use an ellipsis so that the compiler will calculate the length. See the following example:
magic := [...]byte{0x7f, 'E', 'L', 'F'}

Why do we need to enter an ellipsis instead of entering square brackets without content?
Square brackets without content represent a slice rather than an array.
Example of a slice:
magic2 := buf[0 : 3]

The buf indicates an array, while magic2 indicates a slice from element 0 to element 3.
If a slice begins with the first element, the content before the colon can be omitted. If a slice ends with the last element, the content after the colon can be omitted. If a slice covers an entire array, you can omit both the content before and after the colon.
The following example shows how a byte in a slice can be consumed to generate a new slice:
func ReadU1_v2(data []byte) (byte, []byte) {
    if data == nil {
        return 0, nil
    } else if len(data) > 1 {
        return data[0], data[1:]
    } else {
        return data[0], nil
    }
}


Slice attributes
A slice is a data structure consisting of three pieces of data:
• Pointer pointing to an array
• Length of the slice (In the preceding example, the len() function is used to identify the length.)
• Maximum capacity of the slice, which can be identified using the cap() function
Slice functions
• Append: adds one or more elements to a slice. It can be regarded as implementing the dynamic array function. If the capacity of the array to which a slice points is lower than the capacity of the slice, a new array is allocated to the slice, while the original array remains unchanged.
• Copy: copies data between slices.
Map
The Go language supports maps.
• Map is a reference type. If two maps point to the same bottom-layer data structure, one of the maps changes as the other changes.
• Map values are assigned using key-value pairs.
• A key can be any == or != operation type.
• Maps are not sorted in order and map traversal is not supported.
Memory allocation for reference types
You can use the make function to allocate memory for maps, slices, and channels (introduced in the last section).
User-defined types
We have mentioned above that the this pointer is unavailable. This section provides a summary.
Define user-defined types
Use the struct keyword to define the types.
type ELFFile struct {
    elfFileName  string
    eiClass      int
    littleEndian bool
    osABI        string
}


Use user-defined types
Use them as common types. The simplest way to use such a type is using := to assign it to a variable. In this way, you do not need to specify the variable type.
You can use the key:value method to assign an initial value.
Example:
elfFile := ELFFile{elfFileName: OatFile}

Define methods for user-defined types
As mentioned above, add a target object declaration before a common function.
Example:
func (elfFile *ELFFile) ParseEiData_v2(value byte) { switch value {
    case 1:
        elfFile.littleEndian = true
        fmt.Println("It is Little Endian")
        IsLittleEndian = true
    case 2:
        elfFile.littleEndian = false
        fmt.Println("It is Big Endian")
        IsLittleEndian = false
    default:
        fmt.Println("Unknow Endian, the value is:", value)
    }
}


Multi-task mechanism of the Go language
The Go language supports concurrent processing. Using the go statement, you can run each function in one Goroutine like a thread.
Multiple Goroutines communicate with each other using channels, which transfer messages.
For example, the future mode can be implemented to allow concurrent processing of three dex2oat tasks. See the following code:
func dex2oat(ch chan bool, dexFile string) {
    ch <- dex2oatImpl(dexFile)
    fmt.Println("Dex2OAT finished!")
}

func dex2oatImpl(dexFile string) bool {
    return true
}


According to the preceding code, the dex2oat function has a channel of the bool type. The channel is used to return results to the invocation party.
The invocation code is as follows:
channels := make([]chan bool, 3)
    for i := 0; i < len(channels); i++ {
        channels = make(chan bool)
    }
    go dex2oat(channels[0], "Test1.dex")
    go dex2oat(channels[1], "Test2.dex")
    go dex2oat(channels[2], "Test3.dex")

    for _, ch := range channels {
        value := <-ch
        fmt.Println("The result is ", value)
    }


First, a channel array is created and make is used to create a channel object.
Then, the go keyword is used to create three Goroutine for separate dex2oat execution.
In this case, the major task waits until the three execution tasks return results and joins the results before proceeding to the following operations.
Summary
The preceding sections provide the core content of the Go language.
The content is not detailed. I hope that this introduction brings you an idea that the Go language is easy to pick up.
The Go language has a total of 25 keywords, which are described in this article except select. To help you better understand them, the following structure diagram is provided:


If it is not clear enough, let's divide it into two parts and remove the branches:


Branches:
Guest