草庐IT

001 Android开发初体验

忘记背后,努力面前;心定大志,壮马驰驱。 2023-03-28 原文

1、简介

注:本文代码参考
我们将开发本系列第一个应用,并借此学习一些Android基本概念以及构成应用的UI组件。
初学习,如果没能全部理解,也不必担心,后续还会涉及这些内容并有更加详细的讲解。

马上要开发的应用名叫GeoQuiz,它能给出一道道地理知识问题。用户点击TRUE或FALSE
按钮来回答屏幕上的问题,GeoQuiz即时作出反馈。

下图显示了用户点击TRUE按钮的结果:
用户点击TRUE按钮图,图1

1.1、Activity和布局文件

GeoQuiz应用由一个activity和一个布局(layout)组成。

  • activity是Android SDK中 Activity类的一个实例,负责管理用户与应用界面的交互。
  • 应用的功能是通过编写 Activity 子类来实现的。对于简单的应用来说,一个 Activity 子
    类可能就够了,而复杂的应用则会有多个。 GeoQuiz是个简单应用,因此它只有一个名叫 QuizActivity 的 Activity
    子类。 QuizActivity它所对应的 用户界面。
  • 布局定义了一系列用户界面对象以及它们显示在屏幕上的位置。组成布局的定义保存在
    XML文件中。每个定义用来创建屏幕上的一个对象,如按钮或文本信息。

GeoQuiz应用包含一个名叫activity_quiz.xml的布局文件。该布局文件中的XML标签定义了
具体的用户界面。

QuizActivity 与activity_quiz.xml文件的关系如下图2所示:
Activity java文件与布局xml文件对应关系图:

1.2、用户界面设计

Android Studio如何创建项目以及Android Studio的界面介绍这里不赘述,我们通过Android Studio(以后简称“AS”)创建好GeoQuiz应用后,首先打开app/res/layout/activity_quiz.xml文件。如果看到的是布局预览界面,请点击底部的
Text页切换显示XML代码。

当前,activity_quiz.xml文件定义了默认的activity布局。

应用activity的默认布局定义了两个组件(widget)RelativeLayout 和 TextView
组件是用户界面的构造模块。组件可以显示文字或图像,与用户交互,甚至布置屏幕上的其
他组件。按钮、文本输入控件和选择框等都是组件。
Android SDK内置了多种组件,通过配置各种组件可获得所需的用户界面及行为。每一个组
件都是 View 类或其子类(如 TextView 或 Button )的一个具体实例

RelativeLayout 和 TextView 是在屏幕上的显示图 图3:

QuizActivity 的用户界面需要下列组件:

  • 一个垂直 LinearLayout 组件;
  • 一个 TextView 组件;
  • 一个水平 LinearLayout 组件;
  • 两个 Button组件。

组件是如何构成 QuizActivity 用户界面图 图4:

下面我们在activity_quiz.xml文件中定义这些组件。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical" >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="24dp"
        android:text="@string/question_text" />
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
        <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/true_button" />
        <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/false_button" />
    </LinearLayout>
</LinearLayout>

上述代码暂时不理解也没关系,后续学习中逐渐弄明白的。
注意,开发工具无法校验布局XML内容,拼写错误早晚会出问题,应尽量避免。

可以看到,有三行以 android:text 开头的代码出现了错误信息。暂时忽略它们,稍后会
处理。
对照上图用户界面查看XML文件,可以看出组件与XML元素一一对应。元素的名
称就是组件的类型

各元素均有一组XML属性。属性可以看作如何配置组件的指令。
为方便理解元素与属性的工作原理,接下来我们将以层级视角来研究布局。

1.2.1、视图层级结构

组件包含在视图(View)对象的层级结构中,这种结构又称作视图层级结构(view hierarchy)

上述代码所示的XML布局对应的视图层级结构图 图5:

从布局的视图层级结构可以看到,其根元素是一个 LinearLayout 组件。作为根元素,
LinearLayout 组件必须指定Android XML资源文件的命名空间属性,这里是http://schemas.
android.com/apk/res/android。

