跨程序共享数据:Android四大组件之内容提供器

news2025/1/13 19:47:10

跨程序共享数据:Android四大组件之内容提供器

  • 前言
  • 七、跨程序共享数据:Android四大组件之内容提供器
    • 7.1 内容提供器(Content Provider)简介
    • 7.2 运行时权限(软件不能为所欲为,想要什么权限,还得主人决定)
    • 7.3 访问其它程序中的数据
      • 7.3.1 什么是URI
      • 7.3.2 基于内容提供器对其它程序的数据进行增删改查
      • 7.3.3 查询电话本程序中数据的核心代码示例
    • 7.4 创建自己的内容提供器(提供共享数据的接口)
      • 7.4.1 创建自己的内容提供器步骤
      • 7.4.2 创建自己的内容提供器核心代码示例
      • 7.4.2 创建自己的内容提供器完整代码示例
  • 参考书籍:第一行代码

前言

Android中包含了大量的应用程序,那么如何获取其它应用程序的数据,来做一些更有意思的事情呢?内容提供作为Android的四大组件之一就是用来实现应用程序之间数据共享的。

七、跨程序共享数据:Android四大组件之内容提供器

7.1 内容提供器(Content Provider)简介

内容提供器是实现Android跨程序之间数据共享的一种安全方式。前面我们讲到Android持久化技术,我们也可以让其它应用程序直接访问我们的持久化文件实现数据共享,但是这种方式存在安全隐患,因此又提出了内容提供器技术方案,通过内容提供器可以控制开发哪些数据让别的程序访问,进一步提高数据共享的安全性。

7.2 运行时权限(软件不能为所欲为,想要什么权限,还得主人决定)

内容提供器是实现跨程序数据共享,既然是访问别的程序数据,为了安全性起见,自然不是你想访问谁的数据就访问谁的数据,你需要先申请访问该程序的权限,然后由用户决定是否同意你的申请,只有权限申请通过了,才能利用内容提供器访问程序中的数据,而运行时权限就是实现这一过程的方案

申请的权限包括普通权限和危险权限,普通权限只需要在AndroidManifest.xml文件中声明该权限即可,由系统自动授权;危险权限不仅需要在AndroidManifest.xml文件中声明该权限,而且需要在代码中申请该权限,然后由用户决定是否授权

Android中涉及的危险权限如下表所示:
在这里插入图片描述

  • 普通权限申请,如网络状态权限申请的示例如下,只需要在AndroidManifest.xml文件中添加< use-permission >标签即可。,
<manifest ......>
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
  • 危险权限申请,如读取电话本程序数据权限申请示例如下,需要同时在代码中添加权限申请逻辑。

AndroidManifest.xml文件配置:

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

代码申请权限:

if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED)
{
	//没有读取电话簿的权限,需要代码申请权限,参数1:context 参数2:需要申请的权限列表 参数3:申请权限的标识,在申请结果的回调函数中使用
	ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CONTACTS},1);
}
else
    //有读取电话簿的权限,可以进行读取电话簿数据
	readContacts();
 @Override
    //申请权限结果回调函数
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode)
        {
            case 1:
                //如果用户同意了申请,则读取数据,进行相关操作
                if (grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED)
                    readContacts();
                //如果用户没有同意申请,进行其它逻辑
                else
                    Toast.makeText(this, "未授予读取电话本权限", Toast.LENGTH_SHORT).show();
                break;
            default:
                break;
        }
    }

7.3 访问其它程序中的数据

Android提供ContentResolver类来获取其它应用程序中的数据,ContentResolver的对象通过Context的getContentResolver方法获得。ContentResolver包含操作数据库的常用方法如query,insert,delete,update等等。

7.3.1 什么是URI

我们知道操作数据库首先需要确定是操作数据库中的哪张表,通常我们直接通过表名来确定是哪张表。

