ContentProvider功能變數名稱替換小工具

来源:http://www.cnblogs.com/dingzq/archive/2016/11/17/6073086.html
-Advertisement-
Play Games

開發項目功能變數名稱想怎麼換就怎麼換,就是這麼任性! 這是一個很有意思的小工具! 這是一個方便開發人員和測試人員的小工具!! 吐槽: 一直在做Android開發,一直總有一個問題存在:做自己公司的apk開發時,線上包和測試包不可兼得~總是在 卸載、安裝、卸載、安裝。。。的迴圈操作。很是麻煩,而且另外一個不得 ...


開發項目功能變數名稱想怎麼換就怎麼換,就是這麼任性!

這是一個很有意思的小工具!

這是一個方便開發人員和測試人員的小工具!!

吐槽:

一直在做Android開發,一直總有一個問題存在:做自己公司的apk開發時,線上包和測試包不可兼得~總是在 卸載、安裝、卸載、安裝。。。的迴圈操作。很是麻煩,而且另外一個不得不正視的問題就是:只要跟服務端人員進行聯調時,就得修改項目中的測試功能變數名稱,重新打包,也是夠麻煩的。最近報名了公司的一個服務,就不得不使用線上包了,被逼無奈想起了這個小設計。

原理:

使用ContentProvider數據共用~

展示圖:

 

 

 設計思路及源碼解析:

 1.前期準備

a.ContentProvider在android中的作用是對外共用數據, 也就是說你可以通過ContentProvider把應用中的數據共用給其他應用訪問,其他應用可以通過ContentProvider對你應用中的數據進行添刪改查。

當應用需要通過ContentProvider對外共用數據時,第一步需要繼承ContentProvider並重寫下麵方法:

 

public class PersonContentProvider extends ContentProvider{
    public boolean onCreate()
    public Uri insert(Uri uri, ContentValues values)
    public int delete(Uri uri, String selection, String[] selectionArgs)
    public int update(Uri uri, ContentValues values, String selection,         String[] selectionArgs)
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
    public String getType(Uri uri)
}    

 

b.第二步需要在AndroidManifest.xml使用對該ContentProvider進行配置,為了能讓其他應用找到該ContentProvider ,ContentProvider採用了authorities(主機名/功能變數名稱)對它進行唯一標識,你可以把ContentProvider看作是一個網 站(想想,網站也是提供數據者),authorities 就是他的功能變數名稱

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xc1217.contentprovider">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".activity.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".activity.UrlListActivity"
android:theme="@style/AppTheme.NoActionBar">
</activity>

<provider android:name=".db.MyContentProvider"
android:authorities="com.xc1217.contentprovider.myprovider"
android:exported="true"/>
</application>

</manifest>
2.資料庫設計
/**
 * Created by ding on 2016/11/15.
 */
public class DBOpenHelper extends SQLiteOpenHelper {
    private static final String DBNAME = "1217provider.db"; //資料庫名稱
    private static final int DBVER = 1;//資料庫版本

    public DBOpenHelper(Context context) {
        super(context, DBNAME, null, DBVER);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // id 主鍵id,  url 路徑   selected 1 選中,0 未選中
        String sql = "CREATE TABLE student (id integer primary key autoincrement, url varchar(500), selected int)";
        String sql2 = "CREATE TABLE coach (id integer primary key autoincrement, url varchar(500), selected int)";
        db.execSQL(sql);//執行有更改的sql語句
        db.execSQL(sql2);
        initDb(db);
    }

    private void initDb(SQLiteDatabase db) {
        String sql = "INSERT INTO student VALUES (1,'http://www.1217.com/', 0)";
        String sq2 = "INSERT INTO student VALUES (2,'http://www.1217.com/', 1)";
        String sq3 = "INSERT INTO student VALUES (3,'http://www.1217.com/', 0)";
        String sq4 = "INSERT INTO student VALUES (4,'http://www.1217.com/', 0)";
        db.execSQL(sql);
        db.execSQL(sq2);
        db.execSQL(sq3);
        db.execSQL(sq4);
        initAddDbCoach(db);
    }

