参考《Professional Android 4 Development》

Database and Content Providers

Android Database简介

Android使用SQLite数据库和ContentProvider来实现结构化数据的读写。在Android中,SQLite是以lib的形式存在的,每个应用程序含有自己的SQLite lib,减少了数据库层的耦合,并且提升了安全性。SQLite的数据文件默认存储在这个目录:

/data/data/<package_name>/databases

Content Provider使用URI的方式提供了一个统一的数据访问接口,Schema是content://,与SQLite不同,Content Provider可以跨应用程序使用,可以通过发布自己的Content Provider供他人使用。

Content Values和Cursors

Content Values用于插入数据,Cursor则是返回的查询结果。Content Values对应数据中的一行数据。Cursor类提供了移动游标的方法,常用的有这些:

  • moveToFirst
  • moveToNext
  • moveToPrevious
  • getCount
  • getColumnIndexOrThrow
  • getColumnName
  • getColumnNames
  • moveToPosition
  • getPosition

SQLiteOpenHelper

SQLiteOpenHelper是一个抽象类,使用它可以更方便地进行操作数据库。SQLiteOpenHelper会缓存数据库实例,提升访问效率,也正是因为这种缓存策略,用户只有在不再访问数据库的时候才需要关闭数据库连接。下面是一个SQLiteOpenHelper的实现类例子:

private static class HoardDBOpenHelper extends SQLiteOpenHelper {
  private static final String DATABASE_NAME = “myDatabase.db";
  private static final String DATABASE_TABLE = “GoldHoards";
  private static final int DATABASE_VERSION = 1;
  // SQL Statement to create a new database.
  private static final String DATABASE_CREATE = “create table “ + DATABASE_TABLE + “ (“ + KEY_ID +
      “ integer primary key autoincrement, “ +
      KEY_GOLD_HOARD_NAME_COLUMN + “ text not null, “ +
      KEY_GOLD_HOARDED_COLUMN + “ float, “ +
      KEY_GOLD_HOARD_ACCESSIBLE_COLUMN + “ integer);";
  public HoardDBOpenHelper(Context context, String name, CursorFactory factory, int version) {
    super(context, name, factory, version);
  }
  // Called when no database exists in disk and the helper class needs to create a new one.
  @Override
  public void onCreate(SQLiteDatabase db) {
    db.execSQL(DATABASE_CREATE);
  }
  // Called when there is a database version mismatch meaning that the version of the database on disk needs to be upgraded to
  // the current version.
  @Override
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    // Log the version upgrade.
    Log.w(“TaskDBAdapter", “Upgrading from version “ + oldVersion + “ to “ + newVersion + “, which will destroy all old data");
    // Upgrade the existing database to conform to the new version. Multiple previous versions can be handled by comparing oldVersion and newVersion values.
    // The simplest case is to drop the old table and create a new one.
    db.execSQL(“DROP TABLE IF IT EXISTS “ + DATABASE_TABLE);
    // Create a new one.
    onCreate(db);
  }
}

调用SQLiteOpenHelper的getWritableDatabase或者getReadableDatabase方法可以获得一个数据库实例,若不存在数据库实例,SQLiteOpenHelper的内部实现是使用onCreate()方法来创建一个数据库实例并返回。

使用其他方式创建或打开SQLite数据库

SQLiteDatabase db = context.openOrCreateDatabase(DATABASE_NAME, Context.MODE_PRIVATE, null);

使用上面的代码可以获得数据库,onCreate和onUpdate方法中的逻辑需要手动来完成。

Android数据库设计需要注意的事情

1. 文件不要直接存在数据库中。更好的方法是将文件路径放到数据库中。

2. 表中加入auto-increment的id字段。

数据库查询

使用query方法,传入下面这些参数,即可实现数据库查询:

  1. Boolean类型的参数,用于指示是否包含重复数据。
  2. 要查询的数据库表名。
  3. Projection,指定要返回的列。
  4. Where,可以用?做占位符。
  5. Where参数。
  6. Group by。
  7. Having。
  8. 字符串,指定返回行的顺序。
  9. 字符串,用于指定返回的最大行数