而访问其它程序的数据,首先要确定访问的是哪个程序,然后才能确定是访问的哪张表,因此Android提供一个内容URI来内容提供器中的数据建立唯一的标准,简单来说URI就是指定哪个程序中的哪张表的唯一标识

答疑:URI中不需要指定是哪个数据库,因为开放内容提供器接口的程序内部已经设定了是哪个数据库,访问方只需要确定是哪张表就可以了,这个在下一节我们创建自己的内容提供器的时候你就能看到。

URI的格式为:content://authority和path

  • authority:是用于对不同的应用程序做区分的,一般为了避免冲突,都会采用程序包名的方式来进行命名。比如某个程序的包名是com.example.app,那么该程序对应的 authority就可以命名为com.example.app. provider。
  • path:是用于对同一应用程序中不同的表做区分的,通常都会添加到authority的后面。比如某个程序的数据库里存在两张表:table1和 table2,这时就可以将path分别命名为/table1和/table2。因此完整URI就是content://com.example.app. provider/table1 和content://com.example.app. provider/table2

7.3.2 基于内容提供器对其它程序的数据进行增删改查

  1. 查询数据语法
ContentResolver contentResolver = getContentResolver();
//参数1:唯一标识要查询的是哪个程序下的哪张表; 参数2:指定查询的列名
//参数3:指定where的约束条件; 参数4:为参数3的占位符提供具体的值
//参数5:对查询结果的排序方式
contentResolver.query(uri,projection,selection,selectionArgs,sortOrder);
  1. 插入数据语法
ContentResolver contentResolver = getContentResolver(# 参考书籍:第一行代码
链接:https://pan.baidu.com/s/1aXtOQCXL6qzxEFLBlqXs1Q?pwd=n5ag
提取码:n5ag);
//参数1:URI 参数2:待插入数据的键值对值
contentResolver.insert(uri,ContentValues)
  1. 更新数据语法
ContentResolver contentResolver = getContentResolver();
//参数1:URI 参数2:待插入数据的键值对值 参数3和参数4约束更新范围
contentResolver.update(uri,ContentValues,selection,selectionArgs)
  1. 删除数据语法
ContentResolver contentResolver = getContentResolver();
//参数1:URI; 参数2和参数3约束删除范围
contentResolver.delete(uri,selection,selectionArgs)

7.3.3 查询电话本程序中数据的核心代码示例

 Cursor cursor=null;
        try {
            ContentResolver contentResolver = getContentResolver();
            //电话本程序的URI由ContactsContract.CommonDataKinds.Phone封装好了,我们直接用可以了
            cursor= contentResolver.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));
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally {
            if (cursor!=null)
                cursor.close();
        }

7.4 创建自己的内容提供器(提供共享数据的接口)

7.4.1 创建自己的内容提供器步骤

  1. 创建向其它程序共享自己数据的一系列方法的类,该类继承自ContentProvider
  2. 创建UriMatcher对象,并将自己想要共享的数据URI添加到UriMatcher对象中
  3. 重写ContentProvider类下的增删改查等方法,为其它程序访问自己的数据提供接口
  4. 在AndroidManifest.xml文件中的 < application>标签下添加 < provider>子标签
    在AndroidManifest.xml中配置自己的内容提供器示例:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.xiaomi.databasetest">

    <application
        ......
        <provider
            android:name=".DatabaseProvider" <--创建的内容提供器类的路径-->
            android:authorities="com.xiaomi.databasetest.provider" <!--程序标识-->
            android:enabled="true"
            android:exported="true" />
	</application>
</manifest>

答疑:
UriMatcher对象添加URI的方法是addURI(String authority, String path, int code),authority和path在前面已经讲过,code是当前匹配URI的唯一标识。
上面讲到path代表的是表名,例如/table1,其实表名后面还可以加一个数字,代码这个表的ID,即访问表中的第几行数据,例如/table1/2。

7.4.2 创建自己的内容提供器核心代码示例

