Android数据持久化技术:文件存储、SharedPreferences存储、数据库存储

使用这些持久化技术保存的数据只能再当前的应用程序中访问

但是对于不同应用之间的可以实现跨程序数据共享的功能

此时使用的是内容提供器实现跨程序数据共享

5.1、内容提供器简介

内容提供器主要用于再不同的应用程序之间实现数据共享的功能

提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问的数据的安全性

使用内容提供器是Android实现跨程序共享数据的标准方式

不同于文件存储和SharedPreferences存储中的两种全局可读可写操作模式

内容提供器可以选择对那一部分数据进行共享

从而保证程序中隐私数据不会有泄露的风险

权限:

Android运行时权限,内容提供器中需要使用运行时权限的功能

5.2、运行时权限

Android的权限机制从系统的第一个版本已经开始

之前Android的权限机制在保护用户安全和隐私等方面作用比较有限

再Android6.0之后系统中引入运行时权限这个功能

从而更好的保护了用户的安茜和隐私

5.2.1、权限机制详解

广播机制时使用过:

https://www.cnblogs.com/Mrchengs/p/10678609.html

访问系统的网络状态以及监听开机广播设计了用户设备的安全性

因此必须再AndroidManifest.xml中加入权限声明,否则程序会崩溃

加了权限之后,对于用户有什么影响?为什么这样就可以保护设备的安全性?

用户再一下两个方面得到了保护:

1、如果用户低于6.0系统的设备上安装该程序

  会弹出安装此应用需要以下权限

这样用户 就可以清楚的知道该程序一共需要那些权限

2、用户可以随时在应用程序管理界面查看任意一个程序的权限申请情况

  以保证应用程序不会出现各种滥用权限的情况

这种权限机制的设计思路其实很简单

用户如果认可你所申请的权限

那么就会安装程序

用户不认可所申请的权限

那么就会拒绝安装

限制存在很多的软件普遍纯在滥用权限

不管权限是否用得到

都会进行申请权限

在Android 6.0系统中加入了运行时权限功能

用户不需要再安装软件时一次性授权所有的申请权限

而是再软件使用过程中在对某一项权限进行授权

不是所有的权限都需要在运行时申请

对于用户来说,不停的授权也很烦

Android现在将所有的权限归成了两类:

1、普通权限

2、危险权限

普通权限指的是那些不会直接危险到用户的安全和隐私的权限

对于这部分权限,系统会自动帮助我们进行授权

危险权限指的是那些可能触及用户隐私,或者对设备安全性造成影响的权限

这部分的权限则有用户手动授权才可

Android中共有百种多权限

危险权限总共就那么几个

剩余的均是普通权限

危险权限:(9组24个)

这里是一个参照表

每当使用一个权限时可以查询

如果是属于这张表的权限那么就需要运行时处理

在AndroidManifest.xml文件中进行注册即可

表格中每个权限都属于一个权限组

运行时权限处理使用的权限名

一旦用户同意了

那么该权限对应的权限组中其他权限也会被授权

权限列表:https://blog.csdn.net/HardWorkingAnt/article/details/70952583

5.2.2、程序运行时申请权限

测试打电话的权限

首先定义一个按钮用于打电话的call:

    <Button
android:id="@+id/call"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Call"
/>

在AndroidManifest.xml中进行权限注册:

    <uses-permission android:name="android.permission.CALL_PHONE" />

在MainActivity中:

    protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout); Button call = (Button) findViewById(R.id.call);
call.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (ActivityCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
                      new String[]{Manifest.permission.CALL_PHONE},);
}else {
try {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}catch (Exception e){
e.printStackTrace();
}
}
}
});
}

这里的action指定了Intent.ACTION_CALL

这是系统内置的拨打电话动作,在Data中设置的协议是tel

这个内置的action时直接拨打电话并不是跳转到拨打电话的页面,因此必须声明权限(上述中以进行申请)

运行时权限爱的核心在程序运行的过程中由用户进行授权去执行相关的操作

程序是不可擅自去执行这些操作

第一步是判断用户是否已经授权借助的是:

ActivityCompat.checkSelfPermission()方法接受两个参数

1、COntext

2、具体的权限名

使用方法的返回值和PackageManager.PERMISSION_GRANTED做比较

相等就说明用户已经授权,否则表示没有授权

如果没有授权则执行 ActivityCompat.requestPermissions()方法,3个参数

1、要求是Activity的实例

2、是一个String数组,把要申请的权限名放在数组中即可

3、请求码,必须是唯一值

在弹出请求的按钮之后

不管用户点击那个按钮都会指定onRequestPermissionsResult()

