Android 知识点笔记 (二) —— Activity

  • 2016.3.11
  • android-sdk-23
  • 文中的部分中文术语是自己看文档理解着翻译的,可能有不准确之处,欢迎指出

Activity 概述

定义

官方 API 文档中对Activity的定义如下:

An Activity is an application component that provides a screen with which users can interact in order to do something, such as dial the phone, take a photo, send an email, or view a map. Each activity is given a window in which to draw its user interface. The window typically fills the screen, but may be smaller than the screen and float on top of other windows.

Activity 作为应用程序的组件之一,在屏幕上显示应用程序的界面,用于和用户的交互。

通常而言,activity 提供的界面窗口会占满整个屏幕,但是也可能小于屏幕尺寸,从而浮现在其他应用程序窗口之上(例如带有 windowIsFloating set 的主题),或者嵌入在其他 Activity 中(例如使用 ActivityGroup)。

代码定义

Activity 类的类定义如下:

java
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback {
...
}

简介

  • 一个应用程序 (application)至少要有一个 main activity,在用户启动 application 时显示
  • 一般而言, application 都有多个 activity,activity 之间可以相互调
  • 每当一个新的 activity 启动,之前的 activity 就会停止 (stopped) ,但是系统会将这个停止的 activity 保存在返回栈 (back stack) 中
  • 新的 activity 启动,它会被压入返回栈中,并获得用户焦点
  • back stack 遵循基本的先进后出 (last-in, first-out) 的栈规则
  • 当前栈顶的 activity 完成后(用户按下返回 (Back) 按键),该 activity 出栈(并被摧毁 (destroyed)),之前的 activity 恢复
  • activity 状态的改变会触发不同的 activity 生命周期 (activity lifecycle) 的回调方法

创建 Activity

  • 创建 Activity (或者其子类)的子类
  • 在创建的子类中实现生命周期的各种回调函数
  • 其中最重要的两个回调函数

实现用户界面

  • 用户界面由各种 views —— 来自 View 类的对象提供
  • android 提供了一系列的预定义 views 以供设计和组织布局
    • Widgets: 可视化的交互元素
    • Layouts: 来自 ViewGroup 的 views,提供独特的布局模型
    • 自己定义: ViewViewGroup 的子类
  • 通常,布局定义在 XML 布局文件中,实现界面设计与行为代码的分离 (setContentView(int)) 方法
  • 也可以在 activity 中通过代码实现界面设计(参考 UI 部分)

在 manifest 中声明 activity

xml
<manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >

使用 intent filters

xml
<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

  • <activity> 节点使用各种 <intent-filter> 元素来声明其他应用程序元素如何调用该 activity
  • 不被其他 application 调用的 activity 则不需要任何的 intent filter,只有自己的 application 可以通过显式 intent 来调用
  • 应当只有一个 activity 有 “main” action 和 “launchy” category
  • 想要使用隐式 intent 调用 activity,必须添加 <intent-filter> 节点,在该节点中要包含一个 <action> 节点,此外,还可选添加一个 <category> 和/或 <data> 节点。这三种元素指明了 activity 响应的 intent 类型

启动 activity

  • 通过 startActivity() 方法启动活动,该方法接收一个用于描述目标 activity 的 Intent
    • intent 可以显式地指定想要启动的 activity
    • intent 可以隐式地描述你想要进行的 action 类型(系统根据 intent-filter 中的描述来匹配符合描述类型的 activity)
    • intent 中可以携带少量的数据,供启动的 activity 使用

显式 intent

eg.启动名为 SignInActivity 的 activity
java
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

隐式 intent

intent 指明要操作的行为,然后通过 intent 调用能够响应该行为的 activity。若有多个响应 activity,则会提示用户进行选择。

eg.发送 email

java
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

EXTRA_EMAIL 这个 extra 是电子邮件地址的字符串序列,指明了邮件的接收地址。