// Specify the result column projection. Return the minimum set of columns required to satisfy your requirements.
String[] result_columns = new String[] {KEY_ID, KEY_GOLD_HOARD_ACCESSIBLE_COLUMN, KEY_GOLD_HOARDED_COLUMN };
// Specify the where clause that will limit our results.
String where = KEY_GOLD_HOARD_ACCESSIBLE_COLUMN + “=" + 1;
// Replace these with valid SQL statements as necessary.
String whereArgs[] = null;
String groupBy = null;
String having = null;
String order = null;
SQLiteDatabase db = hoardDBOpenHelper.getWritableDatabase();
Cursor cursor = db.query(HoardDBOpenHelper.DATABASE_TABLE, result_columns, where, whereArgs, groupBy, having, order);

从Cursor中提取数据

从Cursor中提取数据需要两步。首先使用前面介绍的moveTo<loction>等方法首先移动到某行,然后使用get<type>方法获取某列的值。使用getColumnIndexOrThrow或getColumnIndex方法可以通过列名获得该列在Cursor中的index。若找不到该列,getColumnIndex方法将返回-1,而getColumnIndexOrThrow将抛出异常。

int columnIndex = cursor.getColumnIndex(KEY_COLUMN_1_NAME);
if (columnIndex > -1) {
  String columnValue = cursor.getString(columnIndex);
  // Do something with the column value.
}
else {
  // Do something else if the column doesn’t exist.
}

下面是一个从Cursor中读取数据的完整示例:

float totalHoard = 0f;
float averageHoard = 0f;
// Find the index to the column(s) being used.
int GOLD_HOARDED_COLUMN_INDEX = cursor.getColumnIndexOrThrow(KEY_GOLD_HOARDED_COLUMN);
// Iterate over the cursors rows.
// The Cursor is initialized at before first, so we can check only if there is a “next" row available. If the
// result Cursor is empty this will return false.
while (cursor.moveToNext()) {
  float hoard = cursor.getFloat(GOLD_HOARDED_COLUMN_INDEX);
  totalHoard += hoard;
}
// Calculate an average -- checking for divide by zero errors.
float cursorCount = cursor.getCount();
averageHoard = cursorCount > 0 ? (totalHoard / cursorCount) : Float.NaN;
// Close the Cursor when you’ve finished with it.
cursor.close();

增、改、删

插入数据:

// Create a new row of values to insert.
ContentValues newValues = new ContentValues();
// Assign values for each row.
newValues.put(KEY_GOLD_HOARD_NAME_COLUMN, hoardName);
newValues.put(KEY_GOLD_HOARDED_COLUMN, hoardValue);
newValues.put(KEY_GOLD_HOARD_ACCESSIBLE_COLUMN, hoardAccessible);
// [ ... Repeat for each column / value pair ... ]
// Insert the row into your table
SQLiteDatabase db = hoardDBOpenHelper.getWritableDatabase();
db.insert(HoardDBOpenHelper.DATABASE_TABLE, null, newValues);

更新数据:

// Create the updated row Content Values.
ContentValues updatedValues = new ContentValues();
// Assign values for each row.
updatedValues.put(KEY_GOLD_HOARDED_COLUMN, newHoardValue);
// [ ... Repeat for each column to update ... ]
// Specify a where clause the defines which rows should be updated. Specify where arguments as necessary.
String where = KEY_ID + “=" + hoardId;
String whereArgs[] = null;
// Update the row with the specified index with the new values.
SQLiteDatabase db = hoardDBOpenHelper.getWritableDatabase();
db.update(HoardDBOpenHelper.DATABASE_TABLE, updatedValues, where, whereArgs);

删除数据:

// Specify a where clause that determines which row(s) to delete.
// Specify where arguments as necessary.
String where = KEY_GOLD_HOARDED_COLUMN + “=" + 0;
String whereArgs[] = null;
// Delete the rows that match the where clause.
SQLiteDatabase db = hoardDBOpenHelper.getWritableDatabase();
db.delete(HoardDBOpenHelper.DATABASE_TABLE, where, whereArgs);

创建Content Providers

Content Provider是一个接口,Content Resolver可以通过这个接口读取数据。通过继承ContentProvider类,即可创建一个Content Provider:

public class MyContentProvider extends ContentProvider

注册Content Provider

将Content Provider加到Manifest后,Content Resolver才能找到并使用。每个Content Provider标签中都有authorities属性,这个属性标识Content Provider。

<provider android:name=".MyContentProvider" android:authorities="com.paad.skeletondatabaseprovider" />

发布Content Provider