这个方法的逻辑在下面进行实现

授权的结果会封装在grantResults参数中

只需要判断最后的授权结果

用户同意则拨打电话

否则就会提示一条未授权的信息

下面的代码实现:

此时运行程序点击按钮:

这里会有的一个权限的申请允许则进行操作

不允许则进行操作

在MianActivity中添加代码:

    public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) { switch (requestCode){
case :
if (grantResults.length > && grantResults[] == PackageManager.PERMISSION_GRANTED){
if (ActivityCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CALL_PHONE},);
}else {
try {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}catch (Exception e){
e.printStackTrace();
}
}
}else {
Toast.makeText(this,"未授权",Toast.LENGTH_LONG).show();
}
break;
default:
} }

此时点击拒绝授权:

系统会进行提示

5.3、访问其他程序的数据

内容提供器的用法一般分为两种:

1、使用 现有的内容提供器来读取和操作相应程序中的数据

2、创建自己的内容提供器给我们程序的数据提供外部访问的接口

如果一个程序通过内容提供器对其数据提供了外部访问接口

那么其他任何应用程序就都可以对这部分数据进行访问

Android中的电话本、短信、媒体库等程序都提供了类似的访问的接口

使得第三方应用程序可以充分利用这部分数据来实现更好的功能

5.3.1、ContentResolver的基本用法

对于每一个应用程序来说

如果需要访问内容提供器中共享的数据

就一定要借助ContentResolver类

可以通过Context中的getXontentResolver()方法来获取到该类的实例

ContentResolver中提供了一系列的方法用于对数据进行CRUD

其中insert()方法用于添加数据

update()方法用于更新数据

delete()方法用于删除数据

query()方法在、用于查询数据

同SQLiteDatabase中的操作有些类似。

但是在操作过程中方法都是不接收表明参数的

使用Uri参数代替

这个参数被称为:内容URI

内容URI给内容提供器中数据建立了唯一标识符

两部分组成:

1、authority

用于对不同的应用程序做区分的

一般为了避免冲突,都会采用包名的方式来命名

如:com.example.app.activity1

2、path

用于对同一个应用程序中不同的表进行区分

通常在authority的后面

如:/table1,/table2

URI就是将authority和path的组合

URI=com.example.app.activity1/table1

标准的写法:

content://com.example.app.activity/table1

此时的URI可以鲜明的表达要访问的程序的数据表

ContentResolver中的增删该查方法才都接受URI对象作为参数

如果使用表名的话,系统无法得知需要访问的应用程序

得到了URL字符串之后

还需要将器解析成Uri对象才可以作为参数传入

Uri uri = Uri.parse("content://com.example.app.activity/table1");

只需要使用Uri.parse()即可将内容URI字符串解析成Uri对象

使用这个Uri对象查询table边坡中的数据

Cursor sursor = getContentResolver().query(

  uri,projection,selection,selectionArgs,sortOrder);

参数解析:

查询之后返回的仍然是一个Cursor对象

这是就可以将数据句从Cursor对象中逐个读取出来

读取数据是用过移动光标的位置来遍历Cursor:

if( cursor != null){
  while(cursor.moveToNext()){
    String c1 = cursor.getString(cursor.getColumnIndex("column1"));
    String c2 = cursor.getString(cursor.getColumnIndex("column2"));
  }
}

添加

ContentValues values = new ContentValues();

values.put("c1","v1");

values.put("c2","v2");

getContentResolver().insert(uri,values)

更新

ContentValues values = new ContentValues();

values.put("c1","v1");

getContentResolver().update(uri,values,"c1 = ? and c2 = ?",new String [] {"v1","v2"})

使用了selection和selectionArgs参数对其进行约束

以防止所有的行动都会受影响

删除

getContentResolver().delete(uri,"c1 = ? ",new String [] {"1"});

5.3.2、读取联系人的测试

使用ListView进行对联系人的好么进行显示;

    <ListView
android:id="@+id/list_view_phone"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>

在MainActivity中:

public class MainActivity extends AppCompatActivity {

    ArrayAdapter<String> adapter;
List<String> contactList = new ArrayList<>(); @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout); ListView contactsView = (ListView) findViewById(R.id.list_view_phone);
adapter= new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,contactList);
contactsView.setAdapter(adapter); if (ActivityCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
              new String[]{Manifest.permission.READ_CONTACTS},);
}else {
readContacts();
}
} public void readContacts(){
Cursor cursor = null;
try{
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,null,null,null);
if (cursor != null){
while (cursor.moveToNext()){
String name =
          cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number =
          cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contactList.add(name + "\n" + number);
}
adapter.notifyDataSetChanged();
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(cursor != null){
cursor.close();
}
}
} public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
switch (requestCode){
case :
if (grantResults.length > && grantResults[]==PackageManager.PERMISSION_GRANTED){
readContacts();
}else {
Toast.makeText(this,"未授权",Toast.LENGTH_LONG).show();
}
break;
default:
}
} }