    private void initAddDbCoach(SQLiteDatabase db) {
        String sql = "INSERT INTO coach VALUES (1,'http://www.1217.com/', 0)";
        String sq2 = "INSERT INTO coach VALUES (2,'http://www.1217.com/', 1)";
        String sq3 = "INSERT INTO coach VALUES (3,'http://www.1217.com/', 0)";
        String sq4 = "INSERT INTO coach VALUES (4,'http://www.1217.com/', 0)";
        db.execSQL(sql);
        db.execSQL(sq2);
        db.execSQL(sq3);
        db.execSQL(sq4);

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {
        db.execSQL("DROP TABLE IF EXISTS student");
        db.execSQL("DROP TABLE IF EXISTS coach");
        onCreate(db);
    }

}

 

3.繼承ContentProvider並重寫方法

 

/**
 * Created by ding on 2016/11/15.
 */
public class MyContentProvider extends ContentProvider {
    private DBOpenHelper dbOpenHelper;
    //常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼
    private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
    private static final int STUDENTS = 1;
    private static final int STUDENT = 2;
    private static final int COACHS = 3;
    private static final int COACH = 4;

    static {
        //如果match()方法匹配content://com.xc1217.contentprovider.myprovider/student路徑,返回匹配碼為1
        MATCHER.addURI("com.xc1217.contentprovider.myprovider", "student", STUDENTS);
        //如果match()方法匹配content://com.xc1217.contentprovider.myprovider/student/123路徑,返回匹配碼為2
        MATCHER.addURI("com.xc1217.contentprovider.myprovider", "student/#", STUDENT);//#號為通配符
        MATCHER.addURI("com.xc1217.contentprovider.myprovider", "coach", COACHS);
        MATCHER.addURI("com.xc1217.contentprovider.myprovider", "coach/#", COACH);//#號為通配符
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        int count = 0;
        long id = 0;
        String where = "";
        switch (MATCHER.match(uri)) {
            case STUDENTS:
                count = db.delete("student", selection, selectionArgs);
                return count;
            case STUDENT:
                //ContentUris類用於獲取Uri路徑後面的ID部分
                id = ContentUris.parseId(uri);
                where = "id = " + id;
                if (selection != null && !"".equals(selection)) {
                    where = selection + " and " + where;
                }
                count = db.delete("student", where, selectionArgs);
                return count;
            case COACHS:
                count = db.delete("coach", selection, selectionArgs);
                return count;
            case COACH:
                //ContentUris類用於獲取Uri路徑後面的ID部分
                id = ContentUris.parseId(uri);
                where = "id = " + id;
                if (selection != null && !"".equals(selection)) {
                    where = selection + " and " + where;
                }
                count = db.delete("coach", where, selectionArgs);
                return count;
            default:
                throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
        }
    }