启动 activity 并获取结果

  • 使用 [startActivityForResult](http://developer.android.com/reference/android/app/Activity.html#startActivityForResult(android.content.Intent, int)) 来接收启动的 activity 返回的结果。
  • 为了接收结果,还要实现 [onActivityResult()](http://developer.android.com/reference/android/app/Activity.html#onActivityResult(int, int, android.content.Intent)) 方法。
  • 当启动的 activity 结束后,会返回一个 Intent 到 [onActivityResult()](http://developer.android.com/reference/android/app/Activity.html#onActivityResult(int, int, android.content.Intent)) 方法中。

eg.使用 Intent 让用户选取一个联系人,然后在源 activity 中执行某些行为

java
private void pickContact() {
// Create an intent to "pick" a contact, as defined by the content Prover URI
Intent intent = new Intent(Intent.Action_PICK, Contacts.CONTENT_URI);
startActivity(intent, PICK_CONTACT_REQUEST);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// If the request went well (OK) and the request was PICK_CONTACT_REQUEST
if (requestCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
// Perform a query to the contact's content provider for the contact's name
Cursor cursor = getContentResolver().query(data.getData(), new String[] {Contacts.DISPLAY_NAME},
null, null, null);
if (cursor.moveToFirst()) { // True if the cursor is not empty
int ColumnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
String name = cursor.getString(columnIndex);
// Do something with the selected contact's name ...
}
}
}

在上面的例子中,先检查 request 是否成功,若成功,则 resultCode 的值是 RESULT_OK。然后检查 request 的响应结果是不是已知的,本例中,resultCode 应当和 [startActivityForResult](http://developer.android.com/reference/android/app/Activity.html#startActivityForResult(android.content.Intent, int)) 方法传递的第二个参数 (PICK_CONTACT_REQUEST) 相匹配。检测满足条件后,代码开始处理 activity 返回的结果,这个结果包含在一个 Intent 对象中(data 参数)。

然后,contentResolver 向 content provider 进行查询,返回一个包含所需数据的 Cursor 对象。

结束 Activity

  • 调用 activity 的 finish() 方法结束 activity
  • 调用 finishActivity 方法单独关闭在之前启动的 activity

注:大多数情况下,不应当通过上述方法显式关闭 activity。Android 系统会管理为我们管理 activity 的生命周期,自己调用这些方法会显著地影响用户体验。因此,只有当我们确定不希望用户再回到这个 activity 的实例时才会使用这些方法。

管理 Activity 的生命周期 (Activity Lifecycle)

提到生命周期首先想到的果断应当就是这个生命周期图了(显示在方框中的就是能实现的各种回调方法):

一个 activity 主要有三种基本状态:
* Resumed: activity 在屏幕前台运行并且有用户焦点(该状态有时候也被成为 “running”)
* Paused: 另一个 activity 在屏幕前台且有焦点,但是这个 activity 仍然是可见的。也就是说,另一个 activity 在这个 activity 的上层,
* Stopped

实现生命周期的回调

当 activity 在不同状态改变时,会触发各种回调方法。这些回调方法都能被重写。重写回调方法时,必须调用父类的方法实现。如下例所示:

java
public class ExampleActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The activity is being created.
}
@Override
protected void onStart() {
super.onStart();
// The activity is about to become visible.
}
@Override
protected void onResume() {
super.onResume();
// The activity has become visible (it is now "resumed").
}
@Override
protected void onPause() {
super.onPause();
// Another activity is taking focus (this activity is about to be "paused").
}
@Override
protected void onStop() {
super.onStop();
// The activity is no longer visible (it is now "stopped")
}
@Override
protected void onDestroy() {
super.onDestroy();
// The activity is about to be destroyed.
}
}

上例中代码的一系列方法定义了 activity 的整个生命周期。这些方法能够监控 activity 的三个内部生命周期循环:

  • 完整生命周期(entire lifetime): 从调用 onCreate() 方法开始,到调用 onDestroy() 方法结束
    • onCreate() 方法中,activity 进行一些全局操作。例如定义布局
    • onDestroy() 方法中,释放所有的剩余资源
    • 例如,某个 activity 具有一个后台运行的线程 (Thread) 用于下载数据,可以在 onCreate() 中创建线程,在 onDestroy() 中终止 (stop) 线程
  • 可见生命周期(visible lifetime): 从调用 onStart() 方法开始,到调用 onStop() 方法结束
    • 在这个生命周期中,用户能在屏幕上看见这个 activity 并且与之交互。
    • 在这两个方法之间,管理展现 activity 所需要的资源 (resources)
    • 例如,在 onStart() 方法中注册一个 BroadcastReceiver,用于监控那些影响 UI 的改变。当这个 activity 对用户不可见时,在 onStop() 方法中注销该广播接收器 (BroadcastReceiver)
    • 由于 activity 经常会在可见与不可见状态间切换,因此在 activity 的生命周期中,onStart()onStop() 可能会被多次调用
  • 前台生命周期(foregroud lifetime): 从调用 onResume() 方法开始,到调用 onPause() 方法结束
    • 在这个生命周期时,activity 在所有其他 activity 之上并具有用户输入焦点(user input focus)
    • activity 会经常进入和退出前台状态(例如设备休眠,或者某个对话 (dialog) 弹出),也就是说 onResume()onPause() 方法会经常被调用。
    • 因此,这两个方法中的代码应当是轻量级的,减少用户在状态转换时的等待时间。

生命周期回调方法总结表:

方法 描述 Killable after? Next
onCreate() 在 activity 第一次创建时被调用。
在这里进行所有的正常静态初始化 —— 创建视图 (views), 绑定数据到列表 (list) 等等。
该方法会传入一个 Bundle 对象,如果 activity 之前的状态被保存过,这个 Bundle 对象中就会保存这个状态信息,在这之后总是跟着 onStart() 方法
onStart()
onRestart() 在 activity 结束 (stopped) 之后被调用,在 activity 开始(start) 前。
紧接着执行的总是 onStart()
onStart()
onStart() 在 activity 变成可见状态时被调用。
如果 activity 接着变成前台运行 (comes to foreground),onResume() 方法会跟着执行。
如果 activity 接着变成隐藏(hidden)状态,onStop() 方法会跟着执行
onResume() 或 onStop()
onResume() 在 activity 开始与用户交互时被调用。
这时的 activity 在 activity 栈 (activity stack) 的栈顶,用户可以进行操作(可以理解为获得焦点)。
下一个执行的总是 onPause()
onPause()
onPause() 当系统将要启动另外一个 activity 并将之展现到前台时,该方法被调用。
该方法常用于将未保存的变化提交到持续性存储中,停止动画以及其他的消耗CPU的行为,诸如此类。
它的代码块中代码应当被很快执行,因为直到它返回后,下一个 activity 才能被 resume(求大神,这里的 resume 应该怎么翻译比较好~) 到前台;
后面要么是跟着 onResume()(activity 重新回到前台),要么是跟着 onStop() (activity 变成对用户不可见)
onResume() 或 onStop()
onStop() 当 activity 不再对用户可见时被调用。
变为不可见可能是由于该 activity 被销毁 (destroyed) 了,或者是其他的 activity 恢复 (resume) 到前台并将该 activity 覆盖。
后面跟着的要么是 onRestart() (activity 重新与用户交互),要么是 onDestroy() (activity 结束)
onRestart() 或 onDestrooy()
onDestroy() 在 activity 被销毁前调用。
这是 activity 的最后一个回调。它被调用,有可能是 activity 结束了(finish() 方法被调用),或者系统为了节约并释放资源暂时销毁了这个 activity 实例。针对这两种情况,可以使用 isFinishing() 方法进行分辨
nothing

表中的 “Killable after” 用于指示系统能否在方法返回后的任意时间,在不执行其他 activity 代码的情况下,将 activity 的进程终止。

注: Killable 为否的并不意味着不会被系统终止 (killed),但是只有在没有其他可用资源的极端情况下才会被终止。

保存 activity 的状态

有时候,系统为了节约资源,会销毁一个 activity 对象,这是如果用户想要返回到该 activity,则不会直接 resume 到 activity 的原来状态,而是重新创建 (recreate) 一个 activity 对象。为了保证原先状态下的各种信息也能够体现在这个新对象中,就需要使用一个额外的回调方法来存储之前的 activity 状态信息,这就是 onSaveInstanceState() 方法。

系统在使得 activity 变得易于摧毁 (destruction) 前调用 onSaveInstanceState() 方法。该方法有一个 Bundle 类型的参数,该参数有一系列的方法,例如 [putString()](http://developer.android.com/reference/android/os/BaseBundle.html#putString(java.lang.String, java.lang.String)), [putInt()](http://developer.android.com/reference/android/os/BaseBundle.html#putInt(java.lang.String, int)),通过键值对 (name-value pairs) 的形式储存 activity 的状态信息。在系统摧毁并重建 activity 时,这个 Bundle 类型的参数会传递到 onCreate()onRestoreInstanceState() 方法中。然后在这些方法中便可以取出 Bundle 中存储的状态信息并恢复 activity 状态。当没有储存状态信息时,Bundle 参数会传递一个 null 值。

下图展现了 activity 恢复到前台运行状态的两种方式:

注:并不能保证 onSaveInstanceState() 在 activity 被销毁 (destroyed) 前一定被调用,因为有时候并没有保存状态信息(例如用户按下后退按键结束一个 activity,这时用户就是准备结束这个 activity 的)。如果系统调用了 onSaveInstanceSteate(),会在 onStop() 之前调用,也有可能在 onPause() 之前调用。

即使我们并没有在自己的代码中实现 [onSaveInstanceState()][onSaveInstanceSate] 方法,有些 activity 的状态也会通过 ActivityonSaveInstanceSate() 的默认实现来存储。特别地,layout 中的每个 View 都会实现对应的 onSaveInstanceState() 方法,来储存它们应当要被保存的信息。Android framework 中的绝大多数控件 (widget) 都会恰如其分地实现该方法,因此在重新创建 activity 时,UI 的任何可视化变化都会被自动存储和恢复。例如,可编辑文本 (EditText) 控件会储存用户输入的任何文字信息,复选框 (Checkbox) 控件则储存着其勾选状态。我们唯一需要做的,就是给每个控件指定一个唯一的 ID (通过 android:id 属性),这样就能够储存它们的状态了,否则系统是不能储存这些控件的状态的。

注:可以将 android:saveEnabled 属性设置为 “false”,或者调用 setSaveEnabled 显式地禁止布局中的某个 view 储存它自己的状态。

由于 onSaveInstanceState() 的默认实现帮助保存 UI 的状态,因此在我们重写该方法时,要首先调用它的超类实现。同样的,在重写 onRestoreInstanceState() 方法时,也要同样地调用其超类实现来恢复 view 的状态。

注:由于 onSaveInstanceState() 并不能保证一定被调用,因此我们应当只用它来储存 activity 的一些暂时状态(UI 的状态),而不应该在这个方法中来储存持久性数据,储存持久性数据改用 onPause() 方法来实现。

一个用于测试应用程序状态存储的好方法是在打开应用程序时旋转屏幕,这时候系统为了应用新的屏幕参数信息,会摧毁并重建 activity。因此,在 activity 重新创建时,有之前 activity 状态的完整保存很重要。

Activity 的相互协调

  • 一个 activity 启动另一个 activity 时,并不是在第二个 activity 启动前就完全停止了,第二个 activity 和第一个 activity 的启动过程是有重叠的。
  • activity 的生命周期回调顺序是很清晰的,特别是两个运行于同一线程的 activity 间的相互调用。Activity A 启动 Activity B 的顺序如下:
    1. 执行 Activity A 的 onPause() 方法
    2. 按顺序执行 Activity B 的 onCreate(), onStart(), onResume() 方法(此时 Activity B 具有用户焦点)。
    3. 接着,如果 Activity A 不再可见,则执行其 onStop() 方法。
  • 上面的生命周期顺序可以帮助我们更好地管理各类信息的变化和传递。例如,在第一个 activity 中储存数据库信息,并使这些信息能被后一个 activity 读取,我们应该在第一个 activity 的 onPause() 方法而不是 onStop() 方法中进行数据的储存。

Activity 的运行过程

  1. 每个应用程序 (app) 都是一系列活动 (activities),布局文件 (layouts) 和其他资源文件的集合
    • 其中的一个activity 是应用程序的 main activity
    • 每个 activity 都有一个 main activity,由 AndroidManifest.xml 文件指定
  2. 默认情况下每个应用程序都运行在自己的线程中
  3. 通过 startActivity() 传递一个 intent 来启动另一个程序的 activity
  4. 当一个 activity 需要启动时, android 会检查是否已经有一个目标 activity 所在应用程序的线程,如果线程已经存在,则直接在那个线程中启动 activity,否则的话,android 会创建一个线程
  5. Android 启动 activity 时,调用 activity 的 onCreate() 方法

Activity 的生命周期实例

个人感觉 Head First Android Development 中的 StopWatch 例子讲得很好。

这个小项目要做的是一个用来计时的秒表。首先是前置工作,不用多说,界面设计之类的。直接贴代码了。

activity_stopwatch.xml:

xml


string.xml:
xml
...
<string name="start">Start</string>
<string name="stop">Stop</string>
<string name="reset">Reset</string>
...

以上是界面部分,可以看到,第一个 TextView 用于显示时间,后面跟着三个按钮,分别用于开始,暂停和结束计时。然后开始功能部分的实现。

Stopwatch version 1.0 (计时)

java
package com.hfad.stopwatch;

import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.view.View;
import android.widget.TextView;

public class StopwatchActivity extends Activity {

}

运行程序,发现可以运行成功。但是,有一个问题,就是当我们点击 START 按钮开始计时后,如果翻转屏幕,则计时器的值重新变为 00:00:00 了。原因就是系统检测到了屏幕参数的变化,因而摧毁了当前的 activity,并根据当前的屏幕参数重新创建一个 activity。重新调用 onCreate() 方法,因此就变成初始状态了。

Stopwatch version 2.0 (屏幕旋转)

这时我们有两个解决方案:
* 忽略 activity 的重建(re-create)
可以修改 AndroidManifest.xml 文件中 activity 节点的属性配置:

xml
android:configChanges="configuration_change"

在本例中,在 AndroidManifest.xml 文件中作以下修改:
xml
<activity
android:name="com.hfad.stopwatch.StopwatchActivity"
android:label="@string/app_name"
android:configChanges="orientation|screenSize" >

“|” 符号代表或,也就是说忽略这两种情况。如果 android 系统遇到了上面的这种配置改变,就会调用 onConfigurationChanged(Configuration) 方法,而不是去重建 activity。

  • 保存当前的状态值
    在 onSaveInstanceState() 方法中,利用 Bundle 的一系列 put 方法,以键值对的形式将 activity 状态数据放入 Bundle 中。
    xml
    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
    savedInstanceState.putInt("seconds", seconds);
    savedInstanceState.putBoolean("running", running);
    }

    然后在 onCreate() 方法中恢复这些数据
    java
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_stopwatch);
    if (savedInstanceState != null) {
    seconds = savedInstanceState.getInt("seconds");
    running = savedInstanceState.getBoolean("running");
    }
    runTimer();
    }