每个ContentProvider都应该还有一个静态字符串变量CONTENT_URI,这个变量由该ContentProvider的authorities和data path组成,如:

public static final Uri CONTENT_URI = Uri.parse(“content://com.paad.skeletondatabaseprovider/elements”);

在CONTENT_URI后面加上行号,即可获得某一行数据的URI,如:

content://com.paad.skeletondatabaseprovider/elements/5

在实际使用中,最好将这两种URI都发布出来,并配合UriMatcher使用:

// Create the constants used to differentiate between the different URI requests.
private static final int ALLROWS = 1;
private static final int SINGLE_ROW = 2;
private static final UriMatcher uriMatcher;
// Populate the UriMatcher object, where a URI ending in ‘elements’ will correspond to a request for all items,
// and ‘elements/[rowID]’ represents a single row.
static {
  uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
  uriMatcher.addURI(“com.paad.skeletondatabaseprovider”, “elements”, ALLROWS);
  uriMatcher.addURI(“com.paad.skeletondatabaseprovider”, “elements/#”, SINGLE_ROW);
}

UriMatcher经常和SQLiteQueryBuilder配合使用:

SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
// If this is a row query, limit the result set to the passed in row.
switch (uriMatcher.match(uri)) {
  case SINGLE_ROW :
    String rowID = uri.getPathSegments().get(1);
    queryBuilder.appendWhere(KEY_ID + “=” + rowID);
  default: break;
}

创建Content Provider的数据库

使用SQLiteOpenHelper实现ContentProvider的onCreate方法:

private MySQLiteOpenHelper myOpenHelper;
@Override
public boolean onCreate() {
  // Construct the underlying database.
  // Defer opening the database until you need to perform a query or transaction.
  myOpenHelper = new MySQLiteOpenHelper(getContext(), MySQLiteOpenHelper.DATABASE_NAME, null,  MySQLiteOpenHelper.DATABASE_VERSION);
  return true;
}

实现Content Provider查询

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
  // Open the database.
  SQLiteDatabase db;
  try {
    db = myOpenHelper.getWritableDatabase();
  } catch (SQLiteException ex) {
    db = myOpenHelper.getReadableDatabase();
  }
  // Replace these with valid SQL statements if necessary.
  String groupBy = null;
  String having = null;
  // Use an SQLite Query Builder to simplify constructing the database query.
  SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
  // If this is a row query, limit the result set to the passed in row.
  switch (uriMatcher.match(uri)) {
    case SINGLE_ROW :
      String rowID = uri.getPathSegments().get(1);
      queryBuilder.appendWhere(KEY_ID + “=” + rowID);
    default:
      break;
  }
  // Specify the table on which to perform the query. This can be a specific table or a join as required.
  queryBuilder.setTables(MySQLiteOpenHelper.DATABASE_TABLE);
  // Execute the query.
  Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, groupBy, having, sortOrder);
  // Return the result Cursor.
  return cursor;
}

实现query方法之后,还需要提供返回数据的MIME信息。因此要重写getType方法并返回一个字符串来描述你的数据类型。数据类型有两种,一种是单条数据,另一种是集合数据:

  • 单条数据:vnd.android.cursor.item/vnd.<companyname>.<contenttype>
  • 所有数据:vnd.android.cursor.dir/vnd.<companyname>.<contenttype>
@Override
public String getType(Uri uri) {
  // Return a string that identifies the MIME type for a Content Provider URI
  switch (uriMatcher.match(uri)) {
    case ALLROWS:
      return “vnd.android.cursor.dir/vnd.paad.elemental”;
    case SINGLE_ROW:
      return “vnd.android.cursor.item/vnd.paad.elemental”;
    default:
      throw new IllegalArgumentException(“Unsupported URI: “ + uri);
  }
}

Content Provider Transactions