在onCreate()方法中

首先获取ListView控件,并且设置适配器

然后运行调用运行时权限的处理READ_CONTACTS权限属于危险权限

这里使用readContacts()方法来处理读取联系人的信息

关于readContacts()方法:

使用ContentResolver的query()方法来查询系统的联系人数据

这里传入的ContactsContract.CommonDataKinds.Phone.CONTENT_URI是已经封装好的URI字符串

在查询之后在对Cursor对象进行遍历

将数据逐个取出

此时还需要注册权限:

    <uses-permission android:name="android.permission.READ_CONTACTS"/>

此时运行程序:

显示之前会进行权限的授权

不授权的同时还会进行提示未授权的字样

5.4、创建自己的内容提供器

参考:https://juejin.im/post/5af812966fb9a07ac85a8188#heading-9

5.4.1、创建内容提供器的步骤

实现跨程序共享数据功能

官方推荐使用内容提供器

可以创建一类继承ContentProvider的方式创建一个自己的内容提供器

MyContentProvider.java

public class MyContentProvider extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return ;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return ;
}
}

6个方法:

1、onCreate()

初始化内容提供器时候调用,通常会在这里完成对数据库的创建和升级操作

返回未true表示内容提供器初始化成功

返回为false表示失败

只有当存在ContentResolver尝试访问程序中的数据,内容提供器才会初始化

2、query()

从内容提供器中查询数据,使用uri参数确定查询那张表,projection参数用于确定查询那些列

selection和selectionArgs参数用于约束那些行,sortOrder参数用于对结果进行排序

查询的结果存放在Cursor对象中

3、insert()

向内容提供器添加一天数据

使用uri参数来确定要添加的表

待添加的数据保存在values参数中

添加完成之后,返回一个用于表示这条新纪录的URI

4、update()

更新内容提供器中已有的数据

使用uri参数代表来确定更新那张数据表

新数据保存在values参数中

selection和selectionArgs参数用于约束更新那些行

受影响的行数将作为返回值返回

5、delete()

从内容提供器中删除数据

使用uri参数来确定删除那一张数据表的数据

selection和selectionArgs参数用于约束删除那些行,被删除的行数将作为返回值返回

6、getType()

根据传入的内容URI返回相应的MIME类型

几乎每一个方法都带有Uri这个参数

这个参数正是调用ContentResolver的增删改查方法时传递过来的

上文中的URI:

content://com.example.app.activity/table1

可以在URI后面加上一个id

content://com.example.app.activity/table1/

表示调用放期望访问的是:

com.example.app.activity的程序的table1数据表中的id为1的数据

注意:

* :表示匹配任意长度的字符

# :表示匹配任意长度的数字