Stopwatch version 3.0 (不可见时暂停计时)

这时候需求又变更了,我们希望来电话时,我们的 stopwatch 能够自动停止计时。换句话说,只有它可见的时候,我们才希望触发计时。但是经过测试发现,即使是来电时也还是继续在后台进行计时的,不满足需求。这时候就要考虑使用生命周期回调函数中的 onStop() 和 onStart() 方法了。修改代码如下:
java
public class StopwatchActivity extends Activity {
private int seconds = 0;
private boolean running;
private boolean wasRunning;

Stopwatch versin 4.0 (如果可见但是失去焦点,怎么办呢?)

有时候可能会有并不是全屏形式的弹窗,这时候 stopwatch 还是可见的,只不过在它的上层还有其他的 activity。我们希望也能够自动停止计时。这时就需要使用到 onResume() 和 onPause() 两个方法了。代码如下:
java
package com.hfad.stopwatch;

import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.view.View;
import android.widget.TextView;

public class StopwatchActivity extends Activity {
//Number of seconds displayed on the stopwatch.
private int seconds = 0;
//Is the stopwatch running?
private boolean running;
private boolean wasRunning;

}

因此,最终我们的 stopwatch,既可以满足不受屏幕旋转的影响,又可以根据是否有其他更高层次的 activity 运行从而智能暂停和继续计时。而在这过程中,也用到了所有的生命周期回调函数方法。

Oracle 编码转换及相关问题

项目上要用到 oracle,于是乎又重新装了一个 oracle 11g,中间遇到一些小问题,在此记录。由于 oracle 系的产品一直不是很熟悉,以下的记录只是一些个人观点,有理解偏差的地方欢迎指正。

首先是安装,这个没什么可说的,第一步的邮件通知貌似需要连接国外服务器,在中国由于 GFW 的存在,不能正常使用。第二步会有一个选择字符集的选项,根据需要选择,最好同一项目组的数据库字符集保持一致。还有一个就是这里有个密码选项,这里是 sys 的密码。然后一路 next,到最后创建数据库的时候,会弹出一个用户列表,这里取消 scott 的默认禁用勾选项,设置密码。然后 system 用户也设置个密码就可以了。服务器安装完成后把 client 也安装了,这样再使用 navicat 连接和操作会比较方便,特别是像我这样的小白,有个图形化的界面,执行sql的时候就多了不少底气(基本命令忘记了可以直接 GUI 界面操作,哈哈:)顿时感觉一股弱者的气息~~)。