LinearLayout 组件继承自 ViewGroup 组件(也是个 View 子类)。 ViewGroup 组件是包含并配
置其他组件的特殊组件
。想要以一列或一排的样式布置组件,就可以使用 LinearLayout 组件。
其他 ViewGroup 子类还有 FrameLayout 、 TableLayout 和 RelativeLayout 。

若某个组件包含在一个 ViewGroup 中,该组件与 ViewGroup 即构成父子关系。根 Linear-
Layout 有两个子组件: TextView 和另一个 LinearLayout 。作为子组件的 LinearLayout 自己还
有两个 Button 子组件。

1.2.2、组件属性

看看配置组件时常用的一些属性。

  1. android:layout_width 和 android:layout_height 属性
    几乎每类组件都需要 android:layout_width 和 android:layout_height 属性。以下是它
    们的两个常见属性值(二选一)。
    match_parent :视图与其父视图大小相同。
    wrap_content :视图将根据其显示内容自动调整大小。
    (以前还有个 fill_parent 属性值,等同于 match_parent ,现已废弃不用。)
    根 LinearLayout 组件的高度与宽度属性值均为 match_parent 。 LinearLayout 虽然是根元
    素,但它也有父视图——Android提供该父视图来容纳应用的整个视图层级结构。
    其他包含在界面布局中的组件,其高度与宽度属性值均被设置为 wrap_content 。

TextView 组件比其包含的文字内容区域稍大一些,这主要是 android:padding="24dp" 属性
的作用。该属性告诉组件在决定大小时,除内容本身外,还需增加额外指定量的空间。这样屏幕
上显示的问题与按钮之间便会留有一定的空间,使整体显得更为美观。(不理解dp的意思?dp即
density-independent pixel,指与密度无关的像素。)
2. android:orientation 属性
android:orientation 属性是两个 LinearLayout 组件都具有的属性,它决定两者的子组件
是水平放置还是垂直放置。根 LinearLayout 是垂直的,子 LinearLayout 是水平的。

子组件的定义顺序决定其在屏幕上显示的顺序。在垂直的 LinearLayout 中,第一个定义的
子组件出现在屏幕的最上端;而在水平的 LinearLayout 中,第一个定义的子组件出现在屏幕的
最左端。(如果设备文字从右至左显示,如阿拉伯语或者希伯来语,第一个定义的子组件则出现
在屏幕的最右端。)
3. android:text 属性
TextView 与 Button 组件具有 android:text 属性。该属性指定组件要显示的文字内容。
请注意, android:text 属性值不是字符串值,而是对字符串资源(string resource)的引用。

字符串资源包含在一个独立的名叫strings的XML文件中(strings.xml),虽然可以硬编码设置
组件的文本属性值,如 android:text="True" ,但这通常不是个好主意。

比较好的做法是:将文
字内容放置在独立的字符串资源XML文件中,然后引用它们。这样会方便应用的本地化(支持
多国语言)。
需要在activity_quiz.xml文件中引用的字符串资源还没添加。现在就来处理。

1.2.3、创建字符串资源

每个项目都包含一个默认字符串资源文件strings.xml。

在项目工具窗口中,找到app/res/values目录,展开目录,打开strings.xml文件。
可以看到,项目模板已经添加了一些字符串资源。如下代码所示,添加应用布局需要
的三个新的字符串。

<resources>
    <string name="app_name">GeoQuiz</string>
    <string name="question_text">Canberra is the capital of Australia.</string>
    <string name="true_button">True</string>
    <string name="false_button">False</string>
</resources>

(某些版本的Android Studio的strings.xml默认带有其他字符串,请勿删除它们,否则会引发与
其他文件的联动错误。)

现在,在GeoQuiz项目的任何XML文件中,只要引用到 @string/false_button ,应用运行
时,就会得到文本“False”。

保存strings.xml文件。这时,activity_quiz.xml布局缺少字符串资源的提示信息应该就消失了。
(如仍有错误提示,请检查一下这两个文件,确认没有拼写错误。)