    /**
     * 該方法用於返回當前Url所代表數據的MIME類型。
     * 如果操作的數據屬於集合類型,那麼MIME類型字元串應該以vnd.android.cursor.dir/開頭
     * 如果要操作的數據屬於非集合類型數據,那麼MIME類型字元串應該以vnd.android.cursor.item/開頭
     */
    @Override
    public String getType(Uri uri) {
        switch (MATCHER.match(uri)) {
            case STUDENTS:
                return "vnd.android.cursor.dir/student";

            case STUDENT:
                return "vnd.android.cursor.item/student";
            case COACHS:
                return "vnd.android.cursor.dir/coach";

            case COACH:
                return "vnd.android.cursor.item/coach";

            default:
                throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        long rowid = 0;
        Uri insertUri = null;//得到代表新增記錄的Uri
        switch (MATCHER.match(uri)) {
            case STUDENTS:
                rowid = db.insert("student", "url", values);
                insertUri = ContentUris.withAppendedId(uri, rowid);//得到代表新增記錄的Uri
                this.getContext().getContentResolver().notifyChange(uri, null);
                return insertUri;
            case COACHS:
                rowid = db.insert("coach", "url", values);
                insertUri = ContentUris.withAppendedId(uri, rowid);//得到代表新增記錄的Uri
                this.getContext().getContentResolver().notifyChange(uri, null);
                return insertUri;

            default:
                throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
        }
    }

    @Override
    public boolean onCreate() {
        this.dbOpenHelper = new DBOpenHelper(this.getContext());
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                        String sortOrder) {
        SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
        long id = 0;
        String where = null;
        switch (MATCHER.match(uri)) {
            case STUDENTS:
                return db.query("student", projection, selection, selectionArgs, null, null, sortOrder);
            case STUDENT:
                id = ContentUris.parseId(uri);
                where = "id = " + id;
                if (selection != null && !"".equals(selection)) {
                    where = selection + " and " + where;
                }
                return db.query("student", projection, where, selectionArgs, null, null, sortOrder);
            case COACHS:
                return db.query("coach", projection, selection, selectionArgs, null, null, sortOrder);
            case COACH:
                id = ContentUris.parseId(uri);
                where = "id = " + id;
                if (selection != null && !"".equals(selection)) {
                    where = selection + " and " + where;
                }
                return db.query("coach", projection, where, selectionArgs, null, null, sortOrder);
            default:
                throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
        }
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        int count = 0;
        long id = 0;
        String where = null;
        switch (MATCHER.match(uri)) {
            case STUDENTS:
                count = db.update("student", values, selection, selectionArgs);
                return count;
            case STUDENT:
                id = ContentUris.parseId(uri);
                where = "id = " + id;
                if (selection != null && !"".equals(selection)) {
                    where = selection + " and " + where;
                }
                count = db.update("student", values, where, selectionArgs);
                return count;
            case COACHS:
                count = db.update("coach", values, selection, selectionArgs);
                return count;
            case COACH:
                id = ContentUris.parseId(uri);
                where = "id = " + id;
                if (selection != null && !"".equals(selection)) {
                    where = selection + " and " + where;
                }
                count = db.update("coach", values, where, selectionArgs);
                return count;
            default:
                throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
        }
    }
}

 

4.MainActivity. java
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        findViewById(R.id.lay_student).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                Intent intent = new Intent(MainActivity.this, UrlListActivity.class);
                intent.putExtra("table", "student");
                startActivity(intent);
            }
        });
        findViewById(R.id.lay_coach).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, UrlListActivity.class);
                intent.putExtra("table", "coach");
                startActivity(intent);
            }
        });

    }
}
5.UrlListActivity.java
public class UrlListActivity extends AppCompatActivity {
    private static final String TAG = "UrlListActivity";
    String uriString = "content://com.xc1217.contentprovider.myprovider/";
    private String table = "";
    private List<UrlBean> mList = new ArrayList<UrlBean>();
    private ListView listView;
    private int position;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_url_list);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        Intent intent = getIntent();
        if (intent != null) {
            table = intent.getStringExtra("table");
            if (TextUtils.isEmpty(table))
                return;
            setTitle(table);
            uriString = uriString + table;
        }
        initUI();
        getUrlList();

    }

    private void initUI() {
        listView = (ListView) findViewById(R.id.list_url);

        findViewById(R.id.btn_add).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    showAddPop();
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                }
            }
        });
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                position = i;
                showClickPop();
            }
        });
    }

    private void showAddPop() {
        final EditText editText = new EditText(this);
        editText.setText("http://www.1217.com/");
        editText.setHint("http://www.1217.com/");
        final AlertDialog alertDialog = new AlertDialog.Builder(this).
                setTitle("添加")
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // TODO Auto-generated method stub
                    }
                })
                .setPositiveButton("確定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        String url = editText.getText().toString();
                        insertUrl(url);
                    }
                }).setView(editText).create();
        alertDialog.show();
        alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View
                .OnClickListener() {
            @Override
            public void onClick(View v) {
                String url = editText.getText().toString();
                if (TextUtils.isEmpty(url)) {
                    Toast.makeText(UrlListActivity.this, "不能為空", Toast.LENGTH_SHORT).show();
                    return;
                }
                insertUrl(url);
                alertDialog.dismiss();
                getUrlList();
            }
        });
    }

    private void showClickPop() {
        final String[] arrayFruit = new String[]{"選擇", "刪除"};

        Dialog alertDialog = new AlertDialog.Builder(this).
                setTitle("編輯")
                .setItems(arrayFruit, new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                        switch (which) {
                            case 0:
                                for (int i = 0; i < mList.size(); i++) {
                                    UrlBean urlBean = mList.get(i);
                                    urlBean.selected = (position == i ? 1 : 0);
                                    update(urlBean);
                                }
                                UrlAdapter urlAdapter = new UrlAdapter(UrlListActivity.this, mList);
                                listView.setAdapter(urlAdapter);
                                break;
                            case 1:
                                int id = mList.get(position).id;
                                deleteById(id);
                                getUrlList();
                                break;
                            default:
                                break;
                        }

                    }
                }).setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // TODO Auto-generated method stub
                    }
                }).
                        create();
        alertDialog.show();
    }

    //往內容提供者添加數據
    public void insertUrl(String url) {
        try {
            ContentResolver contentResolver = this.getContentResolver();
            Uri insertUri = Uri.parse(uriString);
            ContentValues values = new ContentValues();
            values.put("url", url);
            values.put("selected", 0);
            Uri uri = contentResolver.insert(insertUri, values);
            Log.i(TAG, uri.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //更新內容提供者中的數據
    public void update(UrlBean urlBean) {
        try {
            ContentResolver contentResolver = this.getContentResolver();
            Uri updateUri = Uri.parse(uriString + "/" + urlBean.id);
            ContentValues values = new ContentValues();
            values.put("url", urlBean.url);
            values.put("selected", urlBean.selected);
            contentResolver.update(updateUri, values, null, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //從內容提供者中刪除數據
    public void deleteById(Integer id) {
        try {
            ContentResolver contentResolver = this.getContentResolver();
            Uri deleteUri = Uri.parse(uriString + "/" + id);
            contentResolver.delete(deleteUri, null, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //獲取內容提供者中的數據
    public void getUrlList() {
        try {
            ContentResolver contentResolver = this.getContentResolver();
            Uri selectUri = Uri.parse(uriString);
            Cursor cursor = contentResolver.query(selectUri, null, null, null, null);
            if (cursor == null)
                return;
            mList.clear();
            while (cursor.moveToNext()) {
                int id = cursor.getInt(cursor.getColumnIndex("id"));
                String url = cursor.getString(cursor.getColumnIndex("url"));
                int selected = cursor.getInt(cursor.getColumnIndex("selected"));
                Log.i(TAG, "id=" + id + ",url=" + url + ",selected=" + selected);
                UrlBean urlBean = new UrlBean(id, url, selected);
                mList.add(urlBean);
            }
            UrlAdapter urlAdapter = new UrlAdapter(this, mList);
            listView.setAdapter(urlAdapter);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
6.UrlAdapter.java
public class UrlAdapter extends BaseAdapter {
    private Context mContext;
    private List<UrlBean> mList;

    public UrlAdapter(Context c, List<UrlBean> l) {
        // TODO Auto-generated constructor stub
        mContext = c;
        mList = l;
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return mList.size();
    }

    @Override
    public Object getItem(int arg0) {
        // TODO Auto-generated method stub
        return mList.get(arg0);
    }

    @Override
    public long getItemId(int arg0) {
        // TODO Auto-generated method stub
        return arg0;
    }

    @Override
    public View getView(final int i, View view, ViewGroup arg2) {
        ViewHoldel holdel;
        if (view == null) {
            holdel = new ViewHoldel();
            view = LayoutInflater.from(mContext).inflate(R.layout.url_item,
                    null);
            holdel.tvContent = (TextView) view.findViewById(R.id.tv_content);
            holdel.imageView = (ImageView) view.findViewById(R.id.imv_right);
            view.setTag(holdel);
        } else {
            holdel = (ViewHoldel) view.getTag();
        }
        holdel.tvContent.setText(mList.get(i).url);
        if (mList.get(i).selected == 1) {
            holdel.imageView.setVisibility(View.VISIBLE);
        } else {
            holdel.imageView.setVisibility(View.GONE);
        }

        return view;
    }

    class ViewHoldel {
        TextView tvContent;
        ImageView imageView;
    }

}
7.url_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:padding="@dimen/activity_horizontal_margin"
              android:orientation="horizontal">

    <TextView
            android:id="@+id/tv_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text=",,"
            android:textSize="15sp"/>
    <ImageView
            android:id="@+id/imv_right"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/icon_true_big"/>
</LinearLayout>

  

重點來了!!!

開發人員接入代碼,替換功能變數名稱:

代碼加入到 Application   初始化方法中;根據需要修改下代碼即可。

private void changeServerHostFromProvider() {
    Uri uri = Uri.parse("content://com.xc1217.contentprovider.myprovider/student");
    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    if (cursor != null) {
        while (cursor.moveToNext()) {
            String url = cursor.getString(cursor.getColumnIndex("url"));
            int selected = cursor.getInt(cursor.getColumnIndex("selected"));// 1 選中,否則未選中
            if (selected == 1 && !TextUtils.isEmpty(url)) {
                Urls.BASE_URL_TEST = url;
                Urls.BASE_URL_ONLINE = url;
                
                return;
            }
        }
        cursor.close();
    }
}

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、前言: 安卓(Android):是一種基於Linux的自由及開放源代碼的操作系統,主要用在移動設備上,如手機、平板電腦。其他的設備也有使用安卓操作系統,比如:電視機,游戲機、數位相機等等。 二、具體: 1、首先需要有開發安卓的環境,安卓SDK。在此本人提供Android4.4的版本下載鏈接:ht ...
  • 前言 學習本系列內容需要具備一定 HTML 開發基礎,沒有基礎的朋友可以先轉至 "HTML快速入門(一)" 學習 本人接觸 React Native 時間並不是特別長,所以對其中的內容和性質瞭解可能會有所偏差,在學習中如果有錯會及時修改內容,也歡迎萬能的朋友們批評指出,謝謝 文章第一版出自簡書,如果 ...
  • Android Weekly閱讀筆記, Issue #231, 本期內容包括: MVP中的View做成passive響應式的, 返回Observable; Android Studio使用技巧; `BottomNavigationView`的使用; App tracking; Kotlin; 用Ko... ...
  • 開發環境: macOS 10.12.1 Xcode 8.1 Qt 5.8 iPhone 6S+iOS 10.1.1 源代碼: 我在 Qt 程式里指定了資料庫的名稱來創建資料庫,在 Win10、Android、macOS 下正常運行,但是在蘋果手機上報錯。 QSqlDatabase db; db.se ...
  • 轉載請標明出處:http://www.cnblogs.com/zhaoyanjun/p/6074887.html1 本文出自 "【趙彥軍的博客】" 反射機制是什麼 反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲 ...
  • - (BOOL) isBlankString:(NSString *)string { if (string == nil || string == NULL) { return YES; } if ([string isKindOfClass:[NSNull class]]) { return Y ...
  • 開源項目 wechat-weapp-gank - 微信小程式版Gank客戶端 wechat-dribbble - 微信小程式-Dribbble wechatApp-demo - 微信小程式 DEMO weapp-ide-crack - 微信小應用資源破解 API - API 文檔 weapp-qui ...
  • 在eclipse中導入android項目,項目正常沒有任何錯誤,但是運行時候會報錯,(clean什麼的都沒用了。。。。。)如圖: 百度大神大多說是jdk的問題,解決: 右鍵項目—Properties如下圖: 我的原來是1.8,換成1.7點擊Apply—ok。解決。 備忘,希望給大家帶來幫助,有錯誤請 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...