public class MyProvider extends ContentProvider {

    //自定义code,指定URI的唯一标识
    public static final int TABLE1_DIR=0;
    public static final int TABLE1_ITEM=1;
    private static UriMatcher uriMatcher;

    static {
        //正则表达式: *代码任意长度的字符,#代表任意长度的数字
        uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
        //将content://com.xiaomi.app.provider/table1添加到Matcher对象中,代码想要把table1整张表给其它程序访问
        uriMatcher.addURI("com.xiaomi.app.provider","table1",TABLE1_DIR);
         //将content://com.xiaomi.app.provider/table1/#添加到Matcher对象中,代码想要把table1表中的指定行给其它程序访问
        uriMatcher.addURI("com.xiaomi.app.provider","table1/#",TABLE1_ITEM);
    }

    @Override
    public boolean onCreate() {
        //指定或者创建想要共享数据的数据库(这儿也能解释为什么URI中不需要指定数据库的原因了)
        return false;
    }

    @Nullable
    @Override
    //提供给其它程序查询本程序数据库的方法
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        switch (uriMatcher.match(uri))
        {
            case TABLE1_DIR:
                System.out.printf("向外部提供table1表下的所有数据");
                break;
            case TABLE1_ITEM:
                System.out.printf("向外部提供table1表下的单条数据");
                break;
            default:
                break;
        }
        return null;
    }
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        //获取URI对应的MIME字符串
        //URI以路径结尾,MIME格式:vnd.android.cursor.dir/vnd.<authority>.<path>
        //URI以id结尾,MIME格式:vnd.android.cursor.item/vnd.<authority>.<path>
        switch (uriMatcher.match(uri))
        {
            case TABLE1_DIR:
                return "vnd:android.cursor.dir/vnd.com.xiaomi.app.provider.table1";
            case TABLE1_ITEM:
                return "vnd:android.cursor.item/vnd.com.xiaomi.app.provider.table1";
            default:
                break;
        }
        return null;
    }

}

答疑:
创建的自己的内容提供器除了实现增删改查等操作数据库的方法外,还需要实现getType方法,该方法是根据传入的URI来返回相应的MIME类型。
一个URI对应的MIME字符串主要由一下三部分组成:

    1. 必须以vnd.开头
    1. 如果URI以表名结尾,则后接 android.cursor.dir/;如果URI以id结尾,则后接android.cursor.item/
    1. 最后接上vnd.< authority>.< path>

示例:
URI:content://com.example.app.provider/table1
对应MIME:vnd.android.cursor.dir/vnd.com.example.app.provider.table1
URI:content://com.example.app.provider/table1/3
对应MIME:vnd.android.cursor.item/vnd.com.example.app.provider.table1

7.4.2 创建自己的内容提供器完整代码示例

  1. 创建程序的SQLIte数据库帮助类
public class MyDatabaseHelper extends SQLiteOpenHelper {

    private Context mContext;

    public static final String CREATE_BOOK_TABLE="create table Book(" +
            "id integer primary key autoincrement," +
            "author text," +
            "price real," +
            "pages integer," +
            "name text)";

    public static final String DROP_BOOK="drop table if exists Book";

    public static final String CREATE_CATEGORY_TABLE="create table Category(" +
            "id integer primary key autoincrement," +
            "category_name text," +
            "category_code integer)";

    public static final String DROP_CATEGORY="drop table if exists Category";

    public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        this.mContext=context;
    }

    //数据库不存在的前提下,创建数据库完成后会调用该方法
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL(CREATE_BOOK_TABLE);
        sqLiteDatabase.execSQL(CREATE_CATEGORY_TABLE);//新增这张表
        //Toast.makeText(mContext, "相关表创建完成", Toast.LENGTH_SHORT).show();
    }

    //数据库版本发生变化的时候,创建数据库操作后,会调用该方法
    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
        sqLiteDatabase.execSQL(DROP_BOOK);
        sqLiteDatabase.execSQL(DROP_CATEGORY);
        onCreate(sqLiteDatabase);
    }
}
  1. 创建内容提供器为别的程序访问我们的数据提供接口
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;