默认的字符串文件虽然已命名为strings.xml,仍可以按个人喜好重命名。一个项目也可以
有多个字符串文件。只要这些文件都放在res/values/目录下,含有一个 resources 根元素,以及多
个 string 子元素,应用就能找到并正确使用它们。

1.3、从布局 XML 到视图对象

知道activity_quiz.xml中的XML元素是如何转换为视图对象的吗?答案就在于 QuizActivity
类。

在创建GeoQuiz项目的同时,向导也创建了一个名叫 QuizActivity 的 Activity 子类。
QuizActivity 类文件存放在项目的app/java目录下。java目录是项目全部Java源代码的存放处。

在项目工具窗口中,依次展开app/java目录与com.bignerdranch.android.geoquiz包。找到并打
开QuizActivity.java文件,查看其中的代码,如下所示:

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class QuizActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_quiz);
    }
}

是不是不明白 AppCompatActivity 的作用?它实际就是一个 Activity 子类,能为Android
旧版本系统提供兼容支持。

如果无法看到全部类包导入语句,请单击第一行导入语句左边的⊕符号来显示它们。

该Java类文件有一个 Activity 方法: onCreate(Bundle) 。
(如果你的文件还包含 onCreateOptionsMenu(Menu) 和 onOptionsItemSelected(MenuItem)
方法,暂时不用理会)
activity子类的实例创建后, onCreate(Bundle) 方法会被调用。activity创建后,它需要获取
并管理用户界面。要获取activity的用户界面,可调用以下 Activity 方法:

public void setContentView(int layoutResID)

根据传入的布局资源ID参数,该方法生成指定布局的视图并将其放置在屏幕上。布局视图生
成后,布局文件包含的组件也随之以各自的属性定义完成实例化。

1.3.1、资源与资源 ID

布局是一种资源。资源是应用非代码形式的内容,如图像文件、音频文件以及XML文件等。

项目的所有资源文件都存放在目录app/res的子目录下。在项目工具窗口中可以看到,
activity_quiz.xml布局资源文件存放在res/layout/目录下。strings.xml字符串资源文件存放在
res/values/目录下。

可以使用资源ID在代码中获取相应的资源。activity_quiz.xml布局的资源ID为R.layout.
activity_quiz。

查看GeoQuiz应用的资源ID需要切换项目视图。Android Studio默认使用Android项目视图,
图6所示。为让开发者专注于最常用的文件和目录,默认视图隐藏了Android项目的真实文件
目录结构。

图6:

在项目工具窗口的最上部找到下拉菜单,从Android项目视图切换至Project视图。Project视图
会显示出当前项目的所有文件和目录。

展开目录app/build/generated/source/r/debug,找到项目包名称并打开其中的R.java文件,即可
看到GeoQuiz应用当前所有的资源。R.java文件在Android项目编译过程中自动生成,如该文件头
部的警示所述,请不要修改该文件的内容。

修改布局或字符串等资源后,R.java文件不会实时刷新。Android Studio另外还存有一份代码
编译用的R.java隐藏文件。当前代码编辑区打开的R.java文件仅在应用安装至设备或模拟器前产
生,因此只有在Android Studio中点击运行应用时,它才会得到更新。
R.java文件通常比较大,如下所示:

/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/

public final class R {
public static final class anim {
...
}
...
public static final class id {
...
}
public static final class layout {
    ...
    public static final int activity_quiz=0x7f030017;
    }
    public static final class mipmap {
    public static final int ic_launcher=0x7f030000;
}
public static final class string {
...
    public static final int app_name=0x7f0a0010;
        public static final int false_button=0x7f0a0012;
        public static final int question_text=0x7f0a0014;
        public static final int true_button=0x7f0a0015;
    }
}

可以看到R.layout.activity_quiz即来自该文件。 activity_quiz 是 R 的内部类 layout 里的一个
整型常量名。

GeoQuiz应用需要的字符串同样具有资源ID。目前为止,我们还未在代码中引用过字符串,
如果需要,可以使用以下方法:
setTitle(R.string.app_name);