安装完成后遇到第一个坑,由于个人习惯,数据库的字符集在安装时候选择了 AL32UTF8 编码,然后原始数据存放的数据库,用的很可能是默认安装的,结果是默认的 ZHS16GBK 编码。在导入时候由于字节长度不一致,很多数据由于超出 UTF8 的长度导致无法导入。关于具体区别可以参考这篇文章

GBK的文字编码是双字节来表示的,即不论中、英文字符均使用双字节来表示,只不过为区分中文,将其最高位都定成1。

至于UTF-8编码则是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24位(三个字节)来编码。对于英文字符较多的论坛则用UTF-8节省空间。

GBK包含全部中文字符;UTF-8则包含全世界所有国家需要用到的字符。

GBK是在国家标准GB2312基础上扩容后兼容GB2312的标准(好像还不是国家标准)
UTF-8编码的文字可以在各国各种支持UTF8字符集的浏览器上显示。
比如,如果是UTF8编码,则在外国人的英文IE上也能显示中文,而无需他们下载IE的中文语言支持包。 所以,对于英文比较多的论坛 ,使用GBK则每个字符占用2个字节,而使用UTF-8英文却只占一个字节。

UTF8是国际编码,它的通用性比较好,外国人也可以浏览论坛,GBK是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBK大

为保证数据正确导入,需要进行数据库的编码转换操作。具体操作过程如下(上班好累,有些英文的资料懒得翻译了):

  1. 使用任意账户登录 sql plus.
  2. 在登陆状态下切换到 sys 账户进行登陆。
  3. 关闭数据库
  4. 启动数据库到 mount 状态
  5. start a SQL trace for the current session
  6. 修改权限。(this command will put the db in restricted session. Typically, all users with the CREATE SESSION system privilege can connect to an open database. Opening a database in restricted mode allows database access only to users with both the CREATE SESSION and RESTRICTED SESSION system privilege; only database administrators should have the RESTRICTED SESSION system privilege.)
  7. JOB_QUEUE_PROCESSES specifies the maximum number of job slaves per instance that can be created for the execution of DBMS_JOB jobs and Oracle Scheduler (DBMS_SCHEDULER) jobs. DBMS_JOB and Oracle Scheduler share the same job coordinator and job slaves, and they are both controlled by the JOB_QUEUE_PROCESSES parameter.

    If the value of JOB_QUEUE_PROCESSES is set to 0, then DBMS_JOB jobs and Oracle Scheduler jobs will not run on the instance.

  8. 打开数据库

  9. 修改数据库编码

  10. 再关闭数据库以便设置生效

  11. 重新开启数据库