com.example.app.activity/table/*

com.example.app.activity/table/#

在借助UriMatcher这个类可以轻松匹配内容URI的功能

提供了addURI()方法接受三个参数

分别可以把authority、path、自定义的代码传进去

当调用UriMatcher的match()方法时,就可以把一个Uri对象传入

返回值是某个匹配这个uri对象所对应的自定义的代码

在其中新增4个整形常量

TABLE_DIR表示访问table1表中所有数据

TABLE_ITEM表示访问table1中的单条数据

在静态代码块中创建了UriMatcher的实例

调用addURI()方法,将期望匹配的内容传递进入,这里传递的路径参数可以使用通配符的

在query()方法调用的时候就会通过UriMatcher的match()方法传入的Uri对象进行匹配

发现UriMatcher中某个URI格式成功匹配了该Uri对象

则会返回自定义的代码

在对数据表中进行相应的操作即可

getType()方法

是所有的内容提供器必须提供的一个方法

用于获取Uri对象所对应的MIME类型

一个内容URI所对应的MIME字符串由3部分组成:

此时可以进行完善:

此时一个完整的内容提供器就完成了现在任何一个应用程序都可以使用COntentResolver来访问程序中的数据

关于:如何保存隐私数据不会泄露的问题?

内容提供器提供了良好的机制

所有的CRUD操作都一定要匹配到相应的URI格式才能进行的

不能向UriMatcher中添加隐私数据URI

所以这部分数据根本无法被外部程序访问到,安全性问题也就不存在了

5.4.2、实现跨程序数据共享

数据持久化过程中的数据进行实现

通过内容提供器来给他加入外部访问接口

注意:

跨程序访问时不能直接使用Toast

右键--->new --->Other--->Content Provider

DAtabasetProvider.java

public class DatabasetProvider extends ContentProvider {

    public static final int BOOK_DIR = ;
public static final int BOOK_ITEM = ; public static final String AUTHORITY = "com.example.ccrr.applicationtwo";
private MyDatabaseHelper myDatabaseHelper; public static UriMatcher uriMatcher; static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY,"Book",BOOK_DIR);
uriMatcher.addURI(AUTHORITY,"Book/#",BOOK_ITEM); } public DatabasetProvider() {
}
@Override
public boolean onCreate() {
myDatabaseHelper = new MyDatabaseHelper(getContext(),"bookstore.db",null,);
return true;
} @Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = myDatabaseHelper.getWritableDatabase();
int deleteRows = ;
switch (uriMatcher.match(uri)){
case BOOK_DIR:
deleteRows = db.delete("Book",selection,selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get();
deleteRows=db.delete("Book","id = ?",new String[]{bookId});
}
return deleteRows;
} @Override
public String getType(Uri uri) { switch (uriMatcher.match(uri)){
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.com.example.ccrr.applicationtwo.Book";
case BOOK_ITEM:
return "vnd.android.cursor.dir/vnd.com.example.ccrr.applicationtwo.Book";
}
return null;
} @Override
public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = myDatabaseHelper.getWritableDatabase();
Uri uriReturn = null;
switch (uriMatcher.match(uri)){
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("Book",null,values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/Book"+newBookId);
break;
}
return uriReturn;
} @Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
//查询数据
SQLiteDatabase db = myDatabaseHelper.getReadableDatabase();
Cursor cursor = null;
switch (uriMatcher.match(uri)){
case BOOK_DIR:
cursor = db.query("Book",projection,selection,selectionArgs,null,null,sortOrder);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get();
cursor = db.query("Book",projection,"id = ?",new String[]{bookId},null,null,sortOrder);
break;
default:
break;
}
return cursor;
} @Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) { SQLiteDatabase db = myDatabaseHelper.getWritableDatabase();
int updateRows = ;
switch (uriMatcher.match(uri)){
case BOOK_DIR:
updateRows = db.update("Book",values,selection,selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get();
updateRows = db.update("Book",values,"id = ?" , new String[] {bookId});
break;
}
return updateRows; }
}

布局:

<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/qqq"
android:text="add"/> <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/query_pro"
android:text="query"/> <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/update"
android:text="update"/> <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/delete"
android:text="delete"/>

MainActivity:

public class MainActivity extends AppCompatActivity {

    private MyDatabaseHelper myDatabaseHelper;
private String bewId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout); myDatabaseHelper=new MyDatabaseHelper(this,"bookstore.db",null,);
Button button = (Button) findViewById(R.id.qqq);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse("content://com.example.ccrr.applicationtwo/Book");
ContentValues values = new ContentValues();
values.put("author","Mr");
values.put("price",12.3);
values.put("name","Java");
Uri newNui = getContentResolver().insert(uri,values); }
}); Button query = (Button) findViewById(R.id.query_pro);
query.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse("content://com.example.ccrr.applicationtwo/Book");
Cursor cursor = getContentResolver().query(uri,null,null,null,null);
if (cursor != null){
while (cursor.moveToNext()){
String name = cursor.getString(cursor.getColumnIndex("name"));
String price = cursor.getString(cursor.getColumnIndex("price"));
String author = cursor.getString(cursor.getColumnIndex("author")); Log.d("name",name );
Log.d("price",price );
Log.d("author", author);
}
cursor.close();
} }
}); final Button updata = (Button) findViewById(R.id.update);
updata.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse("content://com.example.ccrr.applicationtwo/Book");
ContentValues values = new ContentValues();
values.put("name","Android");
values.put("price",1.2);
getContentResolver().update(uri,values,null,null);
}
}); Button delete = (Button) findViewById(R.id.delete);
delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse("content://com.example.ccrr.applicationtwo/Book");
getContentResolver().delete(uri,null,null);
}
});
}
}

  <provider
android:name=".DatabasetProvider"
android:authorities="com.example.ccrr.applicationtwo"
android:enabled="true"
android:exported="true"></provider>

小小的测试:

5、Android-跨程序共享数据--内容提供器的更多相关文章

  1. Android学习--跨程序共享数据之内容提供其探究

    什么是内容提供器? 跨程序共享数据之内容提供器,这是个什么功能?看到这个名称的时候最能给我们提供信息的应该是“跨程序”这个词了,是的重点就是这个词,这个内容提供器的作用主要是用于在不同的引用程序之间实 ...

  2. 内容提供者(Content Provider)——跨程序共享数据

    内容提供者 Content Provider 应用的数据库是不允许其他应用访问的 内容提供者的作用就是让别的应用访问到你的数据库 自定义内容提供者,继承ContentProvider类,重写增删改查方 ...

  3. Android学习之基础知识十—内容提供器(Content Provider)

    一.跨程序共享数据——内容提供器简介 内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能 ...

  4. Android之 内容提供器(1)——使用内容提供器访问其它程序共享的数据

    (下面内容是阅读郭霖大神的<第一行代码>总结的) 1 概述 内容提供器是Android实现跨程序共享数据的标准方式. 内容提供器的的使用方法有两种, 一是使用已有的内容提供器对其他程序的数 ...

  5. android——实现跨程序访问数据

    使用之前的SQLite存储的应用程序.首先需要在这个应用程序中创建内容提供器,右击com.example.administrator.exp7包→New→Other→Content Provider, ...

  6. Android入门(十三)内容提供器

    原文链接:http://www.orlion.ga/612/ 内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一 ...

  7. Android学习笔记(二十)——自定义内容提供器

    //此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 如果我们想要实现跨程序共享数据的功能,官方推荐的方式就是使用内容提供器,可以通过新建一个类去继承 Conten ...

  8. Android 创建内容提供器(ContentResolver)

    如果想实现跨程序共享数据的功能,官方推荐的方式就是使用内容提供器,可以通过新建一个类去继承 ContentResolver 的方式来创建一个自己的内容提供器. ContentProvider 类中有六 ...

  9. Android 内容提供器(Content Provider)介绍

    内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性.目前,使用内容 ...

随机推荐

  1. 使用 OLEDB 及 SqlBulkCopy 将多个不在同一文件夹下的 ACCESS mdb 数据文件导入MSSQL

    注:转载请标明文章原始出处及作者信息http://www.cnblogs.com/z-huifei/p/7380388.html 前言 OLE DB 是微软的战略性的通向不同的数据源的低级应用程序接口 ...

  2. 三、hdfs的JavaAPI操作

    下文展示Java的API如何操作hdfs,在这之前你需要先安装配置好hdfs https://www.cnblogs.com/lay2017/p/9919905.html 依赖 你需要引入依赖如下 & ...

  3. layui对json数据的格式要求

    layui有自己的一套特定的数据格式交互,必须参数code:0,msg:“”,count:数据size(int),data:”数据List”.**一般我们选择封装返回接收类**. 若想要绑定数据到la ...

  4. PHP-redis英文文档

    作为程序员,看英文文档是必备技能,所以尽量还是多看英文版的^^ PhpRedis The phpredis extension provides an API for communicating wi ...

  5. MVC 中导出Execl 对 科学计数 的转化

    相信大家在使用MVC 的做execl 数据导出的时候,遇到过 身份证号码 银行卡号 交易号 等一大串数字的时候,在导出execl 的时候就会出现 科学计数的输入:下面来说一下解决方案: 第一种:在MV ...

  6. ThreeJs 选中物体事件

    选中物体变红色demo: https://threejs.org/examples/#webgl_raycast_sprite <!DOCTYPE html> <html lang= ...

  7. MySQL数据库(12)----ALL、ANY、SOME子查询

    运算符 ALL 和 ANY 常与某个关系比较运算符结合在一起使用,以便测试列子查询的结果.它们会测试比较值与子查询返回的全部或部分值是否匹配.例如,当比较值小于或等于子查询返回的每个值时,<= ...

  8. 用Java开发贪吃蛇游戏

    贪吃蛇游戏的设计步骤: Part 1: 设计游戏图纸 画出900*700的白色窗口 在窗口上添加画布 在画布上添加标题 在画布上添加黑色游戏区 Part 2: 放置静态的蛇:一个头.两个身体 加上开始 ...

  9. 使用拦截器拦截html参数

    公司最新需求:根据传递的参数进行业务判断,如果符合条件则继续后面的业务逻辑,否则跳转到指定的错误页面.有些是请求的controller 使用了spring  aop的方式进行验证:但是有些是html页 ...

  10. windows&cmd常用命令&快捷键

    1: cls即CLear Screen 功能:清除屏幕上的所有显示 2:Win+Tab快捷键进行3D窗口切换还有比较快速切换窗口的新方法. 3:利用Alt+Tab快捷键进行切换窗口时,在桌面中间会显示 ...