Android为整个布局文件以及各个字符串生成资源ID,但activity_quiz.xml布局文件中的组件
除外,因为不是所有组件都需要资源ID。在本章中,我们要在代码里与两个按钮交互,因此只需
为它们生成资源ID即可。

这里主要使用Android项目视图,生成资源ID之前,记得切回。当然,如果你就喜欢使用Project
视图,也没啥问题。

要为组件生成资源ID,请在定义组件时为其添加 android:id 属性。在activity_quiz.xml文件
中,分别为两个按钮添加上 android:id 属性,如下代码所示:

<LinearLayout ... >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="24dp"
        android:text="@string/question_text" />
        
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        
        <Button
            android:id="@+id/true_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/true_button" />
        
        <Button
            android:id="@+id/false_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/false_button" />
            
    </LinearLayout>
    
</LinearLayout>

注意, android:id 属性值前面有一个+标志,而 android:text 属性值则没有。这是因为我
们在创建资源ID,而对字符串资源只是做引用

1.4、组件的实际应用

按钮有了资源ID,就可以在 QuizActivity 中直接获取它们。首先,在QuizActivity.java文件
中增加两个成员变量。

在QuizActivity.java文件中输入代码:

public class QuizActivity extends AppCompatActivity {
    private Button mTrueButton;
    private Button mFalseButton;
    @Override
    
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_quiz);
    }
}

文件保存后,会看到两个错误提示。没关系,稍后会处理。请注意新增的两个成员(实例)
变量名称的 m 前缀。该前缀是Android编程应遵循的命名约定

现在,将鼠标移至代码左边的错误提示处时,会看到两条同样的错误信息:Cannot resolve
symbol 'Button'。

这告诉我们,需要在QuizActivity.java文件中导入 android.widget.Button 类包。可在文件
头部手动输入以下代码:
import android.widget.Button;

或者使用Option+Return(Alt+Enter)组合键,让Android Studio自动为你导入。代码有误时,
也可以使用该组合键来修正。记得要常用。

类包导入后,刚才的错误提示应该就消失了。(如果仍然存在,请检查Java代码以及XML文
件,确认是否存在输入或拼写错误。)
接下来,我们来编码使用按钮组件,这需要以下两个步骤:

  • 引用生成的视图对象;
  • 为对象设置监听器,以响应用户操作。

1.4.1 引用组件

在activity中,可调用以下 Activity 方法引用已生成的组件:
public View findViewById(int id)

该方法以组件的资源ID作为参数,返回一个视图对象。
在QuizActivity.java文件中,使用按钮的资源ID获取视图对象,赋值给对应的成员变量。注意,赋值前,必须先将返回的 View 类型转换为 Button 。

public class QuizActivity extends AppCompatActivity {
    private Button mTrueButton;
    private Button mFalseButton;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_quiz);
        mTrueButton = (Button) findViewById(R.id.true_button);
        mFalseButton = (Button) findViewById(R.id.false_button);
    }
}

1.4.2 设置监听器

Android应用属于典型的事件驱动类型。不像命令行或脚本程序,事件驱动型应用启动后,
即开始等待行为事件的发生,如用户点击某个按钮。(事件也可以由操作系统或其他应用触发,
但用户触发的事件更直观,如点击按钮。)

应用等待某个特定事件的发生,也可以说应用正在“监听”特定事件。为响应某个事件而创
建的对象叫作监听器(listener)。监听器会实现特定事件的监听器接口(listener interface)。

无需自己动手,Android SDK已经为各种事件内置了很多监听器接口。当前应用需要监听用
户的按钮“点击”事件,因此监听器需实现 View.OnClickListener 接口。

首先处理TRUE按钮。在QuizActivity.java文件中,在 onCreate(Bundle) 方法的变量赋值语
句后输入下列代码

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_quiz);
    mTrueButton = (Button) findViewById(R.id.true_button);
    mTrueButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // Does nothing yet, but soon!
        }
    });
    mFalseButton = (Button) findViewById(R.id.false_button);
}