完成后再次执行导入命令

注意如果是本地的话 @databasename 貌似并不用写的样子~

可以发现导入成功了。

然后发现 navicat 突然连接不上了,报错

打开 navicat 的设置(工具-选项-其他-OCI),点击下拉按钮,会自动提示安装的 oracle client 安装路径下的OCI文件,例如

再次连接就能正常连接了。

想到接下来要倒腾 oracle ,心里真是好慌啊 T_T,不过这也是成长的机会,加油!向着全栈方向努力前进!

Mysql 5.7.10 zip 版本安装配置

好久没用 mysql 了,今天因为需要去官网下载了一个最新的 5.7.10 zip 版本。发现和以前的配置有些变化,记录一下(windows x64)(每次都跑去看文档,真是够了ヾ(≧へ≦)〃,不过官方文档写得真是好详细,点赞)。

  1. 下载完成后,校验 MD5,确认后解压,拷贝到需要的目录下。
  2. 这时候首先要做的事情是初始化。从 5.7.6 版本开始,解压版本不再有 data 文件夹,因此需要自己先创建一个 data 文件夹存放默认的数据文件。
  3. 创建完成后,复制 mysql 根目录下的 my-default.ini 并另存为 my.ini,my.ini 文件的位置若不在 mysql 根目录下,也不在系统的 path 变量中,则需要编辑 path 环境变量包含该配置文件路径。
  4. 打开 my.ini 文件,取消 basedir 和 datadir 的注释,并指定根目录(basedir)和数据目录(datadir)的路径,注意路径使用反斜杠(/),完成后保存。当然这一步也可以不指定,在启动时作为启动参数传入,然而作为一个懒人不想每次敲命令~
  5. 打开命令行并导航至 mysql 的 bin 目录,运行

    进行初始化,注意这里使用该命令会生成一个默认的 root 密码,具体执行过程建议参考官方文档
  6. 输入 mysqld –console 开启 mysql 服务。可以看到会在命令行打印出 mysql 的运行状态。
  7. 使用

    登陆并修改默认密码。这里注意由于是使用的 –initialize 进行的安全模式初始化,默认会为 ‘root’@’localhost’ 生成一个随机密码串,因此在第一次登陆时,需要找到 basedata 目录下的 计算机名.err 的标准错误输出文件。在里面可以看到有一串

    日志,其中冒号后面的部分的就是系统生成的随机 root 密码,输入该密码即可以 root 身份成功登陆。
  8. 登陆后,运行

    修改 root 密码。