public class DatabaseProvider extends ContentProvider {

    public static final int BOOK_DIR=0;
    public static final int BOOK_ITEM=1;
    public static final int CATEGORY_DIR=2;
    public static final int CATEGORY_ITEM=3;

    public static final String AUTHORITY="com.xiaomi.databasetest.provider";
    private static UriMatcher uriMatcher;

    private MyDatabaseHelper dbHelper;

    static {
        uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY,"book",BOOK_DIR);
        uriMatcher.addURI(AUTHORITY,"book/#",BOOK_ITEM);
        uriMatcher.addURI(AUTHORITY,"category",CATEGORY_DIR);
        uriMatcher.addURI(AUTHORITY,"category/#",CATEGORY_ITEM);
    }

    public DatabaseProvider() {
    }

    @Override
    public boolean onCreate() {
        // TODO: Implement this to initialize your content provider on startup.
        
        dbHelper=new MyDatabaseHelper(getContext(),"BookStore.db",null,1);
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        // TODO: Implement this to handle query requests from clients.
        Cursor cursor=null;
        SQLiteDatabase database = dbHelper.getReadableDatabase();
        switch (uriMatcher.match(uri))
        {
            case BOOK_DIR:
                cursor=database.query("Book",projection,selection,selectionArgs,null,null,sortOrder);
                break;
            case BOOK_ITEM:
                String bookID = uri.getPathSegments().get(1);
                cursor=database.query("Book",projection,"id = ?",new String[]{bookID},null,null,sortOrder);
                break;
            case CATEGORY_DIR:
                cursor=database.query("Category",projection,selection,selectionArgs,null,null,sortOrder);
                break;
            case CATEGORY_ITEM:
                String categoryID = uri.getPathSegments().get(1);
                cursor=database.query("Category",projection,"id = ?",new String[]{categoryID},null,null,sortOrder);
                break;
            default:
                break;
        }
        return cursor;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO: Implement this to handle requests to insert a new row.
        SQLiteDatabase writableDatabase = dbHelper.getWritableDatabase();
        Uri resUri=null;
        long newID=-1;
        switch (uriMatcher.match(uri))
        {
            case BOOK_DIR:
            case BOOK_ITEM:
                newID = writableDatabase.insert("Book", null, values);
                resUri=Uri.parse("content://"+AUTHORITY+"/book/"+newID);
                break;
            case CATEGORY_DIR:
            case CATEGORY_ITEM:
                newID = writableDatabase.insert("Category", null, values);
                resUri=Uri.parse("content://"+AUTHORITY+"/category/"+newID);
                break;
            default:
                break;
        }
        return resUri;
    }


    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // TODO: Implement this to handle requests to update one or more rows.
        SQLiteDatabase writableDatabase = dbHelper.getWritableDatabase();
        int updatedRows=0;
        switch (uriMatcher.match(uri))
        {
            case BOOK_DIR:
                updatedRows = writableDatabase.update("Book", values, selection, selectionArgs);
                break;
            case BOOK_ITEM:
                String bookID = uri.getPathSegments().get(1);
                updatedRows = writableDatabase.update("Book", values, "id = ?", new String[]{bookID});
                break;
            case CATEGORY_DIR:
                updatedRows = writableDatabase.update("Category", values, selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryID = uri.getPathSegments().get(1);
                updatedRows = writableDatabase.update("Category", values, "id = ?", new String[]{categoryID});
                break;
            default:
                break;
        }
        return updatedRows;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        SQLiteDatabase writableDatabase = dbHelper.getWritableDatabase();
        int deleteRows=0;
        switch (uriMatcher.match(uri))
        {
            case BOOK_DIR:
                deleteRows = writableDatabase.delete("Book", selection, selectionArgs);
                break;
            case BOOK_ITEM:
                String bookID = uri.getPathSegments().get(1);
                deleteRows=writableDatabase.delete("Book","id = ?",new String[]{bookID});
                break;
            case CATEGORY_DIR:
                deleteRows = writableDatabase.delete("Category", selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryID = uri.getPathSegments().get(1);
                deleteRows=writableDatabase.delete("Category","id = ?",new String[]{categoryID});
                break;
            default:
                break;
        }
        return deleteRows;
    }

    @Override
    public String getType(Uri uri) {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        switch (uriMatcher.match(uri))
        {
            case BOOK_DIR:
                return "vnd.android.cursor.dir/vnd.com.xiaomi.databasetest.provider.book";
            case BOOK_ITEM:
                return "vnd.android.cursor.item/vnd.com.xiaomi.databasetest.provider.book";
            case CATEGORY_DIR:
                return "vnd.android.cursor.dir/vnd.com.xiaomi.databasetest.provider.category";
            case CATEGORY_ITEM:
                return "vnd.android.cursor.item/vnd.com.xiaomi.databasetest.provider.category";
        }
        return null;
    }# 参考书籍:第一行代码
链接:https://pan.baidu.com/s/1aXtOQCXL6qzxEFLBlqXs1Q?pwd=n5ag
提取码:n5ag
}
  1. 在AndroidManifest.xml中注册内容提供器
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.xiaomi.databasetest">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.DatabaseTest"
        tools:targetApi="31">

        <provider
            android:name=".DatabaseProvider"
            android:authorities="com.xiaomi.databasetest.provider"
            android:enabled="true"
            android:exported="true" />

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
  1. 其它程序通过我们定义的内容提供器访问我们的数据
  • 插入数据代码示例
