Android 平台利用基于用户的 Linux 保护机制来识别和隔离应用资源,可将不同的应用分离开,并保护应用和系统免受恶意应用的攻击。为此,Android 会为每个 Android 应用分配一个独一无二的用户 ID (UID),并在自己的进程中运行。
Android 会使用此 UID 设置一个内核级应用沙盒。内核会在进程级别利用标准的 Linux 机制(例如,分配给应用的用户 ID 和组 ID)实现应用和系统之间的安全防护。 默认情况下,应用不能彼此交互,而且对操作系统的访问权限会受到限制。例如,如果应用 A(一个单独的应用)尝试执行恶意操作,例如在没有权限的情况下读取应用 B 的数据或拨打电话,操作系统会阻止此类行为,因为应用 A 没有适当的用户权限。这一沙盒机制非常简单,可审核,并且基于已有数十年历史的 UNIX 风格的进程用户隔离和文件权限机制。
/** * The buildscript block is where you configure the repositories and * dependencies for Gradle itself—meaning, you should not include dependencies * for your modules here. For example, this block includes the Android plugin for * Gradle as a dependency because it provides the additional instructions Gradle * needs to build Android app modules. */
buildscript {
/** * The repositories block configures the repositories Gradle uses to * search or download the dependencies. Gradle pre-configures support for remote * repositories such as JCenter, Maven Central, and Ivy. You can also use local * repositories or define your own remote repositories. The code below defines * JCenter as the repository Gradle should use to look for its dependencies. * * New projects created using Android Studio 3.0 and higher also include * Google's Maven repository. */
repositories { google() jcenter() }
/** * The dependencies block configures the dependencies Gradle needs to use * to build your project. The following line adds Android plugin for Gradle * version 3.4.2 as a classpath dependency. */
/** * The allprojects block is where you configure the repositories and * dependencies used by all modules in your project, such as third-party plugins * or libraries. However, you should configure module-specific dependencies in * each module-level build.gradle file. For new projects, Android Studio * includes JCenter and Google's Maven repository by default, but it does not * configure any dependencies (unless you select a template that requires some). */
// This block encapsulates custom properties and makes them available to all // modules in the project. ext { // The following are only a few examples of the types of properties you can define. compileSdkVersion = 28 // You can also create properties to specify versions for dependencies. // Having consistent versions between modules can avoid conflicts with behavior. supportLibVersion = "28.0.0" ... }
android { // Use the following syntax to access properties you defined at the project level: // rootProject.ext.property_name compileSdkVersion rootProject.ext.compileSdkVersion ... } ... dependencies { implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" ... }
/** * The first line in the build configuration applies the Android plugin for * Gradle to this build and makes the android block available to specify * Android-specific build options. */
apply plugin:'com.android.application'
/** * The android block is where you configure all your Android-specific * build options. */
android {
/** * compileSdkVersion specifies the Android API level Gradle should use to * compile your app. This means your app can use the API features included in * this API level and lower. */
compileSdkVersion 28
/** * buildToolsVersion specifies the version of the SDK build tools, command-line * utilities, and compiler that Gradle should use to build your app. You need to * download the build tools using the SDK Manager. * * This property is optional because the plugin uses a recommended version of * the build tools by default. */
buildToolsVersion "29.0.0"
/** * The defaultConfig block encapsulates default settings and entries for all * build variants, and can override some attributes in main/AndroidManifest.xml * dynamically from the build system. You can configure product flavors to override * these values for different versions of your app. */
defaultConfig {
/** * applicationId uniquely identifies the package for publishing. * However, your source code should still reference the package name * defined by the package attribute in the main/AndroidManifest.xml file. */
applicationId 'com.example.myapp'
// Defines the minimum API level required to run the app. minSdkVersion 15
// Specifies the API level used to test the app. targetSdkVersion 28
// Defines the version number of your app. versionCode 1
// Defines a user-friendly version name for your app. versionName "1.0" }
/** * The buildTypes block is where you can configure multiple build types. * By default, the build system defines two build types: debug and release. The * debug build type is not explicitly shown in the default build configuration, * but it includes debugging tools and is signed with the debug key. The release * build type applies Proguard settings and is not signed by default. */
buildTypes {
/** * By default, Android Studio configures the release build type to enable code * shrinking, using minifyEnabled, and specifies the Proguard settings file. */
release { minifyEnabled true// Enables code shrinking for the release build type. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
/** * The productFlavors block is where you can configure multiple product flavors. * This allows you to create different versions of your app that can * override the defaultConfig block with their own settings. Product flavors * are optional, and the build system does not create them by default. * * This example creates a free and paid product flavor. Each product flavor * then specifies its own application ID, so that they can exist on the Google * Play Store, or an Android device, simultaneously. * * If you declare product flavors, you must also declare flavor dimensions * and assign each flavor to a flavor dimension. */
/** * The splits block is where you can configure different APK builds that * each contain only code and resources for a supported screen density or * ABI. You'll also need to configure your build so that each APK has a * different versionCode. */
splits { // Settings to build multiple APKs based on screen density. density {
// Enable or disable building multiple APKs. enable false
// Exclude these densities when building multiple APKs. exclude "ldpi", "tvdpi", "xxxhdpi", "400dpi", "560dpi" } } }
/** * The dependencies block in the module-level build configuration file * specifies dependencies required to build only the module itself. * To learn more, go to Add build dependencies. */
如果您要完全重构您的软件包名称,请确保也更新 package 属性。只要您使用 Android Studio 的工具重命名和重构您的软件包,那么这些就会自动保持同步。(如果它们未保持同步,您的应用代码将无法解析 R 类,因为它不再位于同一软件包中,并且清单无法识别您的 Activity 或其他组件。)
dependencies { // Adds a remote binary dependency only for local tests. testImplementation 'junit:junit:4.12'
// Adds a remote binary dependency only for the instrumented test APK. androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' }
dependencies { // Adds libraries defining annotations to only the compile classpath. compileOnly 'com.google.dagger:dagger:version-number' // Adds the annotation processor dependency to the annotation processor classpath. annotationProcessor 'com.google.dagger:dagger-compiler:version-number' }
// Annotates each directory as either an input or output for the // annotation processor. @InputFiles // Using this annotation helps Gradle determine which part of the file path // should be considered during up-to-date checks. @PathSensitive(PathSensitivity.RELATIVE) FileCollection inputDir
@OutputDirectory File outputDir
// The class constructor sets the paths for the input and output directories. MyArgsProvider(FileCollection input, File output) { inputDir = input outputDir = output }
// Specifies each directory as a command line argument for the processor. // The Android plugin uses this method to pass the arguments to the // annotation processor. @Override Iterable<String> asArguments(){ // Use the form '-Akey[=value]' to pass your options to the Java compiler. ["-AinputDir=${inputDir.singleFile.absolutePath}", "-AoutputDir=${outputDir.absolutePath}"] } }
// This is in your module's build.gradle file. android { defaultConfig { javaCompileOptions { annotationProcessorOptions { // Creates a new MyArgsProvider object, specifies the input and // output paths for the constructor, and passes the object // to the Android plugin. compilerArgumentProvider new MyArgsProvider(files("input/path"), new File("output/path")) } } } }
Conflict with dependency 'com.example.library:some-lib:2.0' in project 'my-library'. Resolved versions for runtime classpath (1.0) and compile classpath (2.0) differ.
/** * The `initWith` property allows you to copy configurations from other build types, * then configure only the settings you want to change. This one copies the debug build * type, and then changes the manifest placeholder and application ID. */ staging { initWith debug manifestPlaceholders = [hostName:"internal.example.com"] applicationIdSuffix ".debugStaging" } } }
android { ... defaultConfig {...} buildTypes { debug{...} release{...} } // Specifies one flavor dimension. flavorDimensions "version" productFlavors { demo { // Assigns this product flavor to the "version" flavor dimension. // This property is optional if you are using only one dimension. dimension "version" applicationIdSuffix ".demo" versionNameSuffix "-demo" } full { dimension "version" applicationIdSuffix ".full" versionNameSuffix "-full" } } }
// Specifies the flavor dimensions you want to use. The order in which you // list each dimension determines its priority, from highest to lowest, // when Gradle merges variant sources and configurations. You must assign // each product flavor you configure to one of the flavor dimensions. flavorDimensions "api", "mode"
productFlavors { demo { // Assigns this product flavor to the "mode" flavor dimension. dimension "mode" ... }
full { dimension "mode" ... }
// Configurations in the "api" product flavors override those in "mode" // flavors and the defaultConfig block. Gradle determines the priority // between flavor dimensions based on the order in which they appear next // to the flavorDimensions property above--the first dimension has a higher // priority than the second, and so on. minApi24 { dimension "api" minSdkVersion 24 // To ensure the target device receives the version of the app with // the highest compatible API level, assign version codes in increasing // value with API level. To learn more about assigning version codes to // support app updates and uploading to Google Play, read Multiple APK Support versionCode 30000 + android.defaultConfig.versionCode versionNameSuffix "-minApi24" ... }
variantFilter { variant -> def names = variant.flavors*.name // To check for a certain build type, use variant.buildType.name == "<buildType>" if (names.contains("minApi21") && names.contains("demo")) { // Gradle ignores any variants that satisfy the conditions above. setIgnore(true) } } }
android { ... sourceSets { // Encapsulates configurations for the main source set. main { // Changes the directory for Java sources. The default directory is // 'src/main/java'. java.srcDirs = ['other/java']
// If you list multiple directories, Gradle uses all of them to collect // sources. Because Gradle gives these directories equal priority, if // you define the same resource in more than one directory, you get an // error when merging resources. The default directory is 'src/main/res'. res.srcDirs = ['other/res1', 'other/res2']
// Note: You should avoid specifying a directory which is a parent to one // or more other directories you specify. For example, avoid the following: // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings'] // You should specify either only the root 'other/res1' directory, or only the // nested 'other/res1/layouts' and 'other/res1/strings' directories.
// For each source set, you can specify only one Android manifest. // By default, Android Studio creates a manifest for your main source // set in the src/main/ directory. manifest.srcFile 'other/AndroidManifest.xml' ... }
// Create additional blocks to configure other source sets. androidTest {
// If all the files for a source set are located under a single root // directory, you can specify that directory using the setRoot property. // When gathering sources for the source set, Gradle looks only in locations // relative to the root directory you specify. For example, after applying the // configuration below for the androidTest source set, Gradle looks for Java // sources only in the src/tests/java/ directory. setRoot 'src/tests' ... } } }
dependencies { // Adds the local "mylibrary" module as a dependency to the "free" flavor. freeImplementation project(":mylibrary")
// Adds a remote binary dependency only for local tests. testImplementation 'junit:junit:4.12'
// Adds a remote binary dependency only for the instrumented test APK. androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' }
配置签名设置
除非您明确定义此版本的签名配置,否则 Gradle 不会为该版本的 APK 签名。您可以轻松创建发布密钥并使用 Android Studio 为发布版本类型签名。要使用 Gradle 编译配置为您的发布版本类型手动配置签名,请执行以下操作:
android { // ... splits { // Configures multiple APKs based on screen density. density { // Configures multiple APKs based on screen density. enable true // Specifies a list of screen densities Gradle should not create multiple APKs for. exclude "ldpi", "xxhdpi", "xxxhdpi" // Specifies a list of compatible screen size settings for the manifest. compatibleScreens 'small', 'normal', 'large', 'xlarge' } } }
reset() // Clears the default list from all densities to no densities. include "ldpi", "xxhdpi"// Specifies the two densities we want to generate APKs for.
// Map for the version code that gives each ABI a value. ext.abiCodes = ['armeabi-v7a':1, 'x86':2, 'x86_64':3]
// For per-density APKs, create a similar map like this: // ext.densityCodes = ['mdpi': 1, 'hdpi': 2, 'xhdpi': 3]
import com.android.build.OutputFile
// For each APK output variant, override versionCode with a combination of // ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode // is equal to defaultConfig.versionCode. If you configure product flavors that // define their own versionCode, variant.versionCode uses that value instead. android.applicationVariants.all { variant ->
// Assigns a different version code for each output APK // other than the universal APK. variant.outputs.each { output ->
// Stores the value of ext.abiCodes that is associated with the ABI for this variant. def baseAbiVersionCode = // Determines the ABI for this variant and returns the mapped value. project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
// Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes, // the following code does not override the version code for universal APKs. // However, because we want universal APKs to have the lowest version code, // this outcome is desirable. if (baseAbiVersionCode != null) {
// Assigns the new version code to versionCodeOverride, which changes the version code // for only the output APK, not for the variant itself. Skipping this step simply // causes Gradle to use the value of variant.versionCode for the APK. output.versionCodeOverride = baseAbiVersionCode * 1000 + variant.versionCode } } }
合并工具可以在逻辑上将一个清单中的每个 XML 元素与另一个清单中的对应元素相匹配。如果优先级较低的清单中的某个元素与优先级较高的清单中的任何元素都不匹配,则会将该元素添加到合并后的清单。不过,如果有匹配的元素,则合并工具会尝试将每个元素的所有属性组合到同一元素中。如果该工具发现两个清单包含相同的属性,但值不同,则会发生合并冲突。
高优先级属性
低优先级属性
属性的合并结果
没有值
没有值
没有值(使用默认值)
没有值
值 B
值 B
值 A
没有值
值 A
值 A
值 A
值 A
值 A
值 B
冲突错误 - 您必须添加合并规则标记
不过,在某些情况下,合并工具会采取其他行为方式以避免合并冲突:
<manifest> 元素中的属性绝不会合并在一起 - 仅使用优先级最高的清单中的属性。
<uses-feature> 和 <uses-library> 元素中的 android:required 属性使用 OR 合并,这样一来,如果发生冲突,系统将应用 “true” 并始终包含某个清单所需的功能或库。
注意:如果您已将应用的 targetSdkVersion 设为 23 或更高的值,那么当应用试图访问受任何危险权限保护的 API 时,您必须对这些权限执行运行时权限请求。
优先级较低的清单声明
向合并后的清单添加的权限
targetSdkVersion 为 3 或更低的值
WRITE_EXTERNAL_STORAGE、READ_PHONE_STATE
targetSdkVersion 为 15 或更低的值,并且使用 READ_CONTACTS
READ_CALL_LOG
targetSdkVersion 为 15 或更低的值,并且使用 WRITE_CONTACTS
WRITE_CALL_LOG
检查合并后的清单并查找冲突
可以通过Android Studio提供的工具查看合并后的Manifest文件。
合并策略
清单合并工具可以在逻辑上将一个清单文件中的每个 XML 元素与另一个文件中的对应元素匹配。合并工具会使用“匹配键”来匹配每个元素,匹配键可以是唯一的属性值(如 android:name),也可以是标记本身的自然唯一性(例如,只能有一个 <supports-screen> 元素)。如果两个清单具有相同的 XML 元素,则该工具会采用三种合并策略中的一种,将这两个元素合并在一起:
例如,如果您使用的库包含语言资源(例如使用的是 AppCompat 或 Google Play 服务),则 APK 将包括这些库中消息的所有已翻译语言字符串,无论应用的其余部分是否翻译为同一语言。如果您想只保留应用正式支持的语言,则可以利用 resConfig 属性指定这些语言。系统会移除未指定语言的所有资源。
如果您使用的不是严格检查,则存在看似可用于为动态加载资源构建资源名称的字符串常量时,可将资源 ID 标记为可访问。在这种情况下,如果您在构建输出中搜索资源名称,可能会找到类似下面这样的消息:
1 2
10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506 used because it format-string matches string pool constant ic_plus_anim_%1$d.
android { defaultConfig { ... multiDexEnabled true // The default minimum API level you want to support. minSdkVersion 15 } productFlavors { // Includes settings you want to keep only while developing your app. dev { // Enables pre-dexing for command line builds. When using // Android Studio 2.3 or higher, the IDE enables pre-dexing // when deploying your app to a device running Android 5.0 // (API level 21) or higher—regardless of what you set for // minSdkVersion. minSdkVersion 21 } prod { // If you've configured the defaultConfig block for the production version of // your app, you can leave this block empty and Gradle uses configurations in // the defaultConfig block instead. You still need to include this flavor. // Otherwise, all variants use the "dev" flavor configurations. } } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile 'com.android.support:multidex:1.0.3' }
提示:由于您有满足不同多 dex 文件需求的不同编译变体,因此也可以为不同的变体提供不同的清单文件(这样,只有适用于 API 级别 20 及更低级别的清单文件会更改 <application> 标记名称),或者为每个变体创建不同的 Application 子类(这样,只有适用于 API 级别 20 及更低级别的子类会扩展 MultiDexApplication 类或调用 MultiDex.install(this))。
Android 天生为兼容各种各样不同的设备做了相当多的工作,比如屏幕大小、国际化、键盘、像素密度等等,我们能为各种各样特定的场景下使用特定的资源做兼容而不用改动一行代码,假设我们为各种各样不同的场景适配了不同的资源,如何能快速的应用上这些资源呢?Android 为我们提供了 R 这个类,指定了一个资源的索引(id),然后我们只需要告诉系统在不同的业务场景下,使用对应的资源就好了,至于具体是指定资源里面的哪一个具体文件,由系统根据开发者的配置决定。
在这种场景下,假设我们给定的 id 是 x 值,那么当下业务需要使用这个资源的时候,手机的状态就是 y 值,有了(x,y),在一个表里面就能迅速的定位到资源文件的具体路径了。这个表就是 resources.arsc,它是从 aapt 编译出来的。
其实二进制的资源(比如图片)是不需要编译的,只不过这个“编译”的行为,是为了生成 resources.arsc 以及对 xml 文件进行二进制化等操作,resources.arsc 是上面说的表,xml 的二进制化是为了系统读取上性能更好。AssetManager 在我们调用 R 相关的 id 的时候,就会在这个表里面找到对应的文件,读取出来。
Unable to locate class 'Mai' java.lang.ClassNotFoundException: Didn't find class "Mai" on path: DexPathList[[dex file "classes.dex"],nativeLibraryDirectories=[/system/lib64, /vendor/lib64, /system/lib64, /vendor/lib64]] at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134) at java.lang.ClassLoader.loadClass(ClassLoader.java:379) at java.lang.ClassLoader.loadClass(ClassLoader.java:312) Exception in thread "main" java.lang.ClassNotFoundException: Didn't find class "Mai" on path: DexPathList[[dex file "classes.dex"],nativeLibraryDirectories=[/system/lib64, /vendor/lib64, /system/lib64, /vendor/lib64]] at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134) at java.lang.ClassLoader.loadClass(ClassLoader.java:379) at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
OpenGL ES 版本要求 - 如果应用需要特定版本的 OpenGL ES,则必须通过将以下设置添加到 Manifest 中来声明该要求,如下所示。
1 2 3 4 5 6
<!-- Tell the system this app requires OpenGL ES 2.0. --> <uses-featureandroid:glEsVersion="0x00020000"android:required="true" /> <!-- Tell the system this app requires OpenGL ES 3.0. --> <uses-featureandroid:glEsVersion="0x00030000"android:required="true" /> <!-- Tell the system this app requires OpenGL ES 3.1. --> <uses-featureandroid:glEsVersion="0x00030001"android:required="true" />
init { // Create an OpenGL ES 2.0 context setEGLContextClientVersion(2) renderer = MyGLRenderer() setRenderer(renderer) // 该设置可防止系统在调用 requestRender() 之前重新绘制 GLSurfaceView 帧,更为高效。 // Render the view only when there is a change in the drawing data renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY } }
classMyGLRenderer : GLSurfaceView.Renderer {
overridefunonSurfaceCreated(unused: GL10, config: EGLConfig) { // Set the background frame color GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f) }
overridefunonDrawFrame(unused: GL10) { // Redraw background color GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT) }
// number of coordinates per vertex in this array constval COORDS_PER_VERTEX = 3 var triangleCoords = floatArrayOf( // in counterclockwise order: 0.0f, 0.622008459f, 0.0f, // top -0.5f, -0.311004243f, 0.0f, // bottom left 0.5f, -0.311004243f, 0.0f// bottom right )
classTriangle{
// Set color with red, green, blue and alpha (opacity) values val color = floatArrayOf(0.63671875f, 0.76953125f, 0.22265625f, 1.0f)
// (number of coordinate values * 4 bytes per float) privatevar vertexBuffer: FloatBuffer = ByteBuffer.allocateDirect(triangleCoords.size * 4).run { // use the device hardware's native byte order order(ByteOrder.nativeOrder())
// create a floating point buffer from the ByteBuffer asFloatBuffer().apply { // add the coordinates to the FloatBuffer put(triangleCoords) // set the buffer to read the first coordinate position(0) } } }
定义方形
有多种方式可以定义方形,但在 OpenGL ES 中绘制此类形状的典型方式是使用两个绘制在一起的三角形:
// number of coordinates per vertex in this array constval COORDS_PER_VERTEX = 3 var squareCoords = floatArrayOf( -0.5f, 0.5f, 0.0f, // top left -0.5f, -0.5f, 0.0f, // bottom left 0.5f, -0.5f, 0.0f, // bottom right 0.5f, 0.5f, 0.0f// top right )
classSquare2{
privateval drawOrder = shortArrayOf(0, 1, 2, 0, 2, 3) // order to draw vertices
着色程序包含 OpenGL 着色语言 (GLSL) 代码,必须先对其进行编译,然后才能在 OpenGL ES 环境中使用。要编译此代码,可以在渲染程序类中创建一个实用程序方法:
1 2 3 4 5 6 7 8 9 10 11
funloadShader(type: Int, shaderCode: String): Int {
// create a vertex shader type (GLES20.GL_VERTEX_SHADER) // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) return GLES20.glCreateShader(type).also { shader ->
// add the source code to the shader and compile it GLES20.glShaderSource(shader, shaderCode) GLES20.glCompileShader(shader) } }
要绘制形状,必须编译着色程序代码,将它们添加到 OpenGL ES 程序对象中,然后关联该程序。该操作需要在绘制对象的构造函数中完成,因此只需执行一次。
init { val vertexShader: Int = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode) val fragmentShader: Int = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)
// create empty OpenGL ES Program mProgram = GLES20.glCreateProgram().also {
// add the vertex shader to program GLES20.glAttachShader(it, vertexShader)
// add the fragment shader to program GLES20.glAttachShader(it, fragmentShader)
// creates OpenGL ES program executables GLES20.glLinkProgram(it) } } }
此时可以添加绘制形状的实际调用,使用 OpenGL ES 绘制形状时需要指定多个参数,以告知渲染管道要绘制的形状以及如何进行绘制。由于绘制选项因形状而异,因此最好使形状类包含自身的绘制逻辑。
创建用于绘制形状的 draw() 方法,此代码将位置和颜色值设置为形状的顶点着色程序和 Fragment 着色程序,然后执行绘制功能。
val ratio: Float = width.toFloat() / height.toFloat()
// this projection matrix is applied to object coordinates // in the onDrawFrame() method Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f) }
classTriangle{ privateval vertexShaderCode = // This matrix member variable provides a hook to manipulate // the coordinates of the objects that use this vertex shader "uniform mat4 uMVPMatrix;" + "attribute vec4 vPosition;" + "void main() {" + // the matrix must be included as a modifier of gl_Position // Note that the uMVPMatrix factor *must be first* in order // for the matrix multiplication product to be correct. " gl_Position = uMVPMatrix * vPosition;" + "}"
// Use to access and set the view transformation privatevar vPMatrixHandle: Int = 0 }
overridefunonDrawFrame(gl: GL10) { val scratch = FloatArray(16)
// ...
// Create a rotation transformation for the triangle val time = SystemClock.uptimeMillis() % 4000L val angle = 0.090f * time.toInt() Matrix.setRotateM(rotationMatrix, 0, angle, 0f, 0f, -1.0f)
// Combine the rotation matrix with the projection and camera view // Note that the vPMatrix factor *must be first* in order // for the matrix multiplication product to be correct. Matrix.multiplyMM(scratch, 0, vPMatrix, 0, rotationMatrix, 0)
classMyGLSurfaceView(context: Context) : GLSurfaceView(context) { init { // Render the view only when there is a change in the drawing data. // To allow the triangle to rotate automatically, this line is commented out: //renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY } }
overridefunonTouchEvent(e: MotionEvent): Boolean { // MotionEvent reports input details from the touch screen // and other input controls. In this case, you are only // interested in events where the touch position changed.
val x: Float = e.x val y: Float = e.y
when (e.action) { MotionEvent.ACTION_MOVE -> {
var dx: Float = x - previousX var dy: Float = y - previousY
// reverse direction of rotation above the mid-line if (y > height / 2) { dx *= -1 }
// reverse direction of rotation to left of the mid-line if (x < width / 2) { dy *= -1 }
overridefunonDrawFrame(gl: GL10) { // ... val scratch = FloatArray(16)
// Create a rotation for the triangle // long time = SystemClock.uptimeMillis() % 4000L; // float angle = 0.090f * ((int) time); Matrix.setRotateM(rotationMatrix, 0, angle, 0f, 0f, -1.0f)
// Combine the rotation matrix with the projection and camera view // Note that the mvpMatrix factor *must be first* in order // for the matrix multiplication product to be correct. Matrix.multiplyMM(scratch, 0, mvpMatrix, 0, rotationMatrix, 0)
$ ./gradlew testPluginTask1 > Configure project :app ** Test This is my first gradle plugin ** ## hello before apply CustomPlugin ** This is my first gradle plugin. msg = null after apply CustomPlugin > Task :app:testPluginTask1 ## This is my first gradle plugin in testPlugin task. msg = testMSG
classExample { staticvoid main(String[] args) { String sample = "Hello world"; println(sample[4]); // Print the 5 character in the string //Print the 1st character in the string starting from the back println(sample[-1]); println(sample[1..2]);//Prints a string starting from Index 1 to 2 println(sample[4..2]);//Prints a string starting from Index 4 back to 2 } }
// We are now changing the value of the String str1 which is referenced in the closure str1 = "Welcome"; clos.call("World"); } ->output: Hello World Welcome World
Movie Name:Enemy Behind Movie Type:War, Thriller Movie Format:DVD Movie year:2003 Movie rating:PG Movie stars:10 Movie description:Talk about a US-Japan war ******************************* Movie Name:Transformers Movie Type:Anime, Science Fiction Movie Format:DVD Movie year:1989 Movie rating:R Movie stars:8 Movie description:A schientific fiction ******************************* Movie Name:Trigun Movie Type:Anime, Action Movie Format:DVD Movie year:1986 Movie rating:PG Movie stars:10 Movie description:Vash the Stam pede! ******************************* Movie Name:Ishtar Movie Type:Comedy Movie Format:VHS Movie year:1987 Movie rating:PG Movie stars:2 Movie description:Viewable boredom
// 内存泄漏 public Object pop(){ if (size == 0) thrownew EmptyStackException(); return elements[--size]; }
// 修改版本 public Object pop(){ if (size == 0) thrownew EmptyStackException(); Object result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; }
/** * Ensure space for at least one more element, roughly * doubling the capacity each time the array needs to grow. */ privatevoidensureCapacity(){ if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); } }
// try-with-resources - the the best way to close resources! static String firstLineOfFile(String path)throws IOException { try (BufferedReader br = new BufferedReader( new FileReader(path))) { return br.readLine(); } }
// try-with-resources on multiple resources - short and sweet staticvoidcopy(String src, String dst)throws IOException { try (InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst)) { byte[] buf = newbyte[BUFFER_SIZE]; int n; while ((n = in.read(buf)) >= 0) out.write(buf, 0, n); } }
// Broken - Inappropriate use of inheritance! publicclassInstrumentedHashSet<E> extendsHashSet<E> { // The number of attempted element insertions privateint addCount = 0;
数组是协变的(covariant),这意味着如果 Sub 是 Super 的子类型,则数组类型 Sub[] 是数组类型 Super[] 的子类型;相比之下,泛型是不变的(invariant),对于任何两种不同的类型 Type1 和 Type2,List 既不是 List 的子类型也不是父类型。
1 2 3 4 5 6 7
// Fails at runtime! Object[] objectArray = new Long[1]; objectArray[0] = "I don't fit in"; // Throws ArrayStoreException
// Won't compile! List<Object> ol = new ArrayList<Long>(); // Incompatible types ol.add("I don't fit in");
优先考虑泛型
使用限定通配符来增加API的灵活性
使用枚举类型替代整型常量
注解优于命名模式
如下实例:
1 2 3 4 5 6
// Anonymous class instance as a function object - obsolete! Collections.sort(words, new Comparator<String>() { publicintcompare(String s1, String s2){ return Integer.compare(s1.length(), s2.length()); } });
可以修改为:
1 2
// Lambda expression as function object (replaces anonymous class) Collections.sort(words, (s1, s2) -> Integer.compare(s1.length(), s2.length()));
# 新建Razor页面项目 $ dotnet new webapp -o RazorPagesMovie # 本地部署 $ dotnet run # 输出 : Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] User profile is available. Using '/home/hearing/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. Hosting environment: Development Content root path: /home/hearing/WorkSpace/asp/hello Now listening on: https://localhost:5001 Now listening on: http://localhost:5000
# 新建Razor页面项目 $ dotnet new mvc -o MvcHello # 本地部署 $ dotnet run # 输出 : Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] User profile is available. Using '/home/hearing/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. Hosting environment: Development Content root path: /home/hearing/WorkSpace/asp/MvcHello Now listening on: https://localhost:5001 Now listening on: http://localhost:5000
using Microsoft.AspNetCore.Mvc; using ViewInjectSample.Model;
namespaceViewInjectSample.Controllers { publicclassProfileController : Controller { [Route("Profile")] public IActionResult Index() { // TODO: look up profile based on logged-in user var profile = new Profile() { Name = "Steve", FavColor = "Blue", Gender = "Male", State = new State("Ohio","OH") }; return View(profile); } } }
using System.Collections.Generic;
namespaceViewInjectSample.Model.Services { publicclassProfileOptionsService { public List<string> ListGenders() { // keeping this simple returnnew List<string>() {"Female", "Male"}; }
public List<State> ListStates() { // a few states from USA returnnew List<State>() { new State("Alabama", "AL"), new State("Alaska", "AK"), new State("Ohio", "OH") }; }
<ul> @for (int i = 0; i < (int)ViewData["NumTimes"]; i++) { <li>@ViewData["Message"]</li> } </ul>
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
public IActionResult SomeAction() { ViewData["Greeting"] = "Hello"; ViewData["Address"] = new Address() { Name = "Steve", Street = "123 Main St", City = "Hudson", State = "OH", PostalCode = "44236" };
return View(); }
在视图中处理数据:
1 2 3 4 5 6 7 8 9 10 11 12
@{ // Since Address isn't a string, it requires a cast. var address = ViewData["Address"] as Address; }
//插入数据 public ActionResult<string> Create(string name, string password) { if (string.IsNullOrEmpty(name.Trim())) { return"姓名不能为空"; } if (string.IsNullOrEmpty(password.Trim())) { return"密码不能为空"; }
var user = new User() { Name = name, Password = password };
var result = iHelloDao.CreateUser(user);
if (result) { return"学生插入成功"; } else { return"学生插入失败"; } }
//取全部记录 public ActionResult<string> Gets() { var names = "没有数据"; var users = iHelloDao.GetUsers();
if (users != null) { names = ""; foreach (var s in users) { names += $"{s.Name} \r\n"; } }
return names; }
在Starup.cs中注册数据库服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14
publicvoidConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; });
classCaller { publicvoidCallArea(Shape sh) { int a; a = sh.area(); Console.WriteLine("面积: {0}", a); } }
classTester { staticvoidMain(string[] args) { Caller c = new Caller(); Rectangle r = new Rectangle(10, 7); Triangle t = new Triangle(10, 5); c.CallArea(r); c.CallArea(t); Console.ReadKey(); } } }
注意:override重写的属性必须是virtual、abstract或override。
interface
类似于Java。
namespace
在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突。
1 2 3
namespacenamespace_name { // 代码声明 }
可通过namespace_name.item_name方式调用命名空间中的item。使用 using 命名空间指令,这样在使用的时候就不用在前面加上命名空间名称。
delegatevoidTestDelegate(string s); // ... TestDelegate myDel = n => { string s = n + " " + "World"; Console.WriteLine(s); }; myDel("Hello");
线程
Thread类
Thread类的常用属性:
属性
描述
CurrentContext
获取线程正在其中执行的当前上下文。
CurrentCulture
获取或设置当前线程的区域性。
CurrentPrinciple
获取或设置线程的当前负责人(对基于角色的安全性而言)。
CurrentThread
获取当前正在运行的线程。
CurrentUICulture
获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源。
ExecutionContext
获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。
IsAlive
获取一个值,该值指示当前线程的执行状态。
IsBackground
获取或设置一个值,该值指示某个线程是否为后台线程。
IsThreadPoolThread
获取一个值,该值指示线程是否属于托管线程池。
ManagedThreadId
获取当前托管线程的唯一标识符。
Name
获取或设置线程的名称。
Priority
获取或设置一个值,该值指示线程的调度优先级。
ThreadState
获取一个值,该值包含当前线程的状态。
Thread类的常用方法:
序号
方法名 & 描述
1
public void Abort() 在调用此方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。调用此方法通常会终止线程。
2
public static LocalDataStoreSlot AllocateDataSlot() 在所有的线程上分配未命名的数据槽。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
3
public static LocalDataStoreSlot AllocateNamedDataSlot( string name) 在所有线程上分配已命名的数据槽。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
4
public static void BeginCriticalRegion() 通知主机执行将要进入一个代码区域,在该代码区域内线程中止或未经处理的异常的影响可能会危害应用程序域中的其他任务。
5
public static void BeginThreadAffinity() 通知主机托管代码将要执行依赖于当前物理操作系统线程的标识的指令。
6
public static void EndCriticalRegion() 通知主机执行将要进入一个代码区域,在该代码区域内线程中止或未经处理的异常仅影响当前任务。
7
public static void EndThreadAffinity() 通知主机托管代码已执行完依赖于当前物理操作系统线程的标识的指令。
8
public static void FreeNamedDataSlot(string name) 为进程中的所有线程消除名称与槽之间的关联。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
9
public static Object GetData( LocalDataStoreSlot slot ) 在当前线程的当前域中从当前线程上指定的槽中检索值。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
10
public static AppDomain GetDomain() 返回当前线程正在其中运行的当前域。
11
public static AppDomain GetDomainID() 返回唯一的应用程序域标识符。
12
public static LocalDataStoreSlot GetNamedDataSlot( string name ) 查找已命名的数据槽。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
13
public void Interrupt() 中断处于 WaitSleepJoin 线程状态的线程。
14
public void Join() 在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻塞调用线程,直到某个线程终止为止。此方法有不同的重载形式。
15
public static void MemoryBarrier() 按如下方式同步内存存取:执行当前线程的处理器在对指令重新排序时,不能采用先执行 MemoryBarrier 调用之后的内存存取,再执行 MemoryBarrier 调用之前的内存存取的方式。
16
public static void ResetAbort() 取消为当前线程请求的 Abort。
17
public static void SetData( LocalDataStoreSlot slot, Object data ) 在当前正在运行的线程上为此线程的当前域在指定槽中设置数据。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
18
public void Start() 开始一个线程。
19
public static void Sleep( int millisecondsTimeout ) 让线程暂停一段时间。
20
public static void SpinWait( int iterations ) 导致线程等待由 iterations 参数定义的时间量。
21
public static byte VolatileRead( ref byte address ) public static double VolatileRead( ref double address ) public static int VolatileRead( ref int address ) public static Object VolatileRead( ref Object address ) 读取字段值。无论处理器的数目或处理器缓存的状态如何,该值都是由计算机的任何处理器写入的最新值。此方法有不同的重载形式。这里只给出了一些形式。
22
public static void VolatileWrite( ref byte address, byte value ) public static void VolatileWrite( ref double address, double value ) public static void VolatileWrite( ref int address, int value ) public static void VolatileWrite( ref Object address, Object value ) 立即向字段写入一个值,以使该值对计算机中的所有处理器都可见。此方法有不同的重载形式。这里只给出了一些形式。
23
public static bool Yield() 导致调用线程执行准备好在当前处理器上运行的另一个线程。由操作系统选择要执行的线程。