新年

dfb637ce86850a1e3568f652e19f4ae2
一个人的房间
窗外下过雨的街
再强烈的烟火都冷却
一句无声的再见
最后一日的湖边
连星光就要熄灭

一年年
心有相思血
听错的词
唱对的自己
野花明眸清亮
守候路口等到黑夜

渺小的光托在掌中
怎能轻易说出完满
纵然无知无解
心甘情愿
除了爱你
拱手让过去留有残缺

独一无二的每一日
我无比真实
喝最烈的酒
爱最凉的花
千江不远
一苇杭之
长风浩荡已来赴约
2015.12.31-2016.1.1

Android 虚拟机运行缓慢的解决方法

1. 直接在手机上跑,最简单最快。

2. 创建虚拟机时,选择与自己计算机 CPU 类型相同的处理器类型。如果是 intel x86 系列,那么在选择虚拟机的 System Image 时也选择 x86 类型的 ABI(Application Binary Interface)。另外,如果计算机使用的是 intel 处理器,可以下载安装 HAXM 进行加速。下载地址:https://software.intel.com/en-us/android/articles/intel-hardware-accelerated-execution-manager

3.创建虚拟机时,启用 Snapshot.

4.可以试一试 GenyMotion 的虚拟机,个人使用是免费的。速度相对自带的会快不少。