ContentResolver contentResolver = getContentResolver();

ContentValues contentValues = new ContentValues();
contentValues.put("name","这个世界真是很美好呀");
contentValues.put("author","从外部程序来的作者");
contentValues.put("pages",666);
contentValues.put("price",19.99);

Uri uri = Uri.parse("content://com.xiaomi.databasetest.provider/book");
Uri newUri = contentResolver.insert(uri, contentValues);
String newID = newUri.getPathSegments().get(1);//返回的新插入数据的id
Toast.makeText(MainActivity.this, "添加数据成功", Toast.LENGTH_SHORT).show();
  • 查询数据代码示例
Uri uri = Uri.parse("content://com.xiaomi.databasetest.provider/book");
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor==null)
{
	Toast.makeText(MainActivity.this, "未查询到数据", Toast.LENGTH_SHORT).show();
	return;
}
if (cursor.moveToFirst())
{
	StringBuilder stringBuilder = new StringBuilder();
	stringBuilder.append("name"+"     "+"author"+"     "+"pages"+"     "+"price\r\n");
	do {
		String name = cursor.getString(cursor.getColumnIndex("name"));
		String author = cursor.getString(cursor.getColumnIndex("author"));
		int pages = cursor.getInt(cursor.getColumnIndex("pages"));
		double price = cursor.getDouble(cursor.getColumnIndex("price"));
		stringBuilder.append(name+"     "+author+"     "+pages+"     "+price+"\r\n");
}while (cursor.moveToNext());
  • 更新数据示例代码:
Uri uri = Uri.parse("content://com.xiaomi.databasetest.provider/book/"+newID);
ContentValues contentValues = new ContentValues();
contentValues.put("price","26.66");
contentValues.put("name","这个世界多么美好");
getContentResolver().update(uri,contentValues,null,null);
  • 删除代码示例
Uri uri = Uri.parse("content://com.xiaomi.databasetest.provider/book/"+newID);
getContentResolver().delete(uri,null,null);

参考书籍:第一行代码

链接:https://pan.baidu.com/s/1aXtOQCXL6qzxEFLBlqXs1Q?pwd=n5ag
提取码:n5ag

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/24439.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【project 】软件使用

project软件使用 1.如何为某任务或资源创建日历 创建新日历 工具->更改工作时间->新建->定义日历名称&#xff0c;选择“新建基准日历”->根据各承建商的日历创建相应的日历 使用新日历 拷贝各承建商的各项任务到指定的项目计划中&#xff0c;然后&#xff…

基于特征选择的二元蜻蜓算法(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…

C. String Equality(思维)

Problem - 1451C - Codeforces Ashish有两个字符串a和b&#xff0c;每个字符串的长度为n&#xff0c;还有一个整数k。 他想通过对a进行一些&#xff08;可能是零&#xff09;操作&#xff0c;将字符串a转换成字符串b。 在一次操作中&#xff0c;他可以 选择一个索引i&#x…

哪吒汽车的技术发布会都发布了什么?纯干货抢先看

11月21日&#xff0c;哪吒汽车发布了浩智超算、浩智电驱、浩智增程三大技术品牌&#xff0c;并推出三款技术产品&#xff0c;包括智能汽车中央超算平台、800V SiC高性能电驱系统、高效三合一增程器。去年年底&#xff0c;哪吒曾经发布过山海平台&#xff0c;据说是一个支持哪吒…

性能环境搭建(0-CentOS7 安装配置)

1.前言 根据现有的组件&#xff0c;准备动手搭建一套完整的监控环境。既然是练手&#xff0c;还是在虚拟机里自己先练习一下。出了问题也好恢复。所有就先从最基本的开始。那就是操作系统开始搭建玩起来。 2.环境 资源有效利用吧&#xff0c;公司的资源能自由使用的那最方便…

数据结构-复杂度(一)

目录 一、什么是复杂度&#xff1f; 算法效率&#xff1a; 复杂度&#xff1a; 二、复杂度分类 一、时间复杂度 二、空间复杂度&#xff08;Space Complexity&#xff09; 了解数据结构之前需要了解复杂度。 一、什么是复杂度&#xff1f; 在介绍复杂度之前我们现分享一…

CengBox靶机

0x01 信息收集 nmap -sV 10.0.2.6 22 ssh端口&#xff0c;弱口令爆破为主 80 web页面 目录遍历&#xff0c;备份文件查找等 dirsearch -u http://10.0.2.6 获取了目录&#xff0c;发现存在一个maseradmin目录&#xff0c;可能存在些东西&#xff0c;继续扫。 dirsearch -u …

C规范编辑笔记(四)

大家好&#xff0c;今天来给大家分享一下C规范编辑笔记第四篇&#xff0c;距离我们C规范编辑笔记第三篇也快过去了一个月&#xff0c;这次继续分享一波~ 1、以大写形式声明常量&#xff0c; 为避免误解&#xff0c;常量值必须根据其类型使用后缀。这不仅有助于代码阅读&#x…

CSO面对面丨如何通过“联合作战”,加强银行安全体系建设

随着数字化转型的深入&#xff0c;以银行为代表的金融机构不断加码金融科技建设。然而随着线上业务量不断上升&#xff0c;银行面临的安全风险暴露面也愈大、问题愈加复杂。本期腾讯安全《CSO面对面》栏目&#xff0c;邀请到某头部商业银行安全主管&#xff0c;以金融行业的安全…

【Lilishop商城】No1-1.业务了解+划分各模块逻辑

目录 A1.整体业务逻辑 B1.模块整理 C1.运营后台 C2.店铺后台 C3.买方平台 B2.重点模块梳理图 C1.订单模块 C2.退货/退款模块&#xff08;即售后模块&#xff09; C3.促销活动模块 A2.模块划分&#xff08;自己思考的&#xff09; A3.数据结构划分&#xff08;自己思考…

算法设计与分析 SCAU11079 可以移动的石子合并(优先做)

11079 可以移动的石子合并&#xff08;优先做&#xff09; 时间限制:1000MS 代码长度限制:10KB 提交次数:25 通过次数:9 题型: 编程题 语言: G;GCC;VC;JAVA Description 有n堆石子形成一行(a1,a2,…,an&#xff0c;ai为第i堆石子个数)&#xff0c;现要将石子合并成一堆&…

Android App开发手机阅读中贝塞尔曲线的原理讲解及实现波浪起伏动画实战(附源码和演示视频 可直接使用)

需要图片集和源码请点赞关注收藏后评论区留言~~~ 一、贝塞尔曲线的原理 贝塞尔曲线是一种用于二维图形的数学曲线。贝塞尔曲线由节点和线段构成&#xff0c;其中节点是可拖动的支点&#xff0c;而线段彷佛有弹性的牛皮筋。它除了起点和终点之外&#xff0c;不再描绘中间的折现…

嵌入式(驱动开发)(中断处理)

一、什么是中断 一种硬件上的通知机制&#xff0c;用来通知CPU发生了某种需要立即处理的事件 分为&#xff1a; 内部中断 CPU执行程序的过程中&#xff0c;发生的一些硬件出错、运算出错事件&#xff08;如分母为0、溢出等等&#xff09;&#xff0c;不可屏蔽外部中断 外设发…

SpringBoot怎么整合第三方缓存技术/EhCache缓存技术使用以及Redis缓存技术使用怎么在SpringBoot中使用

写在前面&#xff1a; 继续记录自己的SpringBoot学习之旅&#xff0c;这次是SpringBoot应用相关知识学习记录。若看不懂则建议先看前几篇博客&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 3.5 整合第三方技术 3.5.1 缓存 3.5.1.1 介绍 缓…

2022/11/21[指针] 多维数组与指针的联系

1、指向数组元素的指针变量 #include<stdio.h> int main() {int* p;int a[3][4] { {1,2,3,4},{5,6,7,8},{9,10,11,12} };int i, j;//将第0行第0列的地址赋给pfor (p a[0]; p < a[0] 12; p)//注意是a[0]{if ((p - a[0]) % 4 0)printf("\n");printf("…

java--Lambda(2)表达式语法

文章目录0 写在前面1 Lambda 表达式的五种形式1.1 不包含参数&#xff1a;1.2 包含一个参数&#xff1a;1.3 有多个参数1.4 表达式主体是不是一个代码块1.5 不声明参数类型2 写在末尾0 写在前面 最基本的 Lambda 表达式&#xff0c;它由三部分组成具体格式是这样子的&#xff…

【Py】使用flask-apscheduler动态调整作业参数(附源码)

之前的项目常使用Apscheduler进行定时任务调度&#xff0c;但最近想通过接口对这些任务进行动态调整&#xff0c;比如调整任务启停、调整任务执行时间、间隔时间等等 flask-apscheduler这个基于flask的库能够满足上面的需求&#xff0c;而且由于基于flask&#xff0c;所以我常…

查题校园免费题库接口

查题校园免费题库接口 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点…

ButterKnife依赖注入框架源码解析

BuffterKnife 采用 注解 APT技术 APT&#xff1a;Annotation Processor tool 注解处理器&#xff0c;是javac的一个工具&#xff0c;每个处理器都是继承于AbstractProcessor 注解处理器是运行在自己的java虚拟机中 APT如何生成字节码文件&#xff1a; Annotation Processing 不…

李立宗《讲给入门者的深度学习》

14天学习训练营导师课程&#xff1a; 李立宗《讲给入门者的深度学习》 一、什么是深度学习&#xff1f; 1、传统方法、机器学习、深度学习的区别&#xff1f; 以取暖为例&#xff0c;来说明三者的不同之处。 传统方法&#xff1a;通过火炉生火&#xff0c;需要生火、添柴、…