在修改完数据后,需要调用Content Resolver的notifyChange方法,这样Content Observer即可获得数据修改通知。下面是一段比较全面的示例代码:

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
  // Open a read / write database to support the transaction.
  SQLiteDatabase db = myOpenHelper.getWritableDatabase();
  // If this is a row URI, limit the deletion to the specified row.
  switch (uriMatcher.match(uri)) {
    case SINGLE_ROW :
      String rowID = uri.getPathSegments().get(1);
      selection = KEY_ID + “=” + rowID + (!TextUtils.isEmpty(selection) ?“ AND (“ + selection + ‘)’ : “”);
  default: break;
}
// To return the number of deleted items you must specify a where clause. To delete all rows and return a value pass in “1”.
if (selection == null)
  selection = “1”;
  // Perform the deletion.
  int deleteCount = db.delete(MySQLiteOpenHelper.DATABASE_TABLE, selection, selectionArgs);
  // Notify any observers of the change in the data set.
  getContext().getContentResolver().notifyChange(uri, null);
  // Return the number of deleted items.
  return deleteCount;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
  // Open a read / write database to support the transaction.
  SQLiteDatabase db = myOpenHelper.getWritableDatabase();
  // To add empty rows to your database by passing in an empty
  // Content Values object you must use the null column hack parameter to specify the name of the column that can be set to null.
  String nullColumnHack = null;
  // Insert the values into the table
  long id = db.insert(MySQLiteOpenHelper.DATABASE_TABLE, nullColumnHack, values);
  // Construct and return the URI of the newly inserted row.
  if (id > -1) {
    // Construct and return the URI of the newly inserted row.
    Uri insertedId = ContentUris.withAppendedId(CONTENT_URI, id);
    // Notify any observers of the change in the data set.
    getContext().getContentResolver().notifyChange(insertedId, null);
    return insertedId;
  }else
    return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
  // Open a read / write database to support the transaction.
  SQLiteDatabase db = myOpenHelper.getWritableDatabase();
  // If this is a row URI, limit the deletion to the specified row.
  switch (uriMatcher.match(uri)) {
    case SINGLE_ROW :
      String rowID = uri.getPathSegments().get(1);
      selection = KEY_ID + “=” + rowID + (!TextUtils.isEmpty(selection) ?“ AND (“ + selection + ‘)’ : “”);
    default: break;
  }
  // Perform the update.
  int updateCount = db.update(MySQLiteOpenHelper.DATABASE_TABLE, values, selection, selectionArgs);
  // Notify any observers of the change in the data set.
  getContext().getContentResolver().notifyChange(uri, null);
  return updateCount;
}

在Content Provider中存储文件

文件存储的最佳设计模式是将文件路径存储在数据库中,使用Content Provider的接口方法读写。文件路径应该存放在数据库表中的_data列中,并重写Content Provider的openFile方法,返回ParcelFileDescriptor:

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
  // Find the row ID and use it as a filename.
  String rowID = uri.getPathSegments().get(1);
  // Create a file object in the application’s external files directory.
  String picsDir = Environment.DIRECTORY_PICTURES;
  File file = new File(getContext().getExternalFilesDir(picsDir), rowID);
  // If the file doesn’t exist, create it now.
  if (!file.exists()) {
    try {
      file.createNewFile();
    } catch (IOException e) {
      Log.d(TAG, “File creation failed: “ + e.getMessage());
    }
  }
  // Translate the mode parameter to the corresponding Parcel File Descriptor open mode.
  int fileMode = 0;
  if (mode.contains(“w”))
    fileMode |= ParcelFileDescriptor.MODE_WRITE_ONLY;
  if (mode.contains(“r”))
    fileMode |= ParcelFileDescriptor.MODE_READ_ONLY;
  if (mode.contains(“+”))
    fileMode |= ParcelFileDescriptor.MODE_APPEND;
  // Return a Parcel File Descriptor that represents the file.
  return ParcelFileDescriptor.open(file, fileMode);
}