(如果遇到View cannot be resolved to a type的错误提示,请使用Option+Return(Alt+Enter)快
捷键导入 View 类。)

在上面代码中,我们设置了一个监听器。按钮 mTrueButton 被点击后,监听器会立即通
知我们。传入 setOnClickListener(OnClickListener) 方法的参数是一个监听器。它是一个实
现了 OnClickListener 接口的对象。

使用匿名内部类
这里,一个匿名内部类(anonymous inner class)实现了 OnClickListener 接口。语法看上去
稍显复杂,不过有个助记小技巧最外层一对括号内的全部代码就是传入 setOnClickListener
(OnClickListener) 方法的参数

mTrueButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // Does nothing yet, but soon!
    }
});

后续所有的监听器都以匿名内部类来实现。这样做有两大好处:

  • 第一,使用匿名内部类,可 以相对集中地实现监听器方法,一眼可见;
  • 第二,事件监听器一般只在一个地方使用,使用匿名
    内部类,就不用去创建繁琐的命名类了。

匿名内部类实现了 OnClickListener 接口,因此它也必须实现该接口唯一的 onClick(View)
方法。 onClick(View) 现在是个空方法。虽然必须实现 onClick(View) 方法,但具体如何实现取
决于使用者,因此即使是个空方法,编译器也可以编译通过。
(如果匿名内部类、监听器、接口等概念已忘得差不多了,现在就该去复习一下,或找本参
考手册备查。)

1.5、创建提示消息

接下来要实现的是,分别点击两个按钮,弹出我们称之为toast的提示消息。Android的toast
是用来通知用户的简短弹出消息,用户无需输入什么,也不用做任何干预操作。这里,我们要用
toast来反馈答案,如图7所示:

首先回到strings.xml文件,如下代码所示,为toast添加消息显示用的字符串资源。

<resources>
    <string name="app_name">GeoQuiz</string>
    <string name="question_text">Canberra is the capital of Australia.</string>
    <string name="true_button">True</string>
    <string name="false_button">False</string>
    <string name="correct_toast">Correct!</string>
    <string name="incorrect_toast">Incorrect!</string>
</resources>

调用 Toast 类的以下方法,可创建toast:
public static Toast makeText(Context context, int resId, int duration)

该方法的 Context 参数通常是 Activity 的一个实例( Activity 本身就是 Context 的子类)。
第二个参数是toast要显示字符串消息的资源ID。 Toast 类必须借助 Context 才能找到并使用字符
串资源ID。第三个参数通常是两个 Toast 常量中的一个,用来指定toast消息的停留时间。

创建toast后,可调用 Toast.show() 方法在屏幕上显示toast消息。
在 QuizActivity 代码里,分别调用 makeText(...) 方法。在添加
makeText(...) 时,可利用Android Studio的代码自动补全功能,让代码输入更轻松。

使用代码自动补全
代码自动补全功能可以节约大量开发时间,越早掌握受益越多。

依次输入下面代码。当输入到 Toast 类后的点号时,Android Studio会弹出一
个窗口,窗口内显示了建议使用的 Toast 类的常量与方法。

要选择需要的建议方法,使用上下键。(如果不想使用代码自动补全功能,请不要按Tab键、
Return/Enter键,或使用鼠标点击弹出窗口,只管继续输入代码直至完成。)

在建议列表里,选择 makeText(Context context, int resID, int duration) 方法,代
码自动补全功能会自动添加完整的方法调用。
完成 makeText 方法的全部参数设置,完成后的代码如下代码所示:

mTrueButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(QuizActivity.this,
        R.string.correct_toast,
        Toast.LENGTH_SHORT).show();
        // Does nothing yet, but soon!
    }
});
mFalseButton = (Button) find ViewById(R.id.false_button);
mFalseButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(QuizActivity.this,
        R.string.incorrect_toast,Toast.LENGTH_SHORT).show();
    }
});