Back to Android

Start to read Head First Android Development.

Issues about the xml attribute android:ems

Chapter 3

I get a little confused about what is meant by ems. And found the answer below.

android:ems or setEms(n) sets the width of a TextView to fit a text of n ‘M’ letters regardless of the actual text extension and text size. See wikipedia Em unit

but only when the layout_width is set to "wrap_content". Other layout_width values override the ems width setting.

Adding an android:textSize attribute determines the physical width of the view to the textSize * length of a text of n ‘M’s set above.

Issues about the frament onAttach() method.

When tested the code showed in Chapter 7,

Android Studio indicated that this method is deprecated, and suggest to use the code

instead.

However, after I changed the code

to

it occurred to me that the onAttach(Context context) didn’t even be called when running the app on virtual device with an API level of 21. Seem that many people also meet the problem.(eg, see here, and here) And when testing the same code on devices running an API level of 23, it performs perfectly. TL; DR: when the API level is less than 23, override onAttach(Activity). When the API level is equal or bigger than 23, then override onAttach(Context).

Marks on Fragment Life Circle

onCreate():

The onCreate() method in a Fragment is called after the Activity‘s onAttachFragment() but before that Fragment‘s onCreateView().
In this method, you can assign variables, get Intent extras, and anything else that doesn’t involve the View hierarchy (i.e. non-graphical initialisations). This is because this method can be called when the Activity‘s onCreate() is not finished, and so trying to access the View hierarchy here may result in a crash.

onCreateView():

After the onCreate() is called (in the Fragment), the Fragment‘s onCreateView() is called. You can assign your View variables and do any graphical initialisations. You are expected to return a View to this method, and this is the main UI view, but if your Fragment does not use any layouts or graphics, you can return null.

onActivityCreated():

As the name states, this is called after the Activity‘s onCreate() has completed. It is called afteronCreateView(), and is mainly used for final initialisations (for example, modifying UI elements).


To sum up…
… they are all called in the Fragment but are called at different times.
The onCreate() is called first, for doing any non-graphical initialisations. Next, you can assign and declare any View variables you want to use in onCreateView(). Afterwards, use onActivityCreated() to do any final initialisations you want to do once everything has completed.

reference from http://stackoverflow.com/questions/28929637/difference-and-uses-of-oncreate-oncreateview-and-onactivitycreated-in-fra

Found a bug in Android Studio.

In Chapter 9, the book tells us how to add action bars and add action items to the bar. Since I use an API level of 23 to compile the project, I removed the dependency on appCompact-v7, and modified the showAsAction attribute of the item nodes in menu resource file. As is said, if the project has no dependency on v7 appcompact library, showAsAction must be prefixed with

, not

. And can omit the attribute

So the final version of my menu_main.xml becomes:

And then Android Studio code assist showed en error on android:showAsAction

Should use app:showAsAction with the appcompat library with xmlns:app="http://schemas.android.com/apk/res-auto"less… (Ctrl+F1)

When using the appcompat library, menu resources should refer to the showAsAction in the app: namespace, not the android: namespace.

Similarly, when not using the appcompat library, you should be using the android:showAsAction attribute.