Content Provider的Skeleton示例

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
public class MyContentProvider extends ContentProvider {
  public static final Uri CONTENT_URI = Uri.parse(“content://com.paad.skeletondatabaseprovider/elements”);
  // Create the constants used to differentiate between the different URI requests.
  private static final int ALLROWS = 1;
  private static final int SINGLE_ROW = 2;
  private static final UriMatcher uriMatcher;
  // Populate the UriMatcher object, where a URI ending in ‘elements’ will correspond to a request for all
  // items, and ‘elements/[rowID]’ represents a single row.
  static {
    uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    uriMatcher.addURI(“com.paad.skeletondatabaseprovider”, “elements”, ALLROWS);
    uriMatcher.addURI(“com.paad.skeletondatabaseprovider”, “elements/#”, SINGLE_ROW);
  }
  // The index (key) column name for use in where clauses.
  public static final String KEY_ID = “_id”;
  // The name and column index of each column in your database.
  // These should be descriptive.
  public static final String KEY_COLUMN_1_NAME = “KEY_COLUMN_1_NAME”;
  // TODO: Create public field for each column in your table.
  // SQLite Open Helper variable
  private MySQLiteOpenHelper myOpenHelper;
  @Override
  public boolean onCreate() {
    // Construct the underlying database.
    // Defer opening the database until you need to perform a query or transaction.
    myOpenHelper = new MySQLiteOpenHelper(getContext(), MySQLiteOpenHelper.DATABASE_NAME, null, MySQLiteOpenHelper.DATABASE_VERSION);
    return true;
  }
  @Override
  public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
  // Open the database.
  SQLiteDatabase db = myOpenHelper.getWritableDatabase();
  // Replace these with valid SQL statements if necessary.
  String groupBy = null;
  String having = null;
  SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
  queryBuilder.setTables(MySQLiteOpenHelper.DATABASE_TABLE);
  // If this is a row query, limit the result set to the passed in row.
  switch (uriMatcher.match(uri)) {
    case SINGLE_ROW :
      String rowID = uri.getPathSegments().get(1);
      queryBuilder.appendWhere(KEY_ID + “=” + rowID);
    default: break;
  }
  Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, groupBy, having, sortOrder);
  return cursor;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs){
  // Open a read / write database to support the transaction.
  SQLiteDatabase db = myOpenHelper.getWritableDatabase();
  // If this is a row URI, limit the deletion to the specified row.
  switch (uriMatcher.match(uri)) {
    case SINGLE_ROW :
      String rowID = uri.getPathSegments().get(1);
      selection = KEY_ID + “=” + rowID + (!TextUtils.isEmpty(selection) ?“ AND (“ + selection + ‘)’ : “”);
    default: break;
  }
  // To return the number of deleted items, you must specify a where clause. To delete all rows and return a value, pass in “1”.
  if (selection == null)  selection = “1”;
  // Execute the deletion.
  int deleteCount = db.delete(MySQLiteOpenHelper.DATABASE_TABLE, selection, selectionArgs);
  // Notify any observers of the change in the data set.
  getContext().getContentResolver().notifyChange(uri, null);
  return deleteCount;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
  // Open a read / write database to support the transaction.
  SQLiteDatabase db = myOpenHelper.getWritableDatabase();
  // To add empty rows to your database by passing in an empty
  // Content Values object, you must use the null column hack parameter to specify the name of the column that can be set to null.
  String nullColumnHack = null;
  // Insert the values into the table
  long id = db.insert(MySQLiteOpenHelper.DATABASE_TABLE, nullColumnHack, values);
  if (id > -1) {
  // Construct and return the URI of the newly inserted row.
  Uri insertedId = ContentUris.withAppendedId(CONTENT_URI, id);
  // Notify any observers of the change in the data set.
  getContext().getContentResolver().notifyChange(insertedId, null);
  return insertedId;
}else
  return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
  // Open a read / write database to support the transaction.
  SQLiteDatabase db = myOpenHelper.getWritableDatabase();
  // If this is a row URI, limit the deletion to the specified row.
  switch (uriMatcher.match(uri)) {
    case SINGLE_ROW :
      String rowID = uri.getPathSegments().get(1);
      selection = KEY_ID + “=” + rowID + (!TextUtils.isEmpty(selection) ?“ AND (“ + selection + ‘)’ : “”);
    default: break;
  }
  // Perform the update.
  int updateCount = db.update(MySQLiteOpenHelper.DATABASE_TABLE, values, selection, selectionArgs);
  // Notify any observers of the change in the data set.
  getContext().getContentResolver().notifyChange(uri, null);
  return updateCount;
}
@Override
public String getType(Uri uri) {
  // Return a string that identifies the MIME type for a Content Provider URI
  switch (uriMatcher.match(uri)) {
    case ALLROWS:
      return “vnd.android.cursor.dir/vnd.paad.elemental”;
    case SINGLE_ROW:
      return “vnd.android.cursor.item/vnd.paad.elemental”;
    default:
      throw new IllegalArgumentException(“Unsupported URI: “ + uri);
  }
}
private static class MySQLiteOpenHelper extends SQLiteOpenHelper {
  // [ ... SQLite Open Helper Implementation ... ]
}
}