在 makeText(...) 里,传入 QuizActivity 实例作为 Context 的参数值。注意此处应输入的
参数是 QuizActivity.this ,不要想当然地直接输入 this 。因为匿名类的使用,这里的 this 指
的是监听器 View.OnClickListener 。

使用代码自动补全功能,自己也就不用导入 Toast 类了,因为Android Studio会自动导入相
关类。

好了,现在可以运行应用了。

1.6、 使用模拟器运行应用

运行Android应用需使用硬件设备或虚拟设备(virtual device)。包含在开发工具中的Android
设备模拟器可提供多种虚拟设备。

要创建Android虚拟设备(AVD),在Android Studio中,选择Tools → Android → AVD Manager
菜单项。AVD管理器窗口弹出时,点击窗口左下角的+Create Virtual Device…按钮。一路next,详细的创建不赘述了。

创建完成后即可启动运行。

AVD创建成功后,我们用它运行GeoQuiz应用。点击Android Studio工具栏上的Run按钮,或
者使用Control+R快捷键。Android Studio会自动找到新建的虚拟设备,安装应用包(APK),然后
启动并运行应用。
模拟器的启动过程比较耗时,请耐心等待。等设备启动完成,应用运行后,就可以在应用界
面点击按钮,让toast告诉你答案了。
假如启动时或在点击按钮时,GeoQuiz应用崩溃,可以在Android DDMS工具窗口的LogCat
视图中看到有用的诊断信息。(如果LogCat没有自动打开,可点击Android Studio窗口底部的
Android Monitor按钮打开它。),查看日志。

最好不要关掉模拟器,这样就不必在反复运行调试应用时,浪费时间等待AVD启动了。
单击AVD模拟器上的后退按钮可以停止应用。这个后退按钮的形状像一个指向左侧的三角形
(在较早版本的Android中,它像一个U型箭头)。需要调试变更时,再通过Android Studio重新运
行应用。

模拟器虽然好用,但在实体设备上测试应用能获得更准确的结果。

1.7、深入学习:Android 编译过程

学习到这里,你可能迫切想知道Android是如何编译的。
你已经知道在项目文件发生变化时,
Android Studio无需指示便会自动进行编译。

在整个编译过程中,Android开发工具将资源文件、
代码以及AndroidManifest.xml文件(包含应用的元数据)编译生成.apk文件。.apk文件要在模拟器上
运行,还需以debug key签名。(分发.apk应用给用户时,应用必须以release key签名。更多有关编译
过程的信息,可参考Android开发文档网页developer.android.com/tools/publishing/preparing.html。)

那么,应用的activity_quiz.xml布局文件的内容该如何转变为 View 对象呢?作为编译过程的
一部分,aapt(Android Asset Packaging Tool)将布局文件资源编译压缩紧凑后,打包到.apk文件
中。

然后,在 QuizActivity 类的 onCreate(Bundle) 方法调用 setContentView(...) 方法时,
QuizActivity 使用 LayoutInflater 类实例化布局文件中定义的每一个 View 对象,如图8所示:

1.7.1、Android 编译工具

当前,我们看到的项目编译都是在Android Studio里执行的。编译功能已整合到IDE中,IDE
负责调用aapt等Android标准编译工具,但编译过程本身仍由Android Studio管理。

有时,出于某种原因,可能需要脱离Android Studio编译代码。最简单的方法是使用命令行
编译
工具。现代Android编译系统使用Gradle编译工具

要从命令行使用Gradle,请切换到项目目录并执行以下命令:
$ ./gradlew tasks

如果是Windows系统,执行以下命令:

gradlew.bat tasks

执行以上命令会显示一系列可用任务。你需要的任务是installDebug,因此,再执行以下命令:
$ ./gradlew installDebug
如果是Windows系统,执行以下命令:

gradlew.bat installDebug

以上命令将把应用安装到当前连接的设备上,但不会运行它。要运行应用,需要在设备上手
动启动。

有关001 Android开发初体验的更多相关文章

  1. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  2. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  3. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  4. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  5. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  6. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  7. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  8. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  9. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  10. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

随机推荐