It really disappointed me at first, and I searched on the Internet for several hours to find out how could I make this a correct one. Finally I decided to try compile the module regardless of the error. Surprisingly, no error occurs during the build, and when I install the app and run on the android device, it performs perfect. This morning, I searched the Internet again in order to find some suggestion. And I found people here who have the same problem as me.

This really cost me a lot of time. I should learn from the lesson not to always believe the IDE. The thing that really matters is the final result, believe what you’ve learn and always try the ideas.

Another android studio bug

There’s already an issue.

Cannot see files inside /data via DDMS

It seems that /data has a more strict privilege than the common directories. The solution is to use an Android application called Root Explorer to set the /data directory’s privilege to 777.

What do three dots in parameters mean

When studying the Chapter 12, there’s such a code block:

The three dots means arbitrary arguments of the specific type.

一本正经的胡说八道

请原谅我真的不知道这个标题用英文应该怎么说。看到 13 章 Log 部分有个 Log.wft(),书中有段旁注:

There’s also a Log.wtf() method you can use to report exceptions that should never happen. According to the Android documentation, wtf means “What a terrible Failure”. We know it means “Welcome to Fiskidagurinn”, which refers to  the Great Fish Day festival held annually in Dalvik, Iceland. Android Developers can often be heard to say “My AVD just took 8 minutes to boot up. WTF??” as
a tribute to the small town that gave its name to the standard Android executable bytecode format.

想到之前还被书中特意放的一段错误代码耍了好久(Fragment 中的 onClick 绑定),我已经决定有机会要买个 Head First 大全集来拜读了,这种文风实在是 1024 个赞。

为何 JAVA 中子类异常类型不能比父类异常类型的范围更大

如果一个方法声明了会抛错,子类中重写的方法只能声明抛出同样的异常或者是抛出该异常的子类。

SocketException 继承了 IOException, 但是 SQLException 不继承.

这样的规定是出于多态的考虑。

如果 B 要抛出 SQLException,编译器并不能强制让你去捕获该异常,因为此时正使用 B 的父类 A。

The rule that you need to be able to refer to objects by their superclass is the Liskov Substitution Principle.

原帖地址:http://stackoverflow.com/questions/5875414/why-cant-overriding-methods-throw-exceptions-broader-than-the-overriden-method

JAVA 中 LinkedList 使用 iterator 遍历的效率问题

看视频中介绍 java.util.LinkedList 提到使用 iterator 对LinkedList 进行遍历时效率高。一开始没想通,毕竟 LinkedList 本质上就是一个链表,那么无论如何,想取在位置 i 上的链表元素,肯定要从头遍历过去,那么 for 从 1 到 i 和使用 iterator 从 1 到 i 应该并没有什么效率上的差别才对。一开始总以为是 iterator 用了什么黑科技,跑去看了 iterator 的源码,就是一个cursor 从头开始的遍历,理论上应该是一样才对。然而结果确实是 iterator 的效率要高很多。最后试着从 for 循环遍历的角度去看,发现了问题所在。

在 for 循环的遍历中,取到具体某个元素要调用 LinkedList 的 get() 方法,而在 get() 实现时,其实又利用 index 去做了一次 for 循环。相当于 for 循环的时间复杂度为

(1)   \begin{equation*}O(n^2)\end{equation*}

,而 iterator 的时间复杂度是

(2)   \begin{equation*}O(n)\end{equation*}

,因此说 iterator 对 LinkedList 的遍历有着更高的效率。

java 中 == 和 equal() 的区别

1.     Java == vs equals() confusion

Q:

I wanted to clarify if I understand this correctly:

== -> is a reference comparison, i.e. both objects point to the same memory location
.equals() -> evaluates to the comparison of values in the objects
Am I correct in my understanding ?

A:

In general, the answer to your question is “yes”, but…

equals will only compare what it is written to compare, no more, no less.
if a class does not override the equals method, then it defaults to the equals(Object o) method of the closest parent class that has overridden this method.
If no parent classes have provided an override, then it defaults to the method from the ultimate parent class, Object, and so you’re left with the Object#equals(Object o) method. Per the Object API this is the same as ==; that is, it returns true if and only if both variables refer to the same object, if their references are one and the same. Thus you will be testing for object equality and not functional equality.
Always remember to override hashCode if you override equals so as not to “break the contract”. As per the API, the result returned from the hashCode() method for two objects must be the same if their equals methods shows that they are equivalent. The converse is not necessarily true.