Android 4 学习(16):Database and Content Providers的更多相关文章

  1. Content Providers的步骤,来自官网文档

    Content Providers In this document Content provider basics Querying a content provider Modifying dat ...

  2. Content Providers

    Content providers manage access to a structured set of data. They encapsulate the data, and provide ...

  3. 我的Android 4 学习系列之数据库和Content Provider

    目录 创建数据库和使用SQLite 使用Content Provider.Cusor和Content Value来存储.共享和使用应用程序数据 使用Cursor Loader异步查询Content P ...

  4. Android Animation学习(二) ApiDemos解析:基本Animators使用

    Android Animation学习(二) ApiDemos解析:基本Animatiors使用 Animator类提供了创建动画的基本结构,但是一般使用的是它的子类: ValueAnimator.O ...

  5. Android Animation学习(二) ApiDemos解析:基本Animatiors使用

    Animator类提供了创建动画的基本结构,但是一般使用的是它的子类: ValueAnimator.ObjectAnimator.AnimatorSet ApiDemos中Animation部分是单独 ...

  6. Android:日常学习笔记(9)———探究持久化技术

    Android:日常学习笔记(9)———探究持久化技术 引入持久化技术 什么是持久化技术 持久化技术就是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑关机的情况下,这些数据仍然不会丢失 ...

  7. 【转】Android开发学习笔记(一)——初识Android

    对于一名程序员来说,“自顶向下”虽然是一种最普通不过的分析问题和解决问题的方式,但其却是简单且较为有效的一种.所以,将其应用到Android的学习中来,不至于将自己的冲动演变为一种盲目和不知所措. 根 ...

  8. Android:日常学习笔记(10)———使用LitePal操作数据库

    Android:日常学习笔记(10)———使用LitePal操作数据库 引入LitePal 什么是LitePal LitePal是一款开源的Android数据库框架,采用了对象关系映射(ORM)的模式 ...

  9. Android自动化学习笔记之MonkeyRunner:官方介绍和简单实例

    ---------------------------------------------------------------------------------------------------- ...

随机推荐

  1. 如何退出telnet

    ctrl键+ENter键 然后输入 进入telnet 命令 quit

  2. pdb 源码索引符号服务器创建过程

    pdb是调试程序必不可少的东西,它保存着一个exe或dll的调试信息,对pdb进行源码索引可以快速找到软件对应该版本的代码,本文以subversion版本控制服务器进行介绍 一.需要安装的软件 win ...

  3. tf随笔-1

    生成新的计算图,并完成常量初始化,在新的计算 图中完成加法计算 import tensorflow as tf g1=tf.Graph() with g1.as_default(): value=[1 ...

  4. [C#] Newtonsoft.Json 版本冲突

    在web.config或者app.config里面加上一段: <runtime> <assemblyBinding xmlns="urn:schemas-microsoft ...

  5. ng 依赖注入

    将依赖的对象注入到当前对象,直接去使用依赖的对象即可. 降低耦合度.提高开发速度.. 文件压缩:yui-compressor有两种方案:①CLI(command line interface)java ...

  6. 迭代器模式在 Java 容器中的实现

    迭代器接口是迭代器模式实现的精髓: public interface Iterator<E> { boolean hasNext(); E next(); ... } 假设某容器名为 Xx ...

  7. HDU - 6395:Sequence (分块+矩阵)

    题面太丑了,就不复制了. 题意:F1=A: F2=B: Fn=D*Fn-1+C*Fn-2+P/i:求Fn. 思路:根据P/i的值划分区间,每个区间矩阵求. 带常数的矩阵: #include<bi ...

  8. ZOJ-Big string(服气思维)

    个人心得:我在分治上看到的,但是感觉跟分治没关系,一眼想到斐波那契数可以找到此时n的字符串,但是无法精确到字母,题解的思路 真是令人佩服,以BA为基准,然后只要此时的长度大于7那么必然可以减去最大的斐 ...

  9. hexo搭建个人主页托管于github

    之前学习了 如何利用Github免费搭建个人主页,今天利用hexo来快速生成个人网页托管于github上. hexo系列教程:(一)hexo介绍 什么是hexo hexo是一个基于Node.js的静态 ...

  10. Windows 系统定时自动重启

    1.创建新文本并输入 shutdown -r -t 0 保存成.bat文件 2.创建系统任务计划 2.1 在开始中打开[任务计划程序] 2.2 新建创建任务计划目录 2.3 在新目录下新建任务计